416 lines
22 KiB
C#
416 lines
22 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.Immutable;
|
|
using System.Linq;
|
|
using System.Text;
|
|
using Microsoft.CodeAnalysis;
|
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
|
using Microsoft.CodeAnalysis.Text;
|
|
|
|
namespace AyCode.Core.Serializers.SourceGenerator;
|
|
|
|
/// <summary>
|
|
/// Generates IGeneratedBinaryWriter implementations for [AcBinarySerializable] types.
|
|
/// Also generates a ModuleInitializer that auto-registers all writers at startup.
|
|
/// </summary>
|
|
[Generator]
|
|
public class AcBinarySourceGenerator : IIncrementalGenerator
|
|
{
|
|
private const string AttributeName = "AyCode.Core.Serializers.Attributes.AcBinarySerializableAttribute";
|
|
|
|
public void Initialize(IncrementalGeneratorInitializationContext context)
|
|
{
|
|
var classDeclarations = context.SyntaxProvider
|
|
.ForAttributeWithMetadataName(
|
|
AttributeName,
|
|
predicate: static (node, _) => node is ClassDeclarationSyntax || node is StructDeclarationSyntax,
|
|
transform: static (ctx, _) => GetClassInfo(ctx))
|
|
.Where(static info => info != null);
|
|
|
|
context.RegisterSourceOutput(classDeclarations.Collect(),
|
|
static (spc, classes) => Execute(classes!, spc));
|
|
}
|
|
|
|
private static SerializableClassInfo? GetClassInfo(GeneratorAttributeSyntaxContext context)
|
|
{
|
|
if (!(context.TargetSymbol is INamedTypeSymbol typeSymbol))
|
|
return null;
|
|
|
|
// Skip nested types — generated writer class can't be placed inside containing type
|
|
if (typeSymbol.ContainingType != null)
|
|
return null;
|
|
|
|
var namespaceName = typeSymbol.ContainingNamespace.IsGlobalNamespace
|
|
? string.Empty
|
|
: typeSymbol.ContainingNamespace.ToDisplayString();
|
|
|
|
var properties = new List<PropInfo>();
|
|
foreach (var member in typeSymbol.GetMembers())
|
|
{
|
|
if (member is IPropertySymbol p &&
|
|
p.DeclaredAccessibility == Accessibility.Public &&
|
|
p.GetMethod != null && p.SetMethod != null &&
|
|
!p.IsIndexer && !p.IsStatic)
|
|
{
|
|
var hasIgnore = p.GetAttributes().Any(a =>
|
|
{
|
|
var name = a.AttributeClass?.Name ?? "";
|
|
return name == "JsonIgnoreAttribute" || name == "IgnoreMemberAttribute" || name == "BsonIgnoreAttribute";
|
|
});
|
|
if (hasIgnore) continue;
|
|
|
|
properties.Add(new PropInfo(
|
|
p.Name,
|
|
p.Type.ToDisplayString(),
|
|
GetKind(p.Type),
|
|
p.Type.NullableAnnotation == NullableAnnotation.Annotated || IsNullableVT(p.Type)));
|
|
}
|
|
}
|
|
|
|
// IId<T>: Id first (index 0), then alphabetical — matches runtime TypeMetadataBase ordering
|
|
var isIId = typeSymbol.AllInterfaces.Any(i =>
|
|
i.IsGenericType &&
|
|
i.OriginalDefinition.ToDisplayString() == "AyCode.Core.Interfaces.IId<T>");
|
|
|
|
if (isIId)
|
|
properties.Sort((a, b) =>
|
|
{
|
|
var aIsId = a.Name == "Id" ? 0 : 1;
|
|
var bIsId = b.Name == "Id" ? 0 : 1;
|
|
if (aIsId != bIsId) return aIsId.CompareTo(bIsId);
|
|
return string.Compare(a.Name, b.Name, StringComparison.Ordinal);
|
|
});
|
|
else
|
|
properties.Sort((a, b) => string.Compare(a.Name, b.Name, StringComparison.Ordinal));
|
|
|
|
return new SerializableClassInfo(namespaceName, typeSymbol.Name, typeSymbol.ToDisplayString(), properties);
|
|
}
|
|
|
|
private static void Execute(ImmutableArray<SerializableClassInfo?> classes, SourceProductionContext context)
|
|
{
|
|
if (classes.IsDefaultOrEmpty) return;
|
|
var valid = classes.Where(c => c != null).Cast<SerializableClassInfo>().ToList();
|
|
if (valid.Count == 0) return;
|
|
|
|
foreach (var ci in valid)
|
|
context.AddSource($"{ci.ClassName}_GeneratedWriter.g.cs", SourceText.From(GenWriter(ci), Encoding.UTF8));
|
|
|
|
context.AddSource("AcBinaryGeneratedWriters_Init.g.cs", SourceText.From(GenInit(valid), Encoding.UTF8));
|
|
}
|
|
|
|
private static string GenWriter(SerializableClassInfo ci)
|
|
{
|
|
var sb = new StringBuilder(2048);
|
|
sb.AppendLine("// <auto-generated/>");
|
|
sb.AppendLine("#nullable enable");
|
|
sb.AppendLine("using System.Runtime.CompilerServices;");
|
|
sb.AppendLine("using AyCode.Core.Serializers.Binaries;");
|
|
sb.AppendLine();
|
|
if (!string.IsNullOrEmpty(ci.Namespace))
|
|
sb.AppendLine($"namespace {ci.Namespace};");
|
|
sb.AppendLine();
|
|
sb.AppendLine($"internal sealed class {ci.ClassName}_GeneratedWriter : IGeneratedBinaryWriter");
|
|
sb.AppendLine("{");
|
|
sb.AppendLine($" internal static readonly {ci.ClassName}_GeneratedWriter Instance = new();");
|
|
sb.AppendLine();
|
|
sb.AppendLine(" public void WriteProperties<TOutput>(object value, AcBinarySerializer.BinarySerializationContext<TOutput> context, int depth) where TOutput : struct, IBinaryOutputBase");
|
|
sb.AppendLine(" {");
|
|
sb.AppendLine($" var obj = Unsafe.As<{ci.FullTypeName}>(value);");
|
|
|
|
foreach (var p in ci.Properties)
|
|
{
|
|
sb.AppendLine();
|
|
EmitProp(sb, p, " ");
|
|
}
|
|
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine("}");
|
|
return sb.ToString();
|
|
}
|
|
|
|
private static void EmitProp(StringBuilder sb, PropInfo p, string i)
|
|
{
|
|
var a = $"obj.{p.Name}";
|
|
|
|
// Nullable value types always use markered path (need Null marker)
|
|
if (IsNullableVTKind(p.TypeKind))
|
|
{
|
|
sb.AppendLine($"{i}if ({a}.HasValue)");
|
|
sb.AppendLine($"{i}{{");
|
|
EmitVal(sb, Underlying(p.TypeKind), $"{a}.Value", i + " ");
|
|
sb.AppendLine($"{i}}}");
|
|
sb.AppendLine($"{i}else context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
return;
|
|
}
|
|
|
|
// Markerless types: write raw value only, no type marker, no PropertySkip
|
|
// Matches runtime WritePropertyMarkerless — these have ExpectedTypeCode
|
|
if (IsMarkerless(p.TypeKind))
|
|
{
|
|
EmitMarkerless(sb, p.TypeKind, a, i);
|
|
return;
|
|
}
|
|
|
|
// Non-markerless types: write WITH type marker byte (markered path)
|
|
switch (p.TypeKind)
|
|
{
|
|
case PropertyTypeKind.String:
|
|
sb.AppendLine($"{i}AcBinarySerializer.WriteStringGenerated({a}, context);");
|
|
break;
|
|
case PropertyTypeKind.Complex:
|
|
case PropertyTypeKind.Collection:
|
|
if (p.IsNullable)
|
|
{
|
|
sb.AppendLine($"{i}if ({a} == null) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else AcBinarySerializer.WriteValueGenerated({a}, {a}.GetType(), context, depth);");
|
|
}
|
|
else
|
|
sb.AppendLine($"{i}AcBinarySerializer.WriteValueGenerated({a}, {a}.GetType(), context, depth);");
|
|
break;
|
|
default:
|
|
EmitSkip(sb, p.TypeKind, a, i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true for property types that use markerless serialization in FastMode.
|
|
/// These types have ExpectedTypeCode at runtime — no type marker byte, no PropertySkip for defaults.
|
|
/// </summary>
|
|
private static bool IsMarkerless(PropertyTypeKind k) => k switch
|
|
{
|
|
PropertyTypeKind.Int32 or PropertyTypeKind.Int64 or PropertyTypeKind.Int16 or
|
|
PropertyTypeKind.Byte or PropertyTypeKind.UInt16 or PropertyTypeKind.UInt32 or PropertyTypeKind.UInt64 or
|
|
PropertyTypeKind.Double or PropertyTypeKind.Single or PropertyTypeKind.Decimal or
|
|
PropertyTypeKind.DateTime or PropertyTypeKind.Guid or
|
|
PropertyTypeKind.TimeSpan or PropertyTypeKind.DateTimeOffset => true,
|
|
_ => false
|
|
};
|
|
|
|
/// <summary>
|
|
/// Emits raw value only — no type marker, no PropertySkip.
|
|
/// Matches runtime WritePropertyMarkerless exactly.
|
|
/// </summary>
|
|
private static void EmitMarkerless(StringBuilder sb, PropertyTypeKind k, string a, string i)
|
|
{
|
|
switch (k)
|
|
{
|
|
case PropertyTypeKind.Int32: sb.AppendLine($"{i}context.WriteVarInt({a});"); break;
|
|
case PropertyTypeKind.Int64: sb.AppendLine($"{i}context.WriteVarLong({a});"); break;
|
|
case PropertyTypeKind.Double: sb.AppendLine($"{i}context.WriteRaw({a});"); break;
|
|
case PropertyTypeKind.Single: sb.AppendLine($"{i}context.WriteRaw({a});"); break;
|
|
case PropertyTypeKind.Decimal: sb.AppendLine($"{i}context.WriteDecimalBits({a});"); break;
|
|
case PropertyTypeKind.DateTime: sb.AppendLine($"{i}context.WriteDateTimeBits({a});"); break;
|
|
case PropertyTypeKind.Guid: sb.AppendLine($"{i}context.WriteGuidBits({a});"); break;
|
|
case PropertyTypeKind.Byte: sb.AppendLine($"{i}context.WriteByte({a});"); break;
|
|
case PropertyTypeKind.Int16: sb.AppendLine($"{i}context.WriteRaw({a});"); break;
|
|
case PropertyTypeKind.UInt16: sb.AppendLine($"{i}context.WriteRaw({a});"); break;
|
|
case PropertyTypeKind.UInt32: sb.AppendLine($"{i}context.WriteVarUInt({a});"); break;
|
|
case PropertyTypeKind.UInt64: sb.AppendLine($"{i}context.WriteVarULong({a});"); break;
|
|
case PropertyTypeKind.TimeSpan: sb.AppendLine($"{i}context.WriteRaw({a}.Ticks);"); break;
|
|
case PropertyTypeKind.DateTimeOffset: sb.AppendLine($"{i}context.WriteDateTimeOffsetBits({a});"); break;
|
|
}
|
|
}
|
|
|
|
private static void EmitSkip(StringBuilder sb, PropertyTypeKind k, string a, string i)
|
|
{
|
|
switch (k)
|
|
{
|
|
case PropertyTypeKind.Int32:
|
|
sb.AppendLine($"{i}if ({a} == 0) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Int32); context.WriteVarInt({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Int64:
|
|
sb.AppendLine($"{i}if ({a} == 0L) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Int64); context.WriteVarLong({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Boolean:
|
|
sb.AppendLine($"{i}if (!{a}) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else context.WriteByte(BinaryTypeCode.True);");
|
|
break;
|
|
case PropertyTypeKind.Double:
|
|
sb.AppendLine($"{i}if ({a} == 0.0) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Float64); context.WriteRaw({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Single:
|
|
sb.AppendLine($"{i}if ({a} == 0f) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Float32); context.WriteRaw({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Decimal:
|
|
sb.AppendLine($"{i}if ({a} == 0m) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Decimal); context.WriteDecimalBits({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.DateTime:
|
|
sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.DateTime); context.WriteDateTimeBits({a});");
|
|
break;
|
|
case PropertyTypeKind.Guid:
|
|
sb.AppendLine($"{i}if ({a} == System.Guid.Empty) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Guid); context.WriteGuidBits({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Byte:
|
|
sb.AppendLine($"{i}if ({a} == 0) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.UInt8); context.WriteByte({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Int16:
|
|
sb.AppendLine($"{i}if ({a} == 0) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Int16); context.WriteRaw({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.UInt16:
|
|
sb.AppendLine($"{i}if ({a} == 0) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.UInt16); context.WriteRaw({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.UInt32:
|
|
sb.AppendLine($"{i}if ({a} == 0U) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.UInt32); context.WriteVarUInt({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.UInt64:
|
|
sb.AppendLine($"{i}if ({a} == 0UL) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.UInt64); context.WriteVarULong({a}); }}");
|
|
break;
|
|
case PropertyTypeKind.Enum:
|
|
var s = a.Replace(".", "_");
|
|
sb.AppendLine($"{i}var ev_{s} = (int){a};");
|
|
sb.AppendLine($"{i}if (ev_{s} == 0) context.WriteByte(BinaryTypeCode.PropertySkip);");
|
|
sb.AppendLine($"{i}else if (BinaryTypeCode.TryEncodeTinyInt(ev_{s}, out var te_{s})) {{ context.WriteByte(BinaryTypeCode.Enum); context.WriteByte(te_{s}); }}");
|
|
sb.AppendLine($"{i}else {{ context.WriteByte(BinaryTypeCode.Enum); context.WriteByte(BinaryTypeCode.Int32); context.WriteVarInt(ev_{s}); }}");
|
|
break;
|
|
case PropertyTypeKind.TimeSpan:
|
|
sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.TimeSpan); context.WriteRaw({a}.Ticks);");
|
|
break;
|
|
case PropertyTypeKind.DateTimeOffset:
|
|
sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.DateTimeOffset); context.WriteDateTimeOffsetBits({a});");
|
|
break;
|
|
default:
|
|
sb.AppendLine($"{i}AcBinarySerializer.WriteValueGenerated({a}, {a}.GetType(), context, depth);");
|
|
break;
|
|
}
|
|
}
|
|
|
|
private static void EmitVal(StringBuilder sb, PropertyTypeKind k, string a, string i)
|
|
{
|
|
switch (k)
|
|
{
|
|
case PropertyTypeKind.Int32: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.Int32); context.WriteVarInt({a});"); break;
|
|
case PropertyTypeKind.Int64: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.Int64); context.WriteVarLong({a});"); break;
|
|
case PropertyTypeKind.Boolean: sb.AppendLine($"{i}context.WriteByte({a} ? BinaryTypeCode.True : BinaryTypeCode.False);"); break;
|
|
case PropertyTypeKind.Double: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.Float64); context.WriteRaw({a});"); break;
|
|
case PropertyTypeKind.Single: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.Float32); context.WriteRaw({a});"); break;
|
|
case PropertyTypeKind.Decimal: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.Decimal); context.WriteDecimalBits({a});"); break;
|
|
case PropertyTypeKind.DateTime: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.DateTime); context.WriteDateTimeBits({a});"); break;
|
|
case PropertyTypeKind.Guid: sb.AppendLine($"{i}context.WriteByte(BinaryTypeCode.Guid); context.WriteGuidBits({a});"); break;
|
|
default: EmitSkip(sb, k, a, i); break;
|
|
}
|
|
}
|
|
|
|
private static string GenInit(List<SerializableClassInfo> classes)
|
|
{
|
|
var sb = new StringBuilder(512);
|
|
sb.AppendLine("// <auto-generated/>");
|
|
sb.AppendLine("using System.Runtime.CompilerServices;");
|
|
sb.AppendLine("using AyCode.Core.Serializers.Binaries;");
|
|
sb.AppendLine();
|
|
sb.AppendLine("namespace AyCode.Core.Serializers.Generated;");
|
|
sb.AppendLine();
|
|
sb.AppendLine("internal static class AcBinaryGeneratedWritersInit");
|
|
sb.AppendLine("{");
|
|
sb.AppendLine(" [ModuleInitializer]");
|
|
sb.AppendLine(" internal static void Register()");
|
|
sb.AppendLine(" {");
|
|
foreach (var ci in classes)
|
|
sb.AppendLine($" AcBinarySerializer.RegisterGeneratedWriter(typeof({ci.FullTypeName}), {ci.FullTypeName}_GeneratedWriter.Instance);");
|
|
sb.AppendLine(" }");
|
|
sb.AppendLine("}");
|
|
return sb.ToString();
|
|
}
|
|
|
|
#region Type analysis
|
|
|
|
private static bool IsNullableVT(ITypeSymbol t) =>
|
|
t is INamedTypeSymbol n && n.IsGenericType && n.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T;
|
|
|
|
private static PropertyTypeKind GetKind(ITypeSymbol type)
|
|
{
|
|
if (type is INamedTypeSymbol n && n.IsGenericType && n.ConstructedFrom.SpecialType == SpecialType.System_Nullable_T)
|
|
return GetKindCore(n.TypeArguments[0], true);
|
|
return GetKindCore(type, false);
|
|
}
|
|
|
|
private static PropertyTypeKind GetKindCore(ITypeSymbol type, bool nullable)
|
|
{
|
|
switch (type.SpecialType)
|
|
{
|
|
case SpecialType.System_String: return PropertyTypeKind.String;
|
|
case SpecialType.System_Int32: return nullable ? PropertyTypeKind.NullableInt32 : PropertyTypeKind.Int32;
|
|
case SpecialType.System_Int64: return nullable ? PropertyTypeKind.NullableInt64 : PropertyTypeKind.Int64;
|
|
case SpecialType.System_Int16: return nullable ? PropertyTypeKind.NullableInt16 : PropertyTypeKind.Int16;
|
|
case SpecialType.System_Byte: return nullable ? PropertyTypeKind.NullableByte : PropertyTypeKind.Byte;
|
|
case SpecialType.System_UInt16: return nullable ? PropertyTypeKind.NullableUInt16 : PropertyTypeKind.UInt16;
|
|
case SpecialType.System_UInt32: return nullable ? PropertyTypeKind.NullableUInt32 : PropertyTypeKind.UInt32;
|
|
case SpecialType.System_UInt64: return nullable ? PropertyTypeKind.NullableUInt64 : PropertyTypeKind.UInt64;
|
|
case SpecialType.System_Boolean: return nullable ? PropertyTypeKind.NullableBoolean : PropertyTypeKind.Boolean;
|
|
case SpecialType.System_Single: return nullable ? PropertyTypeKind.NullableSingle : PropertyTypeKind.Single;
|
|
case SpecialType.System_Double: return nullable ? PropertyTypeKind.NullableDouble : PropertyTypeKind.Double;
|
|
case SpecialType.System_Decimal: return nullable ? PropertyTypeKind.NullableDecimal : PropertyTypeKind.Decimal;
|
|
case SpecialType.System_DateTime: return nullable ? PropertyTypeKind.NullableDateTime : PropertyTypeKind.DateTime;
|
|
default: break;
|
|
}
|
|
var fn = type.ToDisplayString();
|
|
if (fn == "System.Guid") return nullable ? PropertyTypeKind.NullableGuid : PropertyTypeKind.Guid;
|
|
if (fn == "System.TimeSpan") return nullable ? PropertyTypeKind.NullableTimeSpan : PropertyTypeKind.TimeSpan;
|
|
if (fn == "System.DateTimeOffset") return nullable ? PropertyTypeKind.NullableDateTimeOffset : PropertyTypeKind.DateTimeOffset;
|
|
if (type.TypeKind == TypeKind.Enum) return nullable ? PropertyTypeKind.NullableEnum : PropertyTypeKind.Enum;
|
|
if (type is IArrayTypeSymbol) return PropertyTypeKind.Collection;
|
|
if (type is INamedTypeSymbol nt && nt.AllInterfaces.Any(iface => iface.OriginalDefinition.SpecialType == SpecialType.System_Collections_Generic_IEnumerable_T))
|
|
return PropertyTypeKind.Collection;
|
|
if (type.TypeKind == TypeKind.Class || type.TypeKind == TypeKind.Struct) return PropertyTypeKind.Complex;
|
|
return PropertyTypeKind.Unknown;
|
|
}
|
|
|
|
private static bool IsNullableVTKind(PropertyTypeKind k) => k >= PropertyTypeKind.NullableInt32;
|
|
|
|
private static PropertyTypeKind Underlying(PropertyTypeKind k) => k switch
|
|
{
|
|
PropertyTypeKind.NullableInt32 => PropertyTypeKind.Int32, PropertyTypeKind.NullableInt64 => PropertyTypeKind.Int64,
|
|
PropertyTypeKind.NullableInt16 => PropertyTypeKind.Int16, PropertyTypeKind.NullableByte => PropertyTypeKind.Byte,
|
|
PropertyTypeKind.NullableUInt16 => PropertyTypeKind.UInt16, PropertyTypeKind.NullableUInt32 => PropertyTypeKind.UInt32,
|
|
PropertyTypeKind.NullableUInt64 => PropertyTypeKind.UInt64, PropertyTypeKind.NullableBoolean => PropertyTypeKind.Boolean,
|
|
PropertyTypeKind.NullableSingle => PropertyTypeKind.Single, PropertyTypeKind.NullableDouble => PropertyTypeKind.Double,
|
|
PropertyTypeKind.NullableDecimal => PropertyTypeKind.Decimal, PropertyTypeKind.NullableDateTime => PropertyTypeKind.DateTime,
|
|
PropertyTypeKind.NullableDateTimeOffset => PropertyTypeKind.DateTimeOffset, PropertyTypeKind.NullableTimeSpan => PropertyTypeKind.TimeSpan,
|
|
PropertyTypeKind.NullableGuid => PropertyTypeKind.Guid, PropertyTypeKind.NullableEnum => PropertyTypeKind.Enum,
|
|
_ => PropertyTypeKind.Unknown
|
|
};
|
|
|
|
#endregion
|
|
}
|
|
|
|
internal sealed class SerializableClassInfo
|
|
{
|
|
public string Namespace { get; }
|
|
public string ClassName { get; }
|
|
public string FullTypeName { get; }
|
|
public List<PropInfo> Properties { get; }
|
|
public SerializableClassInfo(string ns, string cn, string ftn, List<PropInfo> p)
|
|
{ Namespace = ns; ClassName = cn; FullTypeName = ftn; Properties = p; }
|
|
}
|
|
|
|
internal sealed class PropInfo
|
|
{
|
|
public string Name { get; }
|
|
public string TypeName { get; }
|
|
public PropertyTypeKind TypeKind { get; }
|
|
public bool IsNullable { get; }
|
|
public PropInfo(string n, string tn, PropertyTypeKind tk, bool nullable)
|
|
{ Name = n; TypeName = tn; TypeKind = tk; IsNullable = nullable; }
|
|
}
|
|
|
|
internal enum PropertyTypeKind
|
|
{
|
|
Unknown, String, Int32, Int64, Int16, Byte, UInt16, UInt32, UInt64,
|
|
Boolean, Single, Double, Decimal, DateTime, DateTimeOffset, TimeSpan, Guid, Enum,
|
|
Collection, Complex,
|
|
NullableInt32, NullableInt64, NullableInt16, NullableByte, NullableUInt16, NullableUInt32, NullableUInt64,
|
|
NullableBoolean, NullableSingle, NullableDouble, NullableDecimal, NullableDateTime,
|
|
NullableDateTimeOffset, NullableTimeSpan, NullableGuid, NullableEnum
|
|
}
|