From e8b40f7a9754f317e10346f6ec0853f2dde2cc21 Mon Sep 17 00:00:00 2001 From: Rutherther Date: Sat, 7 Jan 2023 22:09:02 +0100 Subject: [PATCH] feat(packets): add optional wrapper for wrapping optional values inside of a list --- .../OptionalWrapper.cs | 41 ++++++++++ .../Common/OptionalWrapperConverter.cs | 82 +++++++++++++++++++ .../Common/OptionalWrapperConverterFactory.cs | 56 +++++++++++++ .../Extensions/ServiceCollectionExtensions.cs | 1 + 4 files changed, 180 insertions(+) create mode 100644 Packets/NosSmooth.PacketSerializer.Abstractions/OptionalWrapper.cs create mode 100644 Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverter.cs create mode 100644 Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverterFactory.cs diff --git a/Packets/NosSmooth.PacketSerializer.Abstractions/OptionalWrapper.cs b/Packets/NosSmooth.PacketSerializer.Abstractions/OptionalWrapper.cs new file mode 100644 index 0000000..50b9356 --- /dev/null +++ b/Packets/NosSmooth.PacketSerializer.Abstractions/OptionalWrapper.cs @@ -0,0 +1,41 @@ +// +// OptionalWrapper.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System.Diagnostics.CodeAnalysis; + +namespace NosSmooth.PacketSerializer.Abstractions; + +/// +/// Wraps a compound value that may not be present +/// and there will be nothing instead in the packet. +/// The converter of underlying type will be called +/// if and only if the value is not null. +/// +/// The value. +/// The underlying type. +[SuppressMessage("StyleCop.CSharp.NamingRules", "SA1313:Parameter names should begin with lower-case letter", Justification = "Fix this, this should not happen.")] +public record struct OptionalWrapper(bool Present, T? Value) +{ + /// + /// Unwrap the underlying value. + /// + /// The wrapper to unwrap. + /// The unwrapped value. + public static implicit operator T?(OptionalWrapper wrapper) + { + return wrapper.Value; + } + + /// + /// wrap the value in optional wrapper. + /// + /// The value to wrap. + /// The wrapped value. + public static implicit operator OptionalWrapper(T? value) + { + return new OptionalWrapper(true, value); + } +} \ No newline at end of file diff --git a/Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverter.cs b/Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverter.cs new file mode 100644 index 0000000..b38466e --- /dev/null +++ b/Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverter.cs @@ -0,0 +1,82 @@ +// +// OptionalWrapperConverter.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using NosSmooth.PacketSerializer.Abstractions; +using Remora.Results; + +namespace NosSmooth.PacketSerializer.Converters.Common; + +/// +/// Converter of . +/// +/// The underlying type. +public class OptionalWrapperConverter : BaseStringConverter> +{ + private readonly IStringConverterRepository _converterRepository; + + /// + /// Initializes a new instance of the class. + /// + /// The converter repository. + public OptionalWrapperConverter(IStringConverterRepository converterRepository) + { + _converterRepository = converterRepository; + } + + /// + public override Result Serialize(OptionalWrapper obj, PacketStringBuilder builder) + { + if (obj.Value is null) + { + return Result.FromSuccess(); + } + + var converterResult = _converterRepository.GetTypeConverter(); + if (!converterResult.IsDefined(out var converter)) + { + return Result.FromError(converterResult); + } + + return converter.Serialize(obj.Value, builder); + + } + + /// + public override Result> Deserialize(ref PacketStringEnumerator stringEnumerator, DeserializeOptions options) + { + if (stringEnumerator.IsOnLastToken() ?? false) + { + return Result>.FromSuccess(new OptionalWrapper(false, default)); + } + + var tokenResult = stringEnumerator.GetNextToken(out var token, false); + if (!tokenResult.IsSuccess) + { + return Result>.FromError(tokenResult); + } + + if (token.Token.Length == 0) + { + stringEnumerator.GetNextToken(out _); // seek + return Result>.FromSuccess(new OptionalWrapper(false, default)); + } + + var converterResult = _converterRepository.GetTypeConverter(); + if (!converterResult.IsDefined(out var converter)) + { + return Result>.FromError(converterResult); + } + + var deserializationResult = converter.Deserialize(ref stringEnumerator, new DeserializeOptions(true)); + if (!deserializationResult.IsDefined(out var deserialization)) + { + return Result>.FromError(deserializationResult); + } + + return new OptionalWrapper(true, deserialization); + } +} \ No newline at end of file diff --git a/Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverterFactory.cs b/Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverterFactory.cs new file mode 100644 index 0000000..803c186 --- /dev/null +++ b/Packets/NosSmooth.PacketSerializer/Converters/Common/OptionalWrapperConverterFactory.cs @@ -0,0 +1,56 @@ +// +// OptionalWrapperConverterFactory.cs +// +// Copyright (c) František Boháček. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +using System; +using Microsoft.Extensions.DependencyInjection; +using NosSmooth.PacketSerializer.Abstractions; +using NosSmooth.PacketSerializer.Converters.Special; +using NosSmooth.PacketSerializer.Extensions; +using Remora.Results; + +namespace NosSmooth.PacketSerializer.Converters.Common; + +/// +/// Converts . +/// +public class OptionalWrapperConverterFactory : IStringConverterFactory +{ + private readonly IServiceProvider _serviceProvider; + + /// + /// Initializes a new instance of the class. + /// + /// The service provider. + public OptionalWrapperConverterFactory(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + /// + public bool ShouldHandle(Type type) + => type.GetGenericTypeDefinition() == typeof(OptionalWrapper<>); + + /// + public Result CreateTypeSerializer(Type type) + { + var underlyingType = type.GetGenericArguments()[0]; + var serializerType = typeof(OptionalWrapperConverter<>).MakeGenericType(underlyingType); + + try + { + return Result.FromSuccess + ((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, serializerType)); + } + catch (Exception e) + { + return e; + } + } + + /// + public Result> CreateTypeSerializer() + => CreateTypeSerializer(typeof(T)).Cast, IStringConverter>(); +} \ No newline at end of file diff --git a/Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs b/Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs index e7c4f9f..d43c679 100644 --- a/Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs +++ b/Packets/NosSmooth.PacketSerializer/Extensions/ServiceCollectionExtensions.cs @@ -78,6 +78,7 @@ public static class ServiceCollectionExtensions .AddStringConverterFactory() .AddStringConverterFactory() .AddStringConverterFactory() + .AddStringConverterFactory() .AddStringConverter() .AddStringConverter() .AddStringConverter() -- 2.49.0