diff --git a/src/Nitride.IO/IfFoundOutput.cs b/src/Nitride.IO/IfFoundOutput.cs new file mode 100644 index 0000000..1a515ff --- /dev/null +++ b/src/Nitride.IO/IfFoundOutput.cs @@ -0,0 +1,14 @@ +namespace Nitride.IO; + +public enum IfFoundOutput +{ + /// + /// If the entity is found, then remove it from output. + /// + RemoveFromOutput, + + /// + /// If the entity is found, then keep it in the output sequence. + /// + ReturnInOutput, +} diff --git a/src/Nitride.IO/Nitride.IO.csproj b/src/Nitride.IO/Nitride.IO.csproj index 219f01d..5c7e216 100644 --- a/src/Nitride.IO/Nitride.IO.csproj +++ b/src/Nitride.IO/Nitride.IO.csproj @@ -12,6 +12,7 @@ + diff --git a/src/Nitride.IO/NitrideIOEnumerableEntityExtensions.cs b/src/Nitride.IO/NitrideIOEnumerableEntityExtensions.cs new file mode 100644 index 0000000..de75785 --- /dev/null +++ b/src/Nitride.IO/NitrideIOEnumerableEntityExtensions.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; + +using Gallium; + +using MAB.DotIgnore; + +using Zio; + +namespace Nitride.IO; + +/// +/// Extension methods for working with paths. +/// +public static class NitrideIOEnumerableEntityExtensions +{ + /// + /// Retrieves an entity from the sequence of entities. + /// + /// The sequence of iterations to parse. + /// The path to search for. + /// The entity pulled out, if found. Otherwise null. + /// If true, then remove the entity from the list. + /// The sequence of entities, optionally without one. + public static IEnumerable GetEntityByPath( + this IEnumerable input, + UPath path, + out Entity? foundEntity, + IfFoundOutput removeFromResults = IfFoundOutput.RemoveFromOutput) + { + List output = new(); + foundEntity = null; + + foreach (Entity entity in input) + { + // If we don't have a path, it isn't it. + if (!entity.TryGet(out UPath entityPath)) + { + output.Add(entity); + + continue; + } + + // See if the path matches. If it doesn't, then return it. + if (entityPath != path) + { + output.Add(entity); + + continue; + } + + // We found the entity, so optionally return it. + foundEntity = entity; + + if (removeFromResults == IfFoundOutput.ReturnInOutput) + { + output.Add(entity); + } + } + + // Return the resulting output. + return output; + } + + /// + /// Filters out entities that match a .gitignore style list and returns + /// the remaining entities. + /// + public static IEnumerable WhereNotIgnored( + this IEnumerable input, + IgnoreList ignoreList) + { + foreach (Entity entity in input) + { + // If we don't have a path, nothing to do. + if (!entity.TryGet(out UPath path)) + { + yield return entity; + } + + // See if the path matches. We use the "path is directory" set to false + // because Entity represents files, not directories. + string text = path.ToString(); + + if (!ignoreList.IsIgnored(text, false)) + { + yield return entity; + } + } + } +} diff --git a/tests/Nitride.IO.Tests/Paths/GetEntityByPathTests.cs b/tests/Nitride.IO.Tests/Paths/GetEntityByPathTests.cs new file mode 100644 index 0000000..46a82e4 --- /dev/null +++ b/tests/Nitride.IO.Tests/Paths/GetEntityByPathTests.cs @@ -0,0 +1,129 @@ +using System.Linq; + +using Gallium; + +using Nitride.IO.Contents; + +using Xunit; +using Xunit.Abstractions; + +using Zio; + +namespace Nitride.IO.Tests; + +public class GetEntityByPathTests : NitrideIOTestBase +{ + public GetEntityByPathTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void FoundFile() + { + // Set up the test. + using NitrideIOTestContext context = this.CreateContext(); + + // Set up the file. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateFile("/b1.txt"); + fileSystem.CreateFile("/c1.md"); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .GetEntityByPath("/c1.md", out Entity? found) + .Select( + x => x.Get() + .ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/b1.txt", + }, + output); + + Assert.NotNull(found); + Assert.Equal("/c1.md", found!.Get()); + } + + [Fact] + public void FoundFileAndKeep() + { + // Set up the test. + using NitrideIOTestContext context = this.CreateContext(); + + // Set up the file. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateFile("/b1.txt"); + fileSystem.CreateFile("/c1.md"); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .GetEntityByPath("/c1.md", out Entity? found, IfFoundOutput.ReturnInOutput) + .Select( + x => x.Get() + .ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/b1.txt", + "/c1.md", + }, + output); + + Assert.NotNull(found); + Assert.Equal("/c1.md", found!.Get()); + } + + [Fact] + public void NotFoundFile() + { + // Set up the test. + using NitrideIOTestContext context = this.CreateContext(); + + // Set up the file. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateFile("/b1.txt"); + fileSystem.CreateFile("/c1.md"); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .GetEntityByPath("/not-found.md", out Entity? found) + .Select( + x => x.Get() + .ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/b1.txt", + "/c1.md", + }, + output); + + Assert.Null(found); + } +} diff --git a/tests/Nitride.IO.Tests/Paths/WhereNotIgnoredTests.cs b/tests/Nitride.IO.Tests/Paths/WhereNotIgnoredTests.cs new file mode 100644 index 0000000..c599d66 --- /dev/null +++ b/tests/Nitride.IO.Tests/Paths/WhereNotIgnoredTests.cs @@ -0,0 +1,58 @@ +using System.Linq; + +using MAB.DotIgnore; + +using Nitride.IO.Contents; + +using Xunit; +using Xunit.Abstractions; + +using Zio; + +namespace Nitride.IO.Tests; + +public class WhereNotIgnoredTests : NitrideIOTestBase +{ + public WhereNotIgnoredTests(ITestOutputHelper output) + : base(output) + { + } + + [Fact] + public void DoesIgnore() + { + // Set up the test. + using NitrideIOTestContext context = this.CreateContext(); + + // Set up the file. + IFileSystem fileSystem = context.FileSystem; + + fileSystem.CreateFile("/b1.txt"); + fileSystem.CreateFile("/c1.md"); + fileSystem.CreateDirectory("/d"); + fileSystem.CreateFile("/d/b2.txt"); + + // Set up the ignore file. + var ignore = new IgnoreList(new[] { "*.txt" }); + + // Set up the operation. + ReadFiles readFiles = context.Resolve(); + + // Read and replace the paths. + IOrderedEnumerable output = readFiles.WithPattern("/**") + .Run() + .WhereNotIgnored(ignore) + .Select( + x => x.Get() + .ToString()) + .OrderBy(x => x); + + // Verify the results. + Assert.Equal( + new[] + { + "/c1.md", + }, + output); + } +}