diff --git a/Fauna.Test/Serialization/RoundTrip.Tests.cs b/Fauna.Test/Serialization/RoundTrip.Tests.cs new file mode 100644 index 00000000..5dbf07dc --- /dev/null +++ b/Fauna.Test/Serialization/RoundTrip.Tests.cs @@ -0,0 +1,55 @@ +using Fauna.Mapping; +using Fauna.Serialization; +using Fauna.Types; +using NUnit.Framework; +using System.Text; +using System.Text.RegularExpressions; + +namespace Fauna.Test.Serialization; + +[TestFixture] +public class RoundTripTests +{ + private static readonly MappingContext ctx = new(); + + public static string Serialize(object? obj) + { + using var stream = new MemoryStream(); + using var writer = new Utf8FaunaWriter(stream); + Serializer.Serialize(ctx, writer, obj); + writer.Flush(); + return Encoding.UTF8.GetString(stream.ToArray()); + } + + public static T Deserialize(string str) where T : notnull + { + var reader = new Utf8FaunaReader(str); + reader.Read(); + var obj = Deserializer.Generate(ctx).Deserialize(ctx, ref reader); + if (reader.Read()) + { + throw new SerializationException($"Token stream is not exhausted but should be: {reader.CurrentTokenType}"); + } + + return obj; + } + + [Test] + public void RoundTripShort() + { + const short test = 40; + var serialized = Serialize(test); + var deserialized = Deserialize(serialized); + Assert.AreEqual(test, deserialized); + } + + [Test] + public void RoundTripUShort() + { + const ushort test = 40; + var serialized = Serialize(test); + var deserialized = Deserialize(serialized); + Assert.AreEqual(test, deserialized); + } + +} diff --git a/Fauna/Serialization/Deserializer.cs b/Fauna/Serialization/Deserializer.cs index ce9315fc..48387f60 100644 --- a/Fauna/Serialization/Deserializer.cs +++ b/Fauna/Serialization/Deserializer.cs @@ -17,6 +17,8 @@ public static class Deserializer private static readonly CheckedDeserializer _string = new(); private static readonly CheckedDeserializer _int = new(); private static readonly LongDeserializer _long = new(); + private static readonly ShortDeserializer _short = new(); + private static readonly UShortDeserializer _ushort = new(); private static readonly CheckedDeserializer _double = new(); private static readonly CheckedDeserializer _dateOnly = new(); private static readonly CheckedDeserializer _dateTime = new(); @@ -50,6 +52,8 @@ public static IDeserializer Generate(MappingContext context, Type targetType) { if (targetType == typeof(object)) return _object; if (targetType == typeof(string)) return _string; + if (targetType == typeof(short)) return _short; + if (targetType == typeof(ushort)) return _ushort; if (targetType == typeof(int)) return _int; if (targetType == typeof(long)) return _long; if (targetType == typeof(double)) return _double; diff --git a/Fauna/Serialization/NumberDeserializers.cs b/Fauna/Serialization/NumberDeserializers.cs index 0ff2e38c..fcb3fb79 100644 --- a/Fauna/Serialization/NumberDeserializers.cs +++ b/Fauna/Serialization/NumberDeserializers.cs @@ -13,3 +13,27 @@ public override long Deserialize(MappingContext context, ref Utf8FaunaReader rea $"Unexpected token while deserializing: {reader.CurrentTokenType}"), }; } + +internal class ShortDeserializer : BaseDeserializer +{ + public override short Deserialize(MappingContext context, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.Int => reader.GetShort(), + TokenType.Long => reader.GetShort(), + _ => throw new SerializationException( + $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + }; +} + +internal class UShortDeserializer : BaseDeserializer +{ + public override ushort Deserialize(MappingContext context, ref Utf8FaunaReader reader) => + reader.CurrentTokenType switch + { + TokenType.Int => reader.GetUnsignedShort(), + TokenType.Long => reader.GetUnsignedShort(), + _ => throw new SerializationException( + $"Unexpected token while deserializing: {reader.CurrentTokenType}"), + }; +} diff --git a/Fauna/Serialization/Utf8FaunaReader.cs b/Fauna/Serialization/Utf8FaunaReader.cs index 4f57ea58..4b693fea 100644 --- a/Fauna/Serialization/Utf8FaunaReader.cs +++ b/Fauna/Serialization/Utf8FaunaReader.cs @@ -281,6 +281,40 @@ public int GetInt() } } + /// + /// Retrieves an short value from the current token. + /// + /// An short representation of the current token's value. + public short GetShort() + { + ValidateTaggedTypes(TokenType.Int, TokenType.Long); + try + { + return short.Parse(_taggedTokenValue!); + } + catch (Exception e) + { + throw new SerializationException($"Failed to get short from {_taggedTokenValue}", e); + } + } + + /// + /// Retrieves an unsigned short value from the current token. + /// + /// An unsigned short representation of the current token's value. + public ushort GetUnsignedShort() + { + ValidateTaggedTypes(TokenType.Int, TokenType.Long); + try + { + return ushort.Parse(_taggedTokenValue!); + } + catch (Exception e) + { + throw new SerializationException($"Failed to get ushort from {_taggedTokenValue}", e); + } + } + /// /// Retrieves a long value from the current token. /// @@ -406,6 +440,15 @@ private void ValidateTaggedType(TokenType type) } } + private void ValidateTaggedTypes(params TokenType[] types) + { + if (!types.Contains(CurrentTokenType) || _taggedTokenValue == null || _taggedTokenValue.GetType() != typeof(string)) + { + throw new InvalidOperationException($"CurrentTokenType is a {CurrentTokenType.ToString()}, not in {types}."); + } + } + + private void HandleStartObject() { AdvanceTrue();