Skip to content

Commit 6ba91bb

Browse files
authored
Minor attribute cleanups for C# module (#1753)
1 parent ccd7e84 commit 6ba91bb

File tree

7 files changed

+141
-97
lines changed

7 files changed

+141
-97
lines changed

crates/bindings-csharp/BSATN.Codegen/Utils.cs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,10 @@ IEnumerable<T> values
182182
_ => constant.Value,
183183
};
184184

185-
public static T ParseAs<T>(this AttributeData attrData)
185+
public static T ParseAs<T>(this AttributeData attrData, System.Type? type = null)
186186
where T : Attribute
187187
{
188+
type ??= typeof(T);
188189
var ctorArgs = attrData.ConstructorArguments.Select(ResolveConstant).ToArray();
189190
// For now only support attributes with a single constructor.
190191
//
@@ -193,10 +194,10 @@ public static T ParseAs<T>(this AttributeData attrData)
193194
// which prevent APIs like `Activator.CreateInstance` from finding the constructor.
194195
//
195196
// Expand logic in the future if it ever becomes actually necessary.
196-
var attr = (T)typeof(T).GetConstructors().Single().Invoke(ctorArgs);
197+
var attr = (T)type.GetConstructors().Single().Invoke(ctorArgs);
197198
foreach (var arg in attrData.NamedArguments)
198199
{
199-
typeof(T).GetProperty(arg.Key).SetValue(attr, ResolveConstant(arg.Value));
200+
type.GetProperty(arg.Key).SetValue(attr, ResolveConstant(arg.Value));
200201
}
201202
return attr;
202203
}

crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#PublicTable.verified.cs

Lines changed: 6 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bindings-csharp/Codegen.Tests/fixtures/server/snapshots/Module#Timers.SendMessageTimer.verified.cs

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

crates/bindings-csharp/Codegen/Module.cs

Lines changed: 45 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,37 @@ namespace SpacetimeDB.Codegen;
44
using Microsoft.CodeAnalysis;
55
using Microsoft.CodeAnalysis.CSharp;
66
using Microsoft.CodeAnalysis.CSharp.Syntax;
7+
using SpacetimeDB.Internal;
78
using static Utils;
89

10+
readonly record struct ColumnAttr(ColumnAttrs Mask, string? Table = null)
11+
{
12+
private static readonly ImmutableDictionary<string, System.Type> AttrTypes = ImmutableArray
13+
.Create(
14+
typeof(AutoIncAttribute),
15+
typeof(PrimaryKeyAttribute),
16+
typeof(UniqueAttribute),
17+
typeof(IndexedAttribute)
18+
)
19+
.ToImmutableDictionary(t => t.FullName);
20+
21+
public static ColumnAttr Parse(AttributeData attrData)
22+
{
23+
if (
24+
attrData.AttributeClass is not { } attrClass
25+
|| !AttrTypes.TryGetValue(attrClass.ToString(), out var attrType)
26+
)
27+
{
28+
return default;
29+
}
30+
var attr = attrData.ParseAs<ColumnAttribute>(attrType);
31+
return new(attr.Mask, attr.Table);
32+
}
33+
}
34+
935
record ColumnDeclaration : MemberDeclaration
1036
{
11-
public readonly EquatableArray<(string? table, ColumnAttrs mask)> Attrs;
37+
public readonly EquatableArray<ColumnAttr> Attrs;
1238
public readonly bool IsEquatable;
1339
public readonly string FullTableName;
1440

@@ -22,11 +48,16 @@ bool isEquatable
2248
)
2349
: base(name, type, typeInfo)
2450
{
25-
Attrs = new(ImmutableArray.Create((default(string), attrs)));
51+
Attrs = new(ImmutableArray.Create(new ColumnAttr(attrs)));
2652
IsEquatable = isEquatable;
2753
FullTableName = tableName;
2854
}
2955

56+
// A helper to combine multiple column attributes into a single mask.
57+
// Note: it doesn't check the table names, this is left up to the caller.
58+
private static ColumnAttrs CombineColumnAttrs(IEnumerable<ColumnAttr> attrs) =>
59+
attrs.Aggregate(ColumnAttrs.UnSet, (mask, attr) => mask | attr.Mask);
60+
3061
public ColumnDeclaration(string tableName, IFieldSymbol field)
3162
: base(field)
3263
{
@@ -35,21 +66,12 @@ public ColumnDeclaration(string tableName, IFieldSymbol field)
3566
Attrs = new(
3667
field
3768
.GetAttributes()
38-
.Select(a =>
39-
(
40-
table: a.NamedArguments.FirstOrDefault(a => a.Key == "Table").Value.Value
41-
as string,
42-
attr: a.AttributeClass?.ToString() switch
43-
{
44-
"SpacetimeDB.AutoIncAttribute" => ColumnAttrs.AutoInc,
45-
"SpacetimeDB.PrimaryKeyAttribute" => ColumnAttrs.PrimaryKey,
46-
"SpacetimeDB.UniqueAttribute" => ColumnAttrs.Unique,
47-
"SpacetimeDB.IndexedAttribute" => ColumnAttrs.Indexed,
48-
_ => ColumnAttrs.UnSet,
49-
}
50-
)
69+
.Select(ColumnAttr.Parse)
70+
.Where(a => a.Mask != ColumnAttrs.UnSet)
71+
.GroupBy(
72+
a => a.Table,
73+
(key, group) => new ColumnAttr(CombineColumnAttrs(group), key)
5174
)
52-
.Where(a => a.attr != ColumnAttrs.UnSet)
5375
.ToImmutableArray()
5476
);
5577

@@ -75,7 +97,7 @@ or SpecialType.System_Int64
7597
_ => false,
7698
};
7799

78-
var attrs = Attrs.Aggregate(ColumnAttrs.UnSet, (xs, x) => xs | x.mask);
100+
var attrs = CombineColumnAttrs(Attrs);
79101

80102
if (attrs.HasFlag(ColumnAttrs.AutoInc) && !isInteger)
81103
{
@@ -107,9 +129,7 @@ or SpecialType.System_Int64
107129
}
108130

109131
public ColumnAttrs GetAttrs(string tableName) =>
110-
Attrs
111-
.Where(x => x.table == null || x.table == tableName)
112-
.Aggregate(ColumnAttrs.UnSet, (xs, x) => xs | x.mask);
132+
CombineColumnAttrs(Attrs.Where(x => x.Table == null || x.Table == tableName));
113133

114134
// For the `TableDesc` constructor.
115135
public string GenerateColumnDef() =>
@@ -128,16 +148,11 @@ record TableView
128148

129149
public TableView(TableDeclaration table, AttributeData data)
130150
{
131-
Name =
132-
data.NamedArguments.FirstOrDefault(x => x.Key == "Name").Value.Value as string
133-
?? table.ShortName;
134-
135-
IsPublic = data.NamedArguments.Any(pair => pair is { Key: "Public", Value.Value: true });
151+
var attr = data.ParseAs<TableAttribute>();
136152

137-
Scheduled = data
138-
.NamedArguments.Where(pair => pair.Key == "Scheduled")
139-
.Select(pair => (string?)pair.Value.Value)
140-
.SingleOrDefault();
153+
Name = attr.Name ?? table.ShortName;
154+
IsPublic = attr.Public;
155+
Scheduled = attr.Scheduled;
141156
}
142157
}
143158

@@ -347,7 +362,7 @@ public override Scope.Extensions ToExtensions()
347362
nameof({{ShortName}}),
348363
{{tuple.pos}},
349364
nameof({{tuple.col.Name}}),
350-
(SpacetimeDB.ColumnAttrs){{(int)tuple.attr}}
365+
SpacetimeDB.Internal.ColumnAttrs.{{tuple.attr}}
351366
)
352367
"""
353368
)
Lines changed: 72 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,87 @@
1-
namespace SpacetimeDB;
2-
3-
[Flags]
4-
public enum ColumnAttrs : byte
1+
namespace SpacetimeDB
52
{
6-
UnSet = 0b0000,
7-
Indexed = 0b0001,
8-
AutoInc = 0b0010,
9-
Unique = Indexed | 0b0100,
10-
Identity = Unique | AutoInc,
11-
PrimaryKey = Unique | 0b1000,
12-
PrimaryKeyAuto = PrimaryKey | AutoInc,
3+
namespace Internal
4+
{
5+
[Flags]
6+
public enum ColumnAttrs : byte
7+
{
8+
UnSet = 0b0000,
9+
Indexed = 0b0001,
10+
AutoInc = 0b0010,
11+
Unique = Indexed | 0b0100,
12+
Identity = Unique | AutoInc,
13+
PrimaryKey = Unique | 0b1000,
14+
PrimaryKeyAuto = PrimaryKey | AutoInc,
15+
}
1316

14-
// A legacy alias, originally defined as `PrimaryKey | Identity` which is numerically same as above.
15-
PrimaryKeyIdentity = PrimaryKeyAuto,
16-
}
17+
[AttributeUsage(AttributeTargets.Field)]
18+
public abstract class ColumnAttribute : Attribute
19+
{
20+
public string? Table { get; init; }
21+
internal abstract ColumnAttrs Mask { get; }
22+
}
23+
}
1724

18-
/// <summary>
19-
/// Registers a type as the row structure of a SpacetimeDB table, enabling codegen for it.
20-
///
21-
/// <para>
22-
/// Multiple [Table] attributes per type are supported. This is useful to reuse row types.
23-
/// Each attribute instance must have a unique name and will create a SpacetimeDB table.
24-
/// </para>
25-
/// </summary>
26-
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true)]
27-
public sealed class TableAttribute : Attribute
28-
{
2925
/// <summary>
30-
/// This identifier is used to name the SpacetimeDB table on the host as well as the
31-
/// table handle structures generated to access the table from within a reducer call.
26+
/// Registers a type as the row structure of a SpacetimeDB table, enabling codegen for it.
3227
///
33-
/// <para>Defaults to the <c>nameof</c> of the target type.</para>
28+
/// <para>
29+
/// Multiple [Table] attributes per type are supported. This is useful to reuse row types.
30+
/// Each attribute instance must have a unique name and will create a SpacetimeDB table.
31+
/// </para>
3432
/// </summary>
35-
public string? Name { get; init; }
33+
[AttributeUsage(AttributeTargets.Struct | AttributeTargets.Class, AllowMultiple = true)]
34+
public sealed class TableAttribute : Attribute
35+
{
36+
/// <summary>
37+
/// This identifier is used to name the SpacetimeDB table on the host as well as the
38+
/// table handle structures generated to access the table from within a reducer call.
39+
///
40+
/// <para>Defaults to the <c>nameof</c> of the target type.</para>
41+
/// </summary>
42+
public string? Name { get; init; }
3643

37-
/// <summary>
38-
/// Set to <c>true</c> to make the table visible to everyone.
39-
///
40-
/// <para>Defaults to the table only being visible to its owner.</para>
41-
/// </summary>
42-
public bool Public { get; init; } = false;
44+
/// <summary>
45+
/// Set to <c>true</c> to make the table visible to everyone.
46+
///
47+
/// <para>Defaults to the table only being visible to its owner.</para>
48+
/// </summary>
49+
public bool Public { get; init; } = false;
4350

44-
public string? Scheduled { get; init; }
45-
}
51+
public string? Scheduled { get; init; }
52+
}
4653

47-
[AttributeUsage(AttributeTargets.Field)]
48-
public abstract class ColumnAttribute : Attribute
49-
{
50-
public string? Table { get; init; }
51-
}
52-
53-
public sealed class AutoIncAttribute : ColumnAttribute { }
54+
public sealed class AutoIncAttribute : Internal.ColumnAttribute
55+
{
56+
internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.AutoInc;
57+
}
5458

55-
public sealed class PrimaryKeyAttribute : ColumnAttribute { }
59+
public sealed class PrimaryKeyAttribute : Internal.ColumnAttribute
60+
{
61+
internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.PrimaryKey;
62+
}
5663

57-
public sealed class UniqueAttribute : ColumnAttribute { }
64+
public sealed class UniqueAttribute : Internal.ColumnAttribute
65+
{
66+
internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.Unique;
67+
}
5868

59-
public sealed class IndexedAttribute : ColumnAttribute { }
69+
public sealed class IndexedAttribute : Internal.ColumnAttribute
70+
{
71+
internal override Internal.ColumnAttrs Mask => Internal.ColumnAttrs.Indexed;
72+
}
6073

61-
public static class ReducerKind
62-
{
63-
public const string Init = "__init__";
64-
public const string Update = "__update__";
65-
public const string Connect = "__identity_connected__";
66-
public const string Disconnect = "__identity_disconnected__";
67-
}
74+
public static class ReducerKind
75+
{
76+
public const string Init = "__init__";
77+
public const string Update = "__update__";
78+
public const string Connect = "__identity_connected__";
79+
public const string Disconnect = "__identity_disconnected__";
80+
}
6881

69-
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
70-
public sealed class ReducerAttribute(string? name = null) : Attribute
71-
{
72-
public string? Name => name;
82+
[AttributeUsage(AttributeTargets.Method, Inherited = false)]
83+
public sealed class ReducerAttribute(string? name = null) : Attribute
84+
{
85+
public string? Name => name;
86+
}
7387
}

crates/bindings-csharp/SpacetimeSharpSATS.sln

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "spacetimedb-quickstart-serv
2626
EndProject
2727
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "sdk-test-multi-cs", "..\..\modules\sdk-test-multi-cs\sdk-test-multi-cs.csproj", "{960384A9-D78F-4C07-986A-1D1F3846AEBE}"
2828
EndProject
29+
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sdk-tests", "sdk-tests", "{D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}"
30+
EndProject
2931
Global
3032
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3133
Debug|Any CPU = Debug|Any CPU
@@ -53,25 +55,27 @@ Global
5355
{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}.Release|Any CPU.ActiveCfg = Release|Any CPU
5456
{2C282EBD-8E37-4F4C-8EE1-E91E21E75FEE}.Release|Any CPU.Build.0 = Release|Any CPU
5557
{5393711C-44B0-4752-B8D0-852C73D6866F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
56-
{5393711C-44B0-4752-B8D0-852C73D6866F}.Debug|Any CPU.Build.0 = Debug|Any CPU
5758
{5393711C-44B0-4752-B8D0-852C73D6866F}.Release|Any CPU.ActiveCfg = Release|Any CPU
5859
{5393711C-44B0-4752-B8D0-852C73D6866F}.Release|Any CPU.Build.0 = Release|Any CPU
5960
{40F1C615-EDD9-463F-A012-B232F6710FA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
60-
{40F1C615-EDD9-463F-A012-B232F6710FA5}.Debug|Any CPU.Build.0 = Debug|Any CPU
6161
{40F1C615-EDD9-463F-A012-B232F6710FA5}.Release|Any CPU.ActiveCfg = Release|Any CPU
6262
{40F1C615-EDD9-463F-A012-B232F6710FA5}.Release|Any CPU.Build.0 = Release|Any CPU
6363
{FDACD960-168E-44F9-B036-2E29EA391BE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
64-
{FDACD960-168E-44F9-B036-2E29EA391BE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
6564
{FDACD960-168E-44F9-B036-2E29EA391BE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
6665
{FDACD960-168E-44F9-B036-2E29EA391BE7}.Release|Any CPU.Build.0 = Release|Any CPU
6766
{960384A9-D78F-4C07-986A-1D1F3846AEBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68-
{960384A9-D78F-4C07-986A-1D1F3846AEBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
6967
{960384A9-D78F-4C07-986A-1D1F3846AEBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
7068
{960384A9-D78F-4C07-986A-1D1F3846AEBE}.Release|Any CPU.Build.0 = Release|Any CPU
7169
EndGlobalSection
7270
GlobalSection(SolutionProperties) = preSolution
7371
HideSolutionNode = FALSE
7472
EndGlobalSection
73+
GlobalSection(NestedProjects) = preSolution
74+
{5393711C-44B0-4752-B8D0-852C73D6866F} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}
75+
{40F1C615-EDD9-463F-A012-B232F6710FA5} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}
76+
{FDACD960-168E-44F9-B036-2E29EA391BE7} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}
77+
{960384A9-D78F-4C07-986A-1D1F3846AEBE} = {D39E8203-6C3C-4C4B-9C7D-7911AA19D7CC}
78+
EndGlobalSection
7579
GlobalSection(ExtensibilityGlobals) = postSolution
7680
SolutionGuid = {8A5DE392-1C9D-4806-B6C7-EDD4D33C5D1E}
7781
EndGlobalSection

modules/Directory.Build.props

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
22
<!-- Only needed when referencing local dependencies as projects. For published packages, these are imported automatically. -->
33
<Import Project="../crates/bindings-csharp/Runtime/build/SpacetimeDB.Runtime.props" />
4+
5+
<!-- Prevent test projects from being picked up by `dotnet pack`. -->
6+
<PropertyGroup>
7+
<IsPackable>false</IsPackable>
8+
</PropertyGroup>
49
</Project>

0 commit comments

Comments
 (0)