Editor-side catalog management types. Defines ArchShelf role identifiers, the IArchCatalog interface, an ArchCatalogs table that aggregates specific catalog implementations, and helpers to list shipped/authored entries and merge/save catalog items.
namespace Sunless.Architecture;
// Which shelf a catalog stands on. A role rather than an interface per catalog, because the catalogs differ only in
// what they hold - a module brings its own with new ArchShelf( role ), and the reservation only stops two of them
// meaning the same thing.
public readonly record struct ArchShelf( string Role )
{
public static readonly ArchShelf Openings = new( "openings" );
public static readonly ArchShelf Walls = new( "walls" );
public static readonly ArchShelf Pillars = new( "pillars" );
public static readonly ArchShelf RoadLines = new( "roadlines" );
public static readonly ArchShelf Profiles = new( "profiles" );
public static readonly ArchShelf Types = new( "types" );
public override string ToString() => Role;
}
// One catalog of authored types, owned by the module whose subtool authors them. The TYPE itself stays in core: a
// porch fits an opening preset it never authored, so a preset is shared geometry by the same rule a payload is,
// while the catalog around it belongs to one module alone.
public interface IArchCatalog
{
ArchShelf Shelf { get; }
string Named( object entry );
// What the module brings with it; an authored entry of the same name wins, so a tuned preset survives upgrades.
IEnumerable<object> Shipped();
// What stands on disk beside it, which tops what ships.
IEnumerable<object> Authored();
bool Save( object entry );
}
public sealed class ArchCatalogs : ArchTable<ArchCatalogs, ArchShelf, IArchCatalog>
{
protected override IEnumerable<IArchCatalog> Standing()
{
yield return new ArchArchetypeCatalog();
yield return new ArchProfileCatalog();
yield return new ArchOpeningCatalog();
yield return new ArchWallCatalog();
yield return new ArchPillarCatalog();
yield return new ArchRoadLineCatalog();
}
protected override ArchShelf KeyOf( IArchCatalog catalog ) => catalog.Shelf;
protected override bool Accepts( IArchCatalog catalog ) => !string.IsNullOrWhiteSpace( catalog.Shelf.Role );
protected override string Collision( IArchCatalog standing, IArchCatalog catalog )
{
return $"{catalog.Shelf} is claimed by both {standing.GetType().Name} and {catalog.GetType().Name}";
}
// A shelf no catalog claims stands empty rather than erroring.
public IArchCatalog On( ArchShelf shelf ) => Held( shelf );
public List<T> Ships<T>( ArchShelf shelf ) where T : class
{
return On( shelf ) is { } catalog ? catalog.Shipped().OfType<T>().ToList() : new List<T>();
}
// For a catalog whose authored folder IS the offering - a picker that browses what an author has on disk.
public List<T> Authors<T>( ArchShelf shelf ) where T : class
{
return On( shelf ) is { } catalog ? catalog.Authored().OfType<T>().ToList() : new List<T>();
}
// What ships never displaces what is already held, and what is authored on disk always does - so an upgrade
// brings new presets in beside the tuned ones and a written file is the last word on its own name.
public void Stock<T>( ArchShelf shelf, List<T> saved ) where T : class
{
if ( saved is null || On( shelf ) is not { } catalog )
{
return;
}
foreach ( var entry in catalog.Shipped().OfType<T>() )
{
if ( !saved.Any( existing => Same( catalog.Named( existing ), catalog.Named( entry ) ) ) )
{
saved.Add( entry );
}
}
foreach ( var entry in catalog.Authored().OfType<T>() )
{
var index = saved.FindIndex( existing => Same( catalog.Named( existing ), catalog.Named( entry ) ) );
if ( index >= 0 )
{
saved[index] = entry;
}
else
{
saved.Add( entry );
}
}
}
public bool Save( ArchShelf shelf, object entry ) => On( shelf ) is { } catalog && catalog.Save( entry );
static bool Same( string first, string second ) => string.Equals( first, second, StringComparison.OrdinalIgnoreCase );
}
// For a one-off caller holding no table of its own. Reaching through it in a loop loads the shelf every time.
public static class ArchShelved
{
public static List<T> Ships<T>( ArchShelf shelf ) where T : class => ArchCatalogs.Load().Ships<T>( shelf );
public static List<T> Authors<T>( ArchShelf shelf ) where T : class => ArchCatalogs.Load().Authors<T>( shelf );
public static bool Save( ArchShelf shelf, object entry ) => ArchCatalogs.Load().Save( shelf, entry );
}