M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs +1 -1
@@ 67,7 67,7 @@ public class BasicInlineConverterGenerator : IInlineConverterGenerator
foreach (var type in HandleTypes)
{
textWriter.WriteMultiline($@"
-public static Result<{type}?> ParseBasic{type}(ITypeConverter typeConverter, PacketStringEnumerator stringEnumerator)
+public static Result<{type}?> ParseBasic{type}(IStringConverter typeConverter, PacketStringEnumerator stringEnumerator)
{{
var tokenResult = stringEnumerator.GetNextToken();
if (!tokenResult.IsSuccess)
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs +1 -1
@@ 80,7 80,7 @@ public class EnumInlineConverterGenerator : IInlineConverterGenerator
textWriter.WriteMultiline
(
$@"
-public static Result<{type}?> ParseEnum{type.ToString().Replace('.', '_')}(ITypeConverter typeConverter, PacketStringEnumerator stringEnumerator)
+public static Result<{type}?> ParseEnum{type.ToString().Replace('.', '_')}(IStringConverter typeConverter, PacketStringEnumerator stringEnumerator)
{{
var tokenResult = stringEnumerator.GetNextToken();
if (!tokenResult.IsSuccess)
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs +2 -2
@@ 32,7 32,7 @@ public class FallbackInlineConverterGenerator : IInlineConverterGenerator
var resultName = $"{variableName.Replace(".", string.Empty)}Result";
textWriter.WriteLine
(
- $"var {resultName} = _typeConverterRepository.Serialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>({variableName}, builder);"
+ $"var {resultName} = _stringSerializer.Serialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>({variableName}, builder);"
);
textWriter.WriteLine($"if (!{resultName}.IsSuccess)");
textWriter.WriteLine("{");
@@ 48,7 48,7 @@ public class FallbackInlineConverterGenerator : IInlineConverterGenerator
{
textWriter.WriteLine
(
- $"_typeConverterRepository.Deserialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>(stringEnumerator);"
+ $"_stringSerializer.Deserialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>(stringEnumerator);"
);
return null;
}
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs +2 -2
@@ 89,7 89,7 @@ public class ListInlineConverterGenerator : IInlineConverterGenerator
}
textWriter.WriteLine
- ($"{Constants.HelperClass}.{GetMethodName(genericArgument)}(typeConverter, _typeConverterRepository, stringEnumerator);");
+ ($"{Constants.HelperClass}.{GetMethodName(genericArgument)}(typeConverter, _stringSerializer, stringEnumerator);");
return null;
}
@@ 107,7 107,7 @@ public class ListInlineConverterGenerator : IInlineConverterGenerator
textWriter.WriteLine
(
@$"
-public static Result<IReadOnlyList<{type.GetActualType()}>> {GetMethodName(type)}(ITypeConverter typeConverter, ITypeConverterRepository _typeConverterRepository, PacketStringEnumerator stringEnumerator)
+public static Result<IReadOnlyList<{type.GetActualType()}>> {GetMethodName(type)}(IStringConverter typeConverter, IStringSerializer _stringSerializer, PacketStringEnumerator stringEnumerator)
{{
var data = new List<{type.GetActualType()}>();
M Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs +4 -4
@@ 58,18 58,18 @@ using Remora.Results;
namespace {_packetInfo.Namespace}.Generated;
-public class {_packetInfo.Name}Converter : BaseTypeConverter<{_packetInfo.Name}>
+public class {_packetInfo.Name}Converter : BaseStringConverter<{_packetInfo.Name}>
{{"
);
textWriter.Indent++;
textWriter.WriteLine
(
$@"
-private readonly ITypeConverterRepository _typeConverterRepository;
+private readonly IStringSerializer _stringSerializer;
-public {_packetInfo.Name}Converter(ITypeConverterRepository typeConverterRepository)
+public {_packetInfo.Name}Converter(IStringSerializer stringSerializer)
{{
- _typeConverterRepository = typeConverterRepository;
+ _stringSerializer = stringSerializer;
}}
/// <inheritdoc />
R Core/NosSmooth.Packets/Converters/BaseTypeConverter.cs => Core/NosSmooth.Packets/Converters/BaseStringConverter.cs +4 -4
@@ 1,5 1,5 @@
//
-// BaseTypeConverter.cs
+// BaseStringConverter.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.
@@ 13,7 13,7 @@ namespace NosSmooth.Packets.Converters;
/// Base type for converting objects that maps object converting methods to the actual type.
/// </summary>
/// <typeparam name="TParseType">The type of the object that this converts.</typeparam>
-public abstract class BaseTypeConverter<TParseType> : ITypeConverter<TParseType>
+public abstract class BaseStringConverter<TParseType> : IStringConverter<TParseType>
{
/// <inheritdoc />
public abstract Result Serialize(TParseType? obj, PacketStringBuilder builder);
@@ 22,7 22,7 @@ public abstract class BaseTypeConverter<TParseType> : ITypeConverter<TParseType>
public abstract Result<TParseType?> Deserialize(PacketStringEnumerator stringEnumerator);
/// <inheritdoc/>
- Result<object?> ITypeConverter.Deserialize(PacketStringEnumerator stringEnumerator)
+ Result<object?> IStringConverter.Deserialize(PacketStringEnumerator stringEnumerator)
{
var result = Deserialize(stringEnumerator);
if (!result.IsSuccess)
@@ 34,7 34,7 @@ public abstract class BaseTypeConverter<TParseType> : ITypeConverter<TParseType>
}
/// <inheritdoc/>
- Result ITypeConverter.Serialize(object? obj, PacketStringBuilder builder)
+ Result IStringConverter.Serialize(object? obj, PacketStringBuilder builder)
{
if (!(obj is TParseType parseType))
{
M Core/NosSmooth.Packets/Converters/Basic/BasicTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/BasicTypeConverter.cs +1 -1
@@ 13,7 13,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// Basic type converter for converting using <see cref="Convert"/>.
/// </summary>
/// <typeparam name="TBasicType">The basic type, that contains correct to string.</typeparam>
-public abstract class BasicTypeConverter<TBasicType> : BaseTypeConverter<TBasicType>
+public abstract class BasicTypeConverter<TBasicType> : BaseStringConverter<TBasicType>
{
/// <inheritdoc />
public override Result Serialize(TBasicType? obj, PacketStringBuilder builder)
R Core/NosSmooth.Packets/Converters/Basic/BoolTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/BoolStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// BoolTypeConverter.cs
+// BoolStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="bool"/>.
/// </summary>
-public class BoolTypeConverter : BaseTypeConverter<bool>
+public class BoolStringConverter : BaseStringConverter<bool>
{
/// <inheritdoc />
public override Result Serialize(bool obj, PacketStringBuilder builder)
R Core/NosSmooth.Packets/Converters/Basic/ByteTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/ByteStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// ByteTypeConverter.cs
+// ByteStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="byte"/>.
/// </summary>
-public class ByteTypeConverter : BasicTypeConverter<byte>
+public class ByteStringConverter : BasicTypeConverter<byte>
{
/// <inheritdoc />
protected override Result<byte> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/CharTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/CharStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// CharTypeConverter.cs
+// CharStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="char"/>.
/// </summary>
-public class CharTypeConverter : BasicTypeConverter<char>
+public class CharStringConverter : BasicTypeConverter<char>
{
/// <inheritdoc />
protected override Result<char> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/IntTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/IntStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// IntTypeConverter.cs
+// IntStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="int"/>.
/// </summary>
-public class IntTypeConverter : BasicTypeConverter<int>
+public class IntStringConverter : BasicTypeConverter<int>
{
/// <inheritdoc />
protected override Result<int> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/LongTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/LongStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// LongTypeConverter.cs
+// LongStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="long"/>.
/// </summary>
-public class LongTypeConverter : BasicTypeConverter<long>
+public class LongStringConverter : BasicTypeConverter<long>
{
/// <inheritdoc />
protected override Result<long> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/ShortTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/ShortStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// ShortTypeConverter.cs
+// ShortStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="short"/>.
/// </summary>
-public class ShortTypeConverter : BasicTypeConverter<short>
+public class ShortStringConverter : BasicTypeConverter<short>
{
/// <inheritdoc />
protected override Result<short> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/UIntTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/UIntStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// UIntTypeConverter.cs
+// UIntStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="uint"/>.
/// </summary>
-public class UIntTypeConverter : BasicTypeConverter<uint>
+public class UIntStringConverter : BasicTypeConverter<uint>
{
/// <inheritdoc />
protected override Result<uint> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/ULongTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/ULongStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// ULongTypeConverter.cs
+// ULongStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="ulong"/>.
/// </summary>
-public class ULongTypeConverter : BasicTypeConverter<ulong>
+public class ULongStringConverter : BasicTypeConverter<ulong>
{
/// <inheritdoc />
protected override Result<ulong> Deserialize(string value)
R Core/NosSmooth.Packets/Converters/Basic/UShortTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/UShortStringConverter.cs +2 -2
@@ 1,5 1,5 @@
//
-// UShortTypeConverter.cs
+// UShortStringConverter.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.
@@ 12,7 12,7 @@ namespace NosSmooth.Packets.Converters.Basic;
/// <summary>
/// Converter of <see cref="ushort"/>.
/// </summary>
-public class UShortTypeConverter : BasicTypeConverter<ushort>
+public class UShortStringConverter : BasicTypeConverter<ushort>
{
/// <inheritdoc />
protected override Result<ushort> Deserialize(string value)
M Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs => Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs +1 -1
@@ 13,7 13,7 @@ namespace NosSmooth.Packets.Converters.Common;
/// <summary>
/// Converter of <see cref="NameString"/>.
/// </summary>
-public class NameStringConverter : BaseTypeConverter<NameString>
+public class NameStringConverter : BaseStringConverter<NameString>
{
/// <inheritdoc />
public override Result Serialize(NameString? obj, PacketStringBuilder builder)
R Core/NosSmooth.Packets/Converters/ITypeConverter.cs => Core/NosSmooth.Packets/Converters/IStringConverter.cs +3 -3
@@ 1,5 1,5 @@
//
-// ITypeConverter.cs
+// IStringConverter.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.
@@ 11,7 11,7 @@ namespace NosSmooth.Packets.Converters;
/// <summary>
/// Base type for converting types.
/// </summary>
-public interface ITypeConverter
+public interface IStringConverter
{
/// <summary>
/// Convert the data from the enumerator to the given type.
@@ 36,7 36,7 @@ public interface ITypeConverter
/// Used for converting packets.
/// </remarks>
/// <typeparam name="TParseType">The type that can be parsed.</typeparam>
-public interface ITypeConverter<TParseType> : ITypeConverter
+public interface IStringConverter<TParseType> : IStringConverter
{
/// <summary>
/// Convert the data from the enumerator to the given type.
A Core/NosSmooth.Packets/Converters/IStringConverterRepository.cs => Core/NosSmooth.Packets/Converters/IStringConverterRepository.cs +32 -0
@@ 0,0 1,32 @@
+//
+// IStringConverterRepository.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.Packets.Converters.Special;
+using NosSmooth.Packets.Errors;
+using Remora.Results;
+
+namespace NosSmooth.Packets.Converters;
+
+/// <summary>
+/// Repository for <see cref="IStringConverter"/>.
+/// </summary>
+public interface IStringConverterRepository
+{
+ /// <summary>
+ /// Gets the type converter for the given type.
+ /// </summary>
+ /// <param name="type">The type to find converter for.</param>
+ /// <returns>The type converter or an error.</returns>
+ public Result<IStringConverter> GetTypeConverter(Type type);
+
+ /// <summary>
+ /// Gets the type converter for the given type.
+ /// </summary>
+ /// <typeparam name="TParseType">The type to find converter for.</typeparam>
+ /// <returns>The type converter or an error.</returns>
+ public Result<IStringConverter<TParseType>> GetTypeConverter<TParseType>();
+}<
\ No newline at end of file
R Core/NosSmooth.Packets/Converters/ITypeConverterRepository.cs => Core/NosSmooth.Packets/Converters/IStringSerializer.cs +3 -19
@@ 1,36 1,20 @@
//
-// ITypeConverterRepository.cs
+// IStringSerializer.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.Packets.Converters.Special;
-using NosSmooth.Packets.Errors;
using Remora.Results;
namespace NosSmooth.Packets.Converters;
/// <summary>
-/// Repository for <see cref="ITypeConverter"/>.
+/// Serializer of values from NosTale packet strings.
/// </summary>
-public interface ITypeConverterRepository
+public interface IStringSerializer
{
/// <summary>
- /// Gets the type converter for the given type.
- /// </summary>
- /// <param name="type">The type to find converter for.</param>
- /// <returns>The type converter or an error.</returns>
- public Result<ITypeConverter> GetTypeConverter(Type type);
-
- /// <summary>
- /// Gets the type converter for the given type.
- /// </summary>
- /// <typeparam name="TParseType">The type to find converter for.</typeparam>
- /// <returns>The type converter or an error.</returns>
- public Result<ITypeConverter<TParseType>> GetTypeConverter<TParseType>();
-
- /// <summary>
/// Convert the data from the enumerator to the given type.
/// </summary>
/// <param name="parseType">The type of the object to serialize.</param>
M Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs => Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs +1 -1
@@ 13,7 13,7 @@ namespace NosSmooth.Packets.Converters.Packets;
/// <summary>
/// Converter for <see cref="UpgradeRareSubPacket"/>.
/// </summary>
-public class UpgradeRareSubPacketConverter : BaseTypeConverter<UpgradeRareSubPacket>
+public class UpgradeRareSubPacketConverter : BaseStringConverter<UpgradeRareSubPacket>
{
/// <inheritdoc />
public override Result Serialize(UpgradeRareSubPacket? obj, PacketStringBuilder builder)
A Core/NosSmooth.Packets/Converters/Special/Converters/EnumStringConverter.cs => Core/NosSmooth.Packets/Converters/Special/Converters/EnumStringConverter.cs +49 -0
@@ 0,0 1,49 @@
+//
+// EnumStringConverter.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 Remora.Results;
+
+namespace NosSmooth.Packets.Converters.Special.Converters;
+
+/// <summary>
+/// Converts enum with the given underlying type.
+/// </summary>
+/// <typeparam name="TEnum">The enum.</typeparam>
+/// <typeparam name="TUnderlyingType">The enum's underlying type.</typeparam>
+public class EnumStringConverter<TEnum, TUnderlyingType> : BaseStringConverter<TEnum>
+ where TEnum : TUnderlyingType
+{
+ private readonly IStringSerializer _serializer;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EnumStringConverter{TEnum, TUnderlyingType}"/> class.
+ /// </summary>
+ /// <param name="serializer">The string serializer.</param>
+ public EnumStringConverter(IStringSerializer serializer)
+ {
+ _serializer = serializer;
+ }
+
+ /// <inheritdoc />
+ public override Result Serialize(TEnum? obj, PacketStringBuilder builder)
+ {
+ builder.Append(((TUnderlyingType?)obj)?.ToString() ?? "-");
+ return Result.FromSuccess();
+ }
+
+ /// <inheritdoc />
+ public override Result<TEnum?> Deserialize(PacketStringEnumerator stringEnumerator)
+ {
+ var result = _serializer.Deserialize<TUnderlyingType>(stringEnumerator);
+ if (!result.IsSuccess)
+ {
+ return Result<TEnum?>.FromError(result);
+ }
+
+ return (TEnum?)result.Entity;
+ }
+}<
\ No newline at end of file
R Core/NosSmooth.Packets/Converters/Special/ListTypeConverter.cs => Core/NosSmooth.Packets/Converters/Special/Converters/ListStringConverter.cs +42 -91
@@ 1,81 1,37 @@
//
-// ListTypeConverter.cs
+// ListStringConverter.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 System.Collections;
-using System.Collections.Concurrent;
using System.Collections.Generic;
-using System.Linq;
-using System.Linq.Expressions;
-using System.Reflection;
-using System.Text;
using NosSmooth.Packets.Errors;
using Remora.Results;
-namespace NosSmooth.Packets.Converters.Special;
+namespace NosSmooth.Packets.Converters.Special.Converters;
/// <summary>
-/// Converts lists.
+/// Converter for list types.
/// </summary>
-public class ListTypeConverter : ISpecialTypeConverter
+/// <typeparam name="TGeneric">The generic type argument of the list.</typeparam>
+public class ListStringConverter<TGeneric> : BaseStringConverter<IReadOnlyList<TGeneric>>
{
- private readonly ITypeConverterRepository _typeConverterRepository;
- private readonly ConcurrentDictionary<Type, Func<IEnumerable<object?>, object>> _fillFunctions;
+ private readonly IStringSerializer _serializer;
/// <summary>
- /// Initializes a new instance of the <see cref="ListTypeConverter"/> class.
+ /// Initializes a new instance of the <see cref="ListStringConverter{TGeneric}"/> class.
/// </summary>
- /// <param name="typeConverterRepository">The type converter repository.</param>
- public ListTypeConverter(ITypeConverterRepository typeConverterRepository)
+ /// <param name="serializer">The string serializer.</param>
+ public ListStringConverter(IStringSerializer serializer)
{
- _typeConverterRepository = typeConverterRepository;
- _fillFunctions = new ConcurrentDictionary<Type, Func<IEnumerable<object?>, object>>();
- }
-
- /// <inheritdoc />
- public bool ShouldHandle(Type type)
- => type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type);
-
- /// <inheritdoc />
- public Result<object?> Deserialize(Type type, PacketStringEnumerator stringEnumerator)
- {
- var data = new List<object?>();
- var genericType = type.GetElementType() ?? type.GetGenericArguments()[0];
-
- while (!(stringEnumerator.IsOnLastToken() ?? false))
- {
- if (!stringEnumerator.PushPreparedLevel())
- {
- return new ArgumentInvalidError(nameof(stringEnumerator), "The string enumerator has to have a prepared level for all lists.");
- }
-
- var result = _typeConverterRepository.Deserialize(genericType, stringEnumerator);
-
- // If we know that we are not on the last token in the item level, just skip to the end of the item.
- // Note that if this is the case, then that means the converter is either corrupted
- // or the packet has more fields.
- while (stringEnumerator.IsOnLastToken() == false)
- {
- stringEnumerator.GetNextToken();
- }
-
- stringEnumerator.PopLevel();
- if (!result.IsSuccess)
- {
- return Result<object?>.FromError(new ListSerializerError(result, data.Count), result);
- }
-
- data.Add(result.Entity);
- }
+ _serializer = serializer;
- return _fillFunctions.GetOrAdd(genericType, GetAndFillListMethod)(data);
}
/// <inheritdoc />
- public Result Serialize(Type type, object? obj, PacketStringBuilder builder)
+ public override Result Serialize(IReadOnlyList<TGeneric>? obj, PacketStringBuilder builder)
{
if (obj is null)
{
@@ 83,17 39,14 @@ public class ListTypeConverter : ISpecialTypeConverter
return Result.FromSuccess();
}
- var items = (IEnumerable)obj;
- var genericType = type.GetElementType() ?? type.GetGenericArguments()[0];
-
- foreach (var item in items)
+ foreach (var item in obj)
{
if (!builder.PushPreparedLevel())
{
return new ArgumentInvalidError(nameof(builder), "The string builder has to have a prepared level for all lists.");
}
- var serializeResult = _typeConverterRepository.Serialize(genericType, item, builder);
+ var serializeResult = _serializer.Serialize(item, builder);
builder.PopLevel();
if (!serializeResult.IsSuccess)
{
@@ 104,44 57,42 @@ public class ListTypeConverter : ISpecialTypeConverter
return Result.FromSuccess();
}
- // TODO: cache the functions?
-
- /// <summary>
- /// From https://stackoverflow.com/questions/35913495/moving-from-reflection-to-expression-tree.
- /// </summary>
- /// <param name="genericType">The generic type.</param>
- /// <returns>The function.</returns>
- private Func<IEnumerable<object?>, object> GetAndFillListMethod(Type genericType)
+ /// <inheritdoc />
+ public override Result<IReadOnlyList<TGeneric>?> Deserialize(PacketStringEnumerator stringEnumerator)
{
- var listType = typeof(List<>);
- var listGenericType = listType.MakeGenericType(genericType);
-
- var values = Expression.Parameter(typeof(IEnumerable<object?>), "values");
+ var list = new List<TGeneric>();
- var ctor = listGenericType.GetConstructor(BindingFlags.Instance | BindingFlags.Public, null, new Type[0], null);
-
- // I prefer using Expression.Variable to Expression.Parameter
- // for internal variables
- var instance = Expression.Variable(listGenericType, "list");
+ while (!(stringEnumerator.IsOnLastToken() ?? false))
+ {
+ if (!stringEnumerator.PushPreparedLevel())
+ {
+ return new ArgumentInvalidError(nameof(stringEnumerator), "The string enumerator has to have a prepared level for all lists.");
+ }
- var assign = Expression.Assign(instance, Expression.New(ctor!));
+ var result = _serializer.Deserialize<TGeneric>(stringEnumerator);
- var addMethod = listGenericType.GetMethod("AddRange", new[] { typeof(IEnumerable<>).MakeGenericType(genericType) });
+ // If we know that we are not on the last token in the item level, just skip to the end of the item.
+ // Note that if this is the case, then that means the converter is either corrupted
+ // or the packet has more fields.
+ while (stringEnumerator.IsOnLastToken() == false)
+ {
+ stringEnumerator.GetNextToken();
+ }
- // Enumerable.Cast<T>
- var castMethod = typeof(Enumerable).GetMethod("Cast", new[] { typeof(IEnumerable) })!.MakeGenericMethod(genericType);
+ stringEnumerator.PopLevel();
+ if (!result.IsSuccess)
+ {
+ return Result<IReadOnlyList<TGeneric>?>.FromError(new ListSerializerError(result, list.Count), result);
+ }
- // For the parameters there is a params Expression[], so no explicit array necessary
- var castCall = Expression.Call(castMethod, values);
- var addCall = Expression.Call(instance, addMethod!, castCall);
+ if (result.Entity is null)
+ {
+ return new DeserializedValueNullError(typeof(IReadOnlyList<TGeneric>));
+ }
- var block = Expression.Block(
- new[] { instance },
- assign,
- addCall,
- Expression.Convert(instance, typeof(object))
- );
+ list.Add(result.Entity);
+ }
- return (Func<IEnumerable<object?>, object>)Expression.Lambda(block, values).Compile();
+ return list;
}
}=
\ No newline at end of file
A Core/NosSmooth.Packets/Converters/Special/Converters/NullableStringConverter.cs => Core/NosSmooth.Packets/Converters/Special/Converters/NullableStringConverter.cs +56 -0
@@ 0,0 1,56 @@
+//
+// NullableStringConverter.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 Remora.Results;
+
+namespace NosSmooth.Packets.Converters.Special.Converters;
+
+#pragma warning disable SA1125
+/// <summary>
+/// Converter of nullable types.
+/// </summary>
+/// <typeparam name="T">The nonnullable underlying type.</typeparam>
+public class NullableStringConverter<T> : BaseStringConverter<Nullable<T>>
+ where T : struct
+{
+ private readonly IStringSerializer _stringSerializer;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NullableStringConverter{T}"/> class.
+ /// </summary>
+ /// <param name="stringSerializer">The string serializer.</param>
+ public NullableStringConverter(IStringSerializer stringSerializer)
+ {
+ _stringSerializer = stringSerializer;
+
+ }
+
+ /// <inheritdoc />
+ public override Result Serialize(T? obj, PacketStringBuilder builder)
+ {
+ if (obj is null)
+ {
+ builder.Append('-');
+ return Result.FromSuccess();
+ }
+
+ return _stringSerializer.Serialize<T>(obj.Value, builder);
+ }
+
+ /// <inheritdoc />
+ public override Result<T?> Deserialize(PacketStringEnumerator stringEnumerator)
+ {
+ var result = _stringSerializer.Deserialize<T>(stringEnumerator);
+ if (!result.IsSuccess)
+ {
+ return Result<T?>.FromError(result);
+ }
+
+ return Result<T?>.FromSuccess(result.Entity);
+ }
+}
+#pragma warning restore SA1125
R Core/NosSmooth.Packets/Converters/Special/EnumTypeConverter.cs => Core/NosSmooth.Packets/Converters/Special/EnumStringConverterFactory.cs +30 -20
@@ 1,45 1,55 @@
//
-// EnumTypeConverter.cs
+// EnumStringConverterFactory.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.Packets.Converters.Special.Converters;
+using NosSmooth.Packets.Extensions;
using Remora.Results;
namespace NosSmooth.Packets.Converters.Special;
/// <summary>
-/// Converts all enums.
+/// Factory for all enum converters.
/// </summary>
-public class EnumTypeConverter : ISpecialTypeConverter
+public class EnumStringConverterFactory : IStringConverterFactory
{
+ private readonly IServiceProvider _serviceProvider;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="EnumStringConverterFactory"/> class.
+ /// </summary>
+ /// <param name="serviceProvider">The service provider.</param>
+ public EnumStringConverterFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
/// <inheritdoc />
public bool ShouldHandle(Type type)
=> type.IsEnum;
/// <inheritdoc />
- public Result<object?> Deserialize(Type type, PacketStringEnumerator stringEnumerator)
+ public Result<IStringConverter> CreateTypeSerializer(Type type)
{
- var tokenResult = stringEnumerator.GetNextToken();
- if (!tokenResult.IsSuccess)
+ var underlyingType = type.GetEnumUnderlyingType();
+ var serializerType = typeof(EnumStringConverter<,>).MakeGenericType(type, underlyingType);
+
+ try
{
- return Result.FromError(tokenResult);
+ return Result<IStringConverter>.FromSuccess
+ ((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, serializerType));
}
-
- return Enum.Parse(type, tokenResult.Entity.Token);
- }
-
- /// <inheritdoc />
- public Result Serialize(Type type, object? obj, PacketStringBuilder builder)
- {
- if (obj is null)
+ catch (Exception e)
{
- builder.Append('-');
- return Result.FromSuccess();
+ return e;
}
-
- builder.Append(Convert.ToInt64(obj));
- return Result.FromSuccess();
}
+
+ /// <inheritdoc />
+ public Result<IStringConverter<T>> CreateTypeSerializer<T>()
+ => CreateTypeSerializer(typeof(T)).Cast<IStringConverter<T>, IStringConverter>();
}=
\ No newline at end of file
R Core/NosSmooth.Packets/Converters/Special/ISpecialTypeConverter.cs => Core/NosSmooth.Packets/Converters/Special/IStringConverterFactory.cs +10 -13
@@ 1,5 1,5 @@
//
-// ISpecialTypeConverter.cs
+// IStringConverterFactory.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.
@@ 13,7 13,7 @@ namespace NosSmooth.Packets.Converters.Special;
/// <summary>
/// Converts special types such as enums or lists.
/// </summary>
-public interface ISpecialTypeConverter
+public interface IStringConverterFactory
{
/// <summary>
/// Whether this type converter should handle the given type.
@@ 23,19 23,16 @@ public interface ISpecialTypeConverter
public bool ShouldHandle(Type type);
/// <summary>
- /// Deserialize the given string to the object.
+ /// Create converter for the given type.
/// </summary>
- /// <param name="type">The type to deserialize.</param>
- /// <param name="stringEnumerator">The packets string enumerator.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result<object?> Deserialize(Type type, PacketStringEnumerator stringEnumerator);
+ /// <param name="type">The type to create converter for.</param>
+ /// <returns>The type converter or an error.</returns>
+ public Result<IStringConverter> CreateTypeSerializer(Type type);
/// <summary>
- /// Deserialize the given object into string.
+ /// Create generic converter for the given type.
/// </summary>
- /// <param name="type">The type to serialize.</param>
- /// <param name="obj">The object to serialize.</param>
- /// <param name="builder">The packet string builder.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result Serialize(Type type, object? obj, PacketStringBuilder builder);
+ /// <typeparam name="T">The type to create converter for.</typeparam>
+ /// <returns>The type converter or an error.</returns>
+ public Result<IStringConverter<T>> CreateTypeSerializer<T>();
}=
\ No newline at end of file
A Core/NosSmooth.Packets/Converters/Special/ListStringConverterFactory.cs => Core/NosSmooth.Packets/Converters/Special/ListStringConverterFactory.cs +62 -0
@@ 0,0 1,62 @@
+//
+// ListStringConverterFactory.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 System.Collections;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Reflection;
+using System.Text;
+using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.Packets.Converters.Special.Converters;
+using NosSmooth.Packets.Errors;
+using NosSmooth.Packets.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.Packets.Converters.Special;
+
+/// <summary>
+/// Converts lists.
+/// </summary>
+public class ListStringConverterFactory : IStringConverterFactory
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ListStringConverterFactory"/> class.
+ /// </summary>
+ /// <param name="serviceProvider">The service provider.</param>
+ public ListStringConverterFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ /// <inheritdoc />
+ public bool ShouldHandle(Type type)
+ => type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type);
+
+ /// <inheritdoc />
+ public Result<IStringConverter> CreateTypeSerializer(Type type)
+ {
+ var elementType = type.GetElementType() ?? type.GetGenericArguments()[0];
+ var converterType = typeof(ListStringConverter<>).MakeGenericType(elementType);
+ try
+ {
+ return Result<IStringConverter>
+ .FromSuccess((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, converterType));
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ /// <inheritdoc />
+ public Result<IStringConverter<T>> CreateTypeSerializer<T>()
+ => CreateTypeSerializer(typeof(T)).Cast<IStringConverter<T>, IStringConverter>();
+}<
\ No newline at end of file
A Core/NosSmooth.Packets/Converters/Special/NullableStringConverterFactory.cs => Core/NosSmooth.Packets/Converters/Special/NullableStringConverterFactory.cs +61 -0
@@ 0,0 1,61 @@
+//
+// NullableStringConverterFactory.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.Packets.Converters.Special.Converters;
+using NosSmooth.Packets.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.Packets.Converters.Special;
+
+/// <inheritdoc />
+public class NullableStringConverterFactory : IStringConverterFactory
+{
+ private readonly IServiceProvider _serviceProvider;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="NullableStringConverterFactory"/> class.
+ /// </summary>
+ /// <param name="serviceProvider">The service provider.</param>
+ public NullableStringConverterFactory(IServiceProvider serviceProvider)
+ {
+ _serviceProvider = serviceProvider;
+ }
+
+ /// <inheritdoc />
+ public bool ShouldHandle(Type type)
+ => Nullable.GetUnderlyingType(type) != null;
+
+ /// <inheritdoc />
+ public Result<IStringConverter> CreateTypeSerializer(Type type)
+ {
+ var underlyingType = Nullable.GetUnderlyingType(type);
+ if (underlyingType is null)
+ {
+ throw new InvalidOperationException("Accepts only nullable types.");
+ }
+
+ var nullableConverterType = typeof(NullableStringConverter<>).MakeGenericType(underlyingType);
+ try
+ {
+ return Result<IStringConverter>
+ .FromSuccess
+ ((IStringConverter)ActivatorUtilities.CreateInstance(_serviceProvider, nullableConverterType));
+ }
+ catch (Exception e)
+ {
+ return e;
+ }
+ }
+
+ /// <inheritdoc />
+ public Result<IStringConverter<T>> CreateTypeSerializer<T>()
+ {
+ return CreateTypeSerializer(typeof(T))
+ .Cast<IStringConverter<T>, IStringConverter>();
+ }
+}<
\ No newline at end of file
D Core/NosSmooth.Packets/Converters/Special/NullableTypeConverter.cs => Core/NosSmooth.Packets/Converters/Special/NullableTypeConverter.cs +0 -37
@@ 1,37 0,0 @@
-//
-// NullableTypeConverter.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 Remora.Results;
-
-namespace NosSmooth.Packets.Converters.Special;
-
-/// <inheritdoc />
-public class NullableTypeConverter : ISpecialTypeConverter
-{
- private readonly ITypeConverterRepository _typeConverterRepository;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="NullableTypeConverter"/> class.
- /// </summary>
- /// <param name="typeConverterRepository">The type converter repository.</param>
- public NullableTypeConverter(ITypeConverterRepository typeConverterRepository)
- {
- _typeConverterRepository = typeConverterRepository;
- }
-
- /// <inheritdoc />
- public bool ShouldHandle(Type type)
- => Nullable.GetUnderlyingType(type) != null;
-
- /// <inheritdoc />
- public Result<object?> Deserialize(Type type, PacketStringEnumerator stringEnumerator)
- => _typeConverterRepository.Deserialize(Nullable.GetUnderlyingType(type)!, stringEnumerator);
-
- /// <inheritdoc />
- public Result Serialize(Type type, object? obj, PacketStringBuilder builder)
- => _typeConverterRepository.Serialize(Nullable.GetUnderlyingType(type)!, obj, builder);
-}>
\ No newline at end of file
A Core/NosSmooth.Packets/Converters/Special/StringSerializer.cs => Core/NosSmooth.Packets/Converters/Special/StringSerializer.cs +80 -0
@@ 0,0 1,80 @@
+//
+// StringSerializer.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.Packets.Errors;
+using Remora.Results;
+
+namespace NosSmooth.Packets.Converters.Special;
+
+/// <inheritdoc />
+public class StringSerializer : IStringSerializer
+{
+ private readonly IStringConverterRepository _converterRepository;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StringSerializer"/> class.
+ /// </summary>
+ /// <param name="converterRepository">The repository of string converters.</param>
+ public StringSerializer(IStringConverterRepository converterRepository)
+ {
+ _converterRepository = converterRepository;
+ }
+
+ /// <inheritdoc />
+ public Result<object?> Deserialize(Type parseType, PacketStringEnumerator stringEnumerator)
+ {
+ var converterResult = _converterRepository.GetTypeConverter(parseType);
+ if (!converterResult.IsSuccess)
+ {
+ return Result<object?>.FromError(converterResult);
+ }
+
+ var deserializedResult = converterResult.Entity.Deserialize(stringEnumerator);
+ if (!deserializedResult.IsSuccess)
+ {
+ return Result<object?>.FromError(deserializedResult);
+ }
+
+ return Result<object?>.FromSuccess(deserializedResult.Entity);
+ }
+
+ /// <inheritdoc />
+ public Result Serialize(Type parseType, object? obj, PacketStringBuilder builder)
+ {
+ var converterResult = _converterRepository.GetTypeConverter(parseType);
+ if (!converterResult.IsSuccess)
+ {
+ return Result.FromError(converterResult);
+ }
+
+ return converterResult.Entity.Serialize(obj, builder);
+ }
+
+ /// <inheritdoc />
+ public Result<TParseType?> Deserialize<TParseType>(PacketStringEnumerator stringEnumerator)
+ {
+ var converterResult = _converterRepository.GetTypeConverter<TParseType>();
+ if (!converterResult.IsSuccess)
+ {
+ return Result<TParseType?>.FromError(converterResult);
+ }
+
+ return converterResult.Entity.Deserialize(stringEnumerator);
+ }
+
+ /// <inheritdoc />
+ public Result Serialize<TParseType>(TParseType? obj, PacketStringBuilder builder)
+ {
+ var converterResult = _converterRepository.GetTypeConverter<TParseType>();
+ if (!converterResult.IsSuccess)
+ {
+ return Result.FromError(converterResult);
+ }
+
+ return converterResult.Entity.Serialize(obj, builder);
+ }
+}<
\ No newline at end of file
A Core/NosSmooth.Packets/Converters/StringConverterRepository.cs => Core/NosSmooth.Packets/Converters/StringConverterRepository.cs +106 -0
@@ 0,0 1,106 @@
+//
+// StringConverterRepository.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 System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using Microsoft.Extensions.DependencyInjection;
+using NosSmooth.Packets.Converters.Special;
+using NosSmooth.Packets.Errors;
+using NosSmooth.Packets.Extensions;
+using Remora.Results;
+
+namespace NosSmooth.Packets.Converters;
+
+/// <summary>
+/// Repository for <see cref="IStringConverter"/>.
+/// </summary>
+public class StringConverterRepository : IStringConverterRepository
+{
+ private readonly IServiceProvider _serviceProvider;
+ private readonly ConcurrentDictionary<Type, IStringConverter?> _typeConverters;
+ private IReadOnlyList<IStringConverterFactory>? _converterFactories;
+
+ /// <summary>
+ /// Initializes a new instance of the <see cref="StringConverterRepository"/> class.
+ /// </summary>
+ /// <param name="serviceProvider">The dependency injection service provider.</param>
+ public StringConverterRepository(IServiceProvider serviceProvider)
+ {
+ _typeConverters = new ConcurrentDictionary<Type, IStringConverter?>();
+ _converterFactories = null;
+ _serviceProvider = serviceProvider;
+ }
+
+ private IReadOnlyList<IStringConverterFactory> ConverterFactories
+ {
+ get
+ {
+ if (_converterFactories is null)
+ {
+ _converterFactories = _serviceProvider
+ .GetServices<IStringConverterFactory>()
+ .ToArray();
+ }
+
+ return _converterFactories;
+ }
+ }
+
+ /// <summary>
+ /// Gets the type converter for the given type.
+ /// </summary>
+ /// <param name="type">The type to find converter for.</param>
+ /// <returns>The type converter or an error.</returns>
+ public Result<IStringConverter> GetTypeConverter(Type type)
+ {
+ var typeConverter = _typeConverters.GetOrAddResult
+ (
+ type,
+ (getType) =>
+ {
+ foreach (var converterFactory in ConverterFactories)
+ {
+ if (converterFactory.ShouldHandle(getType))
+ {
+ var result = converterFactory.CreateTypeSerializer(getType);
+ if (!result.IsSuccess)
+ {
+ return Result<IStringConverter?>.FromError(result);
+ }
+
+ return Result<IStringConverter?>.FromSuccess(result.Entity);
+ }
+ }
+
+ var converterType = typeof(IStringConverter<>).MakeGenericType(type);
+ return Result<IStringConverter?>.FromSuccess
+ ((IStringConverter?)_serviceProvider.GetService(converterType));
+ }
+ );
+
+ if (!typeConverter.IsSuccess)
+ {
+ return Result<IStringConverter>.FromError(typeConverter);
+ }
+
+ if (typeConverter.Entity is null)
+ {
+ return new TypeConverterNotFoundError(type);
+ }
+
+ return Result<IStringConverter>.FromSuccess(typeConverter.Entity);
+ }
+
+ /// <summary>
+ /// Gets the type converter for the given type.
+ /// </summary>
+ /// <typeparam name="TParseType">The type to find converter for.</typeparam>
+ /// <returns>The type converter or an error.</returns>
+ public Result<IStringConverter<TParseType>> GetTypeConverter<TParseType>()
+ => GetTypeConverter(typeof(TParseType)).Cast<IStringConverter<TParseType>, IStringConverter>();
+}<
\ No newline at end of file
D Core/NosSmooth.Packets/Converters/TypeConverterRepository.cs => Core/NosSmooth.Packets/Converters/TypeConverterRepository.cs +0 -245
@@ 1,245 0,0 @@
-//
-// TypeConverterRepository.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 System.Collections.Concurrent;
-using System.Collections.Generic;
-using System.Linq;
-using Microsoft.Extensions.DependencyInjection;
-using NosSmooth.Packets.Converters.Special;
-using NosSmooth.Packets.Errors;
-using Remora.Results;
-
-namespace NosSmooth.Packets.Converters;
-
-/// <summary>
-/// Repository for <see cref="ITypeConverter"/>.
-/// </summary>
-public class TypeConverterRepository : ITypeConverterRepository
-{
- private readonly IServiceProvider _serviceProvider;
- private readonly ConcurrentDictionary<Type, ITypeConverter?> _typeConverters;
- private readonly ConcurrentDictionary<Type, ISpecialTypeConverter?> _specialConverter;
- private IReadOnlyList<ISpecialTypeConverter>? _specialTypeConverters;
-
- /// <summary>
- /// Initializes a new instance of the <see cref="TypeConverterRepository"/> class.
- /// </summary>
- /// <param name="serviceProvider">The dependency injection service provider.</param>
- public TypeConverterRepository(IServiceProvider serviceProvider)
- {
- _typeConverters = new ConcurrentDictionary<Type, ITypeConverter?>();
- _specialConverter = new ConcurrentDictionary<Type, ISpecialTypeConverter?>();
- _specialTypeConverters = null;
- _serviceProvider = serviceProvider;
- }
-
- /// <summary>
- /// Gets the type converter for the given type.
- /// </summary>
- /// <param name="type">The type to find converter for.</param>
- /// <returns>The type converter or an error.</returns>
- public Result<ITypeConverter> GetTypeConverter(Type type)
- {
- var typeConverter = _typeConverters.GetOrAdd
- (
- type,
- (getType) =>
- {
- var converterType = typeof(ITypeConverter<>).MakeGenericType(type);
- return (ITypeConverter?)_serviceProvider.GetService(converterType);
- }
- );
-
- if (typeConverter is null)
- {
- return new TypeConverterNotFoundError(type);
- }
-
- return Result<ITypeConverter>.FromSuccess(typeConverter);
- }
-
- /// <summary>
- /// Gets the type converter for the given type.
- /// </summary>
- /// <typeparam name="TParseType">The type to find converter for.</typeparam>
- /// <returns>The type converter or an error.</returns>
- public Result<ITypeConverter<TParseType>> GetTypeConverter<TParseType>()
- {
- var typeConverter = _typeConverters.GetOrAdd
- (
- typeof(TParseType),
- _ => _serviceProvider.GetService<ITypeConverter<TParseType>>()
- );
-
- if (typeConverter is null)
- {
- return new TypeConverterNotFoundError(typeof(TParseType));
- }
-
- return Result<ITypeConverter<TParseType>>.FromSuccess((ITypeConverter<TParseType>)typeConverter);
- }
-
- /// <summary>
- /// Convert the data from the enumerator to the given type.
- /// </summary>
- /// <param name="parseType">The type of the object to serialize.</param>
- /// <param name="stringEnumerator">The packet string enumerator with the current position.</param>
- /// <returns>The parsed object or an error.</returns>
- public Result<object?> Deserialize(Type parseType, PacketStringEnumerator stringEnumerator)
- {
- var specialConverter = GetSpecialConverter(parseType);
- if (specialConverter is not null)
- {
- var deserializeResult = specialConverter.Deserialize(parseType, stringEnumerator);
- if (!deserializeResult.IsSuccess)
- {
- return Result<object?>.FromError(deserializeResult);
- }
-
- if (deserializeResult.Entity is null)
- {
- if (parseType.DeclaringType == typeof(Nullable<>))
- {
- return default;
- }
-
- return Result<object?>.FromError(new DeserializedValueNullError(parseType));
- }
-
- return deserializeResult.Entity;
- }
-
- var converterResult = GetTypeConverter(parseType);
- if (!converterResult.IsSuccess)
- {
- return Result<object?>.FromError(converterResult);
- }
-
- var deserializedResult = converterResult.Entity.Deserialize(stringEnumerator);
- if (!deserializedResult.IsSuccess)
- {
- return Result<object?>.FromError(deserializedResult);
- }
-
- return Result<object?>.FromSuccess(deserializedResult.Entity);
- }
-
- /// <summary>
- /// Serializes the given object to string by appending to the packet string builder.
- /// </summary>
- /// <param name="parseType">The type of the object to serialize.</param>
- /// <param name="obj">The object to serialize.</param>
- /// <param name="builder">The string builder to append to.</param>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result Serialize(Type parseType, object? obj, PacketStringBuilder builder)
- {
- var specialConverter = GetSpecialConverter(parseType);
- if (specialConverter is not null)
- {
- return specialConverter.Serialize(parseType, obj, builder);
- }
-
- var converterResult = GetTypeConverter(parseType);
- if (!converterResult.IsSuccess)
- {
- return Result.FromError(converterResult);
- }
-
- return converterResult.Entity.Serialize(obj, builder);
- }
-
- /// <summary>
- /// Convert the data from the enumerator to the given type.
- /// </summary>
- /// <param name="stringEnumerator">The packet string enumerator with the current position.</param>
- /// <typeparam name="TParseType">The type of the object to serialize.</typeparam>
- /// <returns>The parsed object or an error.</returns>
- public Result<TParseType?> Deserialize<TParseType>(PacketStringEnumerator stringEnumerator)
- {
- var specialConverter = GetSpecialConverter(typeof(TParseType));
- if (specialConverter is not null)
- {
- var deserializeResult = specialConverter.Deserialize(typeof(TParseType), stringEnumerator);
- if (!deserializeResult.IsSuccess)
- {
- return Result<TParseType?>.FromError(deserializeResult);
- }
-
- if (deserializeResult.Entity is null)
- {
- if (typeof(TParseType).DeclaringType == typeof(Nullable<>))
- {
- return default;
- }
-
- return Result<TParseType?>.FromError(new DeserializedValueNullError(typeof(TParseType)));
- }
-
- return (TParseType?)deserializeResult.Entity;
- }
-
- var converterResult = GetTypeConverter<TParseType>();
- if (!converterResult.IsSuccess)
- {
- return Result<TParseType?>.FromError(converterResult);
- }
-
- return converterResult.Entity.Deserialize(stringEnumerator);
- }
-
- /// <summary>
- /// Serializes the given object to string by appending to the packet string builder.
- /// </summary>
- /// <param name="obj">The object to serialize.</param>
- /// <param name="builder">The string builder to append to.</param>
- /// <typeparam name="TParseType">The type of the object to deserialize.</typeparam>
- /// <returns>A result that may or may not have succeeded.</returns>
- public Result Serialize<TParseType>(TParseType? obj, PacketStringBuilder builder)
- {
- if (obj is null)
- {
- builder.Append("-");
- return Result.FromSuccess();
- }
-
- var specialConverter = GetSpecialConverter(typeof(TParseType));
- if (specialConverter is not null)
- {
- return specialConverter.Serialize(typeof(TParseType), obj, builder);
- }
-
- var converterResult = GetTypeConverter<TParseType>();
- if (!converterResult.IsSuccess)
- {
- return Result.FromError(converterResult);
- }
-
- return converterResult.Entity.Serialize(obj, builder);
- }
-
- private ISpecialTypeConverter? GetSpecialConverter(Type type)
- {
- return _specialConverter.GetOrAdd
- (
- type,
- (t) =>
- {
- _specialTypeConverters ??= _serviceProvider.GetServices<ISpecialTypeConverter>().ToList();
-
- foreach (var specialConverter in _specialTypeConverters)
- {
- if (specialConverter.ShouldHandle(t))
- {
- return specialConverter;
- }
- }
-
- return null;
- }
- );
- }
-}>
\ No newline at end of file
M Core/NosSmooth.Packets/Errors/CouldNotConvertError.cs => Core/NosSmooth.Packets/Errors/CouldNotConvertError.cs +1 -1
@@ 16,5 16,5 @@ namespace NosSmooth.Packets.Errors;
/// <param name="Converter">The converter that failed the parsing.</param>
/// <param name="Value">The value that failed to parse.</param>
/// <param name="Reason">The reason for the error.</param>
-public record CouldNotConvertError(ITypeConverter Converter, string Value, string Reason)
+public record CouldNotConvertError(IStringConverter Converter, string Value, string Reason)
: ResultError($"Converter {Converter.GetType().FullName} could not convert value \"{Value}\" due to {Reason}.");=
\ No newline at end of file
M Core/NosSmooth.Packets/Errors/PacketEndNotExpectedError.cs => Core/NosSmooth.Packets/Errors/PacketEndNotExpectedError.cs +1 -1
@@ 15,5 15,5 @@ namespace NosSmooth.Packets.Errors;
/// </summary>
/// <param name="Converter">The type converter.</param>
/// <param name="PropertyName">The property name.</param>
-public record PacketEndNotExpectedError(ITypeConverter Converter, string PropertyName)
+public record PacketEndNotExpectedError(IStringConverter Converter, string PropertyName)
: ResultError($"Unexpected packet end reached in {Converter.GetType()} during deserializing the property {PropertyName}");=
\ No newline at end of file
M Core/NosSmooth.Packets/Errors/PacketParameterSerializerError.cs => Core/NosSmooth.Packets/Errors/PacketParameterSerializerError.cs +1 -1
@@ 17,5 17,5 @@ namespace NosSmooth.Packets.Errors;
/// <param name="PropertyName">The name of the property.</param>
/// <param name="Result">The underlying result.</param>
/// <param name="Reason">The reason for the error, if known.</param>
-public record PacketParameterSerializerError(ITypeConverter Converter, string PropertyName, IResult Result, string? Reason = null)
+public record PacketParameterSerializerError(IStringConverter Converter, string PropertyName, IResult Result, string? Reason = null)
: ResultError($"There was an error deserializing property {PropertyName} in converter {Converter.GetType().FullName}{(Reason is not null ? (", reason: " + Reason) : string.Empty)}");=
\ No newline at end of file
M Core/NosSmooth.Packets/Errors/WrongTypeError.cs => Core/NosSmooth.Packets/Errors/WrongTypeError.cs +1 -1
@@ 16,5 16,5 @@ namespace NosSmooth.Packets.Errors;
/// <param name="TypeConverter">The converter that failed to convert the object.</param>
/// <param name="ExpectedType">The expected type of the converting object.</param>
/// <param name="ActualObject">The actual object the converter got.</param>
-public record WrongTypeError(ITypeConverter TypeConverter, Type ExpectedType, object? ActualObject)
+public record WrongTypeError(IStringConverter TypeConverter, Type ExpectedType, object? ActualObject)
: ResultError($"{TypeConverter.GetType().FullName} expected type {ExpectedType.FullName}, but got {ActualObject?.GetType().FullName ?? "null"}");=
\ No newline at end of file
A Core/NosSmooth.Packets/Extensions/ConcurrentDictionaryExtensions.cs => Core/NosSmooth.Packets/Extensions/ConcurrentDictionaryExtensions.cs +52 -0
@@ 0,0 1,52 @@
+//
+// ConcurrentDictionaryExtensions.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 System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Net.Http;
+using Remora.Results;
+
+namespace NosSmooth.Packets.Extensions;
+
+/// <summary>
+/// Extension methods for <see cref="ConcurrentDictionary{TKey,TValue}"/>.
+/// </summary>
+public static class ConcurrentDictionaryExtensions
+{
+ /// <summary>
+ /// Adds the value from the factory if it doesn't exist already,
+ /// otherwise returns the existing avlue.
+ /// </summary>
+ /// <param name="dictionary">The dictionary.</param>
+ /// <param name="key">The key to add the value at.</param>
+ /// <param name="valueFactory">The factory to obtain the value to add.</param>
+ /// <typeparam name="TKey">The type of the key.</typeparam>
+ /// <typeparam name="TValue">The type of the value.</typeparam>
+ /// <returns>The added value.</returns>
+ public static Result<TValue> GetOrAddResult<TKey, TValue>
+ (
+ this ConcurrentDictionary<TKey, TValue> dictionary,
+ TKey key,
+ Func<TKey, Result<TValue>> valueFactory
+ )
+ where TKey : notnull
+ {
+ if (dictionary.TryGetValue(key, out var val))
+ {
+ return val;
+ }
+
+ var result = valueFactory(key);
+ if (!result.IsSuccess)
+ {
+ return result;
+ }
+
+ dictionary.TryAdd(key, result.Entity);
+ return result;
+ }
+}<
\ No newline at end of file
A Core/NosSmooth.Packets/Extensions/ResultExtensions.cs => Core/NosSmooth.Packets/Extensions/ResultExtensions.cs +35 -0
@@ 0,0 1,35 @@
+//
+// ResultExtensions.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 Remora.Results;
+
+namespace NosSmooth.Packets.Extensions;
+
+/// <summary>
+/// Extensions for <see cref="Result{TEntity}"/> class.
+/// </summary>
+public static class ResultExtensions
+{
+ /// <summary>
+ /// Cast the given non-nullable result type to another type.
+ /// </summary>
+ /// <param name="result">The result to cast.</param>
+ /// <typeparam name="TTo">The type to cast to.</typeparam>
+ /// <typeparam name="TFrom">The type to cast from.</typeparam>
+ /// <returns>The casted result.</returns>
+ public static Result<TTo> Cast<TTo, TFrom>(this Result<TFrom> result)
+ where TTo : notnull
+ where TFrom : notnull
+ {
+ if (!result.IsSuccess)
+ {
+ return Result<TTo>.FromError(result);
+ }
+
+ return Result<TTo>.FromSuccess((TTo)(object)result.Entity);
+ }
+}<
\ No newline at end of file
M Core/NosSmooth.Packets/Extensions/ServiceCollectionExtensions.cs => Core/NosSmooth.Packets/Extensions/ServiceCollectionExtensions.cs +34 -34
@@ 14,7 14,6 @@ using NosSmooth.Packets.Converters.Common;
using NosSmooth.Packets.Converters.Packets;
using NosSmooth.Packets.Converters.Special;
using NosSmooth.Packets.Packets;
-using NosSmooth.Packets.Packets.Server.Weapons;
namespace NosSmooth.Packets.Extensions;
@@ 27,7 26,7 @@ public static class ServiceCollectionExtensions
/// Add packet serialization classes.
/// </summary>
/// <remarks>
- /// All generic implementations of ITypeConverter the class
+ /// All generic implementations of IStringConverter the class
/// implements will be registered.
/// </remarks>
/// <param name="serviceCollection">The service collection.</param>
@@ 35,11 34,12 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddPacketSerialization(this IServiceCollection serviceCollection)
{
return serviceCollection
- .AddSingleton<ITypeConverterRepository, TypeConverterRepository>()
+ .AddSingleton<IStringConverterRepository, StringConverterRepository>()
+ .AddSingleton<IStringSerializer, StringSerializer>()
.AddSingleton<IPacketSerializer, PacketSerializer>()
.AddSingleton<IPacketTypesRepository>(p =>
{
- var repository = new PacketTypesRepository(p.GetRequiredService<ITypeConverterRepository>());
+ var repository = new PacketTypesRepository(p.GetRequiredService<IStringConverterRepository>());
var packetTypes = typeof(ServiceCollectionExtensions).Assembly
.GetExportedTypes()
.Where(x => x != typeof(UnresolvedPacket) && !x.IsAbstract && typeof(IPacket).IsAssignableFrom(x));
@@ 70,11 70,11 @@ public static class ServiceCollectionExtensions
var types = assembly.GetExportedTypes()
.Where(x => x.Namespace?.Contains("Generated") ?? false)
.Where(x => x.GetInterfaces().Any(
- i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeConverter<>)
+ i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IStringConverter<>)
));
foreach (var type in types)
{
- serviceCollection.AddTypeConverter(type);
+ serviceCollection.AddStringConverter(type);
}
return serviceCollection;
@@ 89,62 89,62 @@ public static class ServiceCollectionExtensions
public static IServiceCollection AddBasicConverters(this IServiceCollection serviceCollection)
{
return serviceCollection
- .AddSpecialConverter<ListTypeConverter>()
- .AddSpecialConverter<NullableTypeConverter>()
- .AddSpecialConverter<EnumTypeConverter>()
- .AddTypeConverter<IntTypeConverter>()
- .AddTypeConverter<BoolTypeConverter>()
- .AddTypeConverter<UIntTypeConverter>()
- .AddTypeConverter<ShortTypeConverter>()
- .AddTypeConverter<UShortTypeConverter>()
- .AddTypeConverter<ByteTypeConverter>()
- .AddTypeConverter<ULongTypeConverter>()
- .AddTypeConverter<LongTypeConverter>()
- .AddTypeConverter<StringTypeConverter>()
- .AddTypeConverter<NameStringConverter>()
- .AddTypeConverter<CharTypeConverter>()
- .AddTypeConverter<UpgradeRareSubPacketConverter>();
+ .AddStringConverterFactory<ListStringConverterFactory>()
+ .AddStringConverterFactory<NullableStringConverterFactory>()
+ .AddStringConverterFactory<EnumStringConverterFactory>()
+ .AddStringConverter<IntStringConverter>()
+ .AddStringConverter<BoolStringConverter>()
+ .AddStringConverter<UIntStringConverter>()
+ .AddStringConverter<ShortStringConverter>()
+ .AddStringConverter<UShortStringConverter>()
+ .AddStringConverter<ByteStringConverter>()
+ .AddStringConverter<ULongStringConverter>()
+ .AddStringConverter<LongStringConverter>()
+ .AddStringConverter<StringTypeConverter>()
+ .AddStringConverter<NameStringConverter>()
+ .AddStringConverter<CharStringConverter>()
+ .AddStringConverter<UpgradeRareSubPacketConverter>();
}
/// <summary>
/// Add generic type converter.
/// </summary>
/// <remarks>
- /// All generic implementations of ITypeConverter the class
+ /// All generic implementations of IStringConverter the class
/// implements will be registered.
/// </remarks>
/// <param name="serviceCollection">The service collection.</param>
/// <typeparam name="TConverter">The type of the converter.</typeparam>
/// <returns>The collection.</returns>
- public static IServiceCollection AddTypeConverter<TConverter>(this IServiceCollection serviceCollection)
- where TConverter : ITypeConverter
- => serviceCollection.AddTypeConverter(typeof(TConverter));
+ public static IServiceCollection AddStringConverter<TConverter>(this IServiceCollection serviceCollection)
+ where TConverter : IStringConverter
+ => serviceCollection.AddStringConverter(typeof(TConverter));
/// <summary>
/// Add generic type converter.
/// </summary>
/// <remarks>
- /// All generic implementations of ITypeConverter the class
+ /// All generic implementations of IStringConverter the class
/// implements will be registered.
/// </remarks>
/// <param name="serviceCollection">The service collection.</param>
/// <param name="converterType">The type of the converter.</param>
/// <returns>The collection.</returns>
- public static IServiceCollection AddTypeConverter(this IServiceCollection serviceCollection, Type converterType)
+ public static IServiceCollection AddStringConverter(this IServiceCollection serviceCollection, Type converterType)
{
if (!converterType.GetInterfaces().Any(
- i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(ITypeConverter<>)
+ i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IStringConverter<>)
))
{
throw new ArgumentException(
- $"{nameof(converterType)} should implement ITypeConverter.",
+ $"{nameof(converterType)} should implement IStringConverter.",
nameof(converterType));
}
var handlerTypeInterfaces = converterType.GetInterfaces();
var handlerInterfaces = handlerTypeInterfaces.Where
(
- r => r.IsGenericType && r.GetGenericTypeDefinition() == typeof(ITypeConverter<>)
+ r => r.IsGenericType && r.GetGenericTypeDefinition() == typeof(IStringConverter<>)
);
foreach (var handlerInterface in handlerInterfaces)
@@ 159,11 159,11 @@ public static class ServiceCollectionExtensions
/// Add the specified converter as a special converter.
/// </summary>
/// <param name="serviceCollection">The service collection.</param>
- /// <typeparam name="TSpecialConverter">The type to add as a special converter.</typeparam>
+ /// <typeparam name="TConverterFactory">The type to add as a special converter.</typeparam>
/// <returns>The collection.</returns>
- public static IServiceCollection AddSpecialConverter<TSpecialConverter>(this IServiceCollection serviceCollection)
- where TSpecialConverter : class, ISpecialTypeConverter
+ public static IServiceCollection AddStringConverterFactory<TConverterFactory>(this IServiceCollection serviceCollection)
+ where TConverterFactory : class, IStringConverterFactory
{
- return serviceCollection.AddSingleton<ISpecialTypeConverter, TSpecialConverter>();
+ return serviceCollection.AddSingleton<IStringConverterFactory, TConverterFactory>();
}
}=
\ No newline at end of file
M Core/NosSmooth.Packets/Packets/PacketInfo.cs => Core/NosSmooth.Packets/Packets/PacketInfo.cs +1 -1
@@ 15,4 15,4 @@ namespace NosSmooth.Packets.Packets;
/// <param name="Header">The packet's header, if any.</param>
/// <param name="PacketType">The packet's type.</param>
/// <param name="PacketConverter">The packet's converter.</param>
-public record PacketInfo(string? Header, Type PacketType, ITypeConverter PacketConverter);>
\ No newline at end of file
+public record PacketInfo(string? Header, Type PacketType, IStringConverter PacketConverter);<
\ No newline at end of file
M Core/NosSmooth.Packets/Packets/PacketTypesRepository.cs => Core/NosSmooth.Packets/Packets/PacketTypesRepository.cs +5 -5
@@ 21,17 21,17 @@ namespace NosSmooth.Packets.Packets;
/// </summary>
public class PacketTypesRepository : IPacketTypesRepository
{
- private readonly ITypeConverterRepository _typeConverterRepository;
+ private readonly IStringConverterRepository _stringConverterRepository;
private readonly Dictionary<PacketSource, Dictionary<string, PacketInfo>> _headerToPacket;
private readonly Dictionary<string, PacketInfo> _typeToPacket;
/// <summary>
/// Initializes a new instance of the <see cref="PacketTypesRepository"/> class.
/// </summary>
- /// <param name="typeConverterRepository">The type converter repository.</param>
- public PacketTypesRepository(ITypeConverterRepository typeConverterRepository)
+ /// <param name="stringConverterRepository">The type converter repository.</param>
+ public PacketTypesRepository(IStringConverterRepository stringConverterRepository)
{
- _typeConverterRepository = typeConverterRepository;
+ _stringConverterRepository = stringConverterRepository;
_headerToPacket = new Dictionary<PacketSource, Dictionary<string, PacketInfo>>();
_typeToPacket = new Dictionary<string, PacketInfo>();
}
@@ 54,7 54,7 @@ public class PacketTypesRepository : IPacketTypesRepository
return new ArgumentInvalidError(nameof(type), "Every packet has to specify the header.");
}
- var converterResult = _typeConverterRepository.GetTypeConverter(type);
+ var converterResult = _stringConverterRepository.GetTypeConverter(type);
if (!converterResult.IsSuccess)
{
return Result.FromError(converterResult);