Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 36 additions & 15 deletions crates/bindings-csharp/BSATN.Codegen/Type.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
namespace SpacetimeDB.Codegen;

// Generate code to implement serialization to the BSATN format (https://spacetimedb.com/docs/bsatn).
// C# doesn't support static methods in interfaces, so instead we declare a zero-sized `struct` type that implements
// the serialization interface (IReadWrite) for us.
//
// See BSATN.Runtime for the support code referenced by code generation,
// and see Codegen.Tests/fixtures/*/snapshots for examples of generated code.
// Also, if you set <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles> in a csproj,
// you can find the generated code in obj/Debug/*/generated/SpacetimeDB.BSATN.Codegen.

using System.Collections.Immutable;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
Expand Down Expand Up @@ -27,6 +36,14 @@ namespace SpacetimeDB.Codegen;
/// <param name="BSATNName">The name of the BSATN struct for the type.</param>
public abstract record TypeUse(string Name, string BSATNName)
{
internal static string BSATN_FIELD_SUFFIX = "RW";

/// <summary>
/// The name of the static field containing an IReadWrite in the IReadWrite struct associated with this type.
/// We make sure this is different from the field name so that collisions cannot occur.
/// </summary>
public static string BsatnFieldSuffix => $"{BSATN_FIELD_SUFFIX}";

/// <summary>
/// Parse a type use for a member.
/// </summary>
Expand Down Expand Up @@ -330,14 +347,20 @@ IEnumerable<MemberDeclaration> members
var visStr = SyntaxFacts.GetText(visibility);
return string.Join(
"\n ",
members.Select(m => $"{visStr} static readonly {m.Type.BSATNName} {m.Name} = new();")
members.Select(m =>
$"{visStr} static readonly {m.Type.BSATNName} {m.Name}{TypeUse.BsatnFieldSuffix} = new();"
)
);
}

public static string GenerateDefs(IEnumerable<MemberDeclaration> members) =>
string.Join(
",\n ",
members.Select(m => $"new(nameof({m.Name}), {m.Name}.GetAlgebraicType(registrar))")
// we can't use nameof(m.Type.BsatnFieldName) because the bsatn field name differs from the logical name
// assigned in the type.
members.Select(m =>
$"new(\"{m.Name}\", {m.Name}{TypeUse.BsatnFieldSuffix}.GetAlgebraicType(registrar))"
)
);
}

Expand Down Expand Up @@ -450,8 +473,6 @@ public Scope.Extensions ToExtensions()
var extensions = new Scope.Extensions(Scope, FullName);

var bsatnDecls = Members.Cast<MemberDeclaration>();
var fieldNames = bsatnDecls.Select(m => m.Name);
var fieldNamesAndIds = fieldNames.Select((name, i) => (name, i));

extensions.BaseTypes.Add($"System.IEquatable<{ShortName}>");

Expand Down Expand Up @@ -480,8 +501,8 @@ public override string ToString() =>
return reader.ReadByte() switch {
{{string.Join(
"\n ",
fieldNames.Select((name, i) =>
$"{i} => new {name}({name}.Read(reader)),"
bsatnDecls.Select((m, i) =>
$"{i} => new {m.Name}({m.Name}{TypeUse.BsatnFieldSuffix}.Read(reader)),"
)
)}}
_ => throw new System.InvalidOperationException("Invalid tag value, this state should be unreachable.")
Expand All @@ -492,12 +513,12 @@ public override string ToString() =>
switch (value) {
{{string.Join(
"\n",
fieldNames.Select((name, i) => $"""
case {name}(var inner):
writer.Write((byte){i});
{name}.Write(writer, inner);
break;
"""))}}
bsatnDecls.Select((m, i) => $"""
case {m.Name}(var inner):
writer.Write((byte){i});
{m.Name}{TypeUse.BsatnFieldSuffix}.Write(writer, inner);
break;
"""))}}
}
""";

Expand Down Expand Up @@ -530,14 +551,14 @@ public override string ToString() =>
public void ReadFields(System.IO.BinaryReader reader) {
{{string.Join(
"\n",
fieldNames.Select(name => $" {name} = BSATN.{name}.Read(reader);")
bsatnDecls.Select(m => $" {m.Name} = BSATN.{m.Name}{TypeUse.BsatnFieldSuffix}.Read(reader);")
)}}
}

public void WriteFields(System.IO.BinaryWriter writer) {
{{string.Join(
"\n",
fieldNames.Select(name => $" BSATN.{name}.Write(writer, {name});")
bsatnDecls.Select(m => $" BSATN.{m.Name}{TypeUse.BsatnFieldSuffix}.Write(writer, {m.Name});")
)}}
}

Expand All @@ -557,7 +578,7 @@ object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer() {
public override string ToString() =>
$"{{ShortName}} {{start}} {{string.Join(
", ",
fieldNames.Select(name => $$"""{{name}} = {SpacetimeDB.BSATN.StringUtil.GenericToString({{name}})}""")
bsatnDecls.Select(m => $$"""{{m.Name}} = {SpacetimeDB.BSATN.StringUtil.GenericToString({{m.Name}})}""")
)}} {{end}}";
"""
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@ partial struct CustomClass : System.IEquatable<CustomClass>, SpacetimeDB.BSATN.I
{
public void ReadFields(System.IO.BinaryReader reader)
{
IntField = BSATN.IntField.Read(reader);
StringField = BSATN.StringField.Read(reader);
IntField = BSATN.IntFieldRW.Read(reader);
StringField = BSATN.StringFieldRW.Read(reader);
}

public void WriteFields(System.IO.BinaryWriter writer)
{
BSATN.IntField.Write(writer, IntField);
BSATN.StringField.Write(writer, StringField);
BSATN.IntFieldRW.Write(writer, IntField);
BSATN.StringFieldRW.Write(writer, StringField);
}

object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()
Expand All @@ -26,8 +26,8 @@ public override string ToString() =>

public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomClass>
{
internal static readonly SpacetimeDB.BSATN.I32 IntField = new();
internal static readonly SpacetimeDB.BSATN.String StringField = new();
internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();
internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();

public CustomClass Read(System.IO.BinaryReader reader)
{
Expand All @@ -47,8 +47,8 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar
registrar.RegisterType<CustomClass>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(
new SpacetimeDB.BSATN.AggregateElement[]
{
new(nameof(IntField), IntField.GetAlgebraicType(registrar)),
new(nameof(StringField), StringField.GetAlgebraicType(registrar))
new("IntField", IntFieldRW.GetAlgebraicType(registrar)),
new("StringField", StringFieldRW.GetAlgebraicType(registrar))
}
));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ partial struct CustomStruct
{
public void ReadFields(System.IO.BinaryReader reader)
{
IntField = BSATN.IntField.Read(reader);
StringField = BSATN.StringField.Read(reader);
IntField = BSATN.IntFieldRW.Read(reader);
StringField = BSATN.StringFieldRW.Read(reader);
}

public void WriteFields(System.IO.BinaryWriter writer)
{
BSATN.IntField.Write(writer, IntField);
BSATN.StringField.Write(writer, StringField);
BSATN.IntFieldRW.Write(writer, IntField);
BSATN.StringFieldRW.Write(writer, StringField);
}

object SpacetimeDB.BSATN.IStructuralReadWrite.GetSerializer()
Expand All @@ -28,8 +28,8 @@ public override string ToString() =>

public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomStruct>
{
internal static readonly SpacetimeDB.BSATN.I32 IntField = new();
internal static readonly SpacetimeDB.BSATN.String StringField = new();
internal static readonly SpacetimeDB.BSATN.I32 IntFieldRW = new();
internal static readonly SpacetimeDB.BSATN.String StringFieldRW = new();

public CustomStruct Read(System.IO.BinaryReader reader)
{
Expand All @@ -49,8 +49,8 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar
registrar.RegisterType<CustomStruct>(_ => new SpacetimeDB.BSATN.AlgebraicType.Product(
new SpacetimeDB.BSATN.AggregateElement[]
{
new(nameof(IntField), IntField.GetAlgebraicType(registrar)),
new(nameof(StringField), StringField.GetAlgebraicType(registrar))
new("IntField", IntFieldRW.GetAlgebraicType(registrar)),
new("StringField", StringFieldRW.GetAlgebraicType(registrar))
}
));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ public override string ToString() =>

public readonly partial struct BSATN : SpacetimeDB.BSATN.IReadWrite<CustomTaggedEnum>
{
internal static readonly SpacetimeDB.BSATN.I32 IntVariant = new();
internal static readonly SpacetimeDB.BSATN.String StringVariant = new();
internal static readonly SpacetimeDB.BSATN.I32 IntVariantRW = new();
internal static readonly SpacetimeDB.BSATN.String StringVariantRW = new();

public CustomTaggedEnum Read(System.IO.BinaryReader reader)
{
return reader.ReadByte() switch
{
0 => new IntVariant(IntVariant.Read(reader)),
1 => new StringVariant(StringVariant.Read(reader)),
0 => new IntVariant(IntVariantRW.Read(reader)),
1 => new StringVariant(StringVariantRW.Read(reader)),
_
=> throw new System.InvalidOperationException(
"Invalid tag value, this state should be unreachable."
Expand All @@ -40,11 +40,11 @@ public void Write(System.IO.BinaryWriter writer, CustomTaggedEnum value)
{
case IntVariant(var inner):
writer.Write((byte)0);
IntVariant.Write(writer, inner);
IntVariantRW.Write(writer, inner);
break;
case StringVariant(var inner):
writer.Write((byte)1);
StringVariant.Write(writer, inner);
StringVariantRW.Write(writer, inner);
break;
}
}
Expand All @@ -55,8 +55,8 @@ SpacetimeDB.BSATN.ITypeRegistrar registrar
registrar.RegisterType<CustomTaggedEnum>(_ => new SpacetimeDB.BSATN.AlgebraicType.Sum(
new SpacetimeDB.BSATN.AggregateElement[]
{
new(nameof(IntVariant), IntVariant.GetAlgebraicType(registrar)),
new(nameof(StringVariant), StringVariant.GetAlgebraicType(registrar))
new("IntVariant", IntVariantRW.GetAlgebraicType(registrar)),
new("StringVariant", StringVariantRW.GetAlgebraicType(registrar))
}
));

Expand Down
Loading
Loading