feat: refactored the direct children processing

This commit is contained in:
Dylan R. E. Moonfire 2022-06-26 13:50:51 -05:00
parent 74caa6eefd
commit 63ff163fbf
13 changed files with 320 additions and 22 deletions

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
using Gallium;
namespace Nitride.IO.Paths;
/// <summary>
/// A wrapper around List&lt;Entity&gt; for handling direct children lists.
/// </summary>
public class DirectChildEntityList : List<Entity>
{
/// <inheritdoc />
public DirectChildEntityList()
{
}
/// <inheritdoc />
public DirectChildEntityList(IEnumerable<Entity> collection)
: base(collection)
{
}
}

View 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() };
}
}

View 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));
}
}

View file

@ -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(

View file

@ -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();
}
}

View file

@ -11,9 +11,11 @@
</ItemGroup>
<ItemGroup>
<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>

View file

@ -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());
}
}

View 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);
}
}

View 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);
}
}