Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ namespace Pathy
public static class ChainablePathExtensions
{
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, string globPattern) { }
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, params string[] globPatterns) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ namespace Pathy
public static class ChainablePathExtensions
{
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, string globPattern) { }
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, params string[] globPatterns) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ namespace Pathy
public static class ChainablePathExtensions
{
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, string globPattern) { }
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, params string[] globPatterns) { }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,6 @@ namespace Pathy
public static class ChainablePathExtensions
{
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, string globPattern) { }
public static Pathy.ChainablePath[] GlobFiles(this Pathy.ChainablePath path, params string[] globPatterns) { }
}
}
33 changes: 32 additions & 1 deletion Pathy.Globbing/PathyGlobbing.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,39 @@ internal static class ChainablePathExtensions
/// <param name="globPattern">The glob pattern used to match file paths, e.g. **/*.md or dir/**/*</param>
public static ChainablePath[] GlobFiles(this ChainablePath path, string globPattern)
{
return GlobFiles(path, new[] { globPattern });
}

/// <summary>
/// Matches files in the specified directory or subdirectories according to the provided glob patterns
/// and returns them as an array of <see cref="ChainablePath"/> objects.
/// </summary>
/// <remarks>
/// See also <seealso href="https://learn.microsoft.com/en-us/dotnet/core/extensions/file-globbing"/>
/// </remarks>
/// <param name="path">The base directory path to start the glob search from.</param>
/// <param name="globPatterns">One or more glob patterns used to match file paths, e.g. **/*.md or dir/**/*</param>
/// <exception cref="ArgumentException">Thrown if no glob patterns are provided or if any pattern is null or empty.</exception>
public static ChainablePath[] GlobFiles(this ChainablePath path, params string[] globPatterns)
{
if (globPatterns == null || globPatterns.Length == 0)
{
throw new ArgumentException("At least one glob pattern must be provided", nameof(globPatterns));
}

foreach (string pattern in globPatterns)
{
if (string.IsNullOrWhiteSpace(pattern))
{
throw new ArgumentException("Glob patterns cannot be null or empty", nameof(globPatterns));
}
}

Matcher matcher = new(StringComparison.OrdinalIgnoreCase);
matcher.AddInclude(globPattern);
foreach (string pattern in globPatterns)
{
matcher.AddInclude(pattern);
}

return matcher
.Execute(new DirectoryInfoWrapper(path.ToDirectoryInfo()))
Expand Down
92 changes: 92 additions & 0 deletions Pathy.Specs/ChainablePathSpecs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,98 @@ public void Can_find_files_using_globbing_patterns()
], options => options.ComparingRecordsByValue());
}

[Fact]
public void Can_find_files_using_multiple_globbing_patterns()
{
// Arrange
var temp = ChainablePath.Temp / "dir1" / "dir2" / "dir3" / "file.txt";

temp.CreateDirectoryRecursively();
File.WriteAllText(temp / "file.txt", "Hello World!");
File.WriteAllText(temp / "file2.txt", "Hello World!");
File.WriteAllText(temp / "file3.doc", "Hello World!");
File.WriteAllText(temp / "file4.md", "# Markdown");

// Act
var files = (ChainablePath.Temp / "dir1").GlobFiles("**/*.txt", "**/*.doc");

// Assert
files.Should().BeEquivalentTo([
temp / "file.txt",
temp / "file2.txt",
temp / "file3.doc"
], options => options.ComparingRecordsByValue());
}

[Fact]
public void Can_find_files_using_multiple_globbing_patterns_with_different_depths()
{
// Arrange
var baseDir = testFolder / "MultiPatternTest";
var deepDir = baseDir / "level1" / "level2";
deepDir.CreateDirectoryRecursively();

File.WriteAllText(baseDir / "root.txt", "Root");
File.WriteAllText(baseDir / "root.md", "# Root");
File.WriteAllText(deepDir / "deep.txt", "Deep");
File.WriteAllText(deepDir / "deep.json", "{}");

// Act
var files = baseDir.GlobFiles("**/*.txt", "**/*.json");

// Assert
files.Should().BeEquivalentTo([
baseDir / "root.txt",
deepDir / "deep.txt",
deepDir / "deep.json"
], options => options.ComparingRecordsByValue());
}

[Fact]
public void GlobFiles_with_multiple_patterns_throws_when_no_patterns_provided()
{
// Arrange
var temp = ChainablePath.Temp / "dir1";
temp.CreateDirectoryRecursively();

// Act & Assert
var act = () => temp.GlobFiles(new string[0]);

act.Should().Throw<ArgumentException>()
.WithMessage("*At least one glob pattern must be provided*")
.WithParameterName("globPatterns");
}

[Fact]
public void GlobFiles_with_multiple_patterns_throws_when_pattern_is_null()
{
// Arrange
var temp = ChainablePath.Temp / "dir1";
temp.CreateDirectoryRecursively();

// Act & Assert
var act = () => temp.GlobFiles("**/*.txt", null, "**/*.doc");

act.Should().Throw<ArgumentException>()
.WithMessage("*Glob patterns cannot be null or empty*")
.WithParameterName("globPatterns");
}

[Fact]
public void GlobFiles_with_multiple_patterns_throws_when_pattern_is_empty()
{
// Arrange
var temp = ChainablePath.Temp / "dir1";
temp.CreateDirectoryRecursively();

// Act & Assert
var act = () => temp.GlobFiles("**/*.txt", "", "**/*.doc");

act.Should().Throw<ArgumentException>()
.WithMessage("*Glob patterns cannot be null or empty*")
.WithParameterName("globPatterns");
}

[Fact]
public void Can_convert_to_directory_info()
{
Expand Down
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,11 @@ If you add the `Pathy.Globbing` NuGet source-only package as well, you'll get ac


```csharp
// Match files with a single pattern
ChainablePath[] files = (ChainablePath.Current / "Artifacts").GlobFiles("**/*.json");

// Match files with multiple patterns
ChainablePath[] files = (ChainablePath.Current / "Artifacts").GlobFiles("**/*.txt", "**/*.md", "**/*.json");
```

### File system operations
Expand Down