feat: refactored the direct children processing
This commit is contained in:
parent
74caa6eefd
commit
63ff163fbf
13 changed files with 320 additions and 22 deletions
|
@ -8,16 +8,16 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Autofac" Version="6.4.0"/>
|
||||
<PackageReference Include="DotNet.Glob" Version="3.1.3"/>
|
||||
<PackageReference Include="FluentValidation" Version="11.0.2"/>
|
||||
<PackageReference Include="Gallium" Version="1.0.2"/>
|
||||
<PackageReference Include="Serilog" Version="2.11.0"/>
|
||||
<PackageReference Include="Zio" Version="0.15.0"/>
|
||||
<PackageReference Include="Autofac" Version="6.4.0" />
|
||||
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
||||
<PackageReference Include="FluentValidation" Version="11.0.2" />
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||
<PackageReference Include="Zio" Version="0.15.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj"/>
|
||||
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<!-- Include the source generator -->
|
||||
|
|
22
src/Nitride.IO/Paths/DirectChildEntityList.cs
Normal file
22
src/Nitride.IO/Paths/DirectChildEntityList.cs
Normal file
|
@ -0,0 +1,22 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using Gallium;
|
||||
|
||||
namespace Nitride.IO.Paths;
|
||||
|
||||
/// <summary>
|
||||
/// A wrapper around List<Entity> for handling direct children lists.
|
||||
/// </summary>
|
||||
public class DirectChildEntityList : List<Entity>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DirectChildEntityList()
|
||||
{
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public DirectChildEntityList(IEnumerable<Entity> collection)
|
||||
: base(collection)
|
||||
{
|
||||
}
|
||||
}
|
46
src/Nitride.IO/Paths/DirectChildPathScanner.cs
Normal file
46
src/Nitride.IO/Paths/DirectChildPathScanner.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
using FluentValidation;
|
||||
|
||||
using Gallium;
|
||||
|
||||
using Nitride.Entities;
|
||||
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths;
|
||||
|
||||
/// <summary>
|
||||
/// Implements a scanner that gathers up all the direct child files
|
||||
/// and folders underneath a given path.
|
||||
/// </summary>
|
||||
[WithProperties]
|
||||
public partial class DirectChildPathScanner : EntityScanner
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public DirectChildPathScanner(IValidator<EntityScanner> validator)
|
||||
: base(validator)
|
||||
{
|
||||
this.GetKeysFromEntity = this.InternalGetKeysFromEntity;
|
||||
}
|
||||
|
||||
private IEnumerable<string>? InternalGetKeysFromEntity(Entity entity)
|
||||
{
|
||||
// Get the path for the entity. If we don't have a path, then there
|
||||
// is nothing to do.
|
||||
if (!entity.TryGet(out UPath path))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we are a root path, then we don't have a parent.
|
||||
if (path.IsEmpty || path.IsNull || path == "/")
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
// If we are using directory indexes, skip when we have an index root.
|
||||
// Otherwise, get the parent and use that as the key.
|
||||
return path.GetDirectoryIndexPath() == "/" ? null : new[] { path.GetParentDirectoryIndexPath() };
|
||||
}
|
||||
}
|
46
src/Nitride.IO/Paths/LinkDirectChildren.cs
Normal file
46
src/Nitride.IO/Paths/LinkDirectChildren.cs
Normal file
|
@ -0,0 +1,46 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using FluentValidation;
|
||||
|
||||
using Gallium;
|
||||
|
||||
using Nitride.Entities;
|
||||
|
||||
using Serilog;
|
||||
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Paths;
|
||||
|
||||
[WithProperties]
|
||||
public partial class LinkDirectChildren : CreateOrUpdateIndex
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public LinkDirectChildren(ILogger logger, IValidator<CreateOrUpdateIndex> validator)
|
||||
: base(logger, validator)
|
||||
{
|
||||
this.UpdateIndex = this.InternalUpdateIndex;
|
||||
this.GetIndexKey = e => e.Get<UPath>().GetDirectoryIndexPath();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override IEnumerable<Entity> Run(IEnumerable<Entity> input)
|
||||
{
|
||||
if (this.Scanner != null!)
|
||||
{
|
||||
return base.Run(input);
|
||||
}
|
||||
|
||||
this.Scanner = new DirectChildPathScanner(new EntityScannerValidator());
|
||||
|
||||
input = this.Scanner.Run(input).ToList();
|
||||
|
||||
return base.Run(input);
|
||||
}
|
||||
|
||||
private Entity InternalUpdateIndex(Entity entity, string _, IEnumerable<Entity> list)
|
||||
{
|
||||
return entity.Add(new DirectChildEntityList(list));
|
||||
}
|
||||
}
|
|
@ -36,9 +36,10 @@ public partial class CreateOrUpdateIndex : OperationBase
|
|||
|
||||
/// <summary>
|
||||
/// Creates an index for a given key. This will not be called for any
|
||||
/// index that has been already created.
|
||||
/// index that has been already created. If this is null, no new indexes
|
||||
/// will be made.
|
||||
/// </summary>
|
||||
public Func<string, IList<Entity>, Entity> CreateIndex { get; set; } = null!;
|
||||
public Func<string, IList<Entity>, Entity>? CreateIndex { get; set; } = null!;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the function to retrieve the key from an existing
|
||||
|
@ -94,15 +95,13 @@ public partial class CreateOrUpdateIndex : OperationBase
|
|||
}
|
||||
|
||||
// Once we're done with the list, we need to create the missing indexes.
|
||||
foreach (string? key in scanned.Keys)
|
||||
if (this.CreateIndex != null)
|
||||
{
|
||||
if (existing.Contains(key))
|
||||
foreach (string key in scanned.Keys.Where(key => !existing.Contains(key)))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
yield return this.CreateIndex(key, scanned[key]);
|
||||
}
|
||||
}
|
||||
|
||||
// Report the results.
|
||||
this.logger.Debug(
|
||||
|
|
|
@ -8,7 +8,6 @@ public class CreateOrUpdateIndexValidator : AbstractValidator<CreateOrUpdateInde
|
|||
{
|
||||
this.RuleFor(x => x.Scanner).NotNull();
|
||||
this.RuleFor(x => x.GetIndexKey).NotNull();
|
||||
this.RuleFor(x => x.CreateIndex).NotNull();
|
||||
this.RuleFor(x => x.UpdateIndex).NotNull();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,20 +6,22 @@
|
|||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\src\Nitride.IO\Nitride.IO.csproj"/>
|
||||
<ProjectReference Include="..\Nitride.Tests\Nitride.Tests.csproj"/>
|
||||
<ProjectReference Include="..\..\src\Nitride.IO\Nitride.IO.csproj" />
|
||||
<ProjectReference Include="..\Nitride.Tests\Nitride.Tests.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Gallium" Version="1.0.2"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0"/>
|
||||
<PackageReference Include="JunitXml.TestLogger" Version="3.0.114"/>
|
||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
||||
<PackageReference Include="CompareNETObjects" Version="4.77.0" />
|
||||
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||
<PackageReference Include="JunitXml.TestLogger" Version="3.0.114" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="xunit" Version="2.4.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Zio" Version="0.15.0"/>
|
||||
<PackageReference Include="Zio" Version="0.15.0" />
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
|
|
|
@ -1,6 +1,14 @@
|
|||
using System;
|
||||
using System.Text;
|
||||
|
||||
using KellermanSoftware.CompareNetObjects;
|
||||
|
||||
using MfGames.TestSetup;
|
||||
|
||||
using Newtonsoft.Json;
|
||||
|
||||
using Xunit.Abstractions;
|
||||
using Xunit.Sdk;
|
||||
|
||||
namespace Nitride.IO.Tests;
|
||||
|
||||
|
@ -10,4 +18,39 @@ public abstract class NitrideIOTestBase : TestBase<NitrideIOTestContext>
|
|||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
protected void CompareObjects<T>(T expected, T actual)
|
||||
where T : class
|
||||
{
|
||||
CompareLogic compare = new()
|
||||
{
|
||||
Config =
|
||||
{
|
||||
MaxDifferences = int.MaxValue,
|
||||
},
|
||||
};
|
||||
ComparisonResult comparison = compare.Compare(expected, actual);
|
||||
|
||||
if (comparison.AreEqual)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Format the error message.
|
||||
StringBuilder message = new();
|
||||
|
||||
message.AppendLine("# Expected");
|
||||
message.AppendLine();
|
||||
message.AppendLine(JsonConvert.SerializeObject(expected, Formatting.Indented));
|
||||
message.AppendLine();
|
||||
message.Append("# Actual");
|
||||
message.AppendLine();
|
||||
message.AppendLine(JsonConvert.SerializeObject(actual, Formatting.Indented));
|
||||
message.AppendLine();
|
||||
message.Append("# Results");
|
||||
message.AppendLine();
|
||||
message.AppendLine(comparison.DifferencesString);
|
||||
|
||||
throw new XunitException(message.ToString());
|
||||
}
|
||||
}
|
||||
|
|
64
tests/Nitride.IO.Tests/Paths/DirectChildPathScannerTests.cs
Normal file
64
tests/Nitride.IO.Tests/Paths/DirectChildPathScannerTests.cs
Normal file
|
@ -0,0 +1,64 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
using Nitride.IO.Contents;
|
||||
using Nitride.IO.Paths;
|
||||
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Tests;
|
||||
|
||||
public class DirectChildPathScannerTests : NitrideIOTestBase
|
||||
{
|
||||
public DirectChildPathScannerTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedFoldersTests()
|
||||
{
|
||||
// Set up the test.
|
||||
using NitrideIOTestContext context = this.CreateContext();
|
||||
|
||||
// Set up the file.
|
||||
IFileSystem fileSystem = context.FileSystem;
|
||||
|
||||
fileSystem.CreateDirectory("/a");
|
||||
fileSystem.CreateDirectory("/b");
|
||||
fileSystem.CreateDirectory("/a/c");
|
||||
fileSystem.CreateDirectory("/a/d");
|
||||
fileSystem.CreateDirectory("/a/d/e");
|
||||
fileSystem.CreateFile("/index.md");
|
||||
fileSystem.CreateFile("/a/index.md");
|
||||
fileSystem.CreateFile("/b/index.md");
|
||||
fileSystem.CreateFile("/a/c/index.md");
|
||||
fileSystem.CreateFile("/a/d/index.md");
|
||||
fileSystem.CreateFile("/a/d/e/index.md");
|
||||
|
||||
// Set up the operation.
|
||||
ReadFiles readFiles = context.Resolve<ReadFiles>();
|
||||
DirectChildPathScanner op = context.Resolve<DirectChildPathScanner>();
|
||||
|
||||
// Read and replace the paths.
|
||||
var _ = readFiles.WithPattern("/**").Run().Run(op).ToList();
|
||||
KeyValuePair<string, string[]>[] actual = op.GetScannedResults()
|
||||
.ToDictionary(x => x.Key, x => x.Value.Select(y => y.Get<UPath>().ToString()).ToArray())
|
||||
.ToList()
|
||||
.OrderBy(x => x.Key)
|
||||
.ToArray();
|
||||
|
||||
// Verify the results.
|
||||
KeyValuePair<string, string[]>[] expected = new[]
|
||||
{
|
||||
new KeyValuePair<string, string[]>("/", new[] { "/a/index.md", "/b/index.md" }),
|
||||
new KeyValuePair<string, string[]>("/a/", new[] { "/a/c/index.md", "/a/d/index.md" }),
|
||||
new KeyValuePair<string, string[]>("/a/d/", new[] { "/a/d/e/index.md" }),
|
||||
};
|
||||
|
||||
CompareObjects(expected, actual);
|
||||
}
|
||||
}
|
77
tests/Nitride.IO.Tests/Paths/LinkDirectChildrenTests.cs
Normal file
77
tests/Nitride.IO.Tests/Paths/LinkDirectChildrenTests.cs
Normal file
|
@ -0,0 +1,77 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
|
||||
using Nitride.Entities;
|
||||
using Nitride.IO.Contents;
|
||||
using Nitride.IO.Paths;
|
||||
|
||||
using Xunit;
|
||||
using Xunit.Abstractions;
|
||||
|
||||
using Zio;
|
||||
|
||||
namespace Nitride.IO.Tests;
|
||||
|
||||
public class LinkDirectChildrenTests : NitrideIOTestBase
|
||||
{
|
||||
public LinkDirectChildrenTests(ITestOutputHelper output)
|
||||
: base(output)
|
||||
{
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NestedLinkTest()
|
||||
{
|
||||
// Set up the test.
|
||||
using NitrideIOTestContext context = this.CreateContext();
|
||||
|
||||
// Set up the file.
|
||||
IFileSystem fileSystem = context.FileSystem;
|
||||
|
||||
fileSystem.CreateDirectory("/a");
|
||||
fileSystem.CreateDirectory("/b");
|
||||
fileSystem.CreateDirectory("/a/c");
|
||||
fileSystem.CreateDirectory("/a/d");
|
||||
fileSystem.CreateDirectory("/a/d/e");
|
||||
fileSystem.CreateFile("/index.md");
|
||||
fileSystem.CreateFile("/a/index.md");
|
||||
fileSystem.CreateFile("/b/index.md");
|
||||
fileSystem.CreateFile("/a/c/index.md");
|
||||
fileSystem.CreateFile("/a/d/index.md");
|
||||
fileSystem.CreateFile("/a/d/e/index.md");
|
||||
|
||||
// Set up the operation.
|
||||
ReadFiles readFiles = context.Resolve<ReadFiles>();
|
||||
DirectChildPathScanner scanner = context.Resolve<DirectChildPathScanner>();
|
||||
CreateOrUpdateIndex op = context.Resolve<LinkDirectChildren>().WithScanner(scanner);
|
||||
|
||||
// Read and replace the paths.
|
||||
Tuple<string, string[]?>[]? actual = readFiles.WithPattern("/**")
|
||||
.Run()
|
||||
.Run(scanner)
|
||||
.ToList()
|
||||
.Run(op)
|
||||
.Select(
|
||||
x => new Tuple<string, string[]?>(
|
||||
x.Get<UPath>().ToString(),
|
||||
x.GetOptional<DirectChildEntityList>()
|
||||
?.Select(y => y.Get<UPath>().ToString())
|
||||
.OrderBy(y => y)
|
||||
.ToArray()))
|
||||
.OrderBy(x => x.Item1)
|
||||
.ToArray();
|
||||
|
||||
// Verify the results.
|
||||
Tuple<string, string[]?>[] expected = new[]
|
||||
{
|
||||
new Tuple<string, string[]?>("/a/c/index.md", new string[] { }),
|
||||
new Tuple<string, string[]?>("/a/d/e/index.md", new string[] { }),
|
||||
new Tuple<string, string[]?>("/a/d/index.md", new[] { "/a/d/e/index.md" }),
|
||||
new Tuple<string, string[]?>("/a/index.md", new[] { "/a/c/index.md", "/a/d/index.md" }),
|
||||
new Tuple<string, string[]?>("/b/index.md", new string[] { }),
|
||||
new Tuple<string, string[]?>("/index.md", new[] { "/a/index.md", "/b/index.md" }),
|
||||
};
|
||||
|
||||
this.CompareObjects(expected, actual);
|
||||
}
|
||||
}
|
Reference in a new issue