Add CultMesh multi-document latest helpers

This commit is contained in:
Meta Vens 2026-06-27 18:23:26 +01:00
commit 7bb6d7da8c
2 changed files with 80 additions and 0 deletions

View file

@ -1675,6 +1675,55 @@ namespace GameCult.Mesh
return new CultMeshDocumentCatalog(documents);
}
/// <summary>
/// Reads two typed document handles and projects them into one caller-defined view.
/// </summary>
public static async Task<TResult> LatestAsync<TFirst, TSecond, TResult>(
CultMeshDocumentHandle<TFirst> first,
CultMeshDocumentHandle<TSecond> second,
Func<TFirst, TSecond, TResult> project)
where TFirst : class
where TSecond : class
{
if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
if (project == null) throw new ArgumentNullException(nameof(project));
var firstTask = first.LatestAsync();
var secondTask = second.LatestAsync();
await Task.WhenAll(firstTask, secondTask).ConfigureAwait(false);
return project(
await firstTask.ConfigureAwait(false),
await secondTask.ConfigureAwait(false));
}
/// <summary>
/// Reads three typed document handles and projects them into one caller-defined view.
/// </summary>
public static async Task<TResult> LatestAsync<TFirst, TSecond, TThird, TResult>(
CultMeshDocumentHandle<TFirst> first,
CultMeshDocumentHandle<TSecond> second,
CultMeshDocumentHandle<TThird> third,
Func<TFirst, TSecond, TThird, TResult> project)
where TFirst : class
where TSecond : class
where TThird : class
{
if (first == null) throw new ArgumentNullException(nameof(first));
if (second == null) throw new ArgumentNullException(nameof(second));
if (third == null) throw new ArgumentNullException(nameof(third));
if (project == null) throw new ArgumentNullException(nameof(project));
var firstTask = first.LatestAsync();
var secondTask = second.LatestAsync();
var thirdTask = third.LatestAsync();
await Task.WhenAll(firstTask, secondTask, thirdTask).ConfigureAwait(false);
return project(
await firstTask.ConfigureAwait(false),
await secondTask.ConfigureAwait(false),
await thirdTask.ConfigureAwait(false));
}
/// <summary>
/// Creates a typed collection handle over all local CultCache records assignable to the document type.
/// </summary>

View file

@ -736,6 +736,37 @@ public sealed class CultMeshStreamingTests
catalog.Latest<MeshNoteAliasDocument>().Text.Should().Be("catalog-primary");
}
[Test]
public async Task DocumentHelpers_ProjectLatestSnapshotsFromMultipleHandles()
{
var primary = CultMesh.Document(
"mesh.note.primary",
CultMesh.Verse("starbridge", "unity-pilot"),
_ => Task.FromResult(new MeshNoteDocument
{
Schema = "tests.mesh_note.v1",
Text = "alpha",
Revision = 3
}),
_ => new Subject<MeshNoteDocument>());
var secondary = CultMesh.Document(
"mesh.note.secondary",
CultMesh.Verse("starbridge", "unity-pilot"),
_ => Task.FromResult(new MeshOtherDocument
{
Schema = "tests.mesh_other.v1",
Text = "beta"
}),
_ => new Subject<MeshOtherDocument>());
var projected = await CultMesh.LatestAsync(
primary,
secondary,
(first, second) => $"{first.Text}:{first.Revision}:{second.Text}");
projected.Should().Be("alpha:3:beta");
}
[Test]
public async Task DocumentHandle_ReadsWatchesAndReplacesCultCacheRecords()
{