Skip to content

Commit bd8dba7

Browse files
committed
Implement SpacetimeType for Result<T, E>
1 parent 41eec04 commit bd8dba7

File tree

58 files changed

+2650
-59
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+2650
-59
lines changed

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

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,12 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter
9090
),
9191
INamedTypeSymbol named => named.OriginalDefinition.ToString() switch
9292
{
93+
"SpacetimeDB.Result<T, E>" or "SpacetimeDB.Result<T,E>" => new ResultUse(
94+
type,
95+
typeInfo,
96+
Parse(member, named.TypeArguments[0], diag),
97+
Parse(member, named.TypeArguments[1], diag)
98+
),
9399
"System.Collections.Generic.List<T>" => new ListUse(
94100
type,
95101
typeInfo,
@@ -107,6 +113,19 @@ public static TypeUse Parse(ISymbol member, ITypeSymbol typeSymbol, DiagReporter
107113
};
108114
}
109115

116+
/// <summary>
117+
/// Get the name of the BSATN struct for this type.
118+
/// </summary>
119+
public virtual string ToBSATNString()
120+
{
121+
return this.BSATNName;
122+
}
123+
124+
public virtual string ToBSATNString2()
125+
{
126+
return this.BSATNName;
127+
}
128+
110129
/// <summary>
111130
/// Get a statement that declares outVar and assigns (inVar1 logically-equals inVar2) to it.
112131
/// logically-equals:
@@ -139,6 +158,45 @@ public abstract string EqualsStatement(
139158
public abstract string GetHashCodeStatement(string inVar, string outVar, int level = 0);
140159
}
141160

161+
/// <summary>
162+
/// A use of a Result&lt;T, E&gt; type.
163+
/// </summary>
164+
public sealed record ResultUse : TypeUse
165+
{
166+
public TypeUse Ok { get; }
167+
public TypeUse Err { get; }
168+
169+
public string TypeName { get; }
170+
171+
public ResultUse(string typeName, string typeInfo, TypeUse ok, TypeUse err)
172+
: base(typeName, typeInfo)
173+
{
174+
Ok = ok;
175+
Err = err;
176+
TypeName = typeName;
177+
}
178+
179+
public override string ToBSATNString()
180+
{
181+
return $"{TypeName}.BSATN<{Ok.BSATNName}, {Err.BSATNName}>";
182+
}
183+
184+
public override string ToBSATNString2()
185+
{
186+
return $"{TypeName}.BSATN<{Ok.BSATNName}, {Err.BSATNName}>";
187+
}
188+
189+
public override string EqualsStatement(
190+
string inVar1,
191+
string inVar2,
192+
string outVar,
193+
int level = 0
194+
) => $"var {outVar} = {inVar1} == {inVar2};";
195+
196+
public override string GetHashCodeStatement(string inVar, string outVar, int level = 0) =>
197+
$"var {outVar} = {inVar}.GetHashCode();";
198+
}
199+
142200
/// <summary>
143201
/// A use of an enum type.
144202
/// (This is a C# enum, not one of our tagged enums.)
@@ -354,7 +412,7 @@ IEnumerable<MemberDeclaration> members
354412
return string.Join(
355413
"\n ",
356414
members.Select(m =>
357-
$"{visStr} static readonly {m.Type.BSATNName} {m.Name}{TypeUse.BsatnFieldSuffix} = new();"
415+
$"{visStr} static readonly {m.Type.ToBSATNString()} {m.Name}{TypeUse.BsatnFieldSuffix} = new();"
358416
)
359417
);
360418
}

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,8 @@ Unit F64
4343
// Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as an Option<T>.
4444
internal static AlgebraicType MakeOption(AlgebraicType someType) =>
4545
new Sum([new("some", someType), new("none", Unit)]);
46+
47+
// Special AlgebraicType that can be recognised by the SpacetimeDB `generate` CLI as a Result<T, E>.
48+
internal static AlgebraicType MakeResult(AlgebraicType okType, AlgebraicType errType) =>
49+
new Sum([new("ok", okType), new("err", errType)]);
4650
}

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

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
namespace SpacetimeDB;
22

33
using System.Diagnostics;
4+
using System.Net.WebSockets;
45
using System.Runtime.InteropServices;
56
using SpacetimeDB.BSATN;
67

@@ -605,3 +606,109 @@ public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>
605606
// --- / customized ---
606607
}
607608
}
609+
610+
public partial record Result<T, E> : TaggedEnum<(T Ok, E Err)>
611+
{
612+
public static implicit operator Result<T, E>(T value) => new OkR(value);
613+
614+
public static implicit operator Result<T, E>(E error) => new ErrR(error);
615+
616+
public TResult Match<TResult>(Func<T, TResult> onOk, Func<E, TResult> onErr) =>
617+
this switch
618+
{
619+
OkR(var v) => onOk(v),
620+
ErrR(var e) => onErr(e),
621+
_ => throw new InvalidOperationException("Unknown Result variant."),
622+
};
623+
624+
public static Result<T, E> Ok(T value) => new OkR(value);
625+
626+
public static Result<T, E> Err(E error) => new ErrR(error);
627+
628+
public T UnwrapOrThrow()
629+
{
630+
return this switch
631+
{
632+
OkR(var v) => v,
633+
ErrR(var e) when e is not null => throw new Exception(e.ToString()),
634+
ErrR(_) => throw new InvalidOperationException(
635+
"Result failed without an error object."
636+
),
637+
_ => throw new InvalidOperationException("Unknown Result variant."),
638+
};
639+
}
640+
641+
public T UnwrapOr(T defaultValue) =>
642+
this switch
643+
{
644+
OkR(var v) => v,
645+
_ => defaultValue,
646+
};
647+
648+
public T UnwrapOrElse(Func<E, T> f) =>
649+
this switch
650+
{
651+
OkR(var v) => v,
652+
ErrR(var e) => f(e),
653+
_ => throw new InvalidOperationException("Unknown Result variant."),
654+
};
655+
656+
// ----- auto-generated -----
657+
658+
private Result() { }
659+
660+
internal enum @enum : byte
661+
{
662+
Ok,
663+
Err,
664+
}
665+
666+
public sealed record OkR(T Value) : Result<T, E>;
667+
668+
public sealed record ErrR(E Error) : Result<T, E>;
669+
670+
private enum Variant : byte
671+
{
672+
Ok = 0,
673+
Err = 1,
674+
}
675+
676+
public readonly struct BSATN<OkRW, ErrRW> : IReadWrite<Result<T, E>>
677+
where OkRW : struct, IReadWrite<T>
678+
where ErrRW : struct, IReadWrite<E>
679+
{
680+
private static readonly SpacetimeDB.BSATN.Enum<@enum> __enumTag = new();
681+
private static readonly OkRW okRW = new();
682+
private static readonly ErrRW errRW = new();
683+
684+
public Result<T, E> Read(BinaryReader reader) =>
685+
__enumTag.Read(reader) switch
686+
{
687+
@enum.Ok => new OkR(okRW.Read(reader)),
688+
@enum.Err => new ErrR(errRW.Read(reader)),
689+
_ => throw new InvalidOperationException(),
690+
};
691+
692+
public void Write(BinaryWriter writer, Result<T, E> value)
693+
{
694+
switch (value)
695+
{
696+
case OkR(var v):
697+
__enumTag.Write(writer, @enum.Ok);
698+
okRW.Write(writer, v);
699+
break;
700+
701+
case ErrR(var e):
702+
__enumTag.Write(writer, @enum.Err);
703+
errRW.Write(writer, e);
704+
break;
705+
}
706+
}
707+
708+
public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) =>
709+
AlgebraicType.MakeResult(
710+
okRW.GetAlgebraicType(registrar),
711+
errRW.GetAlgebraicType(registrar)
712+
);
713+
}
714+
}

crates/bindings-csharp/Codegen/Module.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1178,7 +1178,7 @@ public Scope.Extensions GenerateSchedule()
11781178
using var writer = new BinaryWriter(stream);
11791179
{{string.Join(
11801180
"\n",
1181-
Args.Select(a => $"new {a.Type.BSATNName}().Write(writer, {a.Name});")
1181+
Args.Select(a => $"new {a.Type.ToBSATNString()}().Write(writer, {a.Name});")
11821182
)}}
11831183
SpacetimeDB.Internal.IReducer.VolatileNonatomicScheduleImmediate(nameof({{Name}}), stream);
11841184
}
@@ -1295,7 +1295,7 @@ public string GenerateClass()
12951295
}
12961296
else
12971297
{
1298-
var serializer = $"new {ReturnType.BSATNName}()";
1298+
var serializer = $"new {ReturnType.ToBSATNString()}()";
12991299
bodyLines = new[]
13001300
{
13011301
$"var result = {invocation};",
@@ -1321,12 +1321,12 @@ public string GenerateClass()
13211321
? (
13221322
txPayloadIsUnit
13231323
? "SpacetimeDB.BSATN.AlgebraicType.Unit"
1324-
: $"new {txPayload.BSATNName}().GetAlgebraicType(registrar)"
1324+
: $"new {txPayload.ToBSATNString2()}().GetAlgebraicType(registrar)"
13251325
)
13261326
: (
13271327
ReturnType.Name == "SpacetimeDB.Unit"
13281328
? "SpacetimeDB.BSATN.AlgebraicType.Unit"
1329-
: $"new {ReturnType.BSATNName}().GetAlgebraicType(registrar)"
1329+
: $"new {ReturnType.ToBSATNString2()}().GetAlgebraicType(registrar)"
13301330
);
13311331

13321332
var classFields = MemberDeclaration.GenerateBsatnFields(Accessibility.Private, Args);
@@ -1372,7 +1372,7 @@ public Scope.Extensions GenerateSchedule()
13721372
using var writer = new BinaryWriter(stream);
13731373
{{string.Join(
13741374
"\n",
1375-
Args.Select(a => $"new {a.Type.BSATNName}().Write(writer, {a.Name});")
1375+
Args.Select(a => $"new {a.Type.ToBSATNString()}().Write(writer, {a.Name});")
13761376
)}}
13771377
SpacetimeDB.Internal.ProcedureExtensions.VolatileNonatomicScheduleImmediate(nameof({{Name}}), stream);
13781378
}

crates/bindings-csharp/Runtime/ProcedureContext.cs

Lines changed: 10 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,40 +3,6 @@ namespace SpacetimeDB;
33
using System.Diagnostics.CodeAnalysis;
44
using Internal;
55

6-
public readonly struct Result<T, E>(bool isSuccess, T? value, E? error)
7-
where E : Exception
8-
{
9-
public bool IsSuccess { get; } = isSuccess;
10-
public T? Value { get; } = value;
11-
public E? Error { get; } = error;
12-
13-
public static Result<T, E> Ok(T value) => new(true, value, null);
14-
15-
public static Result<T, E> Err(E error) => new(false, default, error);
16-
17-
public T UnwrapOrThrow()
18-
{
19-
if (IsSuccess)
20-
{
21-
return Value!;
22-
}
23-
24-
if (Error is not null)
25-
{
26-
throw Error;
27-
}
28-
29-
throw new InvalidOperationException("Result failed without an error object.");
30-
}
31-
32-
public T UnwrapOr(T defaultValue) => IsSuccess ? Value! : defaultValue;
33-
34-
public T UnwrapOrElse(Func<E, T> f) => IsSuccess ? Value! : f(Error!);
35-
36-
public TResult Match<TResult>(Func<T, TResult> onOk, Func<E, TResult> onErr) =>
37-
IsSuccess ? onOk(Value!) : onErr(Error!);
38-
}
39-
406
#pragma warning disable STDB_UNSTABLE
417
public abstract class ProcedureContextBase(
428
Identity sender,
@@ -122,9 +88,13 @@ Func<ProcedureTxContextBase, Result<TResult, TError>> body
12288
try
12389
{
12490
var result = RunWithRetry(body);
125-
return result.IsSuccess
126-
? TxOutcome<TResult>.Success(result.Value!)
127-
: TxOutcome<TResult>.Failure(result.Error!);
91+
92+
return result switch
93+
{
94+
Result<TResult, TError>.OkR(var value) => TxOutcome<TResult>.Success(value),
95+
Result<TResult, TError>.ErrR(var error) => TxOutcome<TResult>.Failure(error),
96+
_ => throw new InvalidOperationException("Unknown Result variant."),
97+
};
12898
}
12999
catch (Exception ex)
130100
{
@@ -181,15 +151,15 @@ Func<ProcedureTxContextBase, Result<TResult, TError>> body
181151
where TError : Exception
182152
{
183153
var result = RunOnce(body);
184-
if (!result.IsSuccess)
154+
if (result is Result<TResult, TError>.ErrR)
185155
{
186156
return result;
187157
}
188158

189159
bool Retry()
190160
{
191161
result = RunOnce(body);
192-
return result.IsSuccess;
162+
return result is Result<TResult, TError>.OkR;
193163
}
194164

195165
if (!CommitMutTxWithRetry(Retry))
@@ -220,7 +190,7 @@ Func<ProcedureTxContextBase, Result<TResult, TError>> body
220190
throw;
221191
}
222192

223-
if (result.IsSuccess)
193+
if (result is Result<TResult, TError>.OkR)
224194
{
225195
guard.Disarm();
226196
return result;

crates/bindings-typescript/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ export * from './lib/timestamp';
99
export * from './lib/util';
1010
export * from './lib/identity';
1111
export * from './lib/option';
12+
export * from './lib/result';
1213
export * from './sdk';

0 commit comments

Comments
 (0)