Skip to content

Commit c65fc31

Browse files
Add internal docs for C# bindings packages (#2938)
# Description of Changes Adds some docs to the C# bindings packages. # API and ABI breaking changes None # Expected complexity level and risk 0 # Testing N/A --------- Signed-off-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com> Co-authored-by: Tyler Cloutier <cloutiertyler@users.noreply.github.com>
1 parent b8cf10c commit c65fc31

7 files changed

Lines changed: 141 additions & 31 deletions

File tree

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
> ⚠️ **Internal Project** ⚠️
2+
>
3+
> This project is intended for internal use only. It is **not** stable and may change without notice.
4+
5+
## Internal documentation
6+
7+
This project contains Roslyn [incremental source generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md) that augment types with methods for self-describing and serialization. It relies on the [BSATN.Runtime](../BSATN.Runtime/) library in the generated code.
8+
9+
This project provides `[SpacetimeDB.Type]`. This attribute makes types self-describing, allowing them to automatically register their structure with SpacetimeDB. It also generates serialization code to the [BSATN format](https://spacetimedb.com/docs/bsatn). Any C# type annotated with `[SpacetimeDB.Type]` can be used as a table column or reducer argument.
10+
11+
Any `[SpacetimeDB.Type]` must be marked `partial` to allow the generated code to add new functionality.
12+
13+
`[SpacetimeDB.Type]` also supports emulation of tagged enums in C#. For that, the struct needs to inherit a marker interface `SpacetimeDB.TaggedEnum<Variants>` where `Variants` is a named tuple of all possible variants, e.g.:
14+
15+
```csharp
16+
[SpacetimeDB.Type]
17+
partial record Option<T> : SpacetimeDB.TaggedEnum<(T Some, Unit None)>;
18+
```
19+
20+
will generate inherited records `Option.Some(T Some_)` and `Option.None(Unit None_)`. It allows
21+
you to use tagged enums in C# in a similar way to Rust enums by leveraging C# pattern-matching
22+
on any instance of `Option<T>`.
23+
24+
## What is generated
25+
26+
See [`../Codegen.Tests/fixtures/client/snapshots`](../Codegen.Tests/fixtures/client/snapshots/) for examples of the generated code.
27+
[`../Codegen.Tests/fixtures/server/snapshots`](../Codegen.Tests/fixtures/server/snapshots/) also has examples, those filenames starting with `Type#`.
28+
In addition, in any project using this library, you can set `<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>` in the `<PropertyGroup>` of your `.csproj` to see exactly what code is geing generated for your project.
29+
30+
`[SpacetimeDB.Type]` automatically generates correct `Equals`, `GetHashCode`, and `ToString` methods for the type. It also generates serialization code.
31+
32+
Any `[SpacetimeDB.Type]` will have an auto-generated member struct named `BSATN`. This struct is zero-sized and implements the interface `SpacetimeDB.BSATN.IReadWrite<T>` interface. This is used to serialize and deserialize elements of the struct.
33+
34+
```csharp
35+
[SpacetimeDB.Type]
36+
partial struct Banana {
37+
public int Freshness;
38+
public int LengthMeters;
39+
}
40+
41+
void Example(System.IO.BinaryReader reader, System.IO.BinaryWriter writer) {
42+
Banana.BSATN serializer = new();
43+
Banana banana1 = serializer.Read(reader); // read a BSATN-encoded Banana from the reader.
44+
Banana banana2 = serializer.Read(reader);
45+
Console.Log($"bananas: {banana1} {banana2}");
46+
Console.Log($"equal?: {banana1.Equals(banana2)}");
47+
serializer.write(writer, banana2); // write a BSATN-encoded Banana to the writer.
48+
serializer.write(writer, banana1);
49+
}
50+
```
51+
52+
Since `Banana.BSATN` takes up no space in memory, allocating one is free. We use this pattern because the C# versions we target don't support static interface methods.
53+
54+
`[SpacetimeDB.Type]`s that do not inherit from `SpacetimeDB.TaggedEnum` implement an additional interface, `IStructuralReadWrite`. This allows them to be read without using a serializer. (This is not possible for `TaggedEnum`s because their concrete type is not known before deserialization.)
55+
56+
```csharp
57+
void Example(System.IO.BinaryReader reader, System.IO.BinaryWriter writer) {
58+
Banana banana = new(); // has default field values.
59+
banana.ReadFields(reader); // now it is initialized.
60+
banana.WriteFields(writer); // and we can write it out directly as well.
61+
}
62+
```
63+
64+
The `IReadWrite` interface has an additional method, `AlgebraicType GetAlgebraicType()`. This returns a description of the type that is used during module initialization; see [`../Runtime`](../Runtime/) for more information.
65+
66+
## Testing
67+
The testing for this project lives in two places.
68+
- [`../Codegen.Tests`](../Codegen.Tests/) contains snapshot-based tests. These verify that the generated code looks as expected and allow it to be reviewed more easily.
69+
- Randomized runtime tests live in [`../BSATN.Runtime.Tests`](../BSATN.Runtime.Tests/). These tests randomly fuzz the generated serializers for a variety of types.

crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ namespace SpacetimeDB;
66
using SpacetimeDB.BSATN;
77
using Xunit;
88

9+
/// <summary>
10+
/// Unit and randomized tests for the BSATN.Runtime library. Some tests here also test BSATN.Codegen.
11+
///
12+
/// Randomized tests use the CsCheck library to generate large numbers of sample inputs and check that these libraries
13+
/// maintain certain invariants for all of them.
14+
/// </summary>
915
public static partial class BSATNRuntimeTests
1016
{
1117
[Fact]
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
> ⚠️ **Internal Project** ⚠️
2+
>
3+
> This project is intended for internal use only. It is **not** stable and may change without notice.
4+
5+
## Internal documentation
6+
7+
This project contains interfaces and runtime support code for [BSATN serialization](https://spacetimedb.com/docs/bsatn) in C#. It is the companion of the [BSATN.Codegen](../BSATN.Codegen/) project.
8+
9+
See [`../BSATN.Runtime.Tests/`](../BSATN.Runtime.Tests/) for tests.
10+
11+
### User-facing types
12+
13+
This project contains implementations of a number of wide integer types for compatibility with SpacetimeDB. It also has implementations of a number of "special" SpacetimeDB types, including `Identity`, `ConnectionId`, `Timestamp`, `TimeDuration`, and `ScheduleAt`. These live in [`./Builtins.cs`](./Builtins.cs). It also contains the `AlgebraicType` type, which is not really user-facing but is important internally.
14+
15+
There are also two of important interfaces: `IStructuralReadWrite` and `IReadWrite<T>`. See their documentation in [`./BSATN/Runtime.cs`](./BSATN/Runtime.cs) for more information.
16+
17+
### Internal types
18+
19+
This project contains the base implementation of serializers for various primitive BSATN types. These live in [`./BSATN/Runtime.cs`](./BSATN/Runtime.cs). These serializers are mainly used in code generated by `BSATN.Codegen`.

crates/bindings-csharp/Codegen.Tests/Tests.cs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,16 @@ namespace SpacetimeDB.Codegen.Tests;
88
using Microsoft.CodeAnalysis.MSBuild;
99
using Microsoft.CodeAnalysis.Text;
1010

11+
/// <summary>
12+
/// Snapshot tests for the <c>SpacetimeDB.Codegen</c> library.
13+
///
14+
/// These run code generation for the sample projects in <c>fixtures</c>. We compare the generated code
15+
/// to known-good examples of generated code using the Verify library: https://github.com/VerifyTests/Verify
16+
///
17+
/// If you need to update the generated code, you probably want to install the Verify.Terminal tool: https://github.com/VerifyTests/Verify.Terminal
18+
/// Run <c>dotnet tool restore; dotnet verify accept</c> after changing the code generation to compare the old and new generated code and approve it.
19+
/// You'll need to check the updated snapshots into Git with your PR; the .gitignores in this project are set up to add the right files.
20+
/// </summary>
1121
public static class GeneratorSnapshotTests
1222
{
1323
// Note that we can't use assembly path here because it will be put in some deep nested folder.
Lines changed: 8 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,17 @@
1-
# SpacetimeDB.Codegen
1+
> ⚠️ **Internal Project** ⚠️
2+
>
3+
> This project is intended for internal use only. It is **not** stable and may change without notice.
24
3-
This project contains Roslyn [incremental source generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md) that augment types and tables with static methods for self-describing and registration. They look for different attributes to know which types to augment:
5+
See the [C# module library reference](https://spacetimedb.com/docs/modules/c-sharp) for stable, user-facing documentation.
46

5-
- `[SpacetimeDB.Type]` - generates a `GetSatsTypeInfo()` static method that registers this type with the runtime and returns a `TypeInfo` object. It supports only `struct`s for now to explicitly forbid infinitely recursive types and to make the implementation simpler, as it doesn't need to deal with type references - each table is registered as an entirely self-contained type together with its nested structs if any. This is unlikely to be a problem in common scenarios, but it will be optimised in the future.
7+
## Internal documentation
68

7-
All the nested fields will be added to the product type. Because it's not possible to implement static extension methods on 3rd-party types (including built-ins) in C#, the codegen is responsible for manually routing different types to their `TypeInfo` descriptors. See various static `TypeInfo` properties and helper methods on `SpacetimeDB.BSATN.AlgebraicType` (`Runtime/AlgebraicType.cs`) and routing logic in `Utils.GetTypeInfo` (`Codegen/Utils.cs`) for more details.
9+
This project contains Roslyn [incremental source generators](https://github.com/dotnet/roslyn/blob/main/docs/features/incremental-generators.md) that augment tables and reducers with static methods for self-describing and registration.
810

9-
Also, for the same reason - absence of static extension methods in C# - the codegen expects that your struct, as well as any of its parents, is `partial` so methods can be added from extra source files generated by the codegen.
11+
SpacetimeDB modules are compiled to WebAssembly modules that expose a specific interface; see the [module ABI reference](https://spacetimedb.com/docs/webassembly-abi). This interface is implemented in the generated `FFI` class; see [`../Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs`](../Codegen.Tests/fixtures/server/snapshots/Module#FFI.verified.cs) for an example of what this generated code looks like.
1012

11-
- `[SpacetimeDB.Type]` - also supports emulation of tagged enums in C#. For that, the struct needs to inherit a marker interface `SpacetimeDB.TaggedEnum<Variants>` where `Variants` is a named tuple of all possible variants, e.g.:
12-
13-
```csharp
14-
[SpacetimeDB.Type]
15-
partial record Option<T> : SpacetimeDB.TaggedEnum<(T Some, Unit None)>;
16-
```
17-
18-
will generate inherited records `Option.Some(T Some_)` and `Option.None(Unit None_)`. It allows
19-
you to use tagged enums in C# in a similar way to Rust enums by leveraging C# pattern-matching
20-
on any instance of `Option<T>`.
13+
The source generators are implemented via several attributes usable in module code:
2114

2215
- `[SpacetimeDB.Table]` - generates code to register this table in the `FFI` upon startup so that they can be enumerated by the `__describe_module__` FFI API. It implies `[SpacetimeDB.Type]`, so you must not specify both attributes on the same struct.
2316

24-
The fields can be marked with `[SpacetimeDB.ColumnAttrs]` and those will be detected by the codegen and passed on to the runtime as well. Example:
25-
26-
```csharp
27-
[SpacetimeDB.Table]
28-
public partial struct Person
29-
{
30-
[SpacetimeDB.Column(ColumnAttrs.Identity)]
31-
public int Id;
32-
public string Name;
33-
}
34-
```
35-
3617
- `[SpacetimeDB.Reducer]` - generates code to register a static function as a SpacetimeDB reducer in the `FFI` upon startup and creates a wrapper that will parse SATS binary blob into individual arguments and invoke the underlying function for the `__call_reducer__` FFI API.

crates/bindings-csharp/README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,22 @@
1-
# SpacetimeDB
1+
> ⚠️ **Unstable Project** ⚠️
2+
>
3+
> The interface of this project is **not** stable and may change without notice.
24
3-
These projects contain the SpacetimeDB SATS typesystem, codegen and runtime bindings for SpacetimeDB WebAssembly modules.
5+
See the [C# module library reference](https://spacetimedb.com/docs/modules/c-sharp) and the [C# client SDK reference](https://spacetimedb.com/docs/sdks/c-sharp) for stable, user-facing documentation.
6+
7+
## Internal documentation
8+
9+
These projects contain the SpacetimeDB SATS typesystem, codegen and runtime bindings for SpacetimeDB WebAssembly modules. It also contains serialization code for SpacetimeDB C# clients.
10+
11+
12+
The [`BSATN.Codegen`](./BSATN.Codegen/) and [`BSATN.Runtime`](./BSATN.Runtime/) libraries are used by:
13+
- C# Modules
14+
- and C# Client applications.
15+
16+
Together they provide serialization and deserialization to the BSATN format. See their READMEs for more information.
17+
18+
The [`Codegen`](./Codegen/) and [`Runtime`](./Runtime/) libraries are used:
19+
- only by C# Modules.
20+
21+
They provide all of the functionality needed to write SpacetimeDB modules in C#. See their READMEs for more information.
422

5-
Please refer to documentation inside `Codegen` and `Runtime` folders for more details.

crates/bindings-csharp/Runtime/README.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
> ⚠️ **Internal Project** ⚠️
2+
>
3+
> This project is intended for internal use only. It is **not** stable and may change without notice.
4+
15
# SpacetimeDB.Runtime
26

3-
This project contains the core SpacetimeDB SATS typesystem, attributes for the codegen as well as runtime bindings for SpacetimeDB WebAssembly modules.
7+
This project contains the runtime bindings for SpacetimeDB WebAssembly modules. See the [C# module library reference](https://spacetimedb.com/docs/modules/c-sharp) for stable, user-facing documentation.
8+
9+
SpacetimeDB modules are compiled to WebAssembly modules that expose a specific interface; see the [module ABI reference](https://spacetimedb.com/docs/webassembly-abi).
410

511
The runtime bindings are currently implementing via `Wasi.Sdk` package, which is a .NET implementation of the [WASI](https://wasi.dev/) standard. This is likely to change in the future.
612

@@ -21,3 +27,5 @@ To regenenerate the `Autogen` folder, run:
2127
```sh
2228
cargo run -p spacetimedb-codegen --example regen-csharp-moduledef
2329
```
30+
31+
This folder contains the type definitions used to serialize the `RawModuleDef` that is returned by `__describe_module__`.

0 commit comments

Comments
 (0)