diff --git a/crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs b/crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs index 6d8d3c95c2..bec321d931 100644 --- a/crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs +++ b/crates/bindings-csharp/BSATN.Runtime.Tests/Tests.cs @@ -809,6 +809,32 @@ public static partial class BSATNRuntimeTests #pragma warning restore CS8625 // Cannot convert null literal to non-nullable reference type. } + [Fact] + public static void NonNullableStringSerializationRejectsNull() + { + var stream = new MemoryStream(); + var writer = new BinaryWriter(stream); + + var serializer = new BSATN.String(); + var ex = Assert.Throws(() => serializer.Write(writer, null!)); + Assert.Contains("nullable string", ex.Message, StringComparison.OrdinalIgnoreCase); + } + + [Fact] + public static void NullableStringOptionRoundTripsNull() + { + var stream = new MemoryStream(); + var writer = new BinaryWriter(stream); + var serializer = new BSATN.RefOption(); + + serializer.Write(writer, null); + + stream.Seek(0, SeekOrigin.Begin); + var reader = new BinaryReader(stream); + var value = serializer.Read(reader); + Assert.Null(value); + } + [Type] partial struct ContainsEnum { diff --git a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs index 9a7a617a2a..9b6aec79ab 100644 --- a/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs +++ b/crates/bindings-csharp/BSATN.Runtime/BSATN/Runtime.cs @@ -542,8 +542,18 @@ public readonly struct String : IReadWrite public string Read(BinaryReader reader) => Encoding.UTF8.GetString(ByteArray.Instance.Read(reader)); - public void Write(BinaryWriter writer, string value) => + public void Write(BinaryWriter writer, string value) + { + if (value is null) + { + throw new ArgumentNullException( + nameof(value), + "Cannot serialize a null string as BSATN String. To serialize a null string you must use a nullable string (string?) so it is encoded as a BSATN option." + ); + } + ByteArray.Instance.Write(writer, Encoding.UTF8.GetBytes(value)); + } public AlgebraicType GetAlgebraicType(ITypeRegistrar registrar) => new AlgebraicType.String(default); diff --git a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs index 8c09db7c13..ec2331aa6a 100644 --- a/crates/bindings-csharp/BSATN.Runtime/Builtins.cs +++ b/crates/bindings-csharp/BSATN.Runtime/Builtins.cs @@ -53,10 +53,13 @@ internal static class Util public static T Read(ReadOnlySpan source, bool littleEndian) where T : struct { - Debug.Assert( - source.Length == Marshal.SizeOf(), - $"Error while reading ${typeof(T).FullName}: expected source span to be {Marshal.SizeOf()} bytes long, but was {source.Length} bytes." - ); + var expectedSize = Marshal.SizeOf(); + if (source.Length != expectedSize) + { + throw new ArgumentException( + $"Error while reading {typeof(T).FullName}: expected source span to be {expectedSize} bytes long, but was {source.Length} bytes." + ); + } var result = MemoryMarshal.Read(source); @@ -166,8 +169,18 @@ public readonly record struct ConnectionId /// /// /// - public static ConnectionId? FromHexString(string hex) => - FromBigEndian(Util.StringToByteArray(hex)); + public static ConnectionId? FromHexString(string hex) + { + if (hex.Length != 32) + { + throw new ArgumentException( + $"Expected ConnectionId hex string to be 32 characters long, but was {hex.Length}.", + nameof(hex) + ); + } + + return FromBigEndian(Util.StringToByteArray(hex)); + } public static ConnectionId Random() { @@ -268,7 +281,18 @@ public readonly record struct Identity : IEquatable, IComparable, ICom /// /// /// - public static Identity FromHexString(string hex) => FromBigEndian(Util.StringToByteArray(hex)); + public static Identity FromHexString(string hex) + { + if (hex.Length != 64) + { + throw new ArgumentException( + $"Expected Identity hex string to be 64 characters long, but was {hex.Length}.", + nameof(hex) + ); + } + + return FromBigEndian(Util.StringToByteArray(hex)); + } // --- auto-generated --- public readonly struct BSATN : IReadWrite diff --git a/sdks/csharp/.meta-check-ignore b/sdks/csharp/.meta-check-ignore new file mode 100644 index 0000000000..59f3c6ae2c --- /dev/null +++ b/sdks/csharp/.meta-check-ignore @@ -0,0 +1 @@ +release~ diff --git a/sdks/csharp/examples~/regression-tests/client/Program.cs b/sdks/csharp/examples~/regression-tests/client/Program.cs index 6d0e536d88..7871e616b0 100644 --- a/sdks/csharp/examples~/regression-tests/client/Program.cs +++ b/sdks/csharp/examples~/regression-tests/client/Program.cs @@ -65,6 +65,8 @@ void OnConnected(DbConnection conn, Identity identity, string authToken) "SELECT * FROM my_player", "SELECT * FROM players_at_level_one", "SELECT * FROM my_table", + "SELECT * FROM null_string_nonnullable", + "SELECT * FROM null_string_nullable", "SELECT * FROM my_log", "SELECT * FROM Admins", "SELECT * FROM nullable_vec_view", @@ -113,6 +115,56 @@ void OnConnected(DbConnection conn, Identity identity, string authToken) ValidateNullableVecView(ctx); } }; + + conn.Reducers.OnInsertEmptyStringIntoNonNullable += (ReducerEventContext ctx) => + { + Log.Info("Got InsertEmptyStringIntoNonNullable callback"); + waiting--; + Debug.Assert( + ctx.Event.Status is Status.Committed, + $"InsertEmptyStringIntoNonNullable should commit, got {ctx.Event.Status}" + ); + Debug.Assert( + ctx.Db.NullStringNonnullable.Iter().Any(r => r.Name == ""), + "Expected a row inserted into null_string_nonnullable with Name == \"\"" + ); + }; + + conn.Reducers.OnInsertNullStringIntoNonNullable += (ReducerEventContext ctx) => + { + Log.Info("Got InsertNullStringIntoNonNullable callback"); + waiting--; + + if (ctx.Event.Status is Status.Failed(var reason)) + { + Debug.Assert( + reason.Contains("Cannot serialize a null string", StringComparison.OrdinalIgnoreCase) + || reason.Contains("BSATN", StringComparison.OrdinalIgnoreCase) + || reason.Contains("nullable string", StringComparison.OrdinalIgnoreCase), + $"Expected a serialization-related failure message, got: {reason}" + ); + } + else + { + throw new Exception( + $"InsertNullStringIntoNonNullable should fail, got status {ctx.Event.Status}" + ); + } + }; + + conn.Reducers.OnInsertNullStringIntoNullable += (ReducerEventContext ctx) => + { + Log.Info("Got InsertNullStringIntoNullable callback"); + waiting--; + Debug.Assert( + ctx.Event.Status is Status.Committed, + $"InsertNullStringIntoNullable should commit, got {ctx.Event.Status}" + ); + Debug.Assert( + ctx.Db.NullStringNullable.Iter().Any(r => r.Name == null), + "Expected a row inserted into null_string_nullable with Name == null" + ); + }; } const uint MAX_ID = 10; @@ -336,6 +388,18 @@ void OnSubscriptionApplied(SubscriptionEventContext context) waiting++; context.Reducers.SetNullableVec(1, true, 7, 8); + Log.Debug("Calling InsertEmptyStringIntoNonNullable"); + waiting++; + context.Reducers.InsertEmptyStringIntoNonNullable(); + + Log.Debug("Calling InsertNullStringIntoNonNullable (should fail)"); + waiting++; + context.Reducers.InsertNullStringIntoNonNullable(); + + Log.Debug("Calling InsertNullStringIntoNullable"); + waiting++; + context.Reducers.InsertNullStringIntoNullable(); + // Procedures tests Log.Debug("Calling ReadMySchemaViaHttp"); waiting++; diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertEmptyStringIntoNonNullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertEmptyStringIntoNonNullable.g.cs new file mode 100644 index 0000000000..fc57d1837a --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertEmptyStringIntoNonNullable.g.cs @@ -0,0 +1,59 @@ +// 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 InsertEmptyStringIntoNonNullableHandler(ReducerEventContext ctx); + public event InsertEmptyStringIntoNonNullableHandler? OnInsertEmptyStringIntoNonNullable; + + public void InsertEmptyStringIntoNonNullable() + { + conn.InternalCallReducer(new Reducer.InsertEmptyStringIntoNonNullable(), this.SetCallReducerFlags.InsertEmptyStringIntoNonNullableFlags); + } + + public bool InvokeInsertEmptyStringIntoNonNullable(ReducerEventContext ctx, Reducer.InsertEmptyStringIntoNonNullable args) + { + if (OnInsertEmptyStringIntoNonNullable == 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; + } + OnInsertEmptyStringIntoNonNullable( + ctx + ); + return true; + } + } + + public abstract partial class Reducer + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class InsertEmptyStringIntoNonNullable : Reducer, IReducerArgs + { + string IReducerArgs.ReducerName => "InsertEmptyStringIntoNonNullable"; + } + } + + public sealed partial class SetReducerFlags + { + internal CallReducerFlags InsertEmptyStringIntoNonNullableFlags; + public void InsertEmptyStringIntoNonNullable(CallReducerFlags flags) => InsertEmptyStringIntoNonNullableFlags = flags; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertNullStringIntoNonNullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertNullStringIntoNonNullable.g.cs new file mode 100644 index 0000000000..1d27853007 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertNullStringIntoNonNullable.g.cs @@ -0,0 +1,59 @@ +// 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 InsertNullStringIntoNonNullableHandler(ReducerEventContext ctx); + public event InsertNullStringIntoNonNullableHandler? OnInsertNullStringIntoNonNullable; + + public void InsertNullStringIntoNonNullable() + { + conn.InternalCallReducer(new Reducer.InsertNullStringIntoNonNullable(), this.SetCallReducerFlags.InsertNullStringIntoNonNullableFlags); + } + + public bool InvokeInsertNullStringIntoNonNullable(ReducerEventContext ctx, Reducer.InsertNullStringIntoNonNullable args) + { + if (OnInsertNullStringIntoNonNullable == 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; + } + OnInsertNullStringIntoNonNullable( + ctx + ); + return true; + } + } + + public abstract partial class Reducer + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class InsertNullStringIntoNonNullable : Reducer, IReducerArgs + { + string IReducerArgs.ReducerName => "InsertNullStringIntoNonNullable"; + } + } + + public sealed partial class SetReducerFlags + { + internal CallReducerFlags InsertNullStringIntoNonNullableFlags; + public void InsertNullStringIntoNonNullable(CallReducerFlags flags) => InsertNullStringIntoNonNullableFlags = flags; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertNullStringIntoNullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertNullStringIntoNullable.g.cs new file mode 100644 index 0000000000..bd525ca485 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Reducers/InsertNullStringIntoNullable.g.cs @@ -0,0 +1,59 @@ +// 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 InsertNullStringIntoNullableHandler(ReducerEventContext ctx); + public event InsertNullStringIntoNullableHandler? OnInsertNullStringIntoNullable; + + public void InsertNullStringIntoNullable() + { + conn.InternalCallReducer(new Reducer.InsertNullStringIntoNullable(), this.SetCallReducerFlags.InsertNullStringIntoNullableFlags); + } + + public bool InvokeInsertNullStringIntoNullable(ReducerEventContext ctx, Reducer.InsertNullStringIntoNullable args) + { + if (OnInsertNullStringIntoNullable == 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; + } + OnInsertNullStringIntoNullable( + ctx + ); + return true; + } + } + + public abstract partial class Reducer + { + [SpacetimeDB.Type] + [DataContract] + public sealed partial class InsertNullStringIntoNullable : Reducer, IReducerArgs + { + string IReducerArgs.ReducerName => "InsertNullStringIntoNullable"; + } + } + + public sealed partial class SetReducerFlags + { + internal CallReducerFlags InsertNullStringIntoNullableFlags; + public void InsertNullStringIntoNullable(CallReducerFlags flags) => InsertNullStringIntoNullableFlags = 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 a325ba5636..e114efeef4 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.1 (commit 26474240db517aff2f24b1b6b0aff90b0e3b9758). +// This was generated using spacetimedb cli version 1.11.1 (commit b00ba57ed047514aca886b89d52b44520a7ac43d). #nullable enable @@ -34,6 +34,8 @@ namespace SpacetimeDB.Types AddTable(MyLog = new(conn)); AddTable(MyPlayer = new(conn)); AddTable(MyTable = new(conn)); + AddTable(NullStringNonnullable = new(conn)); + AddTable(NullStringNullable = new(conn)); AddTable(NullableVec = new(conn)); AddTable(NullableVecView = new(conn)); AddTable(Player = new(conn)); @@ -603,6 +605,9 @@ namespace SpacetimeDB.Types "Add" => BSATNHelpers.Decode(encodedArgs), "ClientConnected" => BSATNHelpers.Decode(encodedArgs), "Delete" => BSATNHelpers.Decode(encodedArgs), + "InsertEmptyStringIntoNonNullable" => BSATNHelpers.Decode(encodedArgs), + "InsertNullStringIntoNonNullable" => BSATNHelpers.Decode(encodedArgs), + "InsertNullStringIntoNullable" => BSATNHelpers.Decode(encodedArgs), "InsertResult" => BSATNHelpers.Decode(encodedArgs), "SetNullableVec" => BSATNHelpers.Decode(encodedArgs), "ThrowError" => BSATNHelpers.Decode(encodedArgs), @@ -634,6 +639,9 @@ namespace SpacetimeDB.Types Reducer.Add args => Reducers.InvokeAdd(eventContext, args), Reducer.ClientConnected args => Reducers.InvokeClientConnected(eventContext, args), Reducer.Delete args => Reducers.InvokeDelete(eventContext, args), + Reducer.InsertEmptyStringIntoNonNullable args => Reducers.InvokeInsertEmptyStringIntoNonNullable(eventContext, args), + Reducer.InsertNullStringIntoNonNullable args => Reducers.InvokeInsertNullStringIntoNonNullable(eventContext, args), + Reducer.InsertNullStringIntoNullable args => Reducers.InvokeInsertNullStringIntoNullable(eventContext, args), Reducer.InsertResult args => Reducers.InvokeInsertResult(eventContext, args), Reducer.SetNullableVec args => Reducers.InvokeSetNullableVec(eventContext, args), Reducer.ThrowError args => Reducers.InvokeThrowError(eventContext, args), diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/NullStringNonnullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/NullStringNonnullable.g.cs new file mode 100644 index 0000000000..954fe97086 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/NullStringNonnullable.g.cs @@ -0,0 +1,39 @@ +// 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 NullStringNonnullableHandle : RemoteTableHandle + { + protected override string RemoteTableName => "null_string_nonnullable"; + + public sealed class IdUniqueIndex : UniqueIndexBase + { + protected override ulong GetKey(NullStringNonNullable row) => row.Id; + + public IdUniqueIndex(NullStringNonnullableHandle table) : base(table) { } + } + + public readonly IdUniqueIndex Id; + + internal NullStringNonnullableHandle(DbConnection conn) : base(conn) + { + Id = new(this); + } + + protected override object GetPrimaryKey(NullStringNonNullable row) => row.Id; + } + + public readonly NullStringNonnullableHandle NullStringNonnullable; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/NullStringNullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/NullStringNullable.g.cs new file mode 100644 index 0000000000..09955d8744 --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Tables/NullStringNullable.g.cs @@ -0,0 +1,39 @@ +// 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 NullStringNullableHandle : RemoteTableHandle + { + protected override string RemoteTableName => "null_string_nullable"; + + public sealed class IdUniqueIndex : UniqueIndexBase + { + protected override ulong GetKey(NullStringNullable row) => row.Id; + + public IdUniqueIndex(NullStringNullableHandle table) : base(table) { } + } + + public readonly IdUniqueIndex Id; + + internal NullStringNullableHandle(DbConnection conn) : base(conn) + { + Id = new(this); + } + + protected override object GetPrimaryKey(NullStringNullable row) => row.Id; + } + + public readonly NullStringNullableHandle NullStringNullable; + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/NullStringNonNullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/NullStringNonNullable.g.cs new file mode 100644 index 0000000000..2f639a0a5f --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/NullStringNonNullable.g.cs @@ -0,0 +1,35 @@ +// 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 NullStringNonNullable + { + [DataMember(Name = "Id")] + public ulong Id; + [DataMember(Name = "Name")] + public string Name; + + public NullStringNonNullable( + ulong Id, + string Name + ) + { + this.Id = Id; + this.Name = Name; + } + + public NullStringNonNullable() + { + this.Name = ""; + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/NullStringNullable.g.cs b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/NullStringNullable.g.cs new file mode 100644 index 0000000000..9d0d619edd --- /dev/null +++ b/sdks/csharp/examples~/regression-tests/client/module_bindings/Types/NullStringNullable.g.cs @@ -0,0 +1,34 @@ +// 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 NullStringNullable + { + [DataMember(Name = "Id")] + public ulong Id; + [DataMember(Name = "Name")] + public string? Name; + + public NullStringNullable( + ulong Id, + string? Name + ) + { + this.Id = Id; + this.Name = Name; + } + + public NullStringNullable() + { + } + } +} diff --git a/sdks/csharp/examples~/regression-tests/server/Lib.cs b/sdks/csharp/examples~/regression-tests/server/Lib.cs index ef0b259850..454220eecb 100644 --- a/sdks/csharp/examples~/regression-tests/server/Lib.cs +++ b/sdks/csharp/examples~/regression-tests/server/Lib.cs @@ -113,6 +113,26 @@ public static partial class Module public DbVector2? Pos; } + [SpacetimeDB.Table(Name = "null_string_nonnullable", Public = true)] + public partial struct NullStringNonNullable + { + [SpacetimeDB.PrimaryKey] + [SpacetimeDB.AutoInc] + public ulong Id; + + public string Name; + } + + [SpacetimeDB.Table(Name = "null_string_nullable", Public = true)] + public partial struct NullStringNullable + { + [SpacetimeDB.PrimaryKey] + [SpacetimeDB.AutoInc] + public ulong Id; + + public string? Name; + } + // At-most-one row: return T? [SpacetimeDB.View(Name = "my_player", Public = true)] public static Player? MyPlayer(ViewContext ctx) @@ -214,6 +234,24 @@ public static partial class Module } } + [SpacetimeDB.Reducer] + public static void InsertEmptyStringIntoNonNullable(ReducerContext ctx) + { + ctx.Db.null_string_nonnullable.Insert(new NullStringNonNullable { Name = "" }); + } + + [SpacetimeDB.Reducer] + public static void InsertNullStringIntoNonNullable(ReducerContext ctx) + { + ctx.Db.null_string_nonnullable.Insert(new NullStringNonNullable { Name = null! }); + } + + [SpacetimeDB.Reducer] + public static void InsertNullStringIntoNullable(ReducerContext ctx) + { + ctx.Db.null_string_nullable.Insert(new NullStringNullable { Name = null }); + } + [Reducer(ReducerKind.ClientConnected)] public static void ClientConnected(ReducerContext ctx) { diff --git a/sdks/csharp/packages/.gitignore b/sdks/csharp/packages/.gitignore deleted file mode 100644 index 7d9dea737b..0000000000 --- a/sdks/csharp/packages/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -# Ignore most of NuGet package structure, except DLLs which are required by Unity. -/*/*/* - -!/*/*/analyzers -!/*/*/analyzers.meta - -!/*/*/lib -!/*/*/lib.meta - -# Ignore XML documentation metadata from packages too. -*.xml diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll deleted file mode 100755 index 1db1c1f211..0000000000 Binary files a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll and /dev/null differ diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll b/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll deleted file mode 100755 index b66c06e7b8..0000000000 Binary files a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll and /dev/null differ diff --git a/sdks/csharp/packages.meta b/sdks/csharp/release~/packages.meta similarity index 100% rename from sdks/csharp/packages.meta rename to sdks/csharp/release~/packages.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned.meta diff --git a/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/.gitignore b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/.gitignore new file mode 100644 index 0000000000..aff8ec9f01 --- /dev/null +++ b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/.gitignore @@ -0,0 +1,8 @@ +.nupkg.metadata +.signature.p7s +LICENSE +README.md +logo.png +spacetimedb.bsatn.runtime.*.nupkg* +spacetimedb.bsatn.runtime.nuspec +*.xml diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/analyzers/dotnet/cs/SpacetimeDB.BSATN.Codegen.dll.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/lib.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/lib.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1.meta diff --git a/sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta b/sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta similarity index 100% rename from sdks/csharp/packages/spacetimedb.bsatn.runtime/1.5.0/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta rename to sdks/csharp/release~/packages/spacetimedb.bsatn.runtime/unversioned/lib/netstandard2.1/SpacetimeDB.BSATN.Runtime.dll.meta