2026-05-08 01:13:15 +01:00
using System ;
using System.Collections.Generic ;
2026-05-08 01:50:15 +01:00
using System.Collections.Immutable ;
2026-05-08 01:13:15 +01:00
using System.Linq ;
using System.Text ;
using Microsoft.CodeAnalysis ;
using Microsoft.CodeAnalysis.CSharp.Syntax ;
using Microsoft.CodeAnalysis.Text ;
namespace GameCult.Caching.MessagePack.Generator
{
2026-05-08 13:11:01 +01:00
/// <summary>
/// Emits MessagePack payload serializers for CultCache document types.
/// </summary>
[Generator]
public sealed class CultDocumentMessagePackGenerator : IIncrementalGenerator
{
/// <summary>
/// Registers the generator pipeline.
/// </summary>
public void Initialize ( IncrementalGeneratorInitializationContext context )
2026-05-08 01:13:15 +01:00
{
2026-05-08 01:50:15 +01:00
var documentShapes = context . SyntaxProvider
2026-05-08 01:13:15 +01:00
. CreateSyntaxProvider (
static ( node , _ ) = > node is ClassDeclarationSyntax cds & & cds . AttributeLists . Count > 0 ,
2026-05-08 01:50:15 +01:00
static ( ctx , _ ) = > BuildDocumentShape ( ctx ) )
. Where ( static shape = > shape ! = null )
. Select ( static ( shape , _ ) = > shape ! ) ;
var compilationAndShapes = context . CompilationProvider . Combine ( documentShapes . Collect ( ) ) ;
context . RegisterSourceOutput (
compilationAndShapes ,
2026-05-08 02:19:27 +01:00
static ( spc , pair ) = > EmitProvider ( spc , pair . Left , pair . Right ) ) ;
2026-05-08 01:50:15 +01:00
}
private static DocumentShape ? BuildDocumentShape ( GeneratorSyntaxContext context )
{
if ( context . SemanticModel . GetDeclaredSymbol ( context . Node ) is not INamedTypeSymbol typeSymbol )
{
return null ;
}
var documentAttribute = GetAttribute ( typeSymbol , "GameCult.Caching.CultDocumentAttribute" ) ;
if ( documentAttribute = = null | |
documentAttribute . ConstructorArguments . Length < 2 | |
documentAttribute . ConstructorArguments [ 0 ] . Value is not string schemaName | |
documentAttribute . ConstructorArguments [ 1 ] . Value is not string schemaVersion )
{
return null ;
}
var members = DiscoverMembers ( typeSymbol ) ;
var nameMember = members . FirstOrDefault ( member = > member . IsName ) ;
return new DocumentShape (
typeSymbol . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ,
schemaName ,
schemaVersion ,
GetAttribute ( typeSymbol , "GameCult.Caching.CultGlobalAttribute" ) ! = null ,
nameMember ? . Name ,
nameMember ? . AccessorMethodName ,
members . Where ( member = > member . IndexAlias ! = null )
. Select ( member = > new IndexAccessorShape ( member . IndexAlias ! , member . IndexAccessorMethodName ! ) )
. ToImmutableArray ( ) ,
members . ToImmutableArray ( ) ) ;
}
private static ImmutableArray < MemberShape > DiscoverMembers ( INamedTypeSymbol typeSymbol )
{
var candidates = new List < MemberShapeSeed > ( ) ;
foreach ( var member in typeSymbol . GetMembers ( ) )
{
if ( member is IFieldSymbol field )
{
if ( field . IsStatic | | field . DeclaredAccessibility ! = Accessibility . Public | | IsIgnored ( field ) )
{
continue ;
}
candidates . Add ( MemberShapeSeed . From ( field , field . Type ) ) ;
continue ;
}
if ( member is IPropertySymbol property )
{
if ( property . IsStatic | |
property . IsIndexer | |
property . DeclaredAccessibility ! = Accessibility . Public | |
property . GetMethod = = null | |
property . SetMethod = = null | |
IsIgnored ( property ) )
{
continue ;
}
candidates . Add ( MemberShapeSeed . From ( property , property . Type ) ) ;
}
}
var explicitMembers = candidates
. Where ( member = > member . ExplicitSlot . HasValue )
. OrderBy ( member = > member . ExplicitSlot ! . Value )
. ThenBy ( member = > member . SortKey , StringComparer . Ordinal )
. ToArray ( ) ;
var implicitMembers = candidates
. Where ( member = > ! member . ExplicitSlot . HasValue )
. OrderBy ( member = > member . SortKey , StringComparer . Ordinal )
. ToArray ( ) ;
var nextSlot = explicitMembers . Length = = 0 ? 0 : explicitMembers . Max ( member = > member . ExplicitSlot ! . Value ) + 1 ;
var shaped = new List < MemberShape > ( candidates . Count ) ;
foreach ( var member in explicitMembers )
{
shaped . Add ( member . WithSlot ( member . ExplicitSlot ! . Value ) ) ;
}
foreach ( var member in implicitMembers )
{
shaped . Add ( member . WithSlot ( nextSlot + + ) ) ;
}
return shaped
. OrderBy ( member = > member . Slot )
. ToImmutableArray ( ) ;
}
private static bool IsIgnored ( ISymbol member )
{
return GetAttribute ( member , "MessagePack.IgnoreMemberAttribute" ) ! = null ;
}
private static AttributeData ? GetAttribute ( ISymbol symbol , string fullyQualifiedName )
{
return symbol . GetAttributes ( ) . FirstOrDefault ( attribute = >
attribute . AttributeClass ? . ToDisplayString ( ) = = fullyQualifiedName ) ;
}
private static int? GetExplicitSlot ( ISymbol member )
{
var keyAttribute = GetAttribute ( member , "MessagePack.KeyAttribute" ) ;
if ( keyAttribute = = null | | keyAttribute . ConstructorArguments . Length = = 0 )
{
return null ;
}
var argument = keyAttribute . ConstructorArguments [ 0 ] ;
return argument . Kind = = TypedConstantKind . Primitive & & argument . Value is int slot
? slot
: null ;
}
2026-05-08 02:19:27 +01:00
private static void EmitProvider ( SourceProductionContext context , Compilation compilation , ImmutableArray < DocumentShape > shapes )
2026-05-08 01:50:15 +01:00
{
var documents = shapes
. Where ( shape = > shape ! = null )
. GroupBy ( shape = > shape . DocumentTypeName , StringComparer . Ordinal )
. Select ( group = > group . First ( ) )
. OrderBy ( shape = > shape . DocumentTypeName , StringComparer . Ordinal )
. ToArray ( ) ;
if ( documents . Length = = 0 )
{
return ;
}
2026-05-08 02:19:27 +01:00
var canEmitPayloadCodecs =
compilation . GetTypeByMetadataName ( "MessagePack.MessagePackWriter" ) ! = null & &
compilation . GetTypeByMetadataName ( "GameCult.Caching.MessagePack.CultDocumentMessagePackSerialization" ) ! = null ;
var providerName = $"GeneratedCultDocumentMetadataProvider_{SanitizeIdentifier(compilation.AssemblyName ?? " GameCult ")}" ;
2026-05-08 01:50:15 +01:00
var builder = new StringBuilder ( ) ;
builder . AppendLine ( "// <auto-generated/>" ) ;
builder . AppendLine ( "#nullable enable" ) ;
builder . AppendLine ( $"[assembly: global::GameCult.Caching.CultGeneratedDocumentMetadataProviderAttribute(typeof(global::GameCult.Caching.Generated.{providerName}))]" ) ;
builder . AppendLine ( "namespace GameCult.Caching.Generated" ) ;
builder . AppendLine ( "{" ) ;
builder . AppendLine ( $" internal sealed class {providerName} : global::GameCult.Caching.ICultGeneratedDocumentMetadataProvider" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( " private static readonly global::GameCult.Caching.CultGeneratedDocumentDefinition[] s_documents =" ) ;
builder . AppendLine ( " {" ) ;
foreach ( var document in documents )
{
builder . AppendLine ( " new global::GameCult.Caching.CultGeneratedDocumentDefinition(" ) ;
builder . AppendLine ( $" typeof({document.DocumentTypeName})," ) ;
builder . AppendLine ( $" \" { Escape ( document . SchemaName ) } \ "," ) ;
builder . AppendLine ( $" \" { Escape ( document . SchemaVersion ) } \ "," ) ;
builder . AppendLine ( $" {(document.IsGlobal ? " true " : " false ")}," ) ;
builder . AppendLine ( document . NameMember = = null
? " null,"
: $" \" { Escape ( document . NameMember ) } \ "," ) ;
builder . AppendLine ( document . NameAccessorMethodName = = null
? " null,"
: $" {document.NameAccessorMethodName}," ) ;
2026-05-08 02:19:27 +01:00
builder . AppendLine ( canEmitPayloadCodecs
? $" {document.PayloadSerializerMethodName},"
: " null," ) ;
builder . AppendLine ( canEmitPayloadCodecs
? $" {document.PayloadDeserializerMethodName},"
: " null," ) ;
2026-05-08 01:50:15 +01:00
builder . AppendLine ( " new global::GameCult.Caching.CultGeneratedDocumentIndexAccessor[]" ) ;
builder . AppendLine ( " {" ) ;
foreach ( var indexAccessor in document . IndexAccessors )
{
builder . AppendLine ( $" new global::GameCult.Caching.CultGeneratedDocumentIndexAccessor(\" { Escape ( indexAccessor . Alias ) } \ ", {indexAccessor.AccessorMethodName})," ) ;
}
builder . AppendLine ( " }," ) ;
builder . AppendLine ( " new global::GameCult.Caching.CultGeneratedDocumentMemberDefinition[]" ) ;
builder . AppendLine ( " {" ) ;
2026-05-08 02:19:27 +01:00
foreach ( var member in document . Members . OrderBy ( member = > member . Slot ) )
2026-05-08 01:50:15 +01:00
{
builder . AppendLine ( " new global::GameCult.Caching.CultGeneratedDocumentMemberDefinition(" ) ;
builder . AppendLine ( $" \" { Escape ( member . Name ) } \ "," ) ;
builder . AppendLine ( $" {member.Slot}," ) ;
2026-05-08 02:19:27 +01:00
builder . AppendLine ( $" \" { Escape ( member . SchemaTypeName ) } \ "," ) ;
2026-05-08 01:50:15 +01:00
builder . AppendLine ( $" {(member.IsReference ? " true " : " false ")}," ) ;
builder . AppendLine ( $" {(member.IsMany ? " true " : " false ")}," ) ;
builder . AppendLine ( member . TargetSchemaName = = null
? " null,"
: $" \" { Escape ( member . TargetSchemaName ) } \ "," ) ;
builder . AppendLine ( $" {(member.IsName ? " true " : " false ")}," ) ;
builder . AppendLine ( member . IndexAlias = = null
? " null),"
: $" \" { Escape ( member . IndexAlias ) } \ ")," ) ;
}
builder . AppendLine ( " })," ) ;
}
builder . AppendLine ( " };" ) ;
builder . AppendLine ( ) ;
builder . AppendLine ( " public global::System.Collections.Generic.IEnumerable<global::GameCult.Caching.CultGeneratedDocumentDefinition> GetDocumentDefinitions()" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( " return s_documents;" ) ;
builder . AppendLine ( " }" ) ;
builder . AppendLine ( ) ;
foreach ( var document in documents )
{
2026-05-08 02:19:27 +01:00
if ( canEmitPayloadCodecs )
{
EmitPayloadSerializer ( builder , document ) ;
EmitPayloadDeserializer ( builder , document ) ;
}
2026-05-08 01:50:15 +01:00
foreach ( var member in document . Members . Where ( member = > member . AccessorMethodName ! = null ) )
{
builder . AppendLine ( $" private static string? {member.AccessorMethodName}(object document)" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( member . CanBeNull
? $" return (({document.DocumentTypeName})document).{member.Name}?.ToString();"
: $" return (({document.DocumentTypeName})document).{member.Name}.ToString();" ) ;
builder . AppendLine ( " }" ) ;
builder . AppendLine ( ) ;
}
foreach ( var member in document . Members . Where ( member = > member . IndexAccessorMethodName ! = null ) )
{
builder . AppendLine ( $" private static string {member.IndexAccessorMethodName}(object document)" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( member . CanBeNull
? $" return (({document.DocumentTypeName})document).{member.Name}?.ToString() ?? string.Empty;"
: $" return (({document.DocumentTypeName})document).{member.Name}.ToString();" ) ;
builder . AppendLine ( " }" ) ;
builder . AppendLine ( ) ;
}
}
builder . AppendLine ( " }" ) ;
builder . AppendLine ( "}" ) ;
context . AddSource ( $"{providerName}.g.cs" , SourceText . From ( builder . ToString ( ) , Encoding . UTF8 ) ) ;
}
2026-05-08 02:19:27 +01:00
private static void EmitPayloadSerializer ( StringBuilder builder , DocumentShape document )
{
builder . AppendLine ( $" private static byte[] {document.PayloadSerializerMethodName}(object document)" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( $" var typed = ({document.DocumentTypeName})document;" ) ;
builder . AppendLine ( " var options = global::GameCult.Caching.MessagePack.CultDocumentMessagePackSerialization.Options;" ) ;
builder . AppendLine ( " var buffer = new global::System.Buffers.ArrayBufferWriter<byte>();" ) ;
builder . AppendLine ( " var writer = new global::MessagePack.MessagePackWriter(buffer);" ) ;
builder . AppendLine ( $" writer.WriteArrayHeader({document.Members.Length});" ) ;
foreach ( var member in document . Members . OrderBy ( member = > member . Slot ) )
{
builder . AppendLine ( $" global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<{member.TypeSyntaxName}>(options.Resolver).Serialize(ref writer, typed.{member.Name}, options);" ) ;
}
builder . AppendLine ( " writer.Flush();" ) ;
builder . AppendLine ( " return buffer.WrittenSpan.ToArray();" ) ;
builder . AppendLine ( " }" ) ;
builder . AppendLine ( ) ;
}
private static void EmitPayloadDeserializer ( StringBuilder builder , DocumentShape document )
{
builder . AppendLine ( $" private static object {document.PayloadDeserializerMethodName}(byte[] payload)" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( " var options = global::GameCult.Caching.MessagePack.CultDocumentMessagePackSerialization.Options;" ) ;
builder . AppendLine ( " var reader = new global::MessagePack.MessagePackReader(payload);" ) ;
builder . AppendLine ( " var count = reader.ReadArrayHeader();" ) ;
builder . AppendLine ( $" var value = new {document.DocumentTypeName}();" ) ;
foreach ( var member in document . Members . OrderBy ( member = > member . Slot ) )
{
builder . AppendLine ( $" if (count > {member.Slot})" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( $" value.{member.Name} = global::MessagePack.FormatterResolverExtensions.GetFormatterWithVerify<{member.TypeSyntaxName}>(options.Resolver).Deserialize(ref reader, options);" ) ;
builder . AppendLine ( " }" ) ;
}
builder . AppendLine ( $" for (var index = {document.Members.Length}; index < count; index++)" ) ;
builder . AppendLine ( " {" ) ;
builder . AppendLine ( " reader.Skip();" ) ;
builder . AppendLine ( " }" ) ;
builder . AppendLine ( " return value;" ) ;
builder . AppendLine ( " }" ) ;
builder . AppendLine ( ) ;
}
2026-05-08 01:50:15 +01:00
private static string Escape ( string value )
{
return value
. Replace ( "\\" , "\\\\" )
. Replace ( "\"" , "\\\"" )
. Replace ( "\r" , "\\r" )
. Replace ( "\n" , "\\n" )
. Replace ( "\t" , "\\t" ) ;
}
private static string SanitizeIdentifier ( string value )
{
var builder = new StringBuilder ( value . Length ) ;
foreach ( var character in value )
{
builder . Append ( char . IsLetterOrDigit ( character ) ? character : '_' ) ;
}
if ( builder . Length = = 0 | | ! char . IsLetter ( builder [ 0 ] ) & & builder [ 0 ] ! = '_' )
{
builder . Insert ( 0 , '_' ) ;
}
return builder . ToString ( ) ;
}
private static string GetSchemaTypeName ( ITypeSymbol typeSymbol )
{
if ( typeSymbol is IArrayTypeSymbol arrayType )
{
return GetSchemaTypeName ( arrayType . ElementType ) + "[]" ;
}
if ( typeSymbol is INamedTypeSymbol namedType & & namedType . IsGenericType )
{
var baseName = GetNamedTypeBaseName ( namedType . ConstructedFrom ) ;
var arguments = string . Join ( ", " , namedType . TypeArguments . Select ( GetSchemaTypeName ) ) ;
return $"{baseName}<{arguments}>" ;
}
return typeSymbol . SpecialType switch
{
SpecialType . System_Boolean = > "System.Boolean" ,
SpecialType . System_Byte = > "System.Byte" ,
SpecialType . System_Char = > "System.Char" ,
SpecialType . System_Decimal = > "System.Decimal" ,
SpecialType . System_Double = > "System.Double" ,
SpecialType . System_Int16 = > "System.Int16" ,
SpecialType . System_Int32 = > "System.Int32" ,
SpecialType . System_Int64 = > "System.Int64" ,
SpecialType . System_Object = > "System.Object" ,
SpecialType . System_SByte = > "System.SByte" ,
SpecialType . System_Single = > "System.Single" ,
SpecialType . System_String = > "System.String" ,
SpecialType . System_UInt16 = > "System.UInt16" ,
SpecialType . System_UInt32 = > "System.UInt32" ,
SpecialType . System_UInt64 = > "System.UInt64" ,
_ = > GetNamedTypeBaseName ( ( INamedTypeSymbol ) typeSymbol )
} ;
}
private static string GetNamedTypeBaseName ( INamedTypeSymbol typeSymbol )
{
var parts = new Stack < string > ( ) ;
var current = typeSymbol ;
while ( current ! = null )
{
parts . Push ( current . Name ) ;
current = current . ContainingType ;
}
var namespaceName = typeSymbol . ContainingNamespace ? . ToDisplayString ( ) ;
return string . IsNullOrWhiteSpace ( namespaceName )
? string . Join ( "." , parts )
: namespaceName + "." + string . Join ( "." , parts ) ;
}
private static bool CanBeNull ( ITypeSymbol typeSymbol )
{
if ( typeSymbol . NullableAnnotation = = NullableAnnotation . Annotated )
{
return true ;
}
if ( typeSymbol . IsReferenceType )
{
return true ;
}
return typeSymbol is INamedTypeSymbol namedType & &
namedType . IsGenericType & &
namedType . ConstructedFrom . SpecialType = = SpecialType . System_Nullable_T ;
}
private sealed class MemberShapeSeed
{
private MemberShapeSeed (
string name ,
2026-05-08 02:19:27 +01:00
string schemaTypeName ,
string typeSyntaxName ,
2026-05-08 01:50:15 +01:00
int? explicitSlot ,
string sortKey ,
bool isName ,
string? indexAlias ,
bool isReference ,
bool isMany ,
bool canBeNull ,
string? targetSchemaName )
{
Name = name ;
2026-05-08 02:19:27 +01:00
SchemaTypeName = schemaTypeName ;
TypeSyntaxName = typeSyntaxName ;
2026-05-08 01:50:15 +01:00
ExplicitSlot = explicitSlot ;
SortKey = sortKey ;
IsName = isName ;
IndexAlias = indexAlias ;
IsReference = isReference ;
IsMany = isMany ;
CanBeNull = canBeNull ;
TargetSchemaName = targetSchemaName ;
}
public string Name { get ; }
2026-05-08 02:19:27 +01:00
public string SchemaTypeName { get ; }
public string TypeSyntaxName { get ; }
2026-05-08 01:50:15 +01:00
public int? ExplicitSlot { get ; }
public string SortKey { get ; }
public bool IsName { get ; }
public string? IndexAlias { get ; }
public bool IsReference { get ; }
public bool IsMany { get ; }
public bool CanBeNull { get ; }
public string? TargetSchemaName { get ; }
public MemberShape WithSlot ( int slot )
{
var accessorStem = SanitizeIdentifier ( SortKey ) ;
return new MemberShape (
Name ,
slot ,
2026-05-08 02:19:27 +01:00
SchemaTypeName ,
TypeSyntaxName ,
2026-05-08 01:50:15 +01:00
IsReference ,
IsMany ,
TargetSchemaName ,
IsName ,
IndexAlias ,
CanBeNull ,
IsName ? $"Access_{accessorStem}_Name" : null ,
IndexAlias ! = null ? $"Access_{accessorStem}_Index" : null ) ;
}
public static MemberShapeSeed From ( ISymbol member , ITypeSymbol memberType )
{
var referenceAttribute = GetAttribute ( member , "GameCult.Caching.CultReferenceAttribute" ) ;
var explicitTarget = referenceAttribute ? . ConstructorArguments . Length > 0
? referenceAttribute . ConstructorArguments [ 0 ] . Value as INamedTypeSymbol
: null ;
var many = referenceAttribute ? . ConstructorArguments . Length > 1 & &
referenceAttribute . ConstructorArguments [ 1 ] . Value is bool manyValue & &
manyValue ;
var targetType = ResolveReferenceTarget ( memberType , explicitTarget ) ;
var targetSchemaName = targetType = = null
? null
: GetAttribute ( targetType , "GameCult.Caching.CultDocumentAttribute" ) ? . ConstructorArguments [ 0 ] . Value as string ;
var indexAttribute = GetAttribute ( member , "GameCult.Caching.CultIndexAttribute" ) ;
var indexAlias = indexAttribute = = null
? null
: indexAttribute . ConstructorArguments . Length = = 0 | | indexAttribute . ConstructorArguments [ 0 ] . Value is not string alias | | string . IsNullOrWhiteSpace ( alias )
? member . Name
: alias ;
return new MemberShapeSeed (
member . Name ,
GetSchemaTypeName ( memberType ) ,
2026-05-08 02:19:27 +01:00
memberType . ToDisplayString ( SymbolDisplayFormat . FullyQualifiedFormat ) ,
2026-05-08 01:50:15 +01:00
GetExplicitSlot ( member ) ,
BuildAccessorStem ( member ) ,
GetAttribute ( member , "GameCult.Caching.CultNameAttribute" ) ! = null ,
indexAlias ,
targetType ! = null | | referenceAttribute ! = null ,
many ,
CanBeNull ( memberType ) ,
targetSchemaName ) ;
}
private static INamedTypeSymbol ? ResolveReferenceTarget ( ITypeSymbol memberType , INamedTypeSymbol ? explicitTarget )
{
if ( explicitTarget ! = null )
{
return explicitTarget ;
}
return memberType is INamedTypeSymbol namedType & &
namedType . IsGenericType & &
namedType . Name = = "CultRecordRef" & &
namedType . ContainingNamespace . ToDisplayString ( ) = = "GameCult.Caching"
? namedType . TypeArguments [ 0 ] as INamedTypeSymbol
: null ;
}
private static string BuildAccessorStem ( ISymbol member )
{
return member . ContainingType = = null
? member . Name
: member . ContainingType . Name + "_" + member . Name ;
}
}
private sealed class DocumentShape
{
public DocumentShape (
string documentTypeName ,
string schemaName ,
string schemaVersion ,
bool isGlobal ,
string? nameMember ,
string? nameAccessorMethodName ,
ImmutableArray < IndexAccessorShape > indexAccessors ,
ImmutableArray < MemberShape > members )
{
DocumentTypeName = documentTypeName ;
SchemaName = schemaName ;
SchemaVersion = schemaVersion ;
IsGlobal = isGlobal ;
NameMember = nameMember ;
NameAccessorMethodName = nameAccessorMethodName ;
2026-05-08 02:19:27 +01:00
var safeStem = SanitizeIdentifier ( documentTypeName . Replace ( "global::" , string . Empty ) . Replace ( "." , "_" ) ) ;
PayloadSerializerMethodName = $"Serialize_{safeStem}_Payload" ;
PayloadDeserializerMethodName = $"Deserialize_{safeStem}_Payload" ;
2026-05-08 01:50:15 +01:00
IndexAccessors = indexAccessors ;
Members = members ;
}
public string DocumentTypeName { get ; }
public string SchemaName { get ; }
public string SchemaVersion { get ; }
public bool IsGlobal { get ; }
public string? NameMember { get ; }
public string? NameAccessorMethodName { get ; }
2026-05-08 02:19:27 +01:00
public string PayloadSerializerMethodName { get ; }
public string PayloadDeserializerMethodName { get ; }
2026-05-08 01:50:15 +01:00
public ImmutableArray < IndexAccessorShape > IndexAccessors { get ; }
public ImmutableArray < MemberShape > Members { get ; }
}
private sealed class IndexAccessorShape
{
public IndexAccessorShape ( string alias , string accessorMethodName )
{
Alias = alias ;
AccessorMethodName = accessorMethodName ;
}
public string Alias { get ; }
public string AccessorMethodName { get ; }
}
private sealed class MemberShape
{
public MemberShape (
string name ,
int slot ,
2026-05-08 02:19:27 +01:00
string schemaTypeName ,
string typeSyntaxName ,
2026-05-08 01:50:15 +01:00
bool isReference ,
bool isMany ,
string? targetSchemaName ,
bool isName ,
string? indexAlias ,
bool canBeNull ,
string? accessorMethodName ,
string? indexAccessorMethodName )
{
Name = name ;
Slot = slot ;
2026-05-08 02:19:27 +01:00
SchemaTypeName = schemaTypeName ;
TypeSyntaxName = typeSyntaxName ;
2026-05-08 01:50:15 +01:00
IsReference = isReference ;
IsMany = isMany ;
TargetSchemaName = targetSchemaName ;
IsName = isName ;
IndexAlias = indexAlias ;
CanBeNull = canBeNull ;
AccessorMethodName = accessorMethodName ;
IndexAccessorMethodName = indexAccessorMethodName ;
}
public string Name { get ; }
public int Slot { get ; }
2026-05-08 02:19:27 +01:00
public string SchemaTypeName { get ; }
public string TypeSyntaxName { get ; }
2026-05-08 01:50:15 +01:00
public bool IsReference { get ; }
public bool IsMany { get ; }
public string? TargetSchemaName { get ; }
public bool IsName { get ; }
public string? IndexAlias { get ; }
public bool CanBeNull { get ; }
public string? AccessorMethodName { get ; }
public string? IndexAccessorMethodName { get ; }
2026-05-08 01:13:15 +01:00
}
}
}