Skip to content

Commit

Permalink
Generate tagged enums in C# client code (#1421)
Browse files Browse the repository at this point in the history
  • Loading branch information
RReverser committed Jun 13, 2024
1 parent 9e0d1c9 commit 4b7eff6
Show file tree
Hide file tree
Showing 8 changed files with 197 additions and 43 deletions.
2 changes: 1 addition & 1 deletion crates/bindings-csharp/BSATN.Runtime/src/Attrs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ public sealed class TypeAttribute : Attribute { }

// This could be an interface, but using `record` forces C# to check that it can
// only be applied on types that are records themselves.
public record TaggedEnum<Variants>
public abstract record TaggedEnum<Variants>
where Variants : struct, ITuple { }
}
68 changes: 43 additions & 25 deletions crates/cli/src/subcommands/generate/csharp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,8 @@ fn default_init(ctx: &GenCtx, ty: &AlgebraicType) -> &'static str {
if sum_type.as_option().is_some() || sum_type.is_simple_enum() {
""
} else {
unimplemented!()
// TODO: generate some proper default here (what would it be for tagged enums?).
" = null!"
}
}
// For product types, we can just use the default constructor.
Expand Down Expand Up @@ -198,20 +199,7 @@ impl CsharpAutogen {
}
}

pub fn autogen_csharp_sum(
/* will be used in future for tagged enum */ _ctx: &GenCtx,
name: &str,
sum_type: &SumType,
namespace: &str,
) -> String {
if sum_type.is_simple_enum() {
autogen_csharp_enum(name, sum_type, namespace)
} else {
unimplemented!();
}
}

pub fn autogen_csharp_enum(name: &str, sum_type: &SumType, namespace: &str) -> String {
pub fn autogen_csharp_sum(ctx: &GenCtx, name: &str, sum_type: &SumType, namespace: &str) -> String {
let mut output = CsharpAutogen::new(namespace, &[]);

let mut sum_namespace = None;
Expand All @@ -237,17 +225,47 @@ pub fn autogen_csharp_enum(name: &str, sum_type: &SumType, namespace: &str) -> S
}

writeln!(output, "[SpacetimeDB.Type]");
writeln!(output, "public enum {sum_type_name}");
indented_block(&mut output, |output| {
for variant in &*sum_type.variants {
let variant_name = variant
.name
.as_ref()
.expect("All sum variants should have names!")
.replace("r#", "");
writeln!(output, "{variant_name},");

if sum_type.is_simple_enum() {
writeln!(output, "public enum {sum_type_name}");
indented_block(&mut output, |output| {
for variant in &*sum_type.variants {
let variant_name = variant
.name
.as_ref()
.expect("All sum variants should have names!")
.replace("r#", "");
writeln!(output, "{variant_name},");
}
});
} else {
write!(
output,
"public partial record {sum_type_name} : SpacetimeDB.TaggedEnum<("
);
{
indent_scope!(output);
for (i, variant) in sum_type.variants.iter().enumerate() {
if i != 0 {
write!(output, ",");
}
writeln!(output);
if variant.is_unit() {
write!(output, "SpacetimeDB.Unit");
} else {
write!(output, "{}", ty_fmt(ctx, &variant.algebraic_type, namespace));
}
let variant_name = variant
.name
.as_ref()
.expect("All sum variants should have names!")
.replace("r#", "");
write!(output, " {variant_name}");
}
}
});
writeln!(output);
writeln!(output, ")>;");
}

if sum_namespace.is_some() {
for _ in 0..2 {
Expand Down
2 changes: 1 addition & 1 deletion crates/cli/src/subcommands/generate/typescript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ pub fn autogen_typescript_sum(ctx: &GenCtx, name: &str, sum_type: &SumType) -> S
),
_ => writeln!(
output,
"export const {variant_name} = (value: {a_type}): {variant_name} => {{ return {{ tag: \"{variant_name}\", value }} }};"
"export const {variant_name} = (value: {a_type}): {variant_name} => ({{ tag: \"{variant_name}\", value }});"
),
};
}
Expand Down
35 changes: 31 additions & 4 deletions crates/cli/tests/snapshots/codegen__codegen_csharp.snap
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,31 @@ namespace SpacetimeDB
}
}
'''
"NamespaceTestF.cs" = '''
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD.
// <auto-generated />

#nullable enable

using System;

namespace SpacetimeDB
{
public partial class Namespace
{
public partial class Types
{
[SpacetimeDB.Type]
public partial record TestF : SpacetimeDB.TaggedEnum<(
SpacetimeDB.Unit Foo,
SpacetimeDB.Unit Bar,
string Baz
)>;
}
}
}
'''
"PkMultiIdentity.cs" = '''
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD.
Expand Down Expand Up @@ -599,16 +624,17 @@ namespace SpacetimeDB
public SpacetimeDB.TestA Arg = new();
public SpacetimeDB.TestB Arg2 = new();
public SpacetimeDB.Namespace.Types.TestC Arg3;
public SpacetimeDB.Namespace.TestF Arg4 = null!;
}

public static partial class Reducer
{
public delegate void TestHandler(ReducerEvent reducerEvent, SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.Namespace.Types.TestC arg3);
public delegate void TestHandler(ReducerEvent reducerEvent, SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.Namespace.Types.TestC arg3, SpacetimeDB.Namespace.TestF arg4);
public static event TestHandler? OnTestEvent;

public static void Test(SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.Namespace.Types.TestC arg3)
public static void Test(SpacetimeDB.TestA arg, SpacetimeDB.TestB arg2, SpacetimeDB.Namespace.Types.TestC arg3, SpacetimeDB.Namespace.TestF arg4)
{
SpacetimeDBClient.instance.InternalCallReducer(new TestArgsStruct { Arg = arg, Arg2 = arg2, Arg3 = arg3 });
SpacetimeDBClient.instance.InternalCallReducer(new TestArgsStruct { Arg = arg, Arg2 = arg2, Arg3 = arg3, Arg4 = arg4 });
}

public static bool OnTest(ReducerEvent reducerEvent, TestArgsStruct args)
Expand All @@ -618,7 +644,8 @@ namespace SpacetimeDB
reducerEvent,
args.Arg,
args.Arg2,
args.Arg3
args.Arg3,
args.Arg4
);
return true;
}
Expand Down
37 changes: 35 additions & 2 deletions crates/cli/tests/snapshots/codegen__codegen_rust.snap
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,7 @@ use spacetimedb_sdk::spacetime_module::SpacetimeModule;
use std::sync::Arc;

pub mod namespace.test_c;
pub mod namespace.test_f;
pub mod test_b;
pub mod pk_multi_identity;
pub mod point;
Expand All @@ -317,6 +318,7 @@ pub mod repeating_test_reducer;
pub mod test_reducer;

pub use namespace.test_c::*;
pub use namespace.test_f::*;
pub use test_b::*;
pub use pk_multi_identity::*;
pub use point::*;
Expand Down Expand Up @@ -435,6 +437,29 @@ pub enum Namespace.testC {
}
'''
"namespace_test_f.rs" = '''
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD.
#![allow(unused_imports)]use spacetimedb_sdk::{
Address,
sats::{ser::Serialize, de::Deserialize},
table::{TableType, TableIter, TableWithPrimaryKey},
reducer::{Reducer, ReducerCallbackId, Status},
identity::Identity,
spacetimedb_lib,
anyhow::{Result, anyhow},
};
#[derive(Serialize, Deserialize, Clone, PartialEq, Debug)]
pub enum Namespace.testF {
Foo,
Bar,
Baz(String),
}
'''
"pk_multi_identity.rs" = '''
// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
// WILL NOT BE SAVED. MODIFY TABLES IN RUST INSTEAD.
Expand Down Expand Up @@ -837,6 +862,7 @@ impl TestE {
anyhow::{Result, anyhow},
};
use super::namespace.test_c::Namespace.testC;
use super::namespace.test_f::Namespace.testF;
use super::test_a::TestA;
use super::test_b::TestB;

Expand All @@ -845,6 +871,7 @@ pub struct TestArgs {
pub arg: TestA,
pub arg_2: TestB,
pub arg_3: Namespace.testC,
pub arg_4: Namespace.testF,
}

impl Reducer for TestArgs {
Expand All @@ -856,22 +883,25 @@ pub fn test(
arg: TestA,
arg_2: TestB,
arg_3: Namespace.testC,
arg_4: Namespace.testF,
) {
TestArgs {
arg,
arg_2,
arg_3,
arg_4,
} .invoke();
}

#[allow(unused)]
pub fn on_test(mut __callback: impl FnMut(&Identity, Option<Address>, &Status, &TestA, &TestB, &Namespace.testC) + Send + 'static) -> ReducerCallbackId<TestArgs>
pub fn on_test(mut __callback: impl FnMut(&Identity, Option<Address>, &Status, &TestA, &TestB, &Namespace.testC, &Namespace.testF) + Send + 'static) -> ReducerCallbackId<TestArgs>
{
TestArgs::on_reducer(move |__identity, __addr, __status, __args| {
let TestArgs {
arg,
arg_2,
arg_3,
arg_4,
} = __args;
__callback(
__identity,
Expand All @@ -880,18 +910,20 @@ __callback(
arg,
arg_2,
arg_3,
arg_4,
);
})
}

#[allow(unused)]
pub fn once_on_test(__callback: impl FnOnce(&Identity, Option<Address>, &Status, &TestA, &TestB, &Namespace.testC) + Send + 'static) -> ReducerCallbackId<TestArgs>
pub fn once_on_test(__callback: impl FnOnce(&Identity, Option<Address>, &Status, &TestA, &TestB, &Namespace.testC, &Namespace.testF) + Send + 'static) -> ReducerCallbackId<TestArgs>
{
TestArgs::once_on_reducer(move |__identity, __addr, __status, __args| {
let TestArgs {
arg,
arg_2,
arg_3,
arg_4,
} = __args;
__callback(
__identity,
Expand All @@ -900,6 +932,7 @@ __callback(
arg,
arg_2,
arg_3,
arg_4,
);
})
}
Expand Down
Loading

3 comments on commit 4b7eff6

@github-actions
Copy link

@github-actions github-actions bot commented on 4b7eff6 Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bot test failed. Please check the workflow run for details.

@github-actions
Copy link

@github-actions github-actions bot commented on 4b7eff6 Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callgrind benchmark results

Callgrind Benchmark Report

These benchmarks were run using callgrind,
an instruction-level profiler. They allow comparisons between sqlite (sqlite), SpacetimeDB running through a module (stdb_module), and the underlying SpacetimeDB data storage engine (stdb_raw). Callgrind emulates a CPU to collect the below estimates.

Measurement changes larger than five percent are in bold.

In-memory benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 5170 5170 0.00% 5208 5208 0.00%
sqlite 5534 5534 0.00% 6102 6102 0.00%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 1 u64 77864 77864 0.00% 78278 78278 0.00%
stdb_raw u32_u64_str no_index 64 128 2 string 120534 120534 0.00% 121162 121162 0.00%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 23098 23098 0.00% 23416 23412 0.02%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 24136 24136 0.00% 24552 24580 -0.11%
sqlite u32_u64_str no_index 64 128 2 string 143488 143503 -0.01% 145046 145081 -0.02%
sqlite u32_u64_str no_index 64 128 1 u64 122829 122829 0.00% 124093 124089 0.00%
sqlite u32_u64_str btree_each_column 64 128 2 string 133302 133287 0.01% 135032 135001 0.02%
sqlite u32_u64_str btree_each_column 64 128 1 u64 130146 130146 0.00% 131566 131562 0.00%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 862755 863389 -0.07% 878665 879347 -0.08%
stdb_raw u32_u64_str btree_each_column 64 128 1003160 1003286 -0.01% 1023280 1023494 -0.02%
sqlite u32_u64_str unique_0 64 128 396151 396151 0.00% 413799 413807 -0.00%
sqlite u32_u64_str btree_each_column 64 128 981458 981458 0.00% 1023466 1023478 -0.00%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 151365 151365 0.00% 151499 151499 0.00%
stdb_raw u32_u64_str unique_0 64 15350 15350 0.00% 15480 15480 0.00%
sqlite u32_u64_str unique_0 1024 1046686 1046686 0.00% 1050028 1050028 0.00%
sqlite u32_u64_str unique_0 64 74832 74832 0.00% 75958 75958 0.00%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47438 47438 0.00% 49920 49920 0.00%
64 bsatn 25716 25716 0.00% 28028 28028 0.00%
16 bsatn 8117 8117 0.00% 9545 9545 0.00%
16 json 12142 12142 0.00% 13876 13876 0.00%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 20109668 20104627 0.03% 20589144 20583649 0.03%
stdb_raw u32_u64_str unique_0 64 128 1267396 1266938 0.04% 1329024 1328582 0.03%
sqlite u32_u64_str unique_0 1024 1024 1802053 1802053 0.00% 1811281 1811281 0.00%
sqlite u32_u64_str unique_0 64 128 128399 128399 0.00% 131277 131277 0.00%
On-disk benchmarks

callgrind: empty transaction

db total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw 5180 5180 0.00% 5218 5218 0.00%
sqlite 5570 5570 0.00% 6164 6164 0.00%

callgrind: filter

db schema indices count preload _column data_type total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str no_index 64 128 1 u64 77874 77874 0.00% 78284 78284 0.00%
stdb_raw u32_u64_str no_index 64 128 2 string 120544 120544 0.00% 121148 121148 0.00%
stdb_raw u32_u64_str btree_each_column 64 128 1 u64 23108 23108 0.00% 23422 23422 0.00%
stdb_raw u32_u64_str btree_each_column 64 128 2 string 24147 24149 -0.01% 24563 24565 -0.01%
sqlite u32_u64_str no_index 64 128 1 u64 124750 124750 0.00% 126270 126266 0.00%
sqlite u32_u64_str no_index 64 128 2 string 145409 145409 0.00% 147231 147239 -0.01%
sqlite u32_u64_str btree_each_column 64 128 1 u64 132257 132242 0.01% 134219 134188 0.02%
sqlite u32_u64_str btree_each_column 64 128 2 string 135409 135409 0.00% 137637 137641 -0.00%

callgrind: insert bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 64 128 812834 812830 0.00% 827936 827862 0.01%
stdb_raw u32_u64_str btree_each_column 64 128 951288 952943 -0.17% 1000648 1002279 -0.16%
sqlite u32_u64_str unique_0 64 128 413678 413678 0.00% 430634 430634 0.00%
sqlite u32_u64_str btree_each_column 64 128 1019729 1019729 0.00% 1060683 1060691 -0.00%

callgrind: iterate

db schema indices count total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 151375 151375 0.00% 151505 151505 0.00%
stdb_raw u32_u64_str unique_0 64 15360 15360 0.00% 15494 15494 0.00%
sqlite u32_u64_str unique_0 1024 1049748 1049748 0.00% 1053474 1053474 0.00%
sqlite u32_u64_str unique_0 64 76604 76604 0.00% 77934 77934 0.00%

callgrind: serialize_product_value

count format total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
64 json 47438 47438 0.00% 49920 49920 0.00%
64 bsatn 25716 25716 0.00% 28028 28028 0.00%
16 bsatn 8117 8117 0.00% 9545 9545 0.00%
16 json 12142 12142 0.00% 13876 13876 0.00%

callgrind: update bulk

db schema indices count preload total reads + writes old total reads + writes Δrw estimated cycles old estimated cycles Δcycles
stdb_raw u32_u64_str unique_0 1024 1024 18838181 18838738 -0.00% 19341409 19342292 -0.00%
stdb_raw u32_u64_str unique_0 64 128 1222823 1223173 -0.03% 1283529 1283723 -0.02%
sqlite u32_u64_str unique_0 1024 1024 1809608 1809608 0.00% 1818198 1818198 0.00%
sqlite u32_u64_str unique_0 64 128 132519 132519 0.00% 135467 135467 0.00%

@github-actions
Copy link

@github-actions github-actions bot commented on 4b7eff6 Jun 13, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Benchmarking failed. Please check the workflow run for details.

Please sign in to comment.