From d5bd9e09c147b99cd47ca1d5f0bace2c045b35ba Mon Sep 17 00:00:00 2001 From: Mario Alejandro Montoya Cortes Date: Thu, 20 Nov 2025 13:05:07 -0500 Subject: [PATCH 1/2] Implement SpacetimeType for Result --- crates/bindings-csharp/BSATN.Codegen/Type.cs | 60 +++++- .../BSATN.Runtime/BSATN/AlgebraicType.cs | 4 + .../bindings-csharp/BSATN.Runtime/Builtins.cs | 107 +++++++++++ crates/bindings-csharp/Codegen/Module.cs | 10 +- .../Runtime/ProcedureContext.cs | 50 +---- crates/bindings-typescript/src/index.ts | 1 + .../src/lib/algebraic_type.ts | 52 ++++++ crates/bindings-typescript/src/lib/result.ts | 36 ++++ crates/bindings-typescript/src/lib/schema.ts | 6 + .../src/lib/type_builders.ts | 114 ++++++++++++ crates/codegen/src/csharp.rs | 14 ++ crates/codegen/src/rust.rs | 7 + crates/codegen/src/typescript.rs | 14 +- crates/codegen/src/unrealcpp.rs | 26 +++ crates/lib/tests/serde.rs | 6 + .../snapshots/serde__json_mappings-2.snap | 5 +- .../tests/snapshots/serde__json_mappings.snap | 5 +- crates/pg/src/encoder.rs | 9 + crates/sats/src/algebraic_type.rs | 56 +++++- crates/sats/src/algebraic_value.rs | 39 ++++ crates/sats/src/proptest.rs | 3 +- crates/sats/src/sum_type.rs | 57 +++++- crates/sats/src/typespace.rs | 24 +++ crates/schema/src/type_for_generate.rs | 28 +++ modules/sdk-test-cs/Lib.cs | 80 ++++++++ modules/sdk-test-cs/sdk-test-cs.sln | 24 +++ modules/sdk-test-ts/src/index.ts | 35 ++++ modules/sdk-test/src/lib.rs | 10 + .../regression-tests/client/Program.cs | 39 ++++ .../InsertWithTxRollbackResult.g.cs | 64 +++++++ .../Reducers/InsertResult.g.cs | 72 ++++++++ .../module_bindings/SpacetimeDBClient.g.cs | 5 +- .../client/module_bindings/Tables/MyLog.g.cs | 27 +++ .../client/module_bindings/Types/MyLog.g.cs | 28 +++ .../examples~/regression-tests/server/Lib.cs | 38 +++- .../dotnet/cs/SpacetimeDB.BSATN.Codegen.dll | Bin 0 -> 77312 bytes .../SpacetimeDB.BSATN.Runtime.dll | Bin 0 -> 74240 bytes ...t_every_primitive_struct_string_reducer.rs | 119 ++++++++++++ .../insert_result_i_32_string_reducer.rs | 103 +++++++++++ .../insert_result_identity_string_reducer.rs | 103 +++++++++++ .../insert_result_simple_enum_i_32_reducer.rs | 105 +++++++++++ .../insert_result_string_i_32_reducer.rs | 103 +++++++++++ .../insert_result_vec_i_32_string_reducer.rs | 103 +++++++++++ .../test-client/src/module_bindings/mod.rs | 173 +++++++++++++++++- ...ult_every_primitive_struct_string_table.rs | 99 ++++++++++ ...sult_every_primitive_struct_string_type.rs | 17 ++ .../result_i_32_string_table.rs | 95 ++++++++++ .../result_i_32_string_type.rs | 15 ++ .../result_identity_string_table.rs | 95 ++++++++++ .../result_identity_string_type.rs | 15 ++ .../result_simple_enum_i_32_table.rs | 96 ++++++++++ .../result_simple_enum_i_32_type.rs | 17 ++ .../result_string_i_32_table.rs | 95 ++++++++++ .../result_string_i_32_type.rs | 15 ++ .../result_vec_i_32_string_table.rs | 95 ++++++++++ .../result_vec_i_32_string_type.rs | 15 ++ smoketests/tests/pg_wire.py | 36 +++- smoketests/tests/sql.py | 40 +++- 58 files changed, 2650 insertions(+), 59 deletions(-) create mode 100644 crates/bindings-typescript/src/lib/result.ts create mode 100644 modules/sdk-test-cs/sdk-test-cs.sln create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/InsertWithTxRollbackResult.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertResult.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyLog.g.cs create mode 100644 sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyLog.g.cs create mode 100755 sdks/csharp/packages/spacetimedb.bsatn.runtime/1.11.1/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll create mode 100755 sdks/csharp/packages/spacetimedb.bsatn.runtime/1.11.1/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll create mode 100644 sdks/rust/tests/test-client/src/module_bindings/insert_result_every_primitive_struct_string_reducer.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/insert_result_i_32_string_reducer.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/insert_result_identity_string_reducer.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/insert_result_simple_enum_i_32_reducer.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/insert_result_string_i_32_reducer.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/insert_result_vec_i_32_string_reducer.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_table.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_type.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_table.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_type.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_identity_string_table.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_identity_string_type.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_table.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_type.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_table.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_type.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_table.rs create mode 100644 sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_type.rs diff --git a/crates/bindings-csharp/BSATN.Codegen/Type.cs b/crates/bindings-csharp/BSATN.Codegen/Type.cs index 99823234d23..6e59810a8b2 100644 --- a/crates/bindings-csharp/BSATN.Codegen/Type.cs +++ b/crates/bindings-csharp/BSATN.Codegen/Type.cs @@ -90,6 +90,12 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter ), INamedTypeSymbol named => named.OriginalDefinition.ToString() switch { + "SpacetimeDB.Result" or "SpacetimeDB.Result" => new ResultUse( + type, + typeInfo, + Parse(member, named.TypeArguments[0], diag), + Parse(member, named.TypeArguments[1], diag) + ), "System.Collections.Generic.List" => new ListUse( type, typeInfo, @@ -107,6 +113,19 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter }; } + /// + /// Get the name of the BSATN struct for this type. + /// + public virtual string ToBSATNString() + { + return this.BSATNName; + } + + public virtual string ToBSATNString2() + { + return this.BSATNName; + } + /// /// Get a statement that declares outVar and assigns (inVar1 logically-equals inVar2) to it. /// logically-equals: @@ -139,6 +158,45 @@ public abstract string EqualsStatement( public abstract string GetHashCodeStatement(string inVar, string outVar, int level = 0); } +/// +/// A use of a Result<T, E> type. +/// +public sealed record ResultUse : TypeUse +{ + public TypeUse Ok { get; } + public TypeUse Err { get; } + + public string TypeName { get; } + + public ResultUse(string typeName, string typeInfo, TypeUse ok, TypeUse err) + : base(typeName, typeInfo) + { + Ok = ok; + Err = err; + TypeName = typeName; + } + + public override string ToBSATNString() + { + return $"{TypeName}.BSATN<{Ok.BSATNName}, {Err.BSATNName}>"; + } + + public override string ToBSATNString2() + { + return $"{TypeName}.BSATN<{Ok.BSATNName}, {Err.BSATNName}>"; + } + + public override string EqualsStatement( + string inVar1, + string inVar2, + string outVar, + int level = 0 + ) => $"var {outVar} = {inVar1} == {inVar2};"; + + public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) => + $"var {outVar} = {inVar}.GetHashCode();"; +} + /// /// A use of an enum type. /// (This is a C# enum, not one of our tagged enums.) @@ -354,7 +412,7 @@ IEnumerable members return string.Join( "\n ", members.Select(m => - $"{visStr} static readonly {m.Type.BSATNName} {m.Name}{TypeUse.BsatnFieldSuffix} = new();" + $"{visStr} static readonly {m.Type.ToBSATNString()} {m.Name}{TypeUse.BsatnFieldSuffix} = new();" ) ); } diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs b/crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs index 602afa7af5f..b88ae42dcb4 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN/AlgebraicType.cs @@ -43,4 +43,8 @@ Unit F64 // Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as an Option. internal static AlgebraicType MakeOption(AlgebraicType someType) => new Sum([new("some", someType), new("none", Unit)]); + + // Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as a Result. + internal static AlgebraicType MakeResult(AlgebraicType okType, AlgebraicType errType) => + new Sum([new("ok", okType), new("err", errType)]); } diff --git a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs index a3478d18023..9d158606188 100644 --- a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs +++ b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs @@ -1,6 +1,7 @@ namespace SpacetimeDB; using System.Diagnostics; +using System.Net.WebSockets; using System.Runtime.InteropServices; using SpacetimeDB.BSATN; @@ -605,3 +606,109 @@ public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => // --- / customized --- } } + +public partial record Result : TaggedEnum<(T Ok, E Err)> +{ + public static implicit operator Result(T value) => new OkR(value); + + public static implicit operator Result(E error) => new ErrR(error); + + public TResult Match(Func onOk, Func onErr) => + this switch + { + OkR(var v) => onOk(v), + ErrR(var e) => onErr(e), + _ => throw new InvalidOperationException("Unknown Result variant."), + }; + + public static Result Ok(T value) => new OkR(value); + + public static Result Err(E error) => new ErrR(error); + + public T UnwrapOrThrow() + { + return this switch + { + OkR(var v) => v, + ErrR(var e) when e is not null => throw new Exception(e.ToString()), + ErrR(_) => throw new InvalidOperationException( + "Result failed without an error object." + ), + _ => throw new InvalidOperationException("Unknown Result variant."), + }; + } + + public T UnwrapOr(T defaultValue) => + this switch + { + OkR(var v) => v, + _ => defaultValue, + }; + + public T UnwrapOrElse(Func f) => + this switch + { + OkR(var v) => v, + ErrR(var e) => f(e), + _ => throw new InvalidOperationException("Unknown Result variant."), + }; + + // ----- auto-generated ----- + + private Result() { } + + internal enum @enum : byte + { + Ok, + Err, + } + + public sealed record OkR(T Value) : Result; + + public sealed record ErrR(E Error) : Result; + + private enum Variant : byte + { + Ok = 0, + Err = 1, + } + + public readonly struct BSATN : IReadWrite> + where OkRW : struct, IReadWrite + where ErrRW : struct, IReadWrite + { + private static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new(); + private static readonly OkRW okRW = new(); + private static readonly ErrRW errRW = new(); + + public Result Read(BinaryReader reader) => + __enumTag.Read(reader) switch + { + @enum.Ok => new OkR(okRW.Read(reader)), + @enum.Err => new ErrR(errRW.Read(reader)), + _ => throw new InvalidOperationException(), + }; + + public void Write(BinaryWriter writer, Result value) + { + switch (value) + { + case OkR(var v): + __enumTag.Write(writer, @enum.Ok); + okRW.Write(writer, v); + break; + + case ErrR(var e): + __enumTag.Write(writer, @enum.Err); + errRW.Write(writer, e); + break; + } + } + + public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => + AlgebraicType.MakeResult( + okRW.GetAlgebraicType(registrar), + errRW.GetAlgebraicType(registrar) + ); + } +} diff --git a/crates/bindings-csharp/Codegen/Module.cs b/crates/bindings-csharp/Codegen/Module.cs index 75d4bd4fbcf..ab871439a93 100644 --- a/crates/bindings-csharp/Codegen/Module.cs +++ b/crates/bindings-csharp/Codegen/Module.cs @@ -1178,7 +1178,7 @@ public Scope.Extensions GenerateSchedule() using var writer = new BinaryWriter(stream); {{string.Join( "\n", - Args.Select(a => $"new {a.Type.BSATNName}().Write(writer, {a.Name});") + Args.Select(a => $"new {a.Type.ToBSATNString()}().Write(writer, {a.Name});") )}} SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(nameof({{Name}}), stream); } @@ -1295,7 +1295,7 @@ public string GenerateClass() } else { - var serializer = $"new {ReturnType.BSATNName}()"; + var serializer = $"new {ReturnType.ToBSATNString()}()"; bodyLines = new[] { $"var result = {invocation};", @@ -1321,12 +1321,12 @@ public string GenerateClass() ? ( txPayloadIsUnit ? "SpacetimeDB.BSATN.AlgebraicType.Unit" - : $"new {txPayload.BSATNName}().GetAlgebraicType(registrar)" + : $"new {txPayload.ToBSATNString2()}().GetAlgebraicType(registrar)" ) : ( ReturnType.Name == "SpacetimeDB.Unit" ? "SpacetimeDB.BSATN.AlgebraicType.Unit" - : $"new {ReturnType.BSATNName}().GetAlgebraicType(registrar)" + : $"new {ReturnType.ToBSATNString2()}().GetAlgebraicType(registrar)" ); var classFields = MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args); @@ -1372,7 +1372,7 @@ public Scope.Extensions GenerateSchedule() using var writer = new BinaryWriter(stream); {{string.Join( "\n", - Args.Select(a => $"new {a.Type.BSATNName}().Write(writer, {a.Name});") + Args.Select(a => $"new {a.Type.ToBSATNString()}().Write(writer, {a.Name});") )}} SpacetimeDB.Internal.ProcedureExtensions.VolatileNonatomicScheduleImmediate(nameof({{Name}}), stream); } diff --git a/crates/bindings-csharp/Runtime/ProcedureContext.cs b/crates/bindings-csharp/Runtime/ProcedureContext.cs index 2c67396bbb2..018caa64277 100644 --- a/crates/bindings-csharp/Runtime/ProcedureContext.cs +++ b/crates/bindings-csharp/Runtime/ProcedureContext.cs @@ -3,40 +3,6 @@ namespace SpacetimeDB; using System.Diagnostics.CodeAnalysis; using Internal; -public readonly struct Result(bool isSuccess, T? value, E? error) - where E : Exception -{ - public bool IsSuccess { get; } = isSuccess; - public T? Value { get; } = value; - public E? Error { get; } = error; - - public static Result Ok(T value) => new(true, value, null); - - public static Result Err(E error) => new(false, default, error); - - public T UnwrapOrThrow() - { - if (IsSuccess) - { - return Value!; - } - - if (Error is not null) - { - throw Error; - } - - throw new InvalidOperationException("Result failed without an error object."); - } - - public T UnwrapOr(T defaultValue) => IsSuccess ? Value! : defaultValue; - - public T UnwrapOrElse(Func f) => IsSuccess ? Value! : f(Error!); - - public TResult Match(Func onOk, Func onErr) => - IsSuccess ? onOk(Value!) : onErr(Error!); -} - #pragma warning disable STDB_UNSTABLE public abstract class ProcedureContextBase( Identity sender, @@ -122,9 +88,13 @@ Func> body try { var result = RunWithRetry(body); - return result.IsSuccess - ? TxOutcome.Success(result.Value!) - : TxOutcome.Failure(result.Error!); + + return result switch + { + Result.OkR(var value) => TxOutcome.Success(value), + Result.ErrR(var error) => TxOutcome.Failure(error), + _ => throw new InvalidOperationException("Unknown Result variant."), + }; } catch (Exception ex) { @@ -181,7 +151,7 @@ Func> body where TError : Exception { var result = RunOnce(body); - if (!result.IsSuccess) + if (result is Result.ErrR) { return result; } @@ -189,7 +159,7 @@ Func> body bool Retry() { result = RunOnce(body); - return result.IsSuccess; + return result is Result.OkR; } if (!CommitMutTxWithRetry(Retry)) @@ -220,7 +190,7 @@ Func> body throw; } - if (result.IsSuccess) + if (result is Result.OkR) { guard.Disarm(); return result; diff --git a/crates/bindings-typescript/src/index.ts b/crates/bindings-typescript/src/index.ts index 6e8c97d64e7..bac8310e3b0 100644 --- a/crates/bindings-typescript/src/index.ts +++ b/crates/bindings-typescript/src/index.ts @@ -9,4 +9,5 @@ export * from './lib/timestamp'; export * from './lib/util'; export * from './lib/identity'; export * from './lib/option'; +export * from './lib/result'; export * from './sdk'; diff --git a/crates/bindings-typescript/src/lib/algebraic_type.ts b/crates/bindings-typescript/src/lib/algebraic_type.ts index 82a95287eee..99a5da6623e 100644 --- a/crates/bindings-typescript/src/lib/algebraic_type.ts +++ b/crates/bindings-typescript/src/lib/algebraic_type.ts @@ -439,6 +439,36 @@ export const SumType = { } else { writer.writeByte(1); } + } else if ( + ty.variants.length == 2 && + ty.variants[0].name === 'ok' && + ty.variants[1].name === 'err' + ) { + let variantName: 'ok' | 'err'; + let innerValue: any; + let index: number; + if ('ok' in value) { + variantName = 'ok'; + innerValue = value.ok; + index = 0; + } else { + variantName = 'err'; + innerValue = value.err; + index = 1; + } + + if (index < 0) { + throw `Result serialization error: variant '${variantName}' not found in ${JSON.stringify(ty)}`; + } + + writer.writeU8(index); + + AlgebraicType.serializeValue( + writer, + ty.variants[index].algebraicType, + innerValue, + typespace + ); } else { const variant = value['tag']; const index = ty.variants.findIndex(v => v.name === variant); @@ -479,6 +509,28 @@ export const SumType = { } else { throw `Can't deserialize an option type, couldn't find ${tag} tag`; } + } else if ( + ty.variants.length == 2 && + ty.variants[0].name === 'ok' && + ty.variants[1].name === 'err' + ) { + if (tag === 0) { + const value = AlgebraicType.deserializeValue( + reader, + ty.variants[0].algebraicType, + typespace + ); + return { ok: value }; + } else if (tag === 1) { + const value = AlgebraicType.deserializeValue( + reader, + ty.variants[1].algebraicType, + typespace + ); + return { err: value }; + } else { + throw `Can't deserialize a result type, couldn't find ${tag} tag`; + } } else { const variant = ty.variants[tag]; const value = AlgebraicType.deserializeValue( diff --git a/crates/bindings-typescript/src/lib/result.ts b/crates/bindings-typescript/src/lib/result.ts new file mode 100644 index 00000000000..5ea0d682048 --- /dev/null +++ b/crates/bindings-typescript/src/lib/result.ts @@ -0,0 +1,36 @@ +import { AlgebraicType } from './algebraic_type'; + +export type ResultAlgebraicType< + T extends AlgebraicType = AlgebraicType, + E extends AlgebraicType = AlgebraicType, +> = { + tag: 'Sum'; + value: { + variants: [ + { name: 'ok'; algebraicType: T }, + { name: 'err'; algebraicType: E }, + ]; + }; +}; + +export const Result: { + getAlgebraicType< + T extends AlgebraicType = AlgebraicType, + E extends AlgebraicType = AlgebraicType, + >( + okType: T, + errType: E + ): ResultAlgebraicType; +} = { + getAlgebraicType< + T extends AlgebraicType = AlgebraicType, + E extends AlgebraicType = AlgebraicType, + >(okType: T, errType: E): ResultAlgebraicType { + return AlgebraicType.Sum({ + variants: [ + { name: 'ok', algebraicType: okType }, + { name: 'err', algebraicType: errType }, + ], + }); + }, +}; diff --git a/crates/bindings-typescript/src/lib/schema.ts b/crates/bindings-typescript/src/lib/schema.ts index f22727d1848..3dcb886f155 100644 --- a/crates/bindings-typescript/src/lib/schema.ts +++ b/crates/bindings-typescript/src/lib/schema.ts @@ -16,6 +16,7 @@ import { type InferSpacetimeTypeOfTypeBuilder, type RowObj, type VariantsObj, + ResultBuilder, } from './type_builders'; import type { UntypedTableDef } from './table'; import { @@ -187,6 +188,11 @@ export function registerTypesRecursively< return new OptionBuilder( registerTypesRecursively(typeBuilder.value) ) as any; + } else if (typeBuilder instanceof ResultBuilder) { + return new ResultBuilder( + registerTypesRecursively(typeBuilder.ok), + registerTypesRecursively(typeBuilder.err) + ) as any; } else if (typeBuilder instanceof ArrayBuilder) { return new ArrayBuilder( registerTypesRecursively(typeBuilder.element) diff --git a/crates/bindings-typescript/src/lib/type_builders.ts b/crates/bindings-typescript/src/lib/type_builders.ts index 8b777e74f77..0783ac5a64e 100644 --- a/crates/bindings-typescript/src/lib/type_builders.ts +++ b/crates/bindings-typescript/src/lib/type_builders.ts @@ -4,6 +4,7 @@ import type BinaryWriter from './binary_writer'; import { ConnectionId, type ConnectionIdAlgebraicType } from './connection_id'; import { Identity, type IdentityAlgebraicType } from './identity'; import { Option, type OptionAlgebraicType } from './option'; +import { Result, type ResultAlgebraicType } from './result'; import ScheduleAt, { type ScheduleAtAlgebraicType } from './schedule_at'; import type { CoerceRow } from './table'; import { TimeDuration, type TimeDurationAlgebraicType } from './time_duration'; @@ -1403,6 +1404,57 @@ export class ProductBuilder } } +export class ResultBuilder< + Ok extends TypeBuilder, + Err extends TypeBuilder, + > + extends TypeBuilder< + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder, + ResultAlgebraicType< + InferSpacetimeTypeOfTypeBuilder, + InferSpacetimeTypeOfTypeBuilder + > + > + implements + Defaultable< + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder, + ResultAlgebraicType< + InferSpacetimeTypeOfTypeBuilder, + InferSpacetimeTypeOfTypeBuilder + > + > +{ + ok: Ok; + err: Err; + + constructor(ok: Ok, err: Err) { + super(Result.getAlgebraicType(ok.algebraicType, err.algebraicType)); + this.ok = ok; + this.err = err; + } + default( + value: InferTypeOfTypeBuilder | InferTypeOfTypeBuilder + ): ResultColumnBuilder< + Ok, + Err, + SetField< + DefaultMetadata, + 'defaultValue', + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder + > + > { + return new ResultColumnBuilder< + Ok, + Err, + SetField< + DefaultMetadata, + 'defaultValue', + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder + > + >(this, set(defaultMetadata, { defaultValue: value })); + } +} + class UnitBuilder extends TypeBuilder< {}, { tag: 'Product'; value: { elements: [] } } @@ -3029,6 +3081,54 @@ export class OptionColumnBuilder< } } +export class ResultColumnBuilder< + Ok extends TypeBuilder, + Err extends TypeBuilder, + M extends ColumnMetadata< + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder + > = DefaultMetadata, + > + extends ColumnBuilder< + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder, + ResultAlgebraicType< + InferSpacetimeTypeOfTypeBuilder, + InferSpacetimeTypeOfTypeBuilder + >, + M + > + implements + Defaultable< + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder, + ResultAlgebraicType< + InferSpacetimeTypeOfTypeBuilder, + InferSpacetimeTypeOfTypeBuilder + > + > +{ + constructor(typeBuilder: TypeBuilder, metadata: M) { + super(typeBuilder, metadata); + } + + default( + value: InferTypeOfTypeBuilder | InferTypeOfTypeBuilder + ): ResultColumnBuilder< + Ok, + Err, + SetField< + M, + 'defaultValue', + InferTypeOfTypeBuilder | InferTypeOfTypeBuilder + > + > { + return new ResultColumnBuilder( + this.typeBuilder, + set(this.columnMetadata, { + defaultValue: value, + }) + ); + } +} + export class ProductColumnBuilder< Elements extends ElementsObj, M extends ColumnMetadata> = DefaultMetadata, @@ -3726,6 +3826,20 @@ export const t = { return new OptionBuilder(value); }, + /** + * This is a convenience method for creating a column with the {@link Result} type. + * You can create a column of the same type by constructing an enum with an `ok` and `err` variant. + * @param ok The type of the value contained in the `ok` variant of the `Result`. + * @param err The type of the value contained in the `err` variant of the `Result`. + * @returns A new {@link ResultBuilder} instance with the {@link Result} type. + */ + result, Err extends TypeBuilder>( + ok: Ok, + err: Err + ): ResultBuilder { + return new ResultBuilder(ok, err); + }, + /** * This is a convenience method for creating a column with the {@link Identity} type. * You can create a column of the same type by constructing an `object` with a single `__identity__` element. diff --git a/crates/codegen/src/csharp.rs b/crates/codegen/src/csharp.rs index c07265a6d69..d7ba9560c8c 100644 --- a/crates/codegen/src/csharp.rs +++ b/crates/codegen/src/csharp.rs @@ -1103,6 +1103,12 @@ fn ty_fmt<'a>(module: &'a ModuleDef, ty: &'a AlgebraicTypeUse) -> impl fmt::Disp AlgebraicTypeUse::TimeDuration => f.write_str("SpacetimeDB.TimeDuration"), AlgebraicTypeUse::Unit => f.write_str("SpacetimeDB.Unit"), AlgebraicTypeUse::Option(inner_ty) => write!(f, "{}?", ty_fmt(module, inner_ty)), + AlgebraicTypeUse::Result { ok_ty, err_ty } => write!( + f, + "SpacetimeDB.Result<{}, {}>", + ty_fmt(module, ok_ty), + ty_fmt(module, err_ty) + ), AlgebraicTypeUse::Array(elem_ty) => write!(f, "System.Collections.Generic.List<{}>", ty_fmt(module, elem_ty)), AlgebraicTypeUse::String => f.write_str("string"), AlgebraicTypeUse::Ref(r) => f.write_str(&type_ref_name(module, *r)), @@ -1137,6 +1143,12 @@ fn ty_fmt_with_ns<'a>(module: &'a ModuleDef, ty: &'a AlgebraicTypeUse, namespace AlgebraicTypeUse::TimeDuration => f.write_str("SpacetimeDB.TimeDuration"), AlgebraicTypeUse::Unit => f.write_str("SpacetimeDB.Unit"), AlgebraicTypeUse::Option(inner_ty) => write!(f, "{}?", ty_fmt_with_ns(module, inner_ty, namespace)), + AlgebraicTypeUse::Result { ok_ty, err_ty } => write!( + f, + "SpacetimeDB.Result<{}, {}>", + ty_fmt_with_ns(module, ok_ty, namespace), + ty_fmt_with_ns(module, err_ty, namespace) + ), AlgebraicTypeUse::Array(elem_ty) => write!( f, "System.Collections.Generic.List<{}>", @@ -1189,6 +1201,8 @@ fn default_init(ctx: &TypespaceForGenerate, ty: &AlgebraicTypeUse) -> Option<&'s | AlgebraicTypeUse::ConnectionId | AlgebraicTypeUse::Timestamp | AlgebraicTypeUse::TimeDuration => None, + // Result is a struct, initialized to the "Ok with default T" variant. + AlgebraicTypeUse::Result { .. } => None, AlgebraicTypeUse::Never => unimplemented!("never types are not yet supported in C# output"), } } diff --git a/crates/codegen/src/rust.rs b/crates/codegen/src/rust.rs index c6eae0aaa23..77d89a7d5fa 100644 --- a/crates/codegen/src/rust.rs +++ b/crates/codegen/src/rust.rs @@ -622,6 +622,13 @@ pub fn write_type(module: &ModuleDef, out: &mut W, ty: &AlgebraicTypeU write_type(module, out, inner_ty)?; write!(out, ">")?; } + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + write!(out, "Result::<")?; + write_type(module, out, ok_ty)?; + write!(out, ", ")?; + write_type(module, out, err_ty)?; + write!(out, ">")?; + } AlgebraicTypeUse::Primitive(prim) => match prim { PrimitiveType::Bool => write!(out, "bool")?, PrimitiveType::I8 => write!(out, "i8")?, diff --git a/crates/codegen/src/typescript.rs b/crates/codegen/src/typescript.rs index d1f9343a362..249aea06f00 100644 --- a/crates/codegen/src/typescript.rs +++ b/crates/codegen/src/typescript.rs @@ -734,6 +734,13 @@ fn write_type_builder(module: &ModuleDef, out: &mut W, ty: &AlgebraicT write_type_builder(module, out, inner_ty)?; write!(out, ")")?; } + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + write!(out, "__t.result(")?; + write_type_builder(module, out, ok_ty)?; + write!(out, ", ")?; + write_type_builder(module, out, err_ty)?; + write!(out, ")")?; + } AlgebraicTypeUse::Primitive(prim) => match prim { PrimitiveType::Bool => write!(out, "__t.bool()")?, PrimitiveType::I8 => write!(out, "__t.i8()")?, @@ -846,7 +853,7 @@ fn needs_parens_within_array(ty: &AlgebraicTypeUse) -> bool { | AlgebraicTypeUse::String => { false } - AlgebraicTypeUse::ScheduleAt | AlgebraicTypeUse::Option(_) => { + AlgebraicTypeUse::ScheduleAt | AlgebraicTypeUse::Option(_) | AlgebraicTypeUse::Result { .. } => { true } } @@ -874,6 +881,11 @@ pub fn write_type( write_type(module, out, inner_ty, ref_prefix, ref_suffix)?; write!(out, " | undefined")?; } + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + write_type(module, out, ok_ty, ref_prefix, ref_suffix)?; + write!(out, " | ")?; + write_type(module, out, err_ty, ref_prefix, ref_suffix)?; + } AlgebraicTypeUse::Primitive(prim) => match prim { PrimitiveType::Bool => write!(out, "boolean")?, PrimitiveType::I8 => write!(out, "number")?, diff --git a/crates/codegen/src/unrealcpp.rs b/crates/codegen/src/unrealcpp.rs index 2fe7abd60e7..cd93321e458 100644 --- a/crates/codegen/src/unrealcpp.rs +++ b/crates/codegen/src/unrealcpp.rs @@ -4473,6 +4473,9 @@ fn should_pass_by_value_in_delegate(_module: &ModuleDef, ty: &AlgebraicTypeUse) AlgebraicTypeUse::Ref(_) => false, AlgebraicTypeUse::Array(_) => false, // Arrays use const references AlgebraicTypeUse::Option(inner) => should_pass_by_value_in_delegate(_module, inner), + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + should_pass_by_value_in_delegate(_module, ok_ty) && should_pass_by_value_in_delegate(_module, err_ty) + } AlgebraicTypeUse::ScheduleAt => false, AlgebraicTypeUse::Never => false, } @@ -4520,6 +4523,9 @@ fn is_blueprintable(module: &ModuleDef, ty: &AlgebraicTypeUse) -> bool { } } AlgebraicTypeUse::Option(inner) => is_blueprintable(module, inner), + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + is_blueprintable(module, ok_ty) && is_blueprintable(module, err_ty) + } AlgebraicTypeUse::Never => false, } } @@ -4553,6 +4559,9 @@ fn is_type_blueprintable_for_delegates(module: &ModuleDef, ty: &AlgebraicTypeUse } } AlgebraicTypeUse::Option(inner) => is_type_blueprintable_for_delegates(module, inner), + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + is_type_blueprintable_for_delegates(module, ok_ty) && is_type_blueprintable_for_delegates(module, err_ty) + } AlgebraicTypeUse::Never => false, } } @@ -4991,6 +5000,14 @@ fn cpp_ty_fmt_impl<'a>( } } + // Result use the generated result types + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + let ok_type = cpp_ty_fmt_impl(module, ok_ty, module_name).to_string(); + let err_type = cpp_ty_fmt_impl(module, err_ty, module_name).to_string(); + + write!(f, "FSpacetimeDBResult<{ok_type}, {err_type}>") + } + AlgebraicTypeUse::Never => unreachable!("never type"), }) } @@ -5028,6 +5045,8 @@ fn cpp_ty_init_fmt_impl<'a>(ty: &'a AlgebraicTypeUse) -> impl fmt::Display + 'a AlgebraicTypeUse::Ref(_r) => f.write_str(""), // Options use the generated optional types AlgebraicTypeUse::Option(_inner) => f.write_str(""), + // Result use the generated result types + AlgebraicTypeUse::Result { ok_ty: _, err_ty: _ } => f.write_str(""), AlgebraicTypeUse::Never => unreachable!("never type"), }) } @@ -5055,6 +5074,13 @@ fn collect_includes_for_type( // Also collect includes for the inner type collect_includes_for_type(module, inner, out, module_name); } + Result { ok_ty, err_ty } => { + // Add the result type header + out.insert("Types/Result.h".to_string()); + // Also collect includes for the ok and err types + collect_includes_for_type(module, ok_ty, out, module_name); + collect_includes_for_type(module, err_ty, out, module_name); + } Array(inner) => { collect_includes_for_type(module, inner, out, module_name); } diff --git a/crates/lib/tests/serde.rs b/crates/lib/tests/serde.rs index dfc6eef0957..661cc94116d 100644 --- a/crates/lib/tests/serde.rs +++ b/crates/lib/tests/serde.rs @@ -70,6 +70,10 @@ fn test_json_mappings() { enumm([("Hash", AlgebraicType::bytes()), ("Unit", AlgebraicType::unit())]).into(), ), ("and_peggy", AlgebraicType::option(AlgebraicType::F64)), + ( + "result", + AlgebraicType::result(AlgebraicType::U32, AlgebraicType::String), + ), ("identity", Identity::get_type()), ]); @@ -80,6 +84,7 @@ fn test_json_mappings() { "baz": ["heyyyyyy", "hooo"], "quux": { "Hash": "54a3e6d2b0959deaacf102292b1cbd6fcbb8cf237f73306e27ed82c3153878aa" }, "and_peggy": { "some": 3.141592653589793238426 }, + "result": { "ok": 1 }, "identity": ["0x0"] } "#; // all of those ^^^^^^ digits are from memory @@ -92,6 +97,7 @@ fn test_json_mappings() { "baz": ["it's 🥶°C"], "quux": { "Unit": [] }, "and_peggy": null, + "result": { "err": "sorry" }, "identity": ["0x0"] } "#; diff --git a/crates/lib/tests/snapshots/serde__json_mappings-2.snap b/crates/lib/tests/snapshots/serde__json_mappings-2.snap index db77cada11d..659a82a5a1b 100644 --- a/crates/lib/tests/snapshots/serde__json_mappings-2.snap +++ b/crates/lib/tests/snapshots/serde__json_mappings-2.snap @@ -1,6 +1,6 @@ --- source: crates/lib/tests/serde.rs -expression: "de_json({\n \"foo\": 5654,\n \"bar\": [1, 15, 44],\n \"baz\": [\"it's 🥶°C\"],\n \"quux\": { \"Unit\": [] },\n \"and_peggy\": null,\n \"identity\": [\"0x0\"]\n})" +expression: "de_json({\n \"foo\": 5654,\n \"bar\": [1, 15, 44],\n \"baz\": [\"it's 🥶°C\"],\n \"quux\": { \"Unit\": [] },\n \"and_peggy\": null,\n \"result\": { \"err\": \"sorry\" },\n \"identity\": [\"0x0\"]\n})" --- ( foo = 5654, @@ -14,6 +14,9 @@ expression: "de_json({\n \"foo\": 5654,\n \"bar\": [1, 15, 44],\n \"baz and_peggy = ( none = (), ), + result = ( + err = "sorry", + ), identity = ( __identity__ = 0, ), diff --git a/crates/lib/tests/snapshots/serde__json_mappings.snap b/crates/lib/tests/snapshots/serde__json_mappings.snap index 4bd8dcf4986..5f48e829bdd 100644 --- a/crates/lib/tests/snapshots/serde__json_mappings.snap +++ b/crates/lib/tests/snapshots/serde__json_mappings.snap @@ -1,6 +1,6 @@ --- source: crates/lib/tests/serde.rs -expression: "de_json({\n \"foo\": 42,\n \"bar\": \"404040FFFF0A48656C6C6F\",\n \"baz\": [\"heyyyyyy\", \"hooo\"],\n \"quux\": { \"Hash\": \"54a3e6d2b0959deaacf102292b1cbd6fcbb8cf237f73306e27ed82c3153878aa\" },\n \"and_peggy\": { \"some\": 3.141592653589793238426 },\n \"identity\": [\"0x0\"]\n})" +expression: "de_json({\n \"foo\": 42,\n \"bar\": \"404040FFFF0A48656C6C6F\",\n \"baz\": [\"heyyyyyy\", \"hooo\"],\n \"quux\": { \"Hash\": \"54a3e6d2b0959deaacf102292b1cbd6fcbb8cf237f73306e27ed82c3153878aa\" },\n \"and_peggy\": { \"some\": 3.141592653589793238426 },\n \"result\": { \"ok\": 1 },\n \"identity\": [\"0x0\"]\n})" --- ( foo = 42, @@ -15,6 +15,9 @@ expression: "de_json({\n \"foo\": 42,\n \"bar\": \"404040FFFF0A48656C6C6F\ and_peggy = ( some = 3.141592653589793, ), + result = ( + ok = 1, + ), identity = ( __identity__ = 0, ), diff --git a/crates/pg/src/encoder.rs b/crates/pg/src/encoder.rs index f5a6ed990ed..dc07f70c9e5 100644 --- a/crates/pg/src/encoder.rs +++ b/crates/pg/src/encoder.rs @@ -239,6 +239,15 @@ mod tests { let row = run(schema, value).await; assert_eq!(row, "\0\0\0\u{b}{\"some\": 1}\0\0\0\u{c}{\"none\": {}}"); + let result = AlgebraicType::result(AlgebraicType::I64, AlgebraicType::String); + let schema = ProductType::from([result.clone(), result.clone()]); + let value = product![ + AlgebraicValue::sum(0, AlgebraicValue::I64(1)), // Ok(1) + AlgebraicValue::sum(1, AlgebraicValue::String("error".into())), // Err("error") + ]; + let row = run(schema, value).await; + assert_eq!(row, "\0\0\0\t{\"ok\": 1}\0\0\0\u{10}{\"err\": \"error\"}"); + let color = AlgebraicType::Sum([SumTypeVariant::new_named(AlgebraicType::I64, "Gray")].into()); let nested = AlgebraicType::option(color.clone()); let schema = ProductType::from([color, nested]); diff --git a/crates/sats/src/algebraic_type.rs b/crates/sats/src/algebraic_type.rs index e42ba015639..8e0e1922af5 100644 --- a/crates/sats/src/algebraic_type.rs +++ b/crates/sats/src/algebraic_type.rs @@ -6,7 +6,7 @@ use crate::algebraic_value::ser::value_serialize; use crate::de::Deserialize; use crate::meta_type::MetaType; use crate::product_type::{CONNECTION_ID_TAG, IDENTITY_TAG, TIMESTAMP_TAG, TIME_DURATION_TAG}; -use crate::sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG}; +use crate::sum_type::{OPTION_NONE_TAG, OPTION_SOME_TAG, RESULT_ERR_TAG, RESULT_OK_TAG}; use crate::typespace::Typespace; use crate::{i256, u256}; use crate::{AlgebraicTypeRef, AlgebraicValue, ArrayType, ProductType, SpacetimeType, SumType, SumTypeVariant}; @@ -213,6 +213,17 @@ impl AlgebraicType { self.as_sum()?.as_option() } + /// Returns whether this type is a result type. + pub fn is_result(&self) -> bool { + matches!(self, Self::Sum(p) if p.is_result()) + } + + /// If this type is the standard result type, returns the types of the `ok` and `err` variants. + /// Otherwise, returns `None`. + pub fn as_result(&self) -> Option<(&AlgebraicType, &AlgebraicType)> { + self.as_sum()?.as_result() + } + /// Returns whether this type is scalar or a string type. pub fn is_scalar_or_string(&self) -> bool { self.is_scalar() || self.is_string() @@ -306,6 +317,12 @@ impl AlgebraicType { Self::sum([(OPTION_SOME_TAG, some_type), (OPTION_NONE_TAG, AlgebraicType::unit())]) } + /// Returns a structural result type where `ok_type` is the type for the `ok` variant + /// and `err_type` is the type for the `err` variant. + pub fn result(ok_type: Self, err_type: Self) -> Self { + Self::sum([(RESULT_OK_TAG, ok_type), (RESULT_ERR_TAG, err_type)]) + } + /// Returns an unsized array type where the element type is `ty`. pub fn array(ty: Self) -> Self { ArrayType { elem_ty: Box::new(ty) }.into() @@ -432,7 +449,7 @@ impl AlgebraicType { /// - a reference /// - a special, known type /// - a non-compound type like `U8`, `I32`, `F64`, etc. - /// - or a map, array, or option built from types that satisfy [`AlgebraicType::is_valid_for_client_type_use`] + /// - or a map, array, option, or result built from types that satisfy [`AlgebraicType::is_valid_for_client_type_use`] /// /// This method does not actually follow `Ref`s to check the types they point to, /// it only checks the structure of the type. @@ -441,6 +458,8 @@ impl AlgebraicType { AlgebraicType::Sum(sum) => { if let Some(wrapped) = sum.as_option() { wrapped.is_valid_for_client_type_use() + } else if let Some((ok_ty, err_ty)) = sum.as_result() { + ok_ty.is_valid_for_client_type_use() && err_ty.is_valid_for_client_type_use() } else { sum.is_special() || sum.is_empty() } @@ -542,6 +561,21 @@ mod tests { ); } + #[test] + fn result() { + let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String); + assert_eq!("(ok: U8 | err: String)", fmt_algebraic_type(&result).to_string()); + } + + #[test] + fn result_map() { + let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String); + assert_eq!( + "{ ty_: Sum, ok: { ty_: U8 }, err: { ty_: String } }", + fmt_map(&result).to_string() + ); + } + #[test] fn algebraic_type() { let algebraic_type = AlgebraicType::meta_type(); @@ -648,6 +682,18 @@ mod tests { ); } + #[test] + fn result_as_value() { + let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String); + let algebraic_type = AlgebraicType::meta_type(); + let typespace = Typespace::new(vec![algebraic_type]); + let at_ref = AlgebraicType::Ref(AlgebraicTypeRef(0)); + assert_eq!( + r#"(sum = (variants = [(name = (some = "ok"), algebraic_type = (u8 = ())), (name = (some = "err"), algebraic_type = (string = ()))]))"#, + in_space(&typespace, &at_ref, &result.as_value()).to_satn() + ); + } + #[test] fn algebraic_type_as_value() { let algebraic_type = AlgebraicType::meta_type(); @@ -718,6 +764,12 @@ mod tests { AlgebraicType::from_value(&option.as_value()).expect("No errors."); } + #[test] + fn result_from_value() { + let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String); + AlgebraicType::from_value(&result.as_value()).expect("No errors."); + } + #[test] fn builtin_from_value() { let u8 = AlgebraicType::U8; diff --git a/crates/sats/src/algebraic_value.rs b/crates/sats/src/algebraic_value.rs index aff976e2568..1ac10c3b8e9 100644 --- a/crates/sats/src/algebraic_value.rs +++ b/crates/sats/src/algebraic_value.rs @@ -188,6 +188,34 @@ impl AlgebraicValue { Self::sum(1, Self::unit()) } + /// Converts `self` into `Result, Self>`, if applicable. + pub fn into_result(self) -> Result, Self> { + match self { + AlgebraicValue::Sum(sum_value) => match sum_value.tag { + 0 => Ok(Ok(*sum_value.value)), + 1 => Ok(Err(*sum_value.value)), + _ => Err(AlgebraicValue::Sum(sum_value)), + }, + _ => Err(self), + } + } + + /// Returns an [`AlgebraicValue`] for ` Ok: v`. + /// + /// The `Ok` variant is assigned the tag `0`. + #[inline] + pub fn ResultOk(v: Self) -> Self { + Self::sum(0, v) + } + + /// Returns an [`AlgebraicValue`] for ` Err: v`. + /// + /// The `Err` variant is assigned the tag `1`. + #[inline] + pub fn ResultErr(v: Self) -> Self { + Self::sum(1, v) + } + /// Returns an [`AlgebraicValue`] representing a sum value with `tag` and `value`. pub fn sum(tag: u8, value: Self) -> Self { Self::Sum(SumValue::new(tag, value)) @@ -367,6 +395,17 @@ mod tests { assert_eq!("(none = ())", in_space(&typespace, &option, &sum_value).to_satn(),); } + #[test] + fn result() { + let result = AlgebraicType::result(AlgebraicType::U8, AlgebraicType::String); + let ok = AlgebraicValue::ResultOk(AlgebraicValue::U8(42)); + let typespace = Typespace::new(vec![]); + assert_eq!("(ok = 42)", in_space(&typespace, &result, &ok).to_satn(),); + + let err = AlgebraicValue::ResultErr(AlgebraicValue::String("error".into())); + assert_eq!("(err = \"error\")", in_space(&typespace, &result, &err).to_satn(),); + } + #[test] fn primitive() { let u8 = AlgebraicType::U8; diff --git a/crates/sats/src/proptest.rs b/crates/sats/src/proptest.rs index 8312a966beb..756dcfea523 100644 --- a/crates/sats/src/proptest.rs +++ b/crates/sats/src/proptest.rs @@ -241,7 +241,7 @@ fn generate_ref(typespace_len: u32) -> BoxedStrategy { } /// Generate a type valid to be used to generate a type *use* in a client module. -/// That is, a ref, non-compound type, a special type, or an array, map, or option of the same. +/// That is, a ref, non-compound type, a special type, or an array, map, option, result of the same. fn generate_type_valid_for_client_use() -> impl Strategy { let leaf = prop_oneof![ generate_non_compound_algebraic_type(), @@ -255,6 +255,7 @@ fn generate_type_valid_for_client_use() -> impl Strategy prop_oneof![ gen_element.clone().prop_map(AlgebraicType::array), gen_element.clone().prop_map(AlgebraicType::option), + gen_element.clone().prop_map(|x| AlgebraicType::result(x.clone(), x)), ] }) } diff --git a/crates/sats/src/sum_type.rs b/crates/sats/src/sum_type.rs index 7bb26e0bb5f..4d9afca349c 100644 --- a/crates/sats/src/sum_type.rs +++ b/crates/sats/src/sum_type.rs @@ -12,6 +12,10 @@ pub const SCHEDULE_AT_TIME_TAG: &str = "Time"; pub const OPTION_SOME_TAG: &str = "some"; /// The tag used for the `none` variant of the special `option` sum type. pub const OPTION_NONE_TAG: &str = "none"; +/// The tag used for the `ok` variant of the special `result` sum type. +pub const RESULT_OK_TAG: &str = "ok"; +/// The tag used for the `err` variant of the special `result` sum type. +pub const RESULT_ERR_TAG: &str = "err"; /// A structural sum type. /// @@ -119,6 +123,55 @@ impl SumType { self.as_option().is_some() } + /// Check whether this sum type is a structural result type. + /// + /// A structural result type has `ok(T)` as its first variant and `err(E)` as its second. + /// That is, `{ ok(T), err(E) }` or `ok: T | err: E` depending on your notation. + /// Note that `ok` and `err` are lowercase, unlike Rust's `Result`. + /// Order matters, and a result type with these variants in the opposite order will not be recognized. + /// + /// If the type does look like a structural result type, returns the types `T` and `E`. + pub fn as_result(&self) -> Option<(&AlgebraicType, &AlgebraicType)> { + match &*self.variants { + [first, second] if Self::are_variants_result(first, second) => { + Some((&first.algebraic_type, &second.algebraic_type)) + } + _ => None, + } + } + + /// Check whether this sum type is a structural result type. + /// + /// A structural result type has `ok(T)` as its first variant and `err(E)` as its second. + /// That is, `{ ok(T), err(E) }` or `ok: T | err: E` depending on your notation. + /// Note that `ok` and `err` are lowercase, unlike Rust's `Result`. + /// Order matters, and a result type with these variants in the opposite order will not be recognized. + /// + /// If the type does look like a structural result type, returns the types `T` and `E`. + pub fn as_result_mut(&mut self) -> Option<(&mut AlgebraicType, &mut AlgebraicType)> { + match &mut *self.variants { + [first, second] if Self::are_variants_result(first, second) => { + Some((&mut first.algebraic_type, &mut second.algebraic_type)) + } + _ => None, + } + } + + fn are_variants_result(first: &SumTypeVariant, second: &SumTypeVariant) -> bool { + first.has_name(RESULT_OK_TAG) && second.has_name(RESULT_ERR_TAG) + } + + /// Check whether this sum type is a structural result type. + /// + /// A structural result type has `ok(T)` as its first variant and `err(E)` as its second. + /// That is, `{ ok(T), err(E) }` or `ok: T | err: E` depending on your notation. + /// Note that `ok` and `err` are lowercase, unlike Rust's `Result`. + /// + /// Order matters, and a result type with these variants in the opposite order will not be recognized. + pub fn is_result(&self) -> bool { + self.as_result().is_some() + } + /// Return whether this sum type is empty, that is, has no variants. pub fn is_empty(&self) -> bool { self.variants.is_empty() @@ -139,9 +192,9 @@ impl SumType { } } - /// Returns whether this sum type is a special known type, currently `Option` or `ScheduleAt`. + /// Returns whether this sum type is a special known type, currently `Option`, `ScheduleAt`, or `Result`. pub fn is_special(&self) -> bool { - self.is_option() || self.is_schedule_at() + self.is_option() || self.is_schedule_at() || self.is_result() } /// Returns whether this sum type is like on in C without data attached to the variants. diff --git a/crates/sats/src/typespace.rs b/crates/sats/src/typespace.rs index cd6502ca7ba..45a45238526 100644 --- a/crates/sats/src/typespace.rs +++ b/crates/sats/src/typespace.rs @@ -293,6 +293,7 @@ pub trait GroundSpacetimeType { /// - `String` and `&str`, utf-8 string data /// - `()`, the unit type /// - `Option where T: SpacetimeType` +/// - `Result where T: SpacetimeType, E: SpacetimeType` /// - `Vec where T: SpacetimeType` /// /// (Storing collections in rows of a database table is a form of [denormalization](https://en.wikipedia.org/wiki/Denormalization).) @@ -430,6 +431,16 @@ impl_st!([] bytes::Bytes, AlgebraicType::bytes()); #[cfg(feature = "bytestring")] impl_st!([] bytestring::ByteString, AlgebraicType::String); +impl SpacetimeType for Result +where + T: SpacetimeType, + E: SpacetimeType, +{ + fn make_type(typespace: &mut S) -> AlgebraicType { + AlgebraicType::result(T::make_type(typespace), E::make_type(typespace)) + } +} + #[cfg(test)] mod tests { use crate::proptest::generate_typespace_valid_for_codegen; @@ -469,5 +480,18 @@ mod tests { assert_not_valid(AlgebraicType::option(AlgebraicType::array(AlgebraicType::option( bad_inner_1.clone(), )))); + + assert_not_valid(AlgebraicType::result(bad_inner_1.clone(), AlgebraicType::U8)); + assert_not_valid(AlgebraicType::result(AlgebraicType::U8, bad_inner_2.clone())); + + assert_not_valid(AlgebraicType::result( + AlgebraicType::array(AlgebraicType::result(bad_inner_1.clone(), AlgebraicType::U8)), + AlgebraicType::U8, + )); + + assert_not_valid(AlgebraicType::result( + AlgebraicType::U8, + AlgebraicType::array(AlgebraicType::result(AlgebraicType::U8, bad_inner_2.clone())), + )); } } diff --git a/crates/schema/src/type_for_generate.rs b/crates/schema/src/type_for_generate.rs index 6c2a259f080..a915b999b0a 100644 --- a/crates/schema/src/type_for_generate.rs +++ b/crates/schema/src/type_for_generate.rs @@ -281,6 +281,12 @@ pub enum AlgebraicTypeUse { /// A standard structural option type. Option(Arc), + /// A standard structural result type. + Result { + ok_ty: Arc, + err_ty: Arc, + }, + /// The special `ScheduleAt` type. ScheduleAt, @@ -329,6 +335,10 @@ impl AlgebraicTypeUse { AlgebraicTypeUse::Ref(ref_) => f(*ref_), AlgebraicTypeUse::Array(elem_ty) => elem_ty._for_each_ref(f), AlgebraicTypeUse::Option(elem_ty) => elem_ty._for_each_ref(f), + AlgebraicTypeUse::Result { ok_ty, err_ty } => { + ok_ty._for_each_ref(f); + err_ty._for_each_ref(f); + } _ => {} } } @@ -398,6 +408,12 @@ impl TypespaceForGenerateBuilder<'_> { let elem_ty = self.parse_use(elem_ty)?; let interned = self.intern_use(elem_ty); Ok(AlgebraicTypeUse::Option(interned)) + } else if let Some((ok_ty, err_ty)) = ty.as_result() { + let ok = self.parse_use(ok_ty)?; + let err = self.parse_use(err_ty)?; + let ok_ty = self.intern_use(ok); + let err_ty = self.intern_use(err); + Ok(AlgebraicTypeUse::Result { ok_ty, err_ty }) } else if ty.is_schedule_at() { Ok(AlgebraicTypeUse::ScheduleAt) } else { @@ -708,11 +724,19 @@ mod tests { let ref1 = t.add(AlgebraicType::array(AlgebraicType::Ref(def))); let ref2 = t.add(AlgebraicType::option(AlgebraicType::Ref(ref1))); let ref3 = t.add(AlgebraicType::Ref(ref2)); + let ref4 = t.add(AlgebraicType::result( + AlgebraicType::Ref(ref3), + AlgebraicType::Ref(ref2), + )); let expected_0 = AlgebraicTypeUse::Ref(def); let expected_1 = AlgebraicTypeUse::Array(Arc::new(expected_0.clone())); let expected_2 = AlgebraicTypeUse::Option(Arc::new(expected_1.clone())); let expected_3 = expected_2.clone(); + let expected_4 = AlgebraicTypeUse::Result { + ok_ty: Arc::new(expected_3.clone()), + err_ty: Arc::new(expected_2.clone()), + }; let mut for_generate_forward = TypespaceForGenerate::builder(&t, [def]); for_generate_forward.add_definition(def).unwrap(); @@ -720,13 +744,16 @@ mod tests { let use1 = for_generate_forward.parse_use(&ref1.into()).unwrap(); let use2 = for_generate_forward.parse_use(&ref2.into()).unwrap(); let use3 = for_generate_forward.parse_use(&ref3.into()).unwrap(); + let use4 = for_generate_forward.parse_use(&ref4.into()).unwrap(); assert_eq!(use0, expected_0); assert_eq!(use1, expected_1); assert_eq!(use2, expected_2); assert_eq!(use3, expected_3); + assert_eq!(use4, expected_4); let mut for_generate_backward = TypespaceForGenerate::builder(&t, [def]); + let use4 = for_generate_backward.parse_use(&ref4.into()).unwrap(); let use3 = for_generate_forward.parse_use(&ref3.into()).unwrap(); let use2 = for_generate_forward.parse_use(&ref2.into()).unwrap(); let use1 = for_generate_forward.parse_use(&ref1.into()).unwrap(); @@ -737,6 +764,7 @@ mod tests { assert_eq!(use1, expected_1); assert_eq!(use2, expected_2); assert_eq!(use3, expected_3); + assert_eq!(use4, expected_4); } #[test] diff --git a/modules/sdk-test-cs/Lib.cs b/modules/sdk-test-cs/Lib.cs index b17daa81ad4..f1e6b78c02f 100644 --- a/modules/sdk-test-cs/Lib.cs +++ b/modules/sdk-test-cs/Lib.cs @@ -767,6 +767,86 @@ public static void insert_option_every_primitive_struct( ctx.Db.option_every_primitive_struct.Insert(new OptionEveryPrimitiveStruct { s = s }); } + [Table(Name = "result_i32_string", Public = true)] + public partial struct ResultI32String + { + public Result r; + } + + [Reducer] + public static void insert_result_i32_string(ReducerContext ctx, Result r) + { + ctx.Db.result_i32_string.Insert(new ResultI32String { r = r }); + } + + [Table(Name = "result_string_i32", Public = true)] + public partial struct ResultStringI32 + { + public Result r; + } + + [Reducer] + public static void insert_result_string_i32(ReducerContext ctx, Result r) + { + ctx.Db.result_string_i32.Insert(new ResultStringI32 { r = r }); + } + + [Table(Name = "result_identity_string", Public = true)] + public partial struct ResultIdentityString + { + public Result r; + } + + [Reducer] + public static void insert_result_identity_string(ReducerContext ctx, Result r) + { + ctx.Db.result_identity_string.Insert(new ResultIdentityString { r = r }); + } + + [Table(Name = "result_simple_enum_i32", Public = true)] + public partial struct ResultSimpleEnumI32 + { + public Result r; + } + + [Reducer] + public static void insert_result_simple_enum_i32(ReducerContext ctx, Result r) + { + ctx.Db.result_simple_enum_i32.Insert(new ResultSimpleEnumI32 { r = r }); + } + + [Table(Name = "result_every_primitive_struct_string", Public = true)] + public partial struct ResultEveryPrimitiveStructString + { + public Result r; + } + + [Reducer] + public static void insert_result_every_primitive_struct_string( + ReducerContext ctx, + Result r + ) + { + ctx.Db.result_every_primitive_struct_string.Insert( + new ResultEveryPrimitiveStructString { r = r } + ); + } + + [Table(Name = "result_vec_i32_string", Public = true)] + public partial struct ResultVecI32String + { + public Result, string> r; + } + + [Reducer] + public static void insert_result_vec_i32_string( + ReducerContext ctx, + Result, string> r + ) + { + ctx.Db.result_vec_i32_string.Insert(new ResultVecI32String { r = r }); + } + [SpacetimeDB.Table(Name = "option_vec_option_i32", Public = true)] public partial struct OptionVecOptionI32 { diff --git a/modules/sdk-test-cs/sdk-test-cs.sln b/modules/sdk-test-cs/sdk-test-cs.sln new file mode 100644 index 00000000000..9e64702e019 --- /dev/null +++ b/modules/sdk-test-cs/sdk-test-cs.sln @@ -0,0 +1,24 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.5.2.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "sdk-test-cs", "sdk-test-cs.csproj", "{A8BDE7A4-453D-5B7A-BB1B-A86E6E17160F}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A8BDE7A4-453D-5B7A-BB1B-A86E6E17160F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A8BDE7A4-453D-5B7A-BB1B-A86E6E17160F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A8BDE7A4-453D-5B7A-BB1B-A86E6E17160F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A8BDE7A4-453D-5B7A-BB1B-A86E6E17160F}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {76E4F9B9-C27D-4E4E-A381-E195CCBB2E0E} + EndGlobalSection +EndGlobal diff --git a/modules/sdk-test-ts/src/index.ts b/modules/sdk-test-ts/src/index.ts index 76bc93e1748..fd521ef5135 100644 --- a/modules/sdk-test-ts/src/index.ts +++ b/modules/sdk-test-ts/src/index.ts @@ -316,6 +316,40 @@ const optionTables = [ ), ] as const; +// Tables for Result values. +const resultTables = [ + tbl( + 'result_i32_string', + { insert: 'insert_result_i32_string' }, + { r: t.result(t.i32(), t.string()) } + ), + tbl( + 'result_string_i32', + { insert: 'insert_result_string_i32' }, + { r: t.result(t.string(), t.i32()) } + ), + tbl( + 'result_identity_string', + { insert: 'insert_result_identity_string' }, + { r: t.result(t.identity(), t.string()) } + ), + tbl( + 'result_simple_enum_i32', + { insert: 'insert_result_simple_enum_i32' }, + { r: t.result(SimpleEnum, t.i32()) } + ), + tbl( + 'result_every_primitive_struct_string', + { insert: 'insert_result_every_primitive_struct_string' }, + { r: t.result(EveryPrimitiveStruct, t.string()) } + ), + tbl( + 'result_vec_i32_string', + { insert: 'insert_result_vec_i32_string' }, + { r: t.result(t.array(t.i32()), t.string()) } + ), +] as const; + // Tables mapping a unique, but non-pk, key to a boring i32 payload. // This allows us to test delete events, and the semantically correct absence of update events. const uniqueTables = [ @@ -717,6 +751,7 @@ const allTables = [ ...singleValTables, ...vecTables, ...optionTables, + ...resultTables, ...uniqueTables, ...pkTables, ...weirdTables, diff --git a/modules/sdk-test/src/lib.rs b/modules/sdk-test/src/lib.rs index c4dda5aeae3..81a62e324af 100644 --- a/modules/sdk-test/src/lib.rs +++ b/modules/sdk-test/src/lib.rs @@ -335,6 +335,16 @@ define_tables! { OptionVecOptionI32 { insert insert_option_vec_option_i32 } v Option>>; } +// Tables holding a Result of various types. +define_tables! { + ResultI32String { insert insert_result_i32_string } r Result; + ResultStringI32 { insert insert_result_string_i32 } r Result; + ResultIdentityString { insert insert_result_identity_string } r Result; + ResultSimpleEnumI32 { insert insert_result_simple_enum_i32 } r Result; + ResultEveryPrimitiveStructString { insert insert_result_every_primitive_struct_string } r Result; + ResultVecI32String { insert insert_result_vec_i32_string } r Result, String>; +} + // Tables mapping a unique, but non-pk, key to a boring i32 payload. // This allows us to test delete events, and the semantically correct absence of update events. define_tables! { diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index b57c47b9ff2..e5373e9bb4a 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -64,6 +64,7 @@ void OnConnected(DbConnection conn, Identity identity, string authToken) "SELECT * FROM my_player", "SELECT * FROM players_at_level_one", "SELECT * FROM my_table", + "SELECT * FROM my_log", ]); // If testing against Rust, the indexed parameter will need to be changed to: ulong indexed @@ -134,6 +135,28 @@ void OnSubscriptionApplied(SubscriptionEventContext context) waiting++; context.Reducers.ThrowError("this is an error"); + Log.Debug("Calling InsertResult"); + waiting++; + context.Reducers.InsertResult(Result.Ok(new MyTable(new ReturnStruct(42, "magic")))); + waiting++; + context.Reducers.InsertResult(Result.Err("Fail")); + + Log.Debug("Calling RemoteQuery on my_log"); + var logRows = context.Db.MyLog.RemoteQuery("").Result; + Debug.Assert(logRows != null && logRows.Length == 2); + var logs = logRows.ToArray(); + var expected = new[] + { + new MyLog(Result.Ok( + new MyTable(new ReturnStruct(42, "magic")) + )), + new MyLog(Result.Err("Fail")), + }; + Debug.Assert( + logs.SequenceEqual(expected), + "Logs did not match expected results" + ); + // RemoteQuery test Log.Debug("Calling RemoteQuery"); // If testing against Rust, the query will need to be changed to "WHERE id = 0" @@ -230,6 +253,22 @@ void OnSubscriptionApplied(SubscriptionEventContext context) waiting--; }); + Log.Debug("Calling InsertWithTxRollbackResult"); + waiting++; + context.Procedures.InsertWithTxRollbackResult((IProcedureEventContext ctx, ProcedureCallbackResult> result) => + { + if (result.IsSuccess) + { + Debug.Assert(context.Db.MyTable.Count == 0, $"MyTable should remain empty after rollback result. Count was {context.Db.MyTable.Count}"); + Log.Debug("Insert with transaction result rollback succeeded"); + } + else + { + throw new Exception("Expected InsertWithTxRollbackResult to fail, but it succeeded"); + } + waiting--; + }); + Log.Debug("Calling InsertWithTxPanic"); waiting++; context.Procedures.InsertWithTxPanic((IProcedureEventContext ctx, ProcedureCallbackResult result) => diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/InsertWithTxRollbackResult.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/InsertWithTxRollbackResult.g.cs new file mode 100644 index 00000000000..58f21f9328b --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Procedures/InsertWithTxRollbackResult.g.cs @@ -0,0 +1,64 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteProcedures : RemoteBase + { + public void InsertWithTxRollbackResult(ProcedureCallback> callback) + { + // Convert the clean callback to the wrapper callback + InternalInsertWithTxRollbackResult((ctx, result) => + { + if (result.IsSuccess && result.Value != null) + { + callback(ctx, ProcedureCallbackResult>.Success(result.Value.Value)); + } + else + { + callback(ctx, ProcedureCallbackResult>.Failure(result.Error!)); + } + }); + } + + private void InternalInsertWithTxRollbackResult(ProcedureCallback callback) + { + conn.InternalCallProcedure(new Procedure.InsertWithTxRollbackResultArgs(), callback); + } + + } + + public abstract partial class Procedure + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class InsertWithTxRollbackResult + { + [DataMember(Name = "Value")] + public SpacetimeDB.Result Value; + + public InsertWithTxRollbackResult(SpacetimeDB.Result Value) + { + this.Value = Value; + } + + public InsertWithTxRollbackResult() + { + } + } + [SpacetimeDB.Type] + [DataContract] + public sealed partial class InsertWithTxRollbackResultArgs : Procedure, IProcedureArgs + { + string IProcedureArgs.ProcedureName => "InsertWithTxRollbackResult"; + } + + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertResult.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertResult.g.cs new file mode 100644 index 00000000000..6a363f4a8c1 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertResult.g.cs @@ -0,0 +1,72 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteReducers : RemoteBase + { + public delegate void InsertResultHandler(ReducerEventContext ctx, SpacetimeDB.Result msg); + public event InsertResultHandler? OnInsertResult; + + public void InsertResult(SpacetimeDB.Result msg) + { + conn.InternalCallReducer(new Reducer.InsertResult(msg), this.SetCallReducerFlags.InsertResultFlags); + } + + public bool InvokeInsertResult(ReducerEventContext ctx, Reducer.InsertResult args) + { + if (OnInsertResult == null) + { + if (InternalOnUnhandledReducerError != null) + { + switch (ctx.Event.Status) + { + case Status.Failed(var reason): InternalOnUnhandledReducerError(ctx, new Exception(reason)); break; + case Status.OutOfEnergy(var _): InternalOnUnhandledReducerError(ctx, new Exception("out of energy")); break; + } + } + return false; + } + OnInsertResult( + ctx, + args.Msg + ); + return true; + } + } + + public abstract partial class Reducer + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class InsertResult : Reducer, IReducerArgs + { + [DataMember(Name = "msg")] + public SpacetimeDB.Result Msg; + + public InsertResult(SpacetimeDB.Result Msg) + { + this.Msg = Msg; + } + + public InsertResult() + { + } + + string IReducerArgs.ReducerName => "InsertResult"; + } + } + + public sealed partial class SetReducerFlags + { + internal CallReducerFlags InsertResultFlags; + public void InsertResult(CallReducerFlags flags) => InsertResultFlags = flags; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs index 2bd475082ce..1674106f0bf 100644 --- a/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/SpacetimeDBClient.g.cs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.11.0 (commit 492e591845db8b174ee885b74294cb4ecbf655dc). +// This was generated using spacetimedb cli version 1.11.1 (commit 41eec04ea6150114247ff4ae7cbd7a68b1144bd5). #nullable enable @@ -29,6 +29,7 @@ public sealed partial class RemoteTables : RemoteTablesBase public RemoteTables(DbConnection conn) { AddTable(ExampleData = new(conn)); + AddTable(MyLog = new(conn)); AddTable(MyPlayer = new(conn)); AddTable(MyTable = new(conn)); AddTable(Player = new(conn)); @@ -598,6 +599,7 @@ protected override Reducer ToReducer(TransactionUpdate update) "Add" => BSATNHelpers.Decode(encodedArgs), "ClientConnected" => BSATNHelpers.Decode(encodedArgs), "Delete" => BSATNHelpers.Decode(encodedArgs), + "InsertResult" => BSATNHelpers.Decode(encodedArgs), "ThrowError" => BSATNHelpers.Decode(encodedArgs), "" => throw new SpacetimeDBEmptyReducerNameException("Reducer name is empty"), var reducer => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}") @@ -627,6 +629,7 @@ protected override bool Dispatch(IReducerEventContext context, Reducer reducer) Reducer.Add args => Reducers.InvokeAdd(eventContext, args), Reducer.ClientConnected args => Reducers.InvokeClientConnected(eventContext, args), Reducer.Delete args => Reducers.InvokeDelete(eventContext, args), + Reducer.InsertResult args => Reducers.InvokeInsertResult(eventContext, args), Reducer.ThrowError args => Reducers.InvokeThrowError(eventContext, args), _ => throw new ArgumentOutOfRangeException("Reducer", $"Unknown reducer {reducer}") }; diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyLog.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyLog.g.cs new file mode 100644 index 00000000000..50a9e8a2224 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/MyLog.g.cs @@ -0,0 +1,27 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using SpacetimeDB.BSATN; +using SpacetimeDB.ClientApi; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + public sealed partial class RemoteTables + { + public sealed class MyLogHandle : RemoteTableHandle + { + protected override string RemoteTableName => "my_log"; + + internal MyLogHandle(DbConnection conn) : base(conn) + { + } + } + + public readonly MyLogHandle MyLog; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyLog.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyLog.g.cs new file mode 100644 index 00000000000..0cf844f0b9f --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/MyLog.g.cs @@ -0,0 +1,28 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Runtime.Serialization; + +namespace SpacetimeDB.Types +{ + [SpacetimeDB.Type] + [DataContract] + public sealed partial class MyLog + { + [DataMember(Name = "msg")] + public SpacetimeDB.Result Msg; + + public MyLog(SpacetimeDB.Result Msg) + { + this.Msg = Msg; + } + + public MyLog() + { + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index 924c029b978..637ee55d68c 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -46,6 +46,12 @@ public partial struct ExampleData public uint Indexed; } + [SpacetimeDB.Table(Name = "my_log", Public = true)] + public partial struct MyLog + { + public Result msg; + } + [SpacetimeDB.Table(Name = "player", Public = true)] public partial struct Player { @@ -126,6 +132,12 @@ public static void ThrowError(ReducerContext ctx, string error) throw new Exception(error); } + [SpacetimeDB.Reducer] + public static void InsertResult(ReducerContext ctx, Result msg) + { + ctx.Db.my_log.Insert(new MyLog { msg = msg }); + } + [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { @@ -184,7 +196,7 @@ public static void InsertWithTxCommit(ProcedureContext ctx) { Field = new ReturnStruct(a: 42, b: "magic"), }); - return 0; // return value ignored by WithTx + return new Unit(); }); AssertRowCount(ctx, 1); @@ -207,6 +219,30 @@ public static void InsertWithTxRollback(ProcedureContext ctx) AssertRowCount(ctx, 0); } + [SpacetimeDB.Procedure] + public static Result InsertWithTxRollbackResult(ProcedureContext ctx) + { + try + { + var outcome = ctx.TryWithTx(tx => + { + tx.Db.my_table.Insert(new MyTable + { + Field = new ReturnStruct(a: 42, b: "magic") + }); + + throw new InvalidOperationException("rollback"); + }); + Debug.Assert(!outcome.IsSuccess, "TryWithTxAsync should report failure"); + AssertRowCount(ctx, 0); + return Result.Ok(new ReturnStruct(a: 42, b: "magic")); + } + catch (System.Exception e) + { + return Result.Err(e.ToString()); + } + } + private static void AssertRowCount(ProcedureContext ctx, ulong expected) { ctx.WithTx(tx => diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.11.1/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.11.1/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll new file mode 100755 index 0000000000000000000000000000000000000000..0210c2e02038e82e03ac71268e3b0302403020a4 GIT binary patch literal 77312 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, +} + +impl From for super::Reducer { + fn from(args: InsertResultEveryPrimitiveStructStringArgs) -> Self { + Self::InsertResultEveryPrimitiveStructString { r: args.r } + } +} + +impl __sdk::InModule for InsertResultEveryPrimitiveStructStringArgs { + type Module = super::RemoteModule; +} + +pub struct InsertResultEveryPrimitiveStructStringCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_result_every_primitive_struct_string`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_result_every_primitive_struct_string { + /// Request that the remote module invoke the reducer `insert_result_every_primitive_struct_string` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_result_every_primitive_struct_string`] callbacks. + fn insert_result_every_primitive_struct_string(&self, r: Result) + -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_result_every_primitive_struct_string`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertResultEveryPrimitiveStructStringCallbackId`] can be passed to [`Self::remove_on_insert_result_every_primitive_struct_string`] + /// to cancel the callback. + fn on_insert_result_every_primitive_struct_string( + &self, + callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultEveryPrimitiveStructStringCallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_result_every_primitive_struct_string`], + /// causing it not to run in the future. + fn remove_on_insert_result_every_primitive_struct_string( + &self, + callback: InsertResultEveryPrimitiveStructStringCallbackId, + ); +} + +impl insert_result_every_primitive_struct_string for super::RemoteReducers { + fn insert_result_every_primitive_struct_string( + &self, + r: Result, + ) -> __sdk::Result<()> { + self.imp.call_reducer( + "insert_result_every_primitive_struct_string", + InsertResultEveryPrimitiveStructStringArgs { r }, + ) + } + fn on_insert_result_every_primitive_struct_string( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultEveryPrimitiveStructStringCallbackId { + InsertResultEveryPrimitiveStructStringCallbackId(self.imp.on_reducer( + "insert_result_every_primitive_struct_string", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertResultEveryPrimitiveStructString { r }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, r) + }), + )) + } + fn remove_on_insert_result_every_primitive_struct_string( + &self, + callback: InsertResultEveryPrimitiveStructStringCallbackId, + ) { + self.imp + .remove_on_reducer("insert_result_every_primitive_struct_string", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_result_every_primitive_struct_string`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_result_every_primitive_struct_string { + /// Set the call-reducer flags for the reducer `insert_result_every_primitive_struct_string` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_result_every_primitive_struct_string(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_result_every_primitive_struct_string for super::SetReducerFlags { + fn insert_result_every_primitive_struct_string(&self, flags: __ws::CallReducerFlags) { + self.imp + .set_call_reducer_flags("insert_result_every_primitive_struct_string", flags); + } +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/insert_result_i_32_string_reducer.rs b/sdks/rust/tests/test-client/src/module_bindings/insert_result_i_32_string_reducer.rs new file mode 100644 index 00000000000..7ff948fe7f6 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/insert_result_i_32_string_reducer.rs @@ -0,0 +1,103 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct InsertResultI32StringArgs { + pub r: Result, +} + +impl From for super::Reducer { + fn from(args: InsertResultI32StringArgs) -> Self { + Self::InsertResultI32String { r: args.r } + } +} + +impl __sdk::InModule for InsertResultI32StringArgs { + type Module = super::RemoteModule; +} + +pub struct InsertResultI32StringCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_result_i32_string`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_result_i_32_string { + /// Request that the remote module invoke the reducer `insert_result_i32_string` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_result_i_32_string`] callbacks. + fn insert_result_i_32_string(&self, r: Result) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_result_i32_string`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertResultI32StringCallbackId`] can be passed to [`Self::remove_on_insert_result_i_32_string`] + /// to cancel the callback. + fn on_insert_result_i_32_string( + &self, + callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultI32StringCallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_result_i_32_string`], + /// causing it not to run in the future. + fn remove_on_insert_result_i_32_string(&self, callback: InsertResultI32StringCallbackId); +} + +impl insert_result_i_32_string for super::RemoteReducers { + fn insert_result_i_32_string(&self, r: Result) -> __sdk::Result<()> { + self.imp + .call_reducer("insert_result_i32_string", InsertResultI32StringArgs { r }) + } + fn on_insert_result_i_32_string( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultI32StringCallbackId { + InsertResultI32StringCallbackId(self.imp.on_reducer( + "insert_result_i32_string", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertResultI32String { r }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, r) + }), + )) + } + fn remove_on_insert_result_i_32_string(&self, callback: InsertResultI32StringCallbackId) { + self.imp.remove_on_reducer("insert_result_i32_string", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_result_i32_string`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_result_i_32_string { + /// Set the call-reducer flags for the reducer `insert_result_i32_string` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_result_i_32_string(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_result_i_32_string for super::SetReducerFlags { + fn insert_result_i_32_string(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("insert_result_i32_string", flags); + } +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/insert_result_identity_string_reducer.rs b/sdks/rust/tests/test-client/src/module_bindings/insert_result_identity_string_reducer.rs new file mode 100644 index 00000000000..9805c97f958 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/insert_result_identity_string_reducer.rs @@ -0,0 +1,103 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct InsertResultIdentityStringArgs { + pub r: Result<__sdk::Identity, String>, +} + +impl From for super::Reducer { + fn from(args: InsertResultIdentityStringArgs) -> Self { + Self::InsertResultIdentityString { r: args.r } + } +} + +impl __sdk::InModule for InsertResultIdentityStringArgs { + type Module = super::RemoteModule; +} + +pub struct InsertResultIdentityStringCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_result_identity_string`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_result_identity_string { + /// Request that the remote module invoke the reducer `insert_result_identity_string` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_result_identity_string`] callbacks. + fn insert_result_identity_string(&self, r: Result<__sdk::Identity, String>) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_result_identity_string`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertResultIdentityStringCallbackId`] can be passed to [`Self::remove_on_insert_result_identity_string`] + /// to cancel the callback. + fn on_insert_result_identity_string( + &self, + callback: impl FnMut(&super::ReducerEventContext, &Result<__sdk::Identity, String>) + Send + 'static, + ) -> InsertResultIdentityStringCallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_result_identity_string`], + /// causing it not to run in the future. + fn remove_on_insert_result_identity_string(&self, callback: InsertResultIdentityStringCallbackId); +} + +impl insert_result_identity_string for super::RemoteReducers { + fn insert_result_identity_string(&self, r: Result<__sdk::Identity, String>) -> __sdk::Result<()> { + self.imp + .call_reducer("insert_result_identity_string", InsertResultIdentityStringArgs { r }) + } + fn on_insert_result_identity_string( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &Result<__sdk::Identity, String>) + Send + 'static, + ) -> InsertResultIdentityStringCallbackId { + InsertResultIdentityStringCallbackId(self.imp.on_reducer( + "insert_result_identity_string", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertResultIdentityString { r }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, r) + }), + )) + } + fn remove_on_insert_result_identity_string(&self, callback: InsertResultIdentityStringCallbackId) { + self.imp.remove_on_reducer("insert_result_identity_string", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_result_identity_string`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_result_identity_string { + /// Set the call-reducer flags for the reducer `insert_result_identity_string` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_result_identity_string(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_result_identity_string for super::SetReducerFlags { + fn insert_result_identity_string(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("insert_result_identity_string", flags); + } +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/insert_result_simple_enum_i_32_reducer.rs b/sdks/rust/tests/test-client/src/module_bindings/insert_result_simple_enum_i_32_reducer.rs new file mode 100644 index 00000000000..2d8119be232 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/insert_result_simple_enum_i_32_reducer.rs @@ -0,0 +1,105 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::simple_enum_type::SimpleEnum; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct InsertResultSimpleEnumI32Args { + pub r: Result, +} + +impl From for super::Reducer { + fn from(args: InsertResultSimpleEnumI32Args) -> Self { + Self::InsertResultSimpleEnumI32 { r: args.r } + } +} + +impl __sdk::InModule for InsertResultSimpleEnumI32Args { + type Module = super::RemoteModule; +} + +pub struct InsertResultSimpleEnumI32CallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_result_simple_enum_i32`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_result_simple_enum_i_32 { + /// Request that the remote module invoke the reducer `insert_result_simple_enum_i32` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_result_simple_enum_i_32`] callbacks. + fn insert_result_simple_enum_i_32(&self, r: Result) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_result_simple_enum_i32`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertResultSimpleEnumI32CallbackId`] can be passed to [`Self::remove_on_insert_result_simple_enum_i_32`] + /// to cancel the callback. + fn on_insert_result_simple_enum_i_32( + &self, + callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultSimpleEnumI32CallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_result_simple_enum_i_32`], + /// causing it not to run in the future. + fn remove_on_insert_result_simple_enum_i_32(&self, callback: InsertResultSimpleEnumI32CallbackId); +} + +impl insert_result_simple_enum_i_32 for super::RemoteReducers { + fn insert_result_simple_enum_i_32(&self, r: Result) -> __sdk::Result<()> { + self.imp + .call_reducer("insert_result_simple_enum_i32", InsertResultSimpleEnumI32Args { r }) + } + fn on_insert_result_simple_enum_i_32( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultSimpleEnumI32CallbackId { + InsertResultSimpleEnumI32CallbackId(self.imp.on_reducer( + "insert_result_simple_enum_i32", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertResultSimpleEnumI32 { r }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, r) + }), + )) + } + fn remove_on_insert_result_simple_enum_i_32(&self, callback: InsertResultSimpleEnumI32CallbackId) { + self.imp.remove_on_reducer("insert_result_simple_enum_i32", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_result_simple_enum_i32`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_result_simple_enum_i_32 { + /// Set the call-reducer flags for the reducer `insert_result_simple_enum_i32` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_result_simple_enum_i_32(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_result_simple_enum_i_32 for super::SetReducerFlags { + fn insert_result_simple_enum_i_32(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("insert_result_simple_enum_i32", flags); + } +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/insert_result_string_i_32_reducer.rs b/sdks/rust/tests/test-client/src/module_bindings/insert_result_string_i_32_reducer.rs new file mode 100644 index 00000000000..f1cacccf25d --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/insert_result_string_i_32_reducer.rs @@ -0,0 +1,103 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct InsertResultStringI32Args { + pub r: Result, +} + +impl From for super::Reducer { + fn from(args: InsertResultStringI32Args) -> Self { + Self::InsertResultStringI32 { r: args.r } + } +} + +impl __sdk::InModule for InsertResultStringI32Args { + type Module = super::RemoteModule; +} + +pub struct InsertResultStringI32CallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_result_string_i32`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_result_string_i_32 { + /// Request that the remote module invoke the reducer `insert_result_string_i32` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_result_string_i_32`] callbacks. + fn insert_result_string_i_32(&self, r: Result) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_result_string_i32`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertResultStringI32CallbackId`] can be passed to [`Self::remove_on_insert_result_string_i_32`] + /// to cancel the callback. + fn on_insert_result_string_i_32( + &self, + callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultStringI32CallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_result_string_i_32`], + /// causing it not to run in the future. + fn remove_on_insert_result_string_i_32(&self, callback: InsertResultStringI32CallbackId); +} + +impl insert_result_string_i_32 for super::RemoteReducers { + fn insert_result_string_i_32(&self, r: Result) -> __sdk::Result<()> { + self.imp + .call_reducer("insert_result_string_i32", InsertResultStringI32Args { r }) + } + fn on_insert_result_string_i_32( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &Result) + Send + 'static, + ) -> InsertResultStringI32CallbackId { + InsertResultStringI32CallbackId(self.imp.on_reducer( + "insert_result_string_i32", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertResultStringI32 { r }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, r) + }), + )) + } + fn remove_on_insert_result_string_i_32(&self, callback: InsertResultStringI32CallbackId) { + self.imp.remove_on_reducer("insert_result_string_i32", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_result_string_i32`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_result_string_i_32 { + /// Set the call-reducer flags for the reducer `insert_result_string_i32` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_result_string_i_32(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_result_string_i_32 for super::SetReducerFlags { + fn insert_result_string_i_32(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("insert_result_string_i32", flags); + } +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/insert_result_vec_i_32_string_reducer.rs b/sdks/rust/tests/test-client/src/module_bindings/insert_result_vec_i_32_string_reducer.rs new file mode 100644 index 00000000000..fc247f7d0c8 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/insert_result_vec_i_32_string_reducer.rs @@ -0,0 +1,103 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub(super) struct InsertResultVecI32StringArgs { + pub r: Result, String>, +} + +impl From for super::Reducer { + fn from(args: InsertResultVecI32StringArgs) -> Self { + Self::InsertResultVecI32String { r: args.r } + } +} + +impl __sdk::InModule for InsertResultVecI32StringArgs { + type Module = super::RemoteModule; +} + +pub struct InsertResultVecI32StringCallbackId(__sdk::CallbackId); + +#[allow(non_camel_case_types)] +/// Extension trait for access to the reducer `insert_result_vec_i32_string`. +/// +/// Implemented for [`super::RemoteReducers`]. +pub trait insert_result_vec_i_32_string { + /// Request that the remote module invoke the reducer `insert_result_vec_i32_string` to run as soon as possible. + /// + /// This method returns immediately, and errors only if we are unable to send the request. + /// The reducer will run asynchronously in the future, + /// and its status can be observed by listening for [`Self::on_insert_result_vec_i_32_string`] callbacks. + fn insert_result_vec_i_32_string(&self, r: Result, String>) -> __sdk::Result<()>; + /// Register a callback to run whenever we are notified of an invocation of the reducer `insert_result_vec_i32_string`. + /// + /// Callbacks should inspect the [`__sdk::ReducerEvent`] contained in the [`super::ReducerEventContext`] + /// to determine the reducer's status. + /// + /// The returned [`InsertResultVecI32StringCallbackId`] can be passed to [`Self::remove_on_insert_result_vec_i_32_string`] + /// to cancel the callback. + fn on_insert_result_vec_i_32_string( + &self, + callback: impl FnMut(&super::ReducerEventContext, &Result, String>) + Send + 'static, + ) -> InsertResultVecI32StringCallbackId; + /// Cancel a callback previously registered by [`Self::on_insert_result_vec_i_32_string`], + /// causing it not to run in the future. + fn remove_on_insert_result_vec_i_32_string(&self, callback: InsertResultVecI32StringCallbackId); +} + +impl insert_result_vec_i_32_string for super::RemoteReducers { + fn insert_result_vec_i_32_string(&self, r: Result, String>) -> __sdk::Result<()> { + self.imp + .call_reducer("insert_result_vec_i32_string", InsertResultVecI32StringArgs { r }) + } + fn on_insert_result_vec_i_32_string( + &self, + mut callback: impl FnMut(&super::ReducerEventContext, &Result, String>) + Send + 'static, + ) -> InsertResultVecI32StringCallbackId { + InsertResultVecI32StringCallbackId(self.imp.on_reducer( + "insert_result_vec_i32_string", + Box::new(move |ctx: &super::ReducerEventContext| { + #[allow(irrefutable_let_patterns)] + let super::ReducerEventContext { + event: + __sdk::ReducerEvent { + reducer: super::Reducer::InsertResultVecI32String { r }, + .. + }, + .. + } = ctx + else { + unreachable!() + }; + callback(ctx, r) + }), + )) + } + fn remove_on_insert_result_vec_i_32_string(&self, callback: InsertResultVecI32StringCallbackId) { + self.imp.remove_on_reducer("insert_result_vec_i32_string", callback.0) + } +} + +#[allow(non_camel_case_types)] +#[doc(hidden)] +/// Extension trait for setting the call-flags for the reducer `insert_result_vec_i32_string`. +/// +/// Implemented for [`super::SetReducerFlags`]. +/// +/// This type is currently unstable and may be removed without a major version bump. +pub trait set_flags_for_insert_result_vec_i_32_string { + /// Set the call-reducer flags for the reducer `insert_result_vec_i32_string` to `flags`. + /// + /// This type is currently unstable and may be removed without a major version bump. + fn insert_result_vec_i_32_string(&self, flags: __ws::CallReducerFlags); +} + +impl set_flags_for_insert_result_vec_i_32_string for super::SetReducerFlags { + fn insert_result_vec_i_32_string(&self, flags: __ws::CallReducerFlags) { + self.imp.set_call_reducer_flags("insert_result_vec_i32_string", flags); + } +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/mod.rs b/sdks/rust/tests/test-client/src/module_bindings/mod.rs index 9fc08bf5823..27f875f8c0d 100644 --- a/sdks/rust/tests/test-client/src/module_bindings/mod.rs +++ b/sdks/rust/tests/test-client/src/module_bindings/mod.rs @@ -1,7 +1,7 @@ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. -// This was generated using spacetimedb cli version 1.9.0 (commit d694065b403f116daeb90e7cccc0920ce23dfc71). +// This was generated using spacetimedb cli version 1.11.1 (commit 0a192082a8103f3e9b4478835d1d92dec9669c8f). #![allow(unused, clippy::all)] use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; @@ -117,6 +117,12 @@ pub mod insert_pk_u_32_two_reducer; pub mod insert_pk_u_64_reducer; pub mod insert_pk_u_8_reducer; pub mod insert_primitives_as_strings_reducer; +pub mod insert_result_every_primitive_struct_string_reducer; +pub mod insert_result_i_32_string_reducer; +pub mod insert_result_identity_string_reducer; +pub mod insert_result_simple_enum_i_32_reducer; +pub mod insert_result_string_i_32_reducer; +pub mod insert_result_vec_i_32_string_reducer; pub mod insert_table_holds_table_reducer; pub mod insert_unique_bool_reducer; pub mod insert_unique_connection_id_reducer; @@ -262,6 +268,18 @@ pub mod pk_u_64_table; pub mod pk_u_64_type; pub mod pk_u_8_table; pub mod pk_u_8_type; +pub mod result_every_primitive_struct_string_table; +pub mod result_every_primitive_struct_string_type; +pub mod result_i_32_string_table; +pub mod result_i_32_string_type; +pub mod result_identity_string_table; +pub mod result_identity_string_type; +pub mod result_simple_enum_i_32_table; +pub mod result_simple_enum_i_32_type; +pub mod result_string_i_32_table; +pub mod result_string_i_32_type; +pub mod result_vec_i_32_string_table; +pub mod result_vec_i_32_string_type; pub mod scheduled_table_table; pub mod scheduled_table_type; pub mod send_scheduled_message_reducer; @@ -595,6 +613,25 @@ pub use insert_pk_u_8_reducer::{insert_pk_u_8, set_flags_for_insert_pk_u_8, Inse pub use insert_primitives_as_strings_reducer::{ insert_primitives_as_strings, set_flags_for_insert_primitives_as_strings, InsertPrimitivesAsStringsCallbackId, }; +pub use insert_result_every_primitive_struct_string_reducer::{ + insert_result_every_primitive_struct_string, set_flags_for_insert_result_every_primitive_struct_string, + InsertResultEveryPrimitiveStructStringCallbackId, +}; +pub use insert_result_i_32_string_reducer::{ + insert_result_i_32_string, set_flags_for_insert_result_i_32_string, InsertResultI32StringCallbackId, +}; +pub use insert_result_identity_string_reducer::{ + insert_result_identity_string, set_flags_for_insert_result_identity_string, InsertResultIdentityStringCallbackId, +}; +pub use insert_result_simple_enum_i_32_reducer::{ + insert_result_simple_enum_i_32, set_flags_for_insert_result_simple_enum_i_32, InsertResultSimpleEnumI32CallbackId, +}; +pub use insert_result_string_i_32_reducer::{ + insert_result_string_i_32, set_flags_for_insert_result_string_i_32, InsertResultStringI32CallbackId, +}; +pub use insert_result_vec_i_32_string_reducer::{ + insert_result_vec_i_32_string, set_flags_for_insert_result_vec_i_32_string, InsertResultVecI32StringCallbackId, +}; pub use insert_table_holds_table_reducer::{ insert_table_holds_table, set_flags_for_insert_table_holds_table, InsertTableHoldsTableCallbackId, }; @@ -780,6 +817,18 @@ pub use pk_u_64_table::*; pub use pk_u_64_type::PkU64; pub use pk_u_8_table::*; pub use pk_u_8_type::PkU8; +pub use result_every_primitive_struct_string_table::*; +pub use result_every_primitive_struct_string_type::ResultEveryPrimitiveStructString; +pub use result_i_32_string_table::*; +pub use result_i_32_string_type::ResultI32String; +pub use result_identity_string_table::*; +pub use result_identity_string_type::ResultIdentityString; +pub use result_simple_enum_i_32_table::*; +pub use result_simple_enum_i_32_type::ResultSimpleEnumI32; +pub use result_string_i_32_table::*; +pub use result_string_i_32_type::ResultStringI32; +pub use result_vec_i_32_string_table::*; +pub use result_vec_i_32_string_type::ResultVecI32String; pub use scheduled_table_table::*; pub use scheduled_table_type::ScheduledTable; pub use send_scheduled_message_reducer::{ @@ -1290,6 +1339,24 @@ pub enum Reducer { InsertPrimitivesAsStrings { s: EveryPrimitiveStruct, }, + InsertResultEveryPrimitiveStructString { + r: Result, + }, + InsertResultI32String { + r: Result, + }, + InsertResultIdentityString { + r: Result<__sdk::Identity, String>, + }, + InsertResultSimpleEnumI32 { + r: Result, + }, + InsertResultStringI32 { + r: Result, + }, + InsertResultVecI32String { + r: Result, String>, + }, InsertTableHoldsTable { a: OneU8, b: VecU8, @@ -1694,6 +1761,12 @@ impl __sdk::Reducer for Reducer { Reducer::InsertPkU64 { .. } => "insert_pk_u64", Reducer::InsertPkU8 { .. } => "insert_pk_u8", Reducer::InsertPrimitivesAsStrings { .. } => "insert_primitives_as_strings", + Reducer::InsertResultEveryPrimitiveStructString { .. } => "insert_result_every_primitive_struct_string", + Reducer::InsertResultI32String { .. } => "insert_result_i32_string", + Reducer::InsertResultIdentityString { .. } => "insert_result_identity_string", + Reducer::InsertResultSimpleEnumI32 { .. } => "insert_result_simple_enum_i32", + Reducer::InsertResultStringI32 { .. } => "insert_result_string_i32", + Reducer::InsertResultVecI32String { .. } => "insert_result_vec_i32_string", Reducer::InsertTableHoldsTable { .. } => "insert_table_holds_table", Reducer::InsertUniqueBool { .. } => "insert_unique_bool", Reducer::InsertUniqueConnectionId { .. } => "insert_unique_connection_id", @@ -2304,6 +2377,32 @@ impl TryFrom<__ws::ReducerCallInfo<__ws::BsatnFormat>> for Reducer { insert_primitives_as_strings_reducer::InsertPrimitivesAsStringsArgs, >("insert_primitives_as_strings", &value.args)? .into()), + "insert_result_every_primitive_struct_string" => { + Ok(__sdk::parse_reducer_args::< + insert_result_every_primitive_struct_string_reducer::InsertResultEveryPrimitiveStructStringArgs, + >("insert_result_every_primitive_struct_string", &value.args)? + .into()) + } + "insert_result_i32_string" => Ok(__sdk::parse_reducer_args::< + insert_result_i_32_string_reducer::InsertResultI32StringArgs, + >("insert_result_i32_string", &value.args)? + .into()), + "insert_result_identity_string" => Ok(__sdk::parse_reducer_args::< + insert_result_identity_string_reducer::InsertResultIdentityStringArgs, + >("insert_result_identity_string", &value.args)? + .into()), + "insert_result_simple_enum_i32" => Ok(__sdk::parse_reducer_args::< + insert_result_simple_enum_i_32_reducer::InsertResultSimpleEnumI32Args, + >("insert_result_simple_enum_i32", &value.args)? + .into()), + "insert_result_string_i32" => Ok(__sdk::parse_reducer_args::< + insert_result_string_i_32_reducer::InsertResultStringI32Args, + >("insert_result_string_i32", &value.args)? + .into()), + "insert_result_vec_i32_string" => Ok(__sdk::parse_reducer_args::< + insert_result_vec_i_32_string_reducer::InsertResultVecI32StringArgs, + >("insert_result_vec_i32_string", &value.args)? + .into()), "insert_table_holds_table" => Ok(__sdk::parse_reducer_args::< insert_table_holds_table_reducer::InsertTableHoldsTableArgs, >("insert_table_holds_table", &value.args)? @@ -2822,6 +2921,12 @@ pub struct DbUpdate { pk_u_32_two: __sdk::TableUpdate, pk_u_64: __sdk::TableUpdate, pk_u_8: __sdk::TableUpdate, + result_every_primitive_struct_string: __sdk::TableUpdate, + result_i_32_string: __sdk::TableUpdate, + result_identity_string: __sdk::TableUpdate, + result_simple_enum_i_32: __sdk::TableUpdate, + result_string_i_32: __sdk::TableUpdate, + result_vec_i_32_string: __sdk::TableUpdate, scheduled_table: __sdk::TableUpdate, table_holds_table: __sdk::TableUpdate, unique_bool: __sdk::TableUpdate, @@ -3032,6 +3137,24 @@ impl TryFrom<__ws::DatabaseUpdate<__ws::BsatnFormat>> for DbUpdate { .pk_u_64 .append(pk_u_64_table::parse_table_update(table_update)?), "pk_u8" => db_update.pk_u_8.append(pk_u_8_table::parse_table_update(table_update)?), + "result_every_primitive_struct_string" => db_update.result_every_primitive_struct_string.append( + result_every_primitive_struct_string_table::parse_table_update(table_update)?, + ), + "result_i32_string" => db_update + .result_i_32_string + .append(result_i_32_string_table::parse_table_update(table_update)?), + "result_identity_string" => db_update + .result_identity_string + .append(result_identity_string_table::parse_table_update(table_update)?), + "result_simple_enum_i32" => db_update + .result_simple_enum_i_32 + .append(result_simple_enum_i_32_table::parse_table_update(table_update)?), + "result_string_i32" => db_update + .result_string_i_32 + .append(result_string_i_32_table::parse_table_update(table_update)?), + "result_vec_i32_string" => db_update + .result_vec_i_32_string + .append(result_vec_i_32_string_table::parse_table_update(table_update)?), "scheduled_table" => db_update .scheduled_table .append(scheduled_table_table::parse_table_update(table_update)?), @@ -3282,6 +3405,20 @@ impl __sdk::DbUpdate for DbUpdate { diff.pk_u_8 = cache .apply_diff_to_table::("pk_u8", &self.pk_u_8) .with_updates_by_pk(|row| &row.n); + diff.result_every_primitive_struct_string = cache.apply_diff_to_table::( + "result_every_primitive_struct_string", + &self.result_every_primitive_struct_string, + ); + diff.result_i_32_string = + cache.apply_diff_to_table::("result_i32_string", &self.result_i_32_string); + diff.result_identity_string = + cache.apply_diff_to_table::("result_identity_string", &self.result_identity_string); + diff.result_simple_enum_i_32 = + cache.apply_diff_to_table::("result_simple_enum_i32", &self.result_simple_enum_i_32); + diff.result_string_i_32 = + cache.apply_diff_to_table::("result_string_i32", &self.result_string_i_32); + diff.result_vec_i_32_string = + cache.apply_diff_to_table::("result_vec_i32_string", &self.result_vec_i_32_string); diff.scheduled_table = cache .apply_diff_to_table::("scheduled_table", &self.scheduled_table) .with_updates_by_pk(|row| &row.scheduled_id); @@ -3401,6 +3538,12 @@ pub struct AppliedDiff<'r> { pk_u_32_two: __sdk::TableAppliedDiff<'r, PkU32Two>, pk_u_64: __sdk::TableAppliedDiff<'r, PkU64>, pk_u_8: __sdk::TableAppliedDiff<'r, PkU8>, + result_every_primitive_struct_string: __sdk::TableAppliedDiff<'r, ResultEveryPrimitiveStructString>, + result_i_32_string: __sdk::TableAppliedDiff<'r, ResultI32String>, + result_identity_string: __sdk::TableAppliedDiff<'r, ResultIdentityString>, + result_simple_enum_i_32: __sdk::TableAppliedDiff<'r, ResultSimpleEnumI32>, + result_string_i_32: __sdk::TableAppliedDiff<'r, ResultStringI32>, + result_vec_i_32_string: __sdk::TableAppliedDiff<'r, ResultVecI32String>, scheduled_table: __sdk::TableAppliedDiff<'r, ScheduledTable>, table_holds_table: __sdk::TableAppliedDiff<'r, TableHoldsTable>, unique_bool: __sdk::TableAppliedDiff<'r, UniqueBool>, @@ -3532,6 +3675,28 @@ impl<'r> __sdk::AppliedDiff<'r> for AppliedDiff<'r> { callbacks.invoke_table_row_callbacks::("pk_u32_two", &self.pk_u_32_two, event); callbacks.invoke_table_row_callbacks::("pk_u64", &self.pk_u_64, event); callbacks.invoke_table_row_callbacks::("pk_u8", &self.pk_u_8, event); + callbacks.invoke_table_row_callbacks::( + "result_every_primitive_struct_string", + &self.result_every_primitive_struct_string, + event, + ); + callbacks.invoke_table_row_callbacks::("result_i32_string", &self.result_i_32_string, event); + callbacks.invoke_table_row_callbacks::( + "result_identity_string", + &self.result_identity_string, + event, + ); + callbacks.invoke_table_row_callbacks::( + "result_simple_enum_i32", + &self.result_simple_enum_i_32, + event, + ); + callbacks.invoke_table_row_callbacks::("result_string_i32", &self.result_string_i_32, event); + callbacks.invoke_table_row_callbacks::( + "result_vec_i32_string", + &self.result_vec_i_32_string, + event, + ); callbacks.invoke_table_row_callbacks::("scheduled_table", &self.scheduled_table, event); callbacks.invoke_table_row_callbacks::("table_holds_table", &self.table_holds_table, event); callbacks.invoke_table_row_callbacks::("unique_bool", &self.unique_bool, event); @@ -4365,6 +4530,12 @@ impl __sdk::SpacetimeModule for RemoteModule { pk_u_32_two_table::register_table(client_cache); pk_u_64_table::register_table(client_cache); pk_u_8_table::register_table(client_cache); + result_every_primitive_struct_string_table::register_table(client_cache); + result_i_32_string_table::register_table(client_cache); + result_identity_string_table::register_table(client_cache); + result_simple_enum_i_32_table::register_table(client_cache); + result_string_i_32_table::register_table(client_cache); + result_vec_i_32_string_table::register_table(client_cache); scheduled_table_table::register_table(client_cache); table_holds_table_table::register_table(client_cache); unique_bool_table::register_table(client_cache); diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_table.rs b/sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_table.rs new file mode 100644 index 00000000000..39f27548639 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_table.rs @@ -0,0 +1,99 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::every_primitive_struct_type::EveryPrimitiveStruct; +use super::result_every_primitive_struct_string_type::ResultEveryPrimitiveStructString; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `result_every_primitive_struct_string`. +/// +/// Obtain a handle from the [`ResultEveryPrimitiveStructStringTableAccess::result_every_primitive_struct_string`] method on [`super::RemoteTables`], +/// like `ctx.db.result_every_primitive_struct_string()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.result_every_primitive_struct_string().on_insert(...)`. +pub struct ResultEveryPrimitiveStructStringTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `result_every_primitive_struct_string`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ResultEveryPrimitiveStructStringTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ResultEveryPrimitiveStructStringTableHandle`], which mediates access to the table `result_every_primitive_struct_string`. + fn result_every_primitive_struct_string(&self) -> ResultEveryPrimitiveStructStringTableHandle<'_>; +} + +impl ResultEveryPrimitiveStructStringTableAccess for super::RemoteTables { + fn result_every_primitive_struct_string(&self) -> ResultEveryPrimitiveStructStringTableHandle<'_> { + ResultEveryPrimitiveStructStringTableHandle { + imp: self + .imp + .get_table::("result_every_primitive_struct_string"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ResultEveryPrimitiveStructStringInsertCallbackId(__sdk::CallbackId); +pub struct ResultEveryPrimitiveStructStringDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ResultEveryPrimitiveStructStringTableHandle<'ctx> { + type Row = ResultEveryPrimitiveStructString; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ResultEveryPrimitiveStructStringInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultEveryPrimitiveStructStringInsertCallbackId { + ResultEveryPrimitiveStructStringInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ResultEveryPrimitiveStructStringInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ResultEveryPrimitiveStructStringDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultEveryPrimitiveStructStringDeleteCallbackId { + ResultEveryPrimitiveStructStringDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ResultEveryPrimitiveStructStringDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = + client_cache.get_or_make_table::("result_every_primitive_struct_string"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_type.rs b/sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_type.rs new file mode 100644 index 00000000000..27dd840464a --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_every_primitive_struct_string_type.rs @@ -0,0 +1,17 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::every_primitive_struct_type::EveryPrimitiveStruct; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ResultEveryPrimitiveStructString { + pub r: Result, +} + +impl __sdk::InModule for ResultEveryPrimitiveStructString { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_table.rs b/sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_table.rs new file mode 100644 index 00000000000..b18d8c228a1 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::result_i_32_string_type::ResultI32String; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `result_i32_string`. +/// +/// Obtain a handle from the [`ResultI32StringTableAccess::result_i_32_string`] method on [`super::RemoteTables`], +/// like `ctx.db.result_i_32_string()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.result_i_32_string().on_insert(...)`. +pub struct ResultI32StringTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `result_i32_string`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ResultI32StringTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ResultI32StringTableHandle`], which mediates access to the table `result_i32_string`. + fn result_i_32_string(&self) -> ResultI32StringTableHandle<'_>; +} + +impl ResultI32StringTableAccess for super::RemoteTables { + fn result_i_32_string(&self) -> ResultI32StringTableHandle<'_> { + ResultI32StringTableHandle { + imp: self.imp.get_table::("result_i32_string"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ResultI32StringInsertCallbackId(__sdk::CallbackId); +pub struct ResultI32StringDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ResultI32StringTableHandle<'ctx> { + type Row = ResultI32String; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ResultI32StringInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultI32StringInsertCallbackId { + ResultI32StringInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ResultI32StringInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ResultI32StringDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultI32StringDeleteCallbackId { + ResultI32StringDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ResultI32StringDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("result_i32_string"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_type.rs b/sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_type.rs new file mode 100644 index 00000000000..8eb0d4c5856 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_i_32_string_type.rs @@ -0,0 +1,15 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ResultI32String { + pub r: Result, +} + +impl __sdk::InModule for ResultI32String { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_identity_string_table.rs b/sdks/rust/tests/test-client/src/module_bindings/result_identity_string_table.rs new file mode 100644 index 00000000000..8a3d9d78b3e --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_identity_string_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::result_identity_string_type::ResultIdentityString; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `result_identity_string`. +/// +/// Obtain a handle from the [`ResultIdentityStringTableAccess::result_identity_string`] method on [`super::RemoteTables`], +/// like `ctx.db.result_identity_string()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.result_identity_string().on_insert(...)`. +pub struct ResultIdentityStringTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `result_identity_string`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ResultIdentityStringTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ResultIdentityStringTableHandle`], which mediates access to the table `result_identity_string`. + fn result_identity_string(&self) -> ResultIdentityStringTableHandle<'_>; +} + +impl ResultIdentityStringTableAccess for super::RemoteTables { + fn result_identity_string(&self) -> ResultIdentityStringTableHandle<'_> { + ResultIdentityStringTableHandle { + imp: self.imp.get_table::("result_identity_string"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ResultIdentityStringInsertCallbackId(__sdk::CallbackId); +pub struct ResultIdentityStringDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ResultIdentityStringTableHandle<'ctx> { + type Row = ResultIdentityString; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ResultIdentityStringInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultIdentityStringInsertCallbackId { + ResultIdentityStringInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ResultIdentityStringInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ResultIdentityStringDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultIdentityStringDeleteCallbackId { + ResultIdentityStringDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ResultIdentityStringDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("result_identity_string"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_identity_string_type.rs b/sdks/rust/tests/test-client/src/module_bindings/result_identity_string_type.rs new file mode 100644 index 00000000000..e242fbc4d06 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_identity_string_type.rs @@ -0,0 +1,15 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ResultIdentityString { + pub r: Result<__sdk::Identity, String>, +} + +impl __sdk::InModule for ResultIdentityString { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_table.rs b/sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_table.rs new file mode 100644 index 00000000000..4c2a65d9f66 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_table.rs @@ -0,0 +1,96 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::result_simple_enum_i_32_type::ResultSimpleEnumI32; +use super::simple_enum_type::SimpleEnum; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `result_simple_enum_i32`. +/// +/// Obtain a handle from the [`ResultSimpleEnumI32TableAccess::result_simple_enum_i_32`] method on [`super::RemoteTables`], +/// like `ctx.db.result_simple_enum_i_32()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.result_simple_enum_i_32().on_insert(...)`. +pub struct ResultSimpleEnumI32TableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `result_simple_enum_i32`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ResultSimpleEnumI32TableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ResultSimpleEnumI32TableHandle`], which mediates access to the table `result_simple_enum_i32`. + fn result_simple_enum_i_32(&self) -> ResultSimpleEnumI32TableHandle<'_>; +} + +impl ResultSimpleEnumI32TableAccess for super::RemoteTables { + fn result_simple_enum_i_32(&self) -> ResultSimpleEnumI32TableHandle<'_> { + ResultSimpleEnumI32TableHandle { + imp: self.imp.get_table::("result_simple_enum_i32"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ResultSimpleEnumI32InsertCallbackId(__sdk::CallbackId); +pub struct ResultSimpleEnumI32DeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ResultSimpleEnumI32TableHandle<'ctx> { + type Row = ResultSimpleEnumI32; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ResultSimpleEnumI32InsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultSimpleEnumI32InsertCallbackId { + ResultSimpleEnumI32InsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ResultSimpleEnumI32InsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ResultSimpleEnumI32DeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultSimpleEnumI32DeleteCallbackId { + ResultSimpleEnumI32DeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ResultSimpleEnumI32DeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("result_simple_enum_i32"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_type.rs b/sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_type.rs new file mode 100644 index 00000000000..e5a4725035d --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_simple_enum_i_32_type.rs @@ -0,0 +1,17 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +use super::simple_enum_type::SimpleEnum; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ResultSimpleEnumI32 { + pub r: Result, +} + +impl __sdk::InModule for ResultSimpleEnumI32 { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_table.rs b/sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_table.rs new file mode 100644 index 00000000000..ea6b2c3c1aa --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::result_string_i_32_type::ResultStringI32; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `result_string_i32`. +/// +/// Obtain a handle from the [`ResultStringI32TableAccess::result_string_i_32`] method on [`super::RemoteTables`], +/// like `ctx.db.result_string_i_32()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.result_string_i_32().on_insert(...)`. +pub struct ResultStringI32TableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `result_string_i32`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ResultStringI32TableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ResultStringI32TableHandle`], which mediates access to the table `result_string_i32`. + fn result_string_i_32(&self) -> ResultStringI32TableHandle<'_>; +} + +impl ResultStringI32TableAccess for super::RemoteTables { + fn result_string_i_32(&self) -> ResultStringI32TableHandle<'_> { + ResultStringI32TableHandle { + imp: self.imp.get_table::("result_string_i32"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ResultStringI32InsertCallbackId(__sdk::CallbackId); +pub struct ResultStringI32DeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ResultStringI32TableHandle<'ctx> { + type Row = ResultStringI32; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ResultStringI32InsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultStringI32InsertCallbackId { + ResultStringI32InsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ResultStringI32InsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ResultStringI32DeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultStringI32DeleteCallbackId { + ResultStringI32DeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ResultStringI32DeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("result_string_i32"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_type.rs b/sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_type.rs new file mode 100644 index 00000000000..463e9a8996b --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_string_i_32_type.rs @@ -0,0 +1,15 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ResultStringI32 { + pub r: Result, +} + +impl __sdk::InModule for ResultStringI32 { + type Module = super::RemoteModule; +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_table.rs b/sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_table.rs new file mode 100644 index 00000000000..201e7e778d6 --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_table.rs @@ -0,0 +1,95 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use super::result_vec_i_32_string_type::ResultVecI32String; +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +/// Table handle for the table `result_vec_i32_string`. +/// +/// Obtain a handle from the [`ResultVecI32StringTableAccess::result_vec_i_32_string`] method on [`super::RemoteTables`], +/// like `ctx.db.result_vec_i_32_string()`. +/// +/// Users are encouraged not to explicitly reference this type, +/// but to directly chain method calls, +/// like `ctx.db.result_vec_i_32_string().on_insert(...)`. +pub struct ResultVecI32StringTableHandle<'ctx> { + imp: __sdk::TableHandle, + ctx: std::marker::PhantomData<&'ctx super::RemoteTables>, +} + +#[allow(non_camel_case_types)] +/// Extension trait for access to the table `result_vec_i32_string`. +/// +/// Implemented for [`super::RemoteTables`]. +pub trait ResultVecI32StringTableAccess { + #[allow(non_snake_case)] + /// Obtain a [`ResultVecI32StringTableHandle`], which mediates access to the table `result_vec_i32_string`. + fn result_vec_i_32_string(&self) -> ResultVecI32StringTableHandle<'_>; +} + +impl ResultVecI32StringTableAccess for super::RemoteTables { + fn result_vec_i_32_string(&self) -> ResultVecI32StringTableHandle<'_> { + ResultVecI32StringTableHandle { + imp: self.imp.get_table::("result_vec_i32_string"), + ctx: std::marker::PhantomData, + } + } +} + +pub struct ResultVecI32StringInsertCallbackId(__sdk::CallbackId); +pub struct ResultVecI32StringDeleteCallbackId(__sdk::CallbackId); + +impl<'ctx> __sdk::Table for ResultVecI32StringTableHandle<'ctx> { + type Row = ResultVecI32String; + type EventContext = super::EventContext; + + fn count(&self) -> u64 { + self.imp.count() + } + fn iter(&self) -> impl Iterator + '_ { + self.imp.iter() + } + + type InsertCallbackId = ResultVecI32StringInsertCallbackId; + + fn on_insert( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultVecI32StringInsertCallbackId { + ResultVecI32StringInsertCallbackId(self.imp.on_insert(Box::new(callback))) + } + + fn remove_on_insert(&self, callback: ResultVecI32StringInsertCallbackId) { + self.imp.remove_on_insert(callback.0) + } + + type DeleteCallbackId = ResultVecI32StringDeleteCallbackId; + + fn on_delete( + &self, + callback: impl FnMut(&Self::EventContext, &Self::Row) + Send + 'static, + ) -> ResultVecI32StringDeleteCallbackId { + ResultVecI32StringDeleteCallbackId(self.imp.on_delete(Box::new(callback))) + } + + fn remove_on_delete(&self, callback: ResultVecI32StringDeleteCallbackId) { + self.imp.remove_on_delete(callback.0) + } +} + +#[doc(hidden)] +pub(super) fn register_table(client_cache: &mut __sdk::ClientCache) { + let _table = client_cache.get_or_make_table::("result_vec_i32_string"); +} + +#[doc(hidden)] +pub(super) fn parse_table_update( + raw_updates: __ws::TableUpdate<__ws::BsatnFormat>, +) -> __sdk::Result<__sdk::TableUpdate> { + __sdk::TableUpdate::parse_table_update(raw_updates).map_err(|e| { + __sdk::InternalError::failed_parse("TableUpdate", "TableUpdate") + .with_cause(e) + .into() + }) +} diff --git a/sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_type.rs b/sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_type.rs new file mode 100644 index 00000000000..cc6c0a71b6c --- /dev/null +++ b/sdks/rust/tests/test-client/src/module_bindings/result_vec_i_32_string_type.rs @@ -0,0 +1,15 @@ +// THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE +// WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD. + +#![allow(unused, clippy::all)] +use spacetimedb_sdk::__codegen::{self as __sdk, __lib, __sats, __ws}; + +#[derive(__lib::ser::Serialize, __lib::de::Deserialize, Clone, PartialEq, Debug)] +#[sats(crate = __lib)] +pub struct ResultVecI32String { + pub r: Result, String>, +} + +impl __sdk::InModule for ResultVecI32String { + type Module = super::RemoteModule; +} diff --git a/smoketests/tests/pg_wire.py b/smoketests/tests/pg_wire.py index 3e86d619473..31414a6798c 100644 --- a/smoketests/tests/pg_wire.py +++ b/smoketests/tests/pg_wire.py @@ -92,6 +92,19 @@ class SqlFormat(Smoketest): ints: TInts, } +#[derive(Clone)] +#[spacetimedb::table(name = t_enums)] +pub struct TEnums { + bool_opt: Option, + bool_result: Result, + action: Action, +} + +#[spacetimedb::table(name = t_enums_tuple)] +pub struct TEnumsTuple { + tuple: TEnums, +} + #[spacetimedb::reducer] pub fn test(ctx: &ReducerContext) { let tuple = TInts { @@ -141,6 +154,15 @@ class SqlFormat(Smoketest): se: TSimpleEnum { id: 2, action: Action::Active }, ints, }); + + let tuple = TEnums { + bool_opt: Some(true), + bool_result: Ok(false), + action: Action::Active, + }; + + ctx.db.t_enums().insert(tuple.clone()); + ctx.db.t_enums_tuple().insert(TEnumsTuple { tuple }); } """ @@ -235,7 +257,19 @@ def test_sql_format(self): -----------------------------------+-------------------------------------+--------------------------------------------------------------------------------------------------------- {"id": 1, "color": {"Gray": 128}} | {"id": 2, "action": {"Active": {}}} | {"i8": -25, "i16": -3224, "i32": -23443, "i64": -2344353, "i128": -234434897853, "i256": -234434897853} (1 row)""") - + self.assertPsql(token,"SELECT * FROM t_enums", """\ +bool_opt | bool_result | action +----------------+---------------+-------- + {"some": true} | {"ok": false} | Active +(1 row) +""") + self.assertPsql(token,"SELECT * FROM t_enums_tuple", """\ +tuple +-------------------------------------------------------------------------------------- + {"bool_opt": {"some": true}, "bool_result": {"ok": false}, "action": {"Active": {}}} +(1 row) +""") + def test_sql_conn(self): """This test is designed to test connecting to the database and executing queries using `psycopg2`""" token = self.read_token() diff --git a/smoketests/tests/sql.py b/smoketests/tests/sql.py index 6ea5082ebe6..5713e4f775a 100644 --- a/smoketests/tests/sql.py +++ b/smoketests/tests/sql.py @@ -4,7 +4,7 @@ class SqlFormat(Smoketest): MODULE_CODE = """ use spacetimedb::sats::{i256, u256}; -use spacetimedb::{table, ConnectionId, Identity, ReducerContext, Table, Timestamp, TimeDuration}; +use spacetimedb::{table, ConnectionId, Identity, ReducerContext, Table, Timestamp, TimeDuration, SpacetimeType }; #[derive(Copy, Clone)] #[spacetimedb::table(name = t_ints)] @@ -57,6 +57,25 @@ class SqlFormat(Smoketest): tuple: TOthers } +#[derive(SpacetimeType, Debug, Clone, Copy)] +pub enum Action { + Inactive, + Active, +} + +#[derive(Clone)] +#[spacetimedb::table(name = t_enums)] +pub struct TEnums { + bool_opt: Option, + bool_result: Result, + action: Action, +} + +#[spacetimedb::table(name = t_enums_tuple)] +pub struct TEnumsTuple { + tuple: TEnums, +} + #[spacetimedb::reducer] pub fn test(ctx: &ReducerContext) { let tuple = TInts { @@ -94,6 +113,15 @@ class SqlFormat(Smoketest): }; ctx.db.t_others().insert(tuple.clone()); ctx.db.t_others_tuple().insert(TOthersTuple { tuple }); + + let tuple = TEnums { + bool_opt: Some(true), + bool_result: Ok(false), + action: Action::Active, + }; + + ctx.db.t_enums().insert(tuple.clone()); + ctx.db.t_enums_tuple().insert(TEnumsTuple { tuple }); } """ @@ -131,4 +159,14 @@ def test_sql_format(self): tuple ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- (bool = true, f32 = 594806.56, f64 = -3454353.345389043, str = "This is spacetimedb", bytes = 0x01020304050607, identity = 0x0000000000000000000000000000000000000000000000000000000000000001, connection_id = 0x00000000000000000000000000000000, timestamp = 1970-01-01T00:00:00+00:00, duration = +0.000000) +""") + self.assertSql("SELECT * FROM t_enums", """\ + bool_opt | bool_result | action +---------------+--------------+--------------- + (some = true) | (ok = false) | (Active = ()) +""") + self.assertSql("SELECT * FROM t_enums_tuple", """\ + tuple +-------------------------------------------------------------------------------- + (bool_opt = (some = true), bool_result = (ok = false), action = (Active = ())) """) From 6898dc4d92500bc72c2f8d9ea7c037f8c6f9fe1d Mon Sep 17 00:00:00 2001 From: rekhoff Date: Wed, 31 Dec 2025 12:43:20 -0800 Subject: [PATCH 2/2] Added a reducer OnInsert callback to `waiting--` the waiting. --- crates/bindings-csharp/BSATN.Runtime/Builtins.cs | 1 - sdks/csharp/examples~/regression-tests/client/Program.cs | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs index 9d158606188..a07f088ff5b 100644 --- a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs +++ b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs @@ -1,7 +1,6 @@ namespace SpacetimeDB; using System.Diagnostics; -using System.Net.WebSockets; using System.Runtime.InteropServices; using SpacetimeDB.BSATN; diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index e5373e9bb4a..7e169ce7131 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -82,6 +82,12 @@ void OnConnected(DbConnection conn, Identity identity, string authToken) ValidateBTreeIndexes(ctx); }; + conn.Reducers.OnInsertResult += (ReducerEventContext ctx, Result msg) => + { + Log.Info($"Got InsertResult callback: {msg}"); + waiting--; + }; + conn.OnUnhandledReducerError += (ReducerEventContext ctx, Exception exception) => { Log.Info($"Got OnUnhandledReducerError: {exception}");