diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 149fdd98aa0..ed34802f604 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -120,6 +120,7 @@ jobs: run: | sudo mkdir /stdb sudo chmod 777 /stdb + - name: Checkout C# SDK uses: actions/checkout@v4 with: @@ -127,42 +128,18 @@ jobs: ref: staging path: spacetimedb-csharp-sdk - - name: C# SDK tests + - name: Setup NuGet override for C# SDK + working-directory: spacetimedb-csharp-sdk run: | - ( cd crates/bindings-csharp/BSATN.Runtime && dotnet pack ) - cd spacetimedb-csharp-sdk - - # Write out the nuget config file to `nuget.config`. This causes the spacetimedb-csharp-sdk repository - # to be aware of the local versions of the `bindings-csharp` packages in SpacetimeDB, and use them if - # available. Otherwise, `spacetimedb-csharp-sdk` will use the NuGet versions of the packages. - # This means that (if version numbers match) we will test the local versions of the C# packages, even - # if they're not pushed to NuGet. - # See https://learn.microsoft.com/en-us/nuget/reference/nuget-config-file for more info on the config file, - # and https://tldp.org/LDP/abs/html/here-docs.html for more info on this bash feature. - cat >nuget.config < - - - - - - - - - - - - - - - - - - EOF + dotnet pack ../crates/bindings-csharp/BSATN.Runtime + ./tools~/write-nuget-config.sh .. # clear package caches, so we get fresh ones even if version numbers haven't changed dotnet nuget locals all --clear - dotnet test + + - name: Run C# SDK tests + working-directory: spacetimedb-csharp-sdk + run: dotnet test lints: name: Lints diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawRowLevelSecurityDefV9.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawRowLevelSecurityDefV9.cs index 81c75282e85..9b8100d0741 100644 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawRowLevelSecurityDefV9.cs +++ b/crates/bindings-csharp/Runtime/Internal/Autogen/RawRowLevelSecurityDefV9.cs @@ -18,9 +18,7 @@ public partial class RawRowLevelSecurityDefV9 [DataMember(Name = "sql")] public string Sql; - public RawRowLevelSecurityDefV9( - string Sql - ) + public RawRowLevelSecurityDefV9(string Sql) { this.Sql = Sql; } diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/RawUniqueConstraintDataV9.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/RawUniqueConstraintDataV9.cs index 35abef5e88e..b02f8dc7e41 100644 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/RawUniqueConstraintDataV9.cs +++ b/crates/bindings-csharp/Runtime/Internal/Autogen/RawUniqueConstraintDataV9.cs @@ -18,9 +18,7 @@ public partial class RawUniqueConstraintDataV9 [DataMember(Name = "columns")] public System.Collections.Generic.List Columns; - public RawUniqueConstraintDataV9( - System.Collections.Generic.List Columns - ) + public RawUniqueConstraintDataV9(System.Collections.Generic.List Columns) { this.Columns = Columns; } diff --git a/crates/bindings-csharp/Runtime/Internal/Autogen/Typespace.cs b/crates/bindings-csharp/Runtime/Internal/Autogen/Typespace.cs index d9978916278..bf7112ca5bd 100644 --- a/crates/bindings-csharp/Runtime/Internal/Autogen/Typespace.cs +++ b/crates/bindings-csharp/Runtime/Internal/Autogen/Typespace.cs @@ -18,9 +18,7 @@ public partial class Typespace [DataMember(Name = "types")] public System.Collections.Generic.List Types; - public Typespace( - System.Collections.Generic.List Types - ) + public Typespace(System.Collections.Generic.List Types) { this.Types = Types; } diff --git a/crates/cli/src/subcommands/generate/csharp.rs b/crates/cli/src/subcommands/generate/csharp.rs index e5048385955..6e56401028d 100644 --- a/crates/cli/src/subcommands/generate/csharp.rs +++ b/crates/cli/src/subcommands/generate/csharp.rs @@ -6,7 +6,7 @@ use std::ops::Deref; use convert_case::{Case, Casing}; use spacetimedb_lib::sats::{AlgebraicType, AlgebraicTypeRef, ArrayType, ProductType, SumType}; -use spacetimedb_lib::ReducerDef; +use spacetimedb_lib::{ProductTypeElement, ReducerDef}; use spacetimedb_primitives::ColList; use spacetimedb_schema::def::{BTreeAlgorithm, IndexAlgorithm}; use spacetimedb_schema::schema::TableSchema; @@ -265,26 +265,25 @@ pub fn autogen_csharp_sum(ctx: &GenCtx, name: &str, sum_type: &SumType, namespac } pub fn autogen_csharp_tuple(ctx: &GenCtx, name: &str, tuple: &ProductType, namespace: &str) -> String { - autogen_csharp_product_table_common(ctx, name, tuple, None, namespace) + autogen_csharp_product_common_file(ctx, name, &tuple.elements, "", namespace) } #[allow(deprecated)] pub fn autogen_csharp_table(ctx: &GenCtx, table: &TableDescHack, namespace: &str) -> String { - let tuple = ctx.typespace[table.data].as_product().unwrap(); - autogen_csharp_product_table_common( + autogen_csharp_product_common_file( ctx, csharp_typename(ctx, table.data), - tuple, - Some(table.schema.clone()), + &ctx.typespace[table.data].as_product().unwrap().elements, + "IDatabaseRow", namespace, ) } -fn autogen_csharp_product_table_common( +fn autogen_csharp_product_common_file( ctx: &GenCtx, name: &str, - product_type: &ProductType, - schema: Option, + product_type_elems: &[ProductTypeElement], + base: &str, namespace: &str, ) -> String { let mut output = CsharpAutogen::new( @@ -292,16 +291,29 @@ fn autogen_csharp_product_table_common( &["System.Collections.Generic", "System.Runtime.Serialization"], ); + autogen_csharp_product_common(ctx, &mut output, name, product_type_elems, base, namespace, |_| {}); + + output.into_inner() +} + +fn autogen_csharp_product_common( + ctx: &GenCtx, + output: &mut CodeIndenter, + name: &str, + product_type_elems: &[ProductTypeElement], + base: &str, + namespace: &str, + extra_body: impl FnOnce(&mut CodeIndenter), +) { writeln!(output, "[SpacetimeDB.Type]"); writeln!(output, "[DataContract]"); write!(output, "public partial class {name}"); - if schema.is_some() { - write!(output, " : IDatabaseRow"); + if !base.is_empty() { + write!(output, " : {base}"); } writeln!(output); - indented_block(&mut output, |output| { - let fields = product_type - .elements + indented_block(output, |output| { + let fields = product_type_elems .iter() .map(|field| { let orig_name = field @@ -321,32 +333,38 @@ fn autogen_csharp_product_table_common( }) .collect::>(); - // Generate fully-parameterized constructor. - writeln!(output); - writeln!(output, "public {name}("); - { - indent_scope!(output); - for (i, (field_name, ty)) in fields.iter().enumerate() { - if i != 0 { - writeln!(output, ","); + // If we don't have any fields, the default constructor is fine, otherwise we need to generate our own. + if !fields.is_empty() { + // Generate fully-parameterized constructor. + writeln!(output); + write!(output, "public {name}("); + if fields.len() > 1 { + writeln!(output); + } + { + indent_scope!(output); + for (i, (field_name, ty)) in fields.iter().enumerate() { + if i != 0 { + writeln!(output, ","); + } + write!(output, "{ty} {field_name}"); } - write!(output, "{ty} {field_name}"); } - } - writeln!(output); - writeln!(output, ")"); - indented_block(output, |output| { - for (field_name, _ty) in fields.iter() { - writeln!(output, "this.{field_name} = {field_name};"); + if fields.len() > 1 { + writeln!(output); } - }); - writeln!(output); + writeln!(output, ")"); + indented_block(output, |output| { + for (field_name, _ty) in fields.iter() { + writeln!(output, "this.{field_name} = {field_name};"); + } + }); + writeln!(output); - // Generate default constructor (if the one above is not already parameterless). - if !fields.is_empty() { + // Generate default constructor. writeln!(output, "public {name}()"); indented_block(output, |output| { - for ((field_name, _ty), field) in fields.iter().zip(&*product_type.elements) { + for ((field_name, _ty), field) in fields.iter().zip(product_type_elems) { if let Some(default) = default_init(ctx, &field.algebraic_type) { writeln!(output, "this.{field_name} = {default};"); } @@ -354,9 +372,9 @@ fn autogen_csharp_product_table_common( }); writeln!(output); } - }); - output.into_inner() + extra_body(output); + }); } fn indented_block(output: &mut CodeIndenter, f: impl FnOnce(&mut CodeIndenter) -> R) -> R { @@ -517,40 +535,6 @@ fn autogen_csharp_access_funcs_for_struct( } } -pub fn autogen_csharp_reducer(ctx: &GenCtx, reducer: &ReducerDef, namespace: &str) -> String { - let func_name = &*reducer.name; - let func_name_pascal_case = func_name.to_case(Case::Pascal); - - let mut output = CsharpAutogen::new(namespace, &[]); - - //Args struct - writeln!(output, "[SpacetimeDB.Type]"); - writeln!(output, "public partial class {func_name_pascal_case} : IReducerArgs"); - indented_block(&mut output, |output| { - writeln!(output, "string IReducerArgs.ReducerName => \"{func_name}\";"); - if !reducer.args.is_empty() { - writeln!(output); - } - for arg in reducer.args.iter() { - let name = arg - .name - .as_deref() - .unwrap_or_else(|| panic!("reducer args should have names: {func_name}")); - let arg_type_str = ty_fmt(ctx, &arg.algebraic_type, namespace); - let field_name = name.to_case(Case::Pascal); - - write!(output, "public {arg_type_str} {field_name}"); - // Skip default initializer if it's the same as the implicit default. - if let Some(default) = default_init(ctx, &arg.algebraic_type) { - write!(output, " = {default}"); - } - writeln!(output, ";"); - } - }); - - output.into_inner() -} - pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) -> Vec<(String, String)> { let mut results = Vec::new(); @@ -577,7 +561,14 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) .map(|reducer| reducer.name.deref().to_case(Case::Pascal)) .collect(); - let mut output = CsharpAutogen::new(namespace, &["SpacetimeDB.ClientApi", "System.Collections.Generic"]); + let mut output = CsharpAutogen::new( + namespace, + &[ + "SpacetimeDB.ClientApi", + "System.Collections.Generic", + "System.Runtime.Serialization", + ], + ); writeln!(output, "public sealed class RemoteTables"); indented_block(&mut output, |output| { @@ -665,12 +656,12 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) let delegate_separator = if !reducer.args.is_empty() { ", " } else { "" }; let mut func_params: String = String::new(); - let mut field_inits: String = String::new(); + let mut func_args: String = String::new(); for (arg_i, arg) in reducer.args.iter().enumerate() { if arg_i != 0 { func_params.push_str(", "); - field_inits.push_str(", "); + func_args.push_str(", "); } let name = arg @@ -679,10 +670,9 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) .unwrap_or_else(|| panic!("reducer args should have names: {func_name}")); let arg_type_str = ty_fmt(ctx, &arg.algebraic_type, namespace); let arg_name = name.to_case(Case::Camel); - let field_name = name.to_case(Case::Pascal); write!(func_params, "{arg_type_str} {arg_name}").unwrap(); - write!(field_inits, "{field_name} = {arg_name}").unwrap(); + write!(func_args, "{arg_name}").unwrap(); } writeln!( @@ -699,14 +689,14 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) indented_block(output, |output| { writeln!( output, - "conn.InternalCallReducer(new {func_name_pascal_case} {{ {field_inits} }}, this.SetCallReducerFlags.{func_name_pascal_case}Flags);" + "conn.InternalCallReducer(new Reducer.{func_name_pascal_case}({func_args}), this.SetCallReducerFlags.{func_name_pascal_case}Flags);" ); }); writeln!(output); writeln!( output, - "public bool Invoke{func_name_pascal_case}(EventContext ctx, {func_name_pascal_case} args)" + "public bool Invoke{func_name_pascal_case}(EventContext ctx, Reducer.{func_name_pascal_case} args)" ); indented_block(output, |output| { writeln!(output, "if (On{func_name_pascal_case} == null) return false;"); @@ -765,18 +755,31 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) }); writeln!(output); - writeln!(output, "[Type]"); - writeln!(output, "public partial record Reducer : TaggedEnum<("); - { - indent_scope!(output); - for reducer_name in &reducer_names { - writeln!(output, "{reducer_name} {reducer_name},"); + writeln!(output, "public abstract partial class Reducer"); + indented_block(&mut output, |output| { + // Prevent instantiation of this class from outside. + writeln!(output, "private Reducer() {{ }}"); + writeln!(output); + for (reducer, reducer_name) in std::iter::zip(&reducers, &reducer_names) { + let reducer_str_name = &reducer.name; + autogen_csharp_product_common( + ctx, + output, + reducer_name, + &reducer.args, + "Reducer, IReducerArgs", + namespace, + |output| { + writeln!(output, "string IReducerArgs.ReducerName => \"{reducer_str_name}\";"); + }, + ); + writeln!(output); } - writeln!(output, "Unit StdbNone,"); - writeln!(output, "Unit StdbIdentityConnected,"); - writeln!(output, "Unit StdbIdentityDisconnected"); - } - writeln!(output, ")>;"); + writeln!(output, "public class StdbNone : Reducer {{}}"); + writeln!(output, "public class StdbIdentityConnected : Reducer {{}}"); + writeln!(output, "public class StdbIdentityDisconnected : Reducer {{}}"); + }); + writeln!(output); writeln!( output, @@ -818,19 +821,19 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) let reducer_str_name = &reducer.name; writeln!( output, - "\"{reducer_str_name}\" => new Reducer.{reducer_name}(BSATNHelpers.Decode<{reducer_name}>(encodedArgs))," + "\"{reducer_str_name}\" => BSATNHelpers.Decode(encodedArgs)," ); } - writeln!(output, "\"\" => new Reducer.StdbNone(default),"); + writeln!(output, "\"\" => new Reducer.StdbNone(),"); writeln!( output, - "\"__identity_connected__\" => new Reducer.StdbIdentityConnected(default)," + "\"__identity_connected__\" => new Reducer.StdbIdentityConnected()," ); writeln!( output, - "\"__identity_disconnected__\" => new Reducer.StdbIdentityDisconnected(default)," + "\"__identity_disconnected__\" => new Reducer.StdbIdentityDisconnected()," ); - writeln!(output, "\"\" => new Reducer.StdbNone(default),"); //Transaction from CLI command + writeln!(output, "\"\" => new Reducer.StdbNone(),"); //Transaction from CLI command writeln!( output, r#"var reducer => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {{reducer}}")"# @@ -859,7 +862,7 @@ pub fn autogen_csharp_globals(ctx: &GenCtx, items: &[GenItem], namespace: &str) for reducer_name in &reducer_names { writeln!( output, - "Reducer.{reducer_name}(var args) => Reducers.Invoke{reducer_name}(eventContext, args)," + "Reducer.{reducer_name} args => Reducers.Invoke{reducer_name}(eventContext, args)," ); } writeln!(output, "Reducer.StdbNone or"); diff --git a/crates/cli/src/subcommands/generate/mod.rs b/crates/cli/src/subcommands/generate/mod.rs index 4eb2a375952..c2b75983c2a 100644 --- a/crates/cli/src/subcommands/generate/mod.rs +++ b/crates/cli/src/subcommands/generate/mod.rs @@ -20,7 +20,6 @@ use spacetimedb_schema::def::{ModuleDef, ReducerDef, ScopedTypeName, TableDef, T use spacetimedb_schema::identifier::Identifier; use spacetimedb_schema::schema::{Schema, TableSchema}; use std::fs; -use std::ops::Deref; use std::path::{Path, PathBuf}; use wasmtime::{Caller, StoreContextMut}; @@ -367,11 +366,7 @@ impl GenItem { } _ => todo!(), }, - GenItem::Reducer(reducer) => { - let code = csharp::autogen_csharp_reducer(ctx, reducer, namespace); - let pascalcase = reducer.name.deref().to_case(Case::Pascal); - Some((pascalcase + "Reducer.cs", code)) - } + GenItem::Reducer(_) => None, } } } diff --git a/crates/cli/tests/snapshots/codegen__codegen_csharp.snap b/crates/cli/tests/snapshots/codegen__codegen_csharp.snap index 2d01d0e85d2..dfc63bedf25 100644 --- a/crates/cli/tests/snapshots/codegen__codegen_csharp.snap +++ b/crates/cli/tests/snapshots/codegen__codegen_csharp.snap @@ -1,47 +1,8 @@ --- source: crates/cli/tests/codegen.rs expression: outfiles +snapshot_kind: text --- -"AddPlayerReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class AddPlayer : IReducerArgs - { - string IReducerArgs.ReducerName => "add_player"; - - public string Name = ""; - } -} -''' -"AddPrivateReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class AddPrivate : IReducerArgs - { - string IReducerArgs.ReducerName => "add_private"; - - public string Name = ""; - } -} -''' "Baz.cs" = ''' // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. @@ -62,9 +23,7 @@ namespace SpacetimeDB [DataMember(Name = "field")] public string Field; - public Baz( - string Field - ) + public Baz(string Field) { this.Field = Field; } @@ -77,46 +36,6 @@ namespace SpacetimeDB } } ''' -"DeletePlayerReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class DeletePlayer : IReducerArgs - { - string IReducerArgs.ReducerName => "delete_player"; - - public ulong Id; - } -} -''' -"DeletePlayersByNameReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class DeletePlayersByName : IReducerArgs - { - string IReducerArgs.ReducerName => "delete_players_by_name"; - - public string Name = ""; - } -} -''' "Foobar.cs" = ''' // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. @@ -322,9 +241,7 @@ namespace SpacetimeDB [DataMember(Name = "name")] public string Name; - public Private( - string Name - ) + public Private(string Name) { this.Name = Name; } @@ -337,24 +254,6 @@ namespace SpacetimeDB } } ''' -"QueryPrivateReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class QueryPrivate : IReducerArgs - { - string IReducerArgs.ReducerName => "query_private"; - } -} -''' "RepeatingTestArg.cs" = ''' // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. @@ -398,26 +297,6 @@ namespace SpacetimeDB } } ''' -"RepeatingTestReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class RepeatingTest : IReducerArgs - { - string IReducerArgs.ReducerName => "repeating_test"; - - public SpacetimeDB.RepeatingTestArg Arg = new(); - } -} -''' "TestA.cs" = ''' // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. @@ -481,9 +360,7 @@ namespace SpacetimeDB [DataMember(Name = "foo")] public string Foo; - public TestB( - string Foo - ) + public TestB(string Foo) { this.Foo = Foo; } @@ -496,24 +373,6 @@ namespace SpacetimeDB } } ''' -"TestBtreeIndexArgsReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class TestBtreeIndexArgs : IReducerArgs - { - string IReducerArgs.ReducerName => "test_btree_index_args"; - } -} -''' "TestD.cs" = ''' // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. @@ -534,9 +393,7 @@ namespace SpacetimeDB [DataMember(Name = "test_c")] public SpacetimeDB.Namespace.Types.TestC? TestC; - public TestD( - SpacetimeDB.Namespace.Types.TestC? TestC - ) + public TestD(SpacetimeDB.Namespace.Types.TestC? TestC) { this.TestC = TestC; } @@ -607,9 +464,7 @@ namespace SpacetimeDB [DataMember(Name = "field")] public SpacetimeDB.Foobar Field; - public TestFoobar( - SpacetimeDB.Foobar Field - ) + public TestFoobar(SpacetimeDB.Foobar Field) { this.Field = Field; } @@ -622,29 +477,6 @@ namespace SpacetimeDB } } ''' -"TestReducer.cs" = ''' -// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE -// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. -// - -#nullable enable - -using System; - -namespace SpacetimeDB -{ - [SpacetimeDB.Type] - public partial class Test : IReducerArgs - { - string IReducerArgs.ReducerName => "test"; - - public SpacetimeDB.TestA Arg = new(); - public SpacetimeDB.TestB Arg2 = new(); - public SpacetimeDB.Namespace.Types.TestC Arg3; - public SpacetimeDB.Namespace.TestF Arg4 = null!; - } -} -''' "_Globals/SpacetimeDBClient.cs" = ''' // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD. @@ -655,6 +487,7 @@ namespace SpacetimeDB using System; using SpacetimeDB.ClientApi; using System.Collections.Generic; +using System.Runtime.Serialization; namespace SpacetimeDB { @@ -887,10 +720,10 @@ namespace SpacetimeDB public void AddPlayer(string name) { - conn.InternalCallReducer(new AddPlayer { Name = name }, this.SetCallReducerFlags.AddPlayerFlags); + conn.InternalCallReducer(new Reducer.AddPlayer(name), this.SetCallReducerFlags.AddPlayerFlags); } - public bool InvokeAddPlayer(EventContext ctx, AddPlayer args) + public bool InvokeAddPlayer(EventContext ctx, Reducer.AddPlayer args) { if (OnAddPlayer == null) return false; OnAddPlayer( @@ -904,10 +737,10 @@ namespace SpacetimeDB public void AddPrivate(string name) { - conn.InternalCallReducer(new AddPrivate { Name = name }, this.SetCallReducerFlags.AddPrivateFlags); + conn.InternalCallReducer(new Reducer.AddPrivate(name), this.SetCallReducerFlags.AddPrivateFlags); } - public bool InvokeAddPrivate(EventContext ctx, AddPrivate args) + public bool InvokeAddPrivate(EventContext ctx, Reducer.AddPrivate args) { if (OnAddPrivate == null) return false; OnAddPrivate( @@ -921,10 +754,10 @@ namespace SpacetimeDB public void DeletePlayer(ulong id) { - conn.InternalCallReducer(new DeletePlayer { Id = id }, this.SetCallReducerFlags.DeletePlayerFlags); + conn.InternalCallReducer(new Reducer.DeletePlayer(id), this.SetCallReducerFlags.DeletePlayerFlags); } - public bool InvokeDeletePlayer(EventContext ctx, DeletePlayer args) + public bool InvokeDeletePlayer(EventContext ctx, Reducer.DeletePlayer args) { if (OnDeletePlayer == null) return false; OnDeletePlayer( @@ -938,10 +771,10 @@ namespace SpacetimeDB public void DeletePlayersByName(string name) { - conn.InternalCallReducer(new DeletePlayersByName { Name = name }, this.SetCallReducerFlags.DeletePlayersByNameFlags); + conn.InternalCallReducer(new Reducer.DeletePlayersByName(name), this.SetCallReducerFlags.DeletePlayersByNameFlags); } - public bool InvokeDeletePlayersByName(EventContext ctx, DeletePlayersByName args) + public bool InvokeDeletePlayersByName(EventContext ctx, Reducer.DeletePlayersByName args) { if (OnDeletePlayersByName == null) return false; OnDeletePlayersByName( @@ -955,10 +788,10 @@ namespace SpacetimeDB public void QueryPrivate() { - conn.InternalCallReducer(new QueryPrivate { }, this.SetCallReducerFlags.QueryPrivateFlags); + conn.InternalCallReducer(new Reducer.QueryPrivate(), this.SetCallReducerFlags.QueryPrivateFlags); } - public bool InvokeQueryPrivate(EventContext ctx, QueryPrivate args) + public bool InvokeQueryPrivate(EventContext ctx, Reducer.QueryPrivate args) { if (OnQueryPrivate == null) return false; OnQueryPrivate( @@ -971,10 +804,10 @@ namespace SpacetimeDB public void RepeatingTest(SpacetimeDB.RepeatingTestArg arg) { - conn.InternalCallReducer(new RepeatingTest { Arg = arg }, this.SetCallReducerFlags.RepeatingTestFlags); + conn.InternalCallReducer(new Reducer.RepeatingTest(arg), this.SetCallReducerFlags.RepeatingTestFlags); } - public bool InvokeRepeatingTest(EventContext ctx, RepeatingTest args) + public bool InvokeRepeatingTest(EventContext ctx, Reducer.RepeatingTest args) { if (OnRepeatingTest == null) return false; OnRepeatingTest( @@ -988,10 +821,10 @@ namespace SpacetimeDB public void Test(SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.Namespace.Types.TestC arg3, SpacetimeDB.Namespace.TestF arg4) { - conn.InternalCallReducer(new Test { Arg = arg, Arg2 = arg2, Arg3 = arg3, Arg4 = arg4 }, this.SetCallReducerFlags.TestFlags); + conn.InternalCallReducer(new Reducer.Test(arg, arg2, arg3, arg4), this.SetCallReducerFlags.TestFlags); } - public bool InvokeTest(EventContext ctx, Test args) + public bool InvokeTest(EventContext ctx, Reducer.Test args) { if (OnTest == null) return false; OnTest( @@ -1008,10 +841,10 @@ namespace SpacetimeDB public void TestBtreeIndexArgs() { - conn.InternalCallReducer(new TestBtreeIndexArgs { }, this.SetCallReducerFlags.TestBtreeIndexArgsFlags); + conn.InternalCallReducer(new Reducer.TestBtreeIndexArgs(), this.SetCallReducerFlags.TestBtreeIndexArgsFlags); } - public bool InvokeTestBtreeIndexArgs(EventContext ctx, TestBtreeIndexArgs args) + public bool InvokeTestBtreeIndexArgs(EventContext ctx, Reducer.TestBtreeIndexArgs args) { if (OnTestBtreeIndexArgs == null) return false; OnTestBtreeIndexArgs( @@ -1056,20 +889,164 @@ namespace SpacetimeDB } } - [Type] - public partial record Reducer : TaggedEnum<( - AddPlayer AddPlayer, - AddPrivate AddPrivate, - DeletePlayer DeletePlayer, - DeletePlayersByName DeletePlayersByName, - QueryPrivate QueryPrivate, - RepeatingTest RepeatingTest, - Test Test, - TestBtreeIndexArgs TestBtreeIndexArgs, - Unit StdbNone, - Unit StdbIdentityConnected, - Unit StdbIdentityDisconnected - )>; + public abstract partial class Reducer + { + private Reducer() { } + + [SpacetimeDB.Type] + [DataContract] + public partial class AddPlayer : Reducer, IReducerArgs + { + [DataMember(Name = "name")] + public string Name; + + public AddPlayer(string Name) + { + this.Name = Name; + } + + public AddPlayer() + { + this.Name = ""; + } + + string IReducerArgs.ReducerName => "add_player"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class AddPrivate : Reducer, IReducerArgs + { + [DataMember(Name = "name")] + public string Name; + + public AddPrivate(string Name) + { + this.Name = Name; + } + + public AddPrivate() + { + this.Name = ""; + } + + string IReducerArgs.ReducerName => "add_private"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class DeletePlayer : Reducer, IReducerArgs + { + [DataMember(Name = "id")] + public ulong Id; + + public DeletePlayer(ulong Id) + { + this.Id = Id; + } + + public DeletePlayer() + { + } + + string IReducerArgs.ReducerName => "delete_player"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class DeletePlayersByName : Reducer, IReducerArgs + { + [DataMember(Name = "name")] + public string Name; + + public DeletePlayersByName(string Name) + { + this.Name = Name; + } + + public DeletePlayersByName() + { + this.Name = ""; + } + + string IReducerArgs.ReducerName => "delete_players_by_name"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class QueryPrivate : Reducer, IReducerArgs + { + string IReducerArgs.ReducerName => "query_private"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class RepeatingTest : Reducer, IReducerArgs + { + [DataMember(Name = "arg")] + public SpacetimeDB.RepeatingTestArg Arg; + + public RepeatingTest(SpacetimeDB.RepeatingTestArg Arg) + { + this.Arg = Arg; + } + + public RepeatingTest() + { + this.Arg = new(); + } + + string IReducerArgs.ReducerName => "repeating_test"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class Test : Reducer, IReducerArgs + { + [DataMember(Name = "arg")] + public SpacetimeDB.TestA Arg; + [DataMember(Name = "arg2")] + public SpacetimeDB.TestB Arg2; + [DataMember(Name = "arg3")] + public SpacetimeDB.Namespace.Types.TestC Arg3; + [DataMember(Name = "arg4")] + public SpacetimeDB.Namespace.TestF Arg4; + + public Test( + SpacetimeDB.TestA Arg, + SpacetimeDB.TestB Arg2, + SpacetimeDB.Namespace.Types.TestC Arg3, + SpacetimeDB.Namespace.TestF Arg4 + ) + { + this.Arg = Arg; + this.Arg2 = Arg2; + this.Arg3 = Arg3; + this.Arg4 = Arg4; + } + + public Test() + { + this.Arg = new(); + this.Arg2 = new(); + this.Arg4 = null!; + } + + string IReducerArgs.ReducerName => "test"; + } + + [SpacetimeDB.Type] + [DataContract] + public partial class TestBtreeIndexArgs : Reducer, IReducerArgs + { + string IReducerArgs.ReducerName => "test_btree_index_args"; + } + + public class StdbNone : Reducer {} + public class StdbIdentityConnected : Reducer {} + public class StdbIdentityDisconnected : Reducer {} + } + public class DbConnection : DbConnectionBase { public readonly RemoteTables Db = new(); @@ -1096,18 +1073,18 @@ namespace SpacetimeDB { var encodedArgs = update.ReducerCall.Args; return update.ReducerCall.ReducerName switch { - "add_player" => new Reducer.AddPlayer(BSATNHelpers.Decode(encodedArgs)), - "add_private" => new Reducer.AddPrivate(BSATNHelpers.Decode(encodedArgs)), - "delete_player" => new Reducer.DeletePlayer(BSATNHelpers.Decode(encodedArgs)), - "delete_players_by_name" => new Reducer.DeletePlayersByName(BSATNHelpers.Decode(encodedArgs)), - "query_private" => new Reducer.QueryPrivate(BSATNHelpers.Decode(encodedArgs)), - "repeating_test" => new Reducer.RepeatingTest(BSATNHelpers.Decode(encodedArgs)), - "test" => new Reducer.Test(BSATNHelpers.Decode(encodedArgs)), - "test_btree_index_args" => new Reducer.TestBtreeIndexArgs(BSATNHelpers.Decode(encodedArgs)), - "" => new Reducer.StdbNone(default), - "__identity_connected__" => new Reducer.StdbIdentityConnected(default), - "__identity_disconnected__" => new Reducer.StdbIdentityDisconnected(default), - "" => new Reducer.StdbNone(default), + "add_player" => BSATNHelpers.Decode(encodedArgs), + "add_private" => BSATNHelpers.Decode(encodedArgs), + "delete_player" => BSATNHelpers.Decode(encodedArgs), + "delete_players_by_name" => BSATNHelpers.Decode(encodedArgs), + "query_private" => BSATNHelpers.Decode(encodedArgs), + "repeating_test" => BSATNHelpers.Decode(encodedArgs), + "test" => BSATNHelpers.Decode(encodedArgs), + "test_btree_index_args" => BSATNHelpers.Decode(encodedArgs), + "" => new Reducer.StdbNone(), + "__identity_connected__" => new Reducer.StdbIdentityConnected(), + "__identity_disconnected__" => new Reducer.StdbIdentityDisconnected(), + "" => new Reducer.StdbNone(), var reducer => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}") }; } @@ -1119,14 +1096,14 @@ namespace SpacetimeDB { var eventContext = (EventContext)context; return reducer switch { - Reducer.AddPlayer(var args) => Reducers.InvokeAddPlayer(eventContext, args), - Reducer.AddPrivate(var args) => Reducers.InvokeAddPrivate(eventContext, args), - Reducer.DeletePlayer(var args) => Reducers.InvokeDeletePlayer(eventContext, args), - Reducer.DeletePlayersByName(var args) => Reducers.InvokeDeletePlayersByName(eventContext, args), - Reducer.QueryPrivate(var args) => Reducers.InvokeQueryPrivate(eventContext, args), - Reducer.RepeatingTest(var args) => Reducers.InvokeRepeatingTest(eventContext, args), - Reducer.Test(var args) => Reducers.InvokeTest(eventContext, args), - Reducer.TestBtreeIndexArgs(var args) => Reducers.InvokeTestBtreeIndexArgs(eventContext, args), + Reducer.AddPlayer args => Reducers.InvokeAddPlayer(eventContext, args), + Reducer.AddPrivate args => Reducers.InvokeAddPrivate(eventContext, args), + Reducer.DeletePlayer args => Reducers.InvokeDeletePlayer(eventContext, args), + Reducer.DeletePlayersByName args => Reducers.InvokeDeletePlayersByName(eventContext, args), + Reducer.QueryPrivate args => Reducers.InvokeQueryPrivate(eventContext, args), + Reducer.RepeatingTest args => Reducers.InvokeRepeatingTest(eventContext, args), + Reducer.Test args => Reducers.InvokeTest(eventContext, args), + Reducer.TestBtreeIndexArgs args => Reducers.InvokeTestBtreeIndexArgs(eventContext, args), Reducer.StdbNone or Reducer.StdbIdentityConnected or Reducer.StdbIdentityDisconnected => true, diff --git a/smoketests/tests/namespaces.py b/smoketests/tests/namespaces.py index b1bd0eb268a..b6c18338830 100644 --- a/smoketests/tests/namespaces.py +++ b/smoketests/tests/namespaces.py @@ -23,7 +23,7 @@ def test_spacetimedb_ns_csharp(self): with tempfile.TemporaryDirectory() as tmpdir: self.spacetime("generate", "--out-dir", tmpdir, "--lang=cs", "--project-path", self.project_path) - self.assertEqual(count_matches(tmpdir, f"namespace {namespace}"), 4) + self.assertEqual(count_matches(tmpdir, f"namespace {namespace}"), 2) def test_custom_ns_csharp(self): """Ensure that when a custom namespace is specified on the command line, it actually gets used in generation""" @@ -33,5 +33,5 @@ def test_custom_ns_csharp(self): with tempfile.TemporaryDirectory() as tmpdir: self.spacetime("generate", "--out-dir", tmpdir, "--lang=cs", "--namespace", namespace, "--project-path", self.project_path) - self.assertEqual(count_matches(tmpdir, f"namespace {namespace}"), 4) - self.assertEqual(count_matches(tmpdir, "using SpacetimeDB;"), 4) + self.assertEqual(count_matches(tmpdir, f"namespace {namespace}"), 2) + self.assertEqual(count_matches(tmpdir, "using SpacetimeDB;"), 2)