Skip to content

Commit 3b0dc2e

Browse files
committed
Added HasExtension to do a case-insensitive check for an extension (ignoring the dot).
1 parent 08b6f7d commit 3b0dc2e

File tree

9 files changed

+71
-21
lines changed

9 files changed

+71
-21
lines changed

Pathy.ApiVerificationTests/ApprovedApi/pathy.net47.verified.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace Pathy
1919
public static Pathy.ChainablePath Empty { get; }
2020
public static Pathy.ChainablePath New { get; }
2121
public static Pathy.ChainablePath Temp { get; }
22+
public bool HasExtension(string extension) { }
2223
public System.IO.DirectoryInfo ToDirectoryInfo() { }
2324
public System.IO.FileInfo ToFileInfo() { }
2425
public override string ToString() { }

Pathy.ApiVerificationTests/ApprovedApi/pathy.net8.0.verified.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace Pathy
2020
public static Pathy.ChainablePath New { get; }
2121
public static Pathy.ChainablePath Temp { get; }
2222
public Pathy.ChainablePath AsRelativeTo(Pathy.ChainablePath basePath) { }
23+
public bool HasExtension(string extension) { }
2324
public System.IO.DirectoryInfo ToDirectoryInfo() { }
2425
public System.IO.FileInfo ToFileInfo() { }
2526
public override string ToString() { }

Pathy.ApiVerificationTests/ApprovedApi/pathy.netstandard2.0.verified.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ namespace Pathy
1919
public static Pathy.ChainablePath Empty { get; }
2020
public static Pathy.ChainablePath New { get; }
2121
public static Pathy.ChainablePath Temp { get; }
22+
public bool HasExtension(string extension) { }
2223
public System.IO.DirectoryInfo ToDirectoryInfo() { }
2324
public System.IO.FileInfo ToFileInfo() { }
2425
public override string ToString() { }

Pathy.ApiVerificationTests/ApprovedApi/pathy.netstandard2.1.verified.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ namespace Pathy
2020
public static Pathy.ChainablePath New { get; }
2121
public static Pathy.ChainablePath Temp { get; }
2222
public Pathy.ChainablePath AsRelativeTo(Pathy.ChainablePath basePath) { }
23+
public bool HasExtension(string extension) { }
2324
public System.IO.DirectoryInfo ToDirectoryInfo() { }
2425
public System.IO.FileInfo ToFileInfo() { }
2526
public override string ToString() { }

Pathy.ApiVerificationTests/Pathy.ApiVerificationTests.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<ItemGroup>
88
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
99
<PackageReference Include="xunit" Version="2.9.3" />
10-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.2">
10+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
1111
<PrivateAssets>all</PrivateAssets>
1212
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
1313
</PackageReference>

Pathy.Specs/ChainablePathSpecs.cs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
using System;
22
using System.IO;
33
using System.Reflection;
4+
using System.Runtime.InteropServices;
45
using FluentAssertions;
56
using Xunit;
67

@@ -348,6 +349,35 @@ public void Can_add_an_extension()
348349
path.Extension.Should().Be(".txt");
349350
}
350351

352+
[Theory]
353+
[InlineData(".txt", true)]
354+
[InlineData(".TXT", true)]
355+
[InlineData("TXT", true)]
356+
[InlineData("DOC", false)]
357+
public void Can_check_for_an_extension(string extension, bool shouldMatch)
358+
{
359+
// Act
360+
var path = ChainablePath.Temp / "SomeFile.txt";
361+
362+
// Assert
363+
path.HasExtension(extension).Should().Be(shouldMatch);
364+
}
365+
366+
[Theory]
367+
[InlineData(null)]
368+
[InlineData("")]
369+
public void Checking_for_an_extension_requires_a_valid_extension(string extension)
370+
{
371+
// Arrange
372+
var path = ChainablePath.Temp / "SomeFile.txt";
373+
374+
// Act
375+
Action act = () => path.HasExtension(extension);
376+
377+
// Assert
378+
act.Should().Throw<ArgumentException>("*null*empty*");
379+
}
380+
351381
#if NET6_0_OR_GREATER
352382
[Fact]
353383
public void Can_get_the_difference_as_a_relative_path()

Pathy.Specs/Pathy.Specs.csproj

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,9 @@
88
<IsPackable>false</IsPackable>
99
</PropertyGroup>
1010

11-
<ItemGroup Condition="'$(TargetFramework)' == 'net472'">
12-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.2">
13-
<PrivateAssets>all</PrivateAssets>
14-
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
15-
</PackageReference>
16-
</ItemGroup>
17-
1811
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0'">
1912
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
20-
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.2">
13+
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.3">
2114
<PrivateAssets>all</PrivateAssets>
2215
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
2316
</PackageReference>

Pathy/ChainablePath.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -319,6 +319,28 @@ public FileInfo ToFileInfo()
319319
{
320320
return new FileInfo(ToString());
321321
}
322+
323+
/// <summary>
324+
/// Determines if the current path has the specified file extension.
325+
/// </summary>
326+
/// <param name="extension">
327+
/// The file extension to check for, with or without the leading period (e.g., ".txt").
328+
/// </param>
329+
public bool HasExtension(string extension)
330+
{
331+
if (string.IsNullOrEmpty(extension))
332+
{
333+
throw new ArgumentException("Extension cannot be null or empty", nameof(extension));
334+
}
335+
336+
// Ensure the extension starts with a dot
337+
if (!extension.StartsWith(".", StringComparison.InvariantCulture))
338+
{
339+
extension = '.' + extension;
340+
}
341+
342+
return string.Equals(Extension, extension, StringComparison.OrdinalIgnoreCase);
343+
}
322344
}
323345

324346
#if PATHY_PUBLIC

README.md

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<h1 align="center">
22
<br>
3-
<img src="./logo.png" style="width:300px" alt="Pathy"/>
3+
<img src="./logo.png" style="width:300px" alt="Pathy"/>
44
<br>
55
</h1>
66

@@ -39,19 +39,19 @@
3939

4040
### What's this?
4141

42-
Pathy is a tiny source-only library that will allow you to build file and directory paths by chaining together strings like `"c:"`, `"dir1"`, `"dir2"` using
42+
Pathy is a tiny source-only library that will allow you to build file and directory paths by chaining together strings like `"c:"`, `"dir1"`, `"dir2"` using
4343

44-
`ChainablePath.New() / "c:" / "dir1" / "dir2"`.
44+
`ChainablePath.New() / "c:" / "dir1" / "dir2"`.
4545

4646
It was heavily inspired by the best build pipeline framework available in the .NET space, [Nuke](https://nuke.build/). Nuke has supported these concepts for many years, but I needed this capability outside build pipelines. Lots of kudos to [Matthias Koch](https://www.linkedin.com/in/matthias-koch-jb/) for what I see as a brilliant idea.
4747

48-
It doesn't have any dependencies and runs on .NET 4.7, .NET 8, as well as frameworks supporting .NET Standard 2.0 and 2.1.
48+
It doesn't have any dependencies and runs on .NET 4.7, .NET 8, as well as frameworks supporting .NET Standard 2.0 and 2.1.
4949

5050
### What's so special about that?
5151

52-
It makes those chained calls to `Path.Combine` a thing from the past and hides the ugliness of dealing with (trailing) slashes.
52+
It makes those chained calls to `Path.Combine` a thing from the past and hides the ugliness of dealing with (trailing) slashes.
5353

54-
It ships as a source-only package, which means you can use it in your own libraries and projects, without incurring any dependency pain on your consuming projects.
54+
It ships as a source-only package, which means you can use it in your own libraries and projects, without incurring any dependency pain on your consuming projects.
5555

5656
The core Pathy package does not have any dependencies, and I purposely moved the [globbing](https://learn.microsoft.com/en-us/dotnet/core/extensions/file-globbing#pattern-formats) functionality into a separate package as it depends on `Microsoft.Extensions.FileSystemGlobbing`.
5757

@@ -69,7 +69,7 @@ This library is available as [a NuGet package](https://www.nuget.org/packages/Pa
6969
## How do I use it?
7070

7171
### To ChainablePath and back to string
72-
It all starts with the construction of a `ChainablePath` instance to represent a path to a file or directory.
72+
It all starts with the construction of a `ChainablePath` instance to represent a path to a file or directory.
7373

7474
There are several ways of doing that.
7575

@@ -80,11 +80,11 @@ var path = "c:/mypath/to/a/directory".ToPath();
8080
var path = (ChainablePath)"c:/mypath/to/a/directory";
8181
```
8282

83-
Additionally, you can use `ChainablePath.Current` to get the current working directory as an instance of `ChainablePath`, and `ChainablePath.Temp` to get that for the user's temporary folder.
83+
Additionally, you can use `ChainablePath.Current` to get the current working directory as an instance of `ChainablePath`, and `ChainablePath.Temp` to get that for the user's temporary folder.
8484

85-
The first thing you'll notice is how the `/` operator is used to chain multiple parts of a path together. This is the primary feature of Pathy. And it doesn't matter if you do that on Linux or Windows. Internally it'll use whatever path separator is suitable.
85+
The first thing you'll notice is how the `/` operator is used to chain multiple parts of a path together. This is the primary feature of Pathy. And it doesn't matter if you do that on Linux or Windows. Internally it'll use whatever path separator is suitable.
8686

87-
You can also use the `+` operator to add some phrase to the path _without_ using a separator.
87+
You can also use the `+` operator to add some phrase to the path _without_ using a separator.
8888

8989
```csharp
9090
var path = ChainablePath.From("c:") / "my-path" / "to" / "a" / "directory";
@@ -95,7 +95,7 @@ path = path + "2"
9595
string result = path.ToString();
9696
```
9797

98-
To convert an instance of `ChainablePath` back to a `string`, you can either call `ToString()` or cast the instance to a `string`.
98+
To convert an instance of `ChainablePath` back to a `string`, you can either call `ToString()` or cast the instance to a `string`.
9999

100100
```csharp
101101
string rawPath = path.ToString();
@@ -108,10 +108,11 @@ Know that `ChainablePath` overrides `Equals` and `GetHashCode`, so you can alway
108108

109109
Given an instance of `ChainablePath`, you can get a lot of useful information:
110110
* `Name` returns the full name, but without the directory, whereas `Extension` gives you the extension _including_ the dot.
111-
* `Directory`, `Parent` or `DirectoryName` give you the (parent) directory of a file or directory.
111+
* `Directory`, `Parent` or `DirectoryName` give you the (parent) directory of a file or directory.
112112
* To see if a path is absolute, use `IsRooted`
113113
* Not sure if a path points to an actual file system entry? Use `IsFile`, `IsDirectory` or `Exists`
114114
* Want to know the delta between two paths? Use `AsRelativeTo`.
115+
* To determine if a file has a case-insensitive extension, use `HasExtension(".txt")` or `HasExtension("txt")`.
115116

116117
And if the built-in functionality really isn't enough, you can always call `ToDirectoryInfo` or `ToFileInfo` to continue with an instance of `DirectoryInfo` and `FileInfo`.
117118

0 commit comments

Comments
 (0)