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>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Autofac" Version="6.4.0"/>
|
<PackageReference Include="Autofac" Version="6.4.0" />
|
||||||
<PackageReference Include="DotNet.Glob" Version="3.1.3"/>
|
<PackageReference Include="DotNet.Glob" Version="3.1.3" />
|
||||||
<PackageReference Include="FluentValidation" Version="11.0.2"/>
|
<PackageReference Include="FluentValidation" Version="11.0.2" />
|
||||||
<PackageReference Include="Gallium" Version="1.0.2"/>
|
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||||
<PackageReference Include="Serilog" Version="2.11.0"/>
|
<PackageReference Include="Serilog" Version="2.11.0" />
|
||||||
<PackageReference Include="Zio" Version="0.15.0"/>
|
<PackageReference Include="Zio" Version="0.15.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\Nitride\Nitride.csproj"/>
|
<ProjectReference Include="..\Nitride\Nitride.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- Include the source generator -->
|
<!-- 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>
|
/// <summary>
|
||||||
/// Creates an index for a given key. This will not be called for any
|
/// 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>
|
/// </summary>
|
||||||
public Func<string, IList<Entity>, Entity> CreateIndex { get; set; } = null!;
|
public Func<string, IList<Entity>, Entity>? CreateIndex { get; set; } = null!;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the function to retrieve the key from an existing
|
/// 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.
|
// 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]);
|
yield return this.CreateIndex(key, scanned[key]);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Report the results.
|
// Report the results.
|
||||||
this.logger.Debug(
|
this.logger.Debug(
|
||||||
|
|
|
@ -8,7 +8,6 @@ public class CreateOrUpdateIndexValidator : AbstractValidator<CreateOrUpdateInde
|
||||||
{
|
{
|
||||||
this.RuleFor(x => x.Scanner).NotNull();
|
this.RuleFor(x => x.Scanner).NotNull();
|
||||||
this.RuleFor(x => x.GetIndexKey).NotNull();
|
this.RuleFor(x => x.GetIndexKey).NotNull();
|
||||||
this.RuleFor(x => x.CreateIndex).NotNull();
|
|
||||||
this.RuleFor(x => x.UpdateIndex).NotNull();
|
this.RuleFor(x => x.UpdateIndex).NotNull();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,20 +6,22 @@
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\..\src\Nitride.IO\Nitride.IO.csproj"/>
|
<ProjectReference Include="..\..\src\Nitride.IO\Nitride.IO.csproj" />
|
||||||
<ProjectReference Include="..\Nitride.Tests\Nitride.Tests.csproj"/>
|
<ProjectReference Include="..\Nitride.Tests\Nitride.Tests.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Gallium" Version="1.0.2"/>
|
<PackageReference Include="CompareNETObjects" Version="4.77.0" />
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0"/>
|
<PackageReference Include="Gallium" Version="1.0.2" />
|
||||||
<PackageReference Include="JunitXml.TestLogger" Version="3.0.114"/>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.2.0" />
|
||||||
<PackageReference Include="xunit" Version="2.4.1"/>
|
<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">
|
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
</PackageReference>
|
</PackageReference>
|
||||||
<PackageReference Include="Zio" Version="0.15.0"/>
|
<PackageReference Include="Zio" Version="0.15.0" />
|
||||||
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
<PackageReference Include="coverlet.collector" Version="3.1.2">
|
||||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||||
<PrivateAssets>all</PrivateAssets>
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
|
|
@ -1,6 +1,14 @@
|
||||||
|
using System;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
using KellermanSoftware.CompareNetObjects;
|
||||||
|
|
||||||
using MfGames.TestSetup;
|
using MfGames.TestSetup;
|
||||||
|
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
using Xunit.Abstractions;
|
using Xunit.Abstractions;
|
||||||
|
using Xunit.Sdk;
|
||||||
|
|
||||||
namespace Nitride.IO.Tests;
|
namespace Nitride.IO.Tests;
|
||||||
|
|
||||||
|
@ -10,4 +18,39 @@ public abstract class NitrideIOTestBase : TestBase<NitrideIOTestContext>
|
||||||
: base(output)
|
: 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