~ruther/NosSmooth

16df48bb7eaf21f30ae920a2fa630c9b5eb7f519 — František Boháček 3 years ago 1bbb749 + 5f2cfd7
Merge pull request #16 from Rutherther/packets-list-inline-serializer

Add generation of packet list inline serializer
25 files changed, 805 insertions(+), 165 deletions(-)

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketConditionalIndexAttributeGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketContextListAttributeGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketGreedyIndexAttributeGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketIndexAttributeGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketListIndexAttributeGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/Constants.cs
M Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs
A Core/NosSmooth.PacketSerializersGenerator/Extensions/TypeSymbolExtensions.cs
A Core/NosSmooth.PacketSerializersGenerator/Extensions/TypeSyntaxExtensions.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BoolInlineConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/IInlineConverterGenerator.cs
A Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/StringInlineConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/InlineTypeConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs
M Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs
M Core/NosSmooth.Packets/Converters/Basic/BoolTypeConverter.cs
M Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs
M Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs
M Core/NosSmooth.Packets/Converters/Special/EnumTypeConverter.cs
M Core/NosSmooth.Packets/Converters/Special/ListTypeConverter.cs
M Core/NosSmooth.Packets/PacketStringBuilder.cs
M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketConditionalIndexAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketConditionalIndexAttributeGenerator.cs +2 -2
@@ 218,7 218,7 @@ public class PacketConditionalIndexAttributeGenerator : IParameterGenerator
        }

        // serialize, check the error.
        _inlineTypeConverterGenerators.SerializeAndCheck(textWriter, packetInfo);
        _inlineTypeConverterGenerators.Serialize(textWriter, packetInfo);

        // pop inner separator level
        if (pushedLevel)


@@ 274,7 274,7 @@ public class PacketConditionalIndexAttributeGenerator : IParameterGenerator
            pushedLevel = true;
        }

        _inlineTypeConverterGenerators.DeserializeAndCheck(textWriter, packetInfo);
        generator.DeserializeAndCheck(parameter, packetInfo, _inlineTypeConverterGenerators);

        if (!parameter.Nullable)
        {

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketContextListAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketContextListAttributeGenerator.cs +2 -2
@@ 75,7 75,7 @@ public class PacketContextListAttributeGenerator : IParameterGenerator
        var innerSeparator = attribute.GetNamedValue<char>("InnerSeparator", '.');
        generator.PrepareLevel(innerSeparator);

        _inlineTypeConverterGenerators.SerializeAndCheck(textWriter, packetInfo);
        _inlineTypeConverterGenerators.Serialize(textWriter, packetInfo);
        generator.RemovePreparedLevel();
        generator.PopLevel();



@@ 115,7 115,7 @@ public class PacketContextListAttributeGenerator : IParameterGenerator
        var innerSeparator = attribute.GetNamedValue<char>("InnerSeparator", '.');
        generator.PrepareLevel(innerSeparator);

        _inlineTypeConverterGenerators.DeserializeAndCheck(textWriter, packetInfo);
        generator.DeserializeAndCheck(parameter, packetInfo, _inlineTypeConverterGenerators);
        generator.RemovePreparedLevel();
        generator.PopLevel();
        if (!parameter.Nullable)

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketGreedyIndexAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketGreedyIndexAttributeGenerator.cs +1 -1
@@ 82,7 82,7 @@ public class PacketGreedyIndexAttributeGenerator : IParameterGenerator
        }

        generator.SetReadToLast(); // Greedy
        _inlineTypeConverterGenerators.DeserializeAndCheck(textWriter, packetInfo);
        generator.DeserializeAndCheck(parameter, packetInfo, _inlineTypeConverterGenerators);

        if (!parameter.Nullable)
        {

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketIndexAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketIndexAttributeGenerator.cs +4 -4
@@ 77,7 77,7 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
        }

        // serialize, check the error.
        _inlineTypeConverterGenerators.SerializeAndCheck(textWriter, packetInfo);
        _inlineTypeConverterGenerators.Serialize(textWriter, packetInfo);

        // pop inner separator level
        if (pushedLevel)


@@ 124,11 124,11 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
            pushedLevel = true;
        }

        _inlineTypeConverterGenerators.DeserializeAndCheck(textWriter, packetInfo);

        generator.DeserializeAndCheck(parameter, packetInfo, _inlineTypeConverterGenerators);
        if (!parameter.Nullable)
        {
            generator.CheckNullError(parameter.GetNullableVariableName(), parameter.GetResultVariableName(), parameter.Name);
            generator.CheckNullError
                (parameter.GetNullableVariableName(), parameter.GetResultVariableName(), parameter.Name);
        }

        generator.AssignLocalVariable(parameter, false);

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketListIndexAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketListIndexAttributeGenerator.cs +2 -2
@@ 77,7 77,7 @@ public class PacketListIndexAttributeGenerator : IParameterGenerator
        var innerSeparator = attribute.GetNamedValue<char>("InnerSeparator", '.');
        generator.PrepareLevel(innerSeparator);

        _inlineTypeConverterGenerators.SerializeAndCheck(textWriter, packetInfo);
        _inlineTypeConverterGenerators.Serialize(textWriter, packetInfo);
        generator.RemovePreparedLevel();
        generator.PopLevel();



@@ 119,7 119,7 @@ public class PacketListIndexAttributeGenerator : IParameterGenerator
        var innerSeparator = attribute.GetNamedValue<char>("InnerSeparator", '.');
        generator.PrepareLevel(innerSeparator);

        _inlineTypeConverterGenerators.DeserializeAndCheck(textWriter, packetInfo);
        generator.DeserializeAndCheck(parameter, packetInfo, _inlineTypeConverterGenerators);
        generator.RemovePreparedLevel();
        generator.PopLevel();
        if (!parameter.Nullable)

M Core/NosSmooth.PacketSerializersGenerator/Constants.cs => Core/NosSmooth.PacketSerializersGenerator/Constants.cs +5 -0
@@ 20,4 20,9 @@ public class Constants
    /// Gets the full name of the packet attribute classes that are used for the generation.
    /// </summary>
    public static string PacketAttributesClassRegex => @"^NosSmooth\.Packets\.Attributes\.Packet.*";

    /// <summary>
    /// Gets the full name of helper class used for inline type converters.
    /// </summary>
    public static string HelperClass => "NosSmooth.Generated.HelperClass";
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs +25 -0
@@ 178,4 178,29 @@ if ({nullableVariableName} is null) {{
        _textWriter.Indent--;
        _textWriter.WriteLine("}");
    }

    /// <summary>
    /// Call deserializer and check the result.
    /// </summary>
    /// <param name="parameter">The parameter.</param>
    /// <param name="packet">The packet.</param>
    /// <param name="inlineTypeConverter">The inline converter generator.</param>
    public void DeserializeAndCheck(ParameterInfo parameter, PacketInfo packet, InlineTypeConverterGenerator inlineTypeConverter)
    {
        _textWriter.WriteLine($"var {parameter.GetResultVariableName()} = ");
        inlineTypeConverter.CallDeserialize(_textWriter, packet);

        _textWriter.WriteLine($"if (!{parameter.GetResultVariableName()}.IsSuccess)");
        _textWriter.Indent++;
        _textWriter.WriteLine
        (
            $"return Result<{packet.Name}?>.FromError(new PacketParameterSerializerError(this, \"{parameter.Name}\", {parameter.GetResultVariableName()}), {parameter.GetResultVariableName()});"
        );
        _textWriter.Indent--;

        _textWriter.WriteLine
        (
            $"{parameter.GetNullableType()} {parameter.GetNullableVariableName()} = {parameter.GetResultVariableName()}.Entity;"
        );
    }
}
\ No newline at end of file

A Core/NosSmooth.PacketSerializersGenerator/Extensions/TypeSymbolExtensions.cs => Core/NosSmooth.PacketSerializersGenerator/Extensions/TypeSymbolExtensions.cs +57 -0
@@ 0,0 1,57 @@
//
//  TypeSymbolExtensions.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 Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;

namespace NosSmooth.PacketSerializersGenerator.Extensions;

/// <summary>
/// Extension methods for <see cref="ITypeSymbol"/>.
/// </summary>
public static class TypeSymbolExtensions
{
    /// <summary>
    /// Gets whether the type symbol is nullable.
    /// </summary>
    /// <param name="typeSymbol">The type symbol.</param>
    /// <returns>Whether the type symbol is nullable. Returns null if not possible to determine.</returns>
    public static bool? IsNullable(this ITypeSymbol typeSymbol)
    {
        if (typeSymbol.NullableAnnotation == NullableAnnotation.Annotated || typeSymbol.Name == "Nullable")
        {
            return true;
        }

        if (typeSymbol.IsValueType)
        {
            return false;
        }

        if (typeSymbol.ToString().EndsWith("?"))
        {
            return true;
        }

        // cannot determine if not nullable from reference type.
        return null;
    }

    /// <summary>
    /// Gets the type name with ? if it is nullable.
    /// </summary>
    /// <param name="typeSymbol">The type.</param>
    /// <returns>The actual name.</returns>
    public static string GetActualType(this ITypeSymbol typeSymbol)
    {
        if (typeSymbol.IsNullable() ?? false)
        {
            return typeSymbol.ToString().TrimEnd('?') + '?';
        }

        return typeSymbol.ToString();
    }
}
\ No newline at end of file

A Core/NosSmooth.PacketSerializersGenerator/Extensions/TypeSyntaxExtensions.cs => Core/NosSmooth.PacketSerializersGenerator/Extensions/TypeSyntaxExtensions.cs +40 -0
@@ 0,0 1,40 @@
//
//  TypeSyntaxExtensions.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 Microsoft.CodeAnalysis.CSharp.Syntax;

namespace NosSmooth.PacketSerializersGenerator.Extensions;

/// <summary>
/// Extension methods for <see cref="TypeSyntax"/>.
/// </summary>
public static class TypeSyntaxExtensions
{
    /// <summary>
    /// Gets whether the type syntax is nullable.
    /// </summary>
    /// <param name="typeSyntax">The type syntax.</param>
    /// <returns>Whether the type syntax is nullable.</returns>
    public static bool IsNullable(this TypeSyntax typeSyntax)
    {
        return typeSyntax is NullableTypeSyntax || typeSyntax.ToString().EndsWith("?");
    }

    /// <summary>
    /// Gets the type name with ? if it is nullable.
    /// </summary>
    /// <param name="typeSyntax">The type.</param>
    /// <returns>The actual name.</returns>
    public static string GetActualType(this TypeSyntax typeSyntax)
    {
        if (typeSyntax.IsNullable())
        {
            return typeSyntax.ToString().TrimEnd('?') + '?';
        }

        return typeSyntax.ToString();
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs +56 -28
@@ 5,6 5,8 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;


@@ 23,44 25,70 @@ public class BasicInlineConverterGenerator : IInlineConverterGenerator
    public static IReadOnlyList<string> HandleTypes => new[] { "long", "ulong", "int", "uint", "short", "ushort", "byte", "sbyte" };

    /// <inheritdoc />
    public bool ShouldHandle(ParameterInfo parameter)
        => HandleTypes.Contains(parameter.Parameter.Type!.ToString());
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
        => HandleTypes.Contains(typeSyntax?.ToString().TrimEnd('?')) || HandleTypes.Contains(typeSymbol?.ToString().TrimEnd('?'));

    /// <inheritdoc />
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, string variableName, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        if ((typeSyntax is not null && typeSyntax.IsNullable()) || (typeSymbol is not null && (typeSymbol.IsNullable() ?? false)))
        {
            textWriter.WriteLine($"if ({variableName} is null)");
            textWriter.WriteLine("{");
            textWriter.WriteLine("builder.Append('-');");
            textWriter.WriteLine("}");
            textWriter.WriteLine("else");
        }
        textWriter.WriteLine("{");
        textWriter.WriteLine
            ($"builder.Append(obj.{parameter.Name}{(parameter.Nullable ? "?" : string.Empty)}.ToString() ?? \"-\");");
            ($"builder.Append(({(typeSymbol?.ToString() ?? typeSyntax!.ToString()).TrimEnd('?')}){variableName});");
        textWriter.WriteLine("}");
        return null;
    }

    /// <inheritdoc />
    public IError? GenerateDeserializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        var type = parameter.Parameter.Type!.ToString();
        string isLastString = packet.Parameters.IsLast ? "true" : "false";
        textWriter.WriteMultiline
        (
            $@"
var {parameter.GetResultVariableName()} = stringEnumerator.GetNextToken();
var {parameter.GetErrorVariableName()} = CheckDeserializationResult({parameter.GetResultVariableName()}, ""{parameter.Name}"", stringEnumerator, {isLastString});
if ({parameter.GetErrorVariableName()} is not null)
        var type = typeSyntax is not null
            ? typeSyntax.ToString().TrimEnd('?')
            : typeSymbol?.ToString();
        if (type is null)
        {
            throw new Exception("TypeSyntax or TypeSymbol has to be non null.");
        }

        textWriter.WriteLine($"{Constants.HelperClass}.ParseBasic{type}(typeConverter, stringEnumerator);");
        return null;
    }

    /// <inheritdoc />
    public void GenerateHelperMethods(IndentedTextWriter textWriter)
    {
        foreach (var type in HandleTypes)
        {
            textWriter.WriteMultiline($@"
public static Result<{type}?> ParseBasic{type}(ITypeConverter typeConverter, PacketStringEnumerator stringEnumerator)
{{
    return Result<{packet.Name}?>.FromError({parameter.GetErrorVariableName()}, {parameter.GetResultVariableName()});
}}
{parameter.GetVariableName()} = default;
{parameter.GetNullableType()} {parameter.GetNullableVariableName()};
if ({parameter.GetResultVariableName()}.Entity.Token == ""-"") {{
    {parameter.GetNullableVariableName()} = null;
}}
else if (!{type}.TryParse({parameter.GetResultVariableName()}.Entity.Token, out {parameter.GetVariableName()})) {{
    return new PacketParameterSerializerError(this, ""{parameter.Name}"", {parameter.GetResultVariableName()}, $""Could not convert {{{parameter.GetResultVariableName()}.Entity.Token}} as {type} in inline converter"");
    var tokenResult = stringEnumerator.GetNextToken();
    if (!tokenResult.IsSuccess)
    {{
        return Result<{type}?>.FromError(tokenResult);
    }}

    var token = tokenResult.Entity.Token;
    if (token == ""-"")
    {{
        return Result<{type}?>.FromSuccess(null);
    }}

    if (!{type}.TryParse(token, out var val))
    {{
        return new CouldNotConvertError(typeConverter, token, ""Could not convert as {type} in inline converter"");
    }}

    return val;
}}
{parameter.GetNullableVariableName()} = {parameter.GetVariableName()};
"
        );
        return null;
");
        }
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BoolInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BoolInlineConverterGenerator.cs +32 -18
@@ 5,6 5,8 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;


@@ 15,45 17,57 @@ namespace NosSmooth.PacketSerializersGenerator.InlineConverterGenerators;
public class BoolInlineConverterGenerator : IInlineConverterGenerator
{
    /// <inheritdoc />
    public bool ShouldHandle(ParameterInfo parameter)
        => parameter.Parameter.Type!.ToString() == "bool";
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
        => typeSyntax?.ToString().TrimEnd('?') == "bool" || typeSymbol?.ToString().TrimEnd('?') == "bool";

    /// <inheritdoc />
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, string variableName, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        if (parameter.Nullable)
        if ((typeSyntax?.IsNullable() ?? false) || (typeSymbol?.IsNullable() ?? false))
        {
            textWriter.WriteLine($"if (obj.{parameter.Name} is null)");
            textWriter.WriteLine($"if ({variableName} is null)");
            textWriter.WriteLine("{");
            textWriter.Indent++;
            textWriter.WriteLine("builder.Append(\"-\");");
            textWriter.WriteLine("builder.Append('-');");
            textWriter.Indent--;
            textWriter.WriteLine("}");
            textWriter.WriteLine("else");
        }
        textWriter.WriteLine("{");
        textWriter.Indent++;
        textWriter.WriteLine($"builder.Append(obj.{parameter.Name} ? \"1\" : \"0\");");
        textWriter.WriteLine($"builder.Append({variableName} ? '1' : '0');");
        textWriter.Indent--;
        textWriter.WriteLine("}");
        return null;
    }

    /// <inheritdoc />
    public IError? GenerateDeserializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        string isLastString = packet.Parameters.IsLast ? "true" : "false";
        textWriter.WriteMultiline($@"
var {parameter.GetResultVariableName()} = stringEnumerator.GetNextToken();
var {parameter.GetErrorVariableName()} = CheckDeserializationResult({parameter.GetResultVariableName()}, ""{parameter.Name}"", stringEnumerator, {isLastString});
if ({parameter.GetErrorVariableName()} is not null)
        textWriter.WriteLine($"{Constants.HelperClass}.ParseBool(stringEnumerator);");
        return null;
    }

    /// <inheritdoc />
    public void GenerateHelperMethods(IndentedTextWriter textWriter)
    {
        textWriter.WriteLine(@"
public static Result<bool?> ParseBool(PacketStringEnumerator stringEnumerator)
{{
    return Result<{packet.Name}?>.FromError({parameter.GetErrorVariableName()}, {parameter.GetResultVariableName()});
    var tokenResult = stringEnumerator.GetNextToken();
    if (!tokenResult.IsSuccess)
    {{
        return Result<bool?>.FromError(tokenResult);
    }}

    var token = tokenResult.Entity.Token;
    if (token == ""-"")
    {{
        return Result<bool?>.FromSuccess(null);
    }}

    return token == ""1"" ? true : false;
}}
{parameter.GetNullableType()} {parameter.GetNullableVariableName()} = {parameter.GetResultVariableName()}.Entity.Token == ""1"" ? true : ({parameter.GetResultVariableName()}.Entity.Token == ""-"" ? null : false);
");
        return null;
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs +75 -28
@@ 6,6 6,7 @@

using System.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;


@@ 15,47 16,93 @@ namespace NosSmooth.PacketSerializersGenerator.InlineConverterGenerators;
/// <inheritdoc />
public class EnumInlineConverterGenerator : IInlineConverterGenerator
{
    private readonly List<ITypeSymbol> _enumTypes;

    /// <summary>
    /// Initializes a new instance of the <see cref="EnumInlineConverterGenerator"/> class.
    /// </summary>
    public EnumInlineConverterGenerator()
    {
        _enumTypes = new List<ITypeSymbol>();
    }

    /// <inheritdoc />
    public bool ShouldHandle(ParameterInfo parameter)
        => parameter.Type.TypeKind == TypeKind.Enum;
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
        => typeSymbol?.TypeKind == TypeKind.Enum;

    /// <inheritdoc />
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? GenerateSerializerPart
    (
        IndentedTextWriter textWriter,
        string variableName,
        TypeSyntax? typeSyntax,
        ITypeSymbol? typeSymbol
    )
    {
        var parameter = packet.Parameters.Current;
        var underlyingType = ((INamedTypeSymbol)parameter.Type).EnumUnderlyingType!.ToString();
        var underlyingType = ((INamedTypeSymbol)typeSymbol!).EnumUnderlyingType!.ToString();
        if ((typeSyntax?.IsNullable() ?? false) || (typeSymbol?.IsNullable() ?? false))
        {
            textWriter.WriteLine("if (obj is null)");
            textWriter.WriteLine("{");
            textWriter.WriteLine("builder.Append('-');");
            textWriter.WriteLine("}");
            textWriter.WriteLine("else");
        }
        textWriter.WriteLine("{");
        textWriter.WriteLine
            ($"builder.Append((({underlyingType}?)obj.{parameter.Name}{(parameter.Nullable ? "?" : string.Empty)}).ToString() ?? \"-\");");
            ($"builder.Append(({underlyingType}){variableName});");
        textWriter.WriteLine("}");

        return null;
    }

    /// <inheritdoc />
    public IError? GenerateDeserializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        var underlyingType = ((INamedTypeSymbol)parameter.Type).EnumUnderlyingType!.ToString();
        string isLastString = packet.Parameters.IsLast ? "true" : "false";
        textWriter.WriteMultiline
        if (_enumTypes.All(x => x.ToString() != typeSymbol!.ToString()))
        {
            _enumTypes.Add(typeSymbol!);
        }

        textWriter.WriteLine
        (
            $@"
var {parameter.GetResultVariableName()} = stringEnumerator.GetNextToken();
var {parameter.GetErrorVariableName()} = CheckDeserializationResult({parameter.GetResultVariableName()}, ""{parameter.Name}"", stringEnumerator, {isLastString});
if ({parameter.GetErrorVariableName()} is not null)
            $"{Constants.HelperClass}.ParseEnum{typeSymbol?.ToString().TrimEnd('?').Replace('.', '_')}(typeConverter, stringEnumerator);"
        );
        return null;
    }

    /// <inheritdoc />
    public void GenerateHelperMethods(IndentedTextWriter textWriter)
    {
        foreach (var type in _enumTypes)
        {
            var underlyingType = ((INamedTypeSymbol)type).EnumUnderlyingType!.ToString();
            textWriter.WriteMultiline
            (
                $@"
public static Result<{type}?> ParseEnum{type.ToString().Replace('.', '_')}(ITypeConverter typeConverter, PacketStringEnumerator stringEnumerator)
{{
    return Result<{packet.Name}?>.FromError({parameter.GetErrorVariableName()}, {parameter.GetResultVariableName()});
}}
{parameter.GetVariableName()} = default;
{underlyingType} {parameter.GetVariableName()}Underlying = default;
{parameter.GetNullableType()} {parameter.GetNullableVariableName()};
if ({parameter.GetResultVariableName()}.Entity.Token == ""-"") {{
    {parameter.GetNullableVariableName()} = null;
}}
else if (!{underlyingType}.TryParse({parameter.GetResultVariableName()}.Entity.Token, out {parameter.GetVariableName()}Underlying)) {{
    return new PacketParameterSerializerError(this, ""{parameter.Name}"", {parameter.GetResultVariableName()}, $""Could not convert {{{parameter.GetResultVariableName()}.Entity.Token}} as {underlyingType} in inline converter"");
    var tokenResult = stringEnumerator.GetNextToken();
    if (!tokenResult.IsSuccess)
    {{
        return Result<{type}?>.FromError(tokenResult);
    }}

    var token = tokenResult.Entity.Token;
    if (token == ""-"")
    {{
        return Result<{type}?>.FromSuccess(null);
    }}

    if (!{underlyingType}.TryParse(token, out var val))
    {{
        return new CouldNotConvertError(typeConverter, token, ""Could not convert as {type} in inline converter"");
    }}

    return ({type}?)val;
}}
{parameter.GetNullableVariableName()} = ({parameter.GetNullableType()}){parameter.GetVariableName()}Underlying;
"
        );
        return null;
            );
        }
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs +30 -25
@@ 5,9 5,9 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CodeDom.Compiler;
using NosSmooth.PacketSerializersGenerator.Data;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;

namespace NosSmooth.PacketSerializersGenerator.InlineConverterGenerators;



@@ 15,42 15,47 @@ namespace NosSmooth.PacketSerializersGenerator.InlineConverterGenerators;
public class FallbackInlineConverterGenerator : IInlineConverterGenerator
{
    /// <inheritdoc />
    public bool ShouldHandle(ParameterInfo parameter)
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        return true;
    }

    /// <inheritdoc />
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? GenerateSerializerPart
    (
        IndentedTextWriter textWriter,
        string variableName,
        TypeSyntax? typeSyntax,
        ITypeSymbol? typeSymbol
    )
    {
        var parameter = packet.Parameters.Current;
        textWriter.WriteMultiline
        var resultName = $"{variableName.Replace(".", string.Empty)}Result";
        textWriter.WriteLine
        (
            $@"
var {parameter.GetResultVariableName()} = _typeConverterRepository.Serialize<{parameter.GetActualType()}>(obj.{parameter.Name}, builder);
if (!{parameter.GetResultVariableName()}.IsSuccess)
{{
    return Result.FromError(new PacketParameterSerializerError(this, ""{parameter.Name}"", {parameter.GetResultVariableName()}), {parameter.GetResultVariableName()});
}}
"
            $"var {resultName} = _typeConverterRepository.Serialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>({variableName}, builder);"
        );
        textWriter.WriteLine($"if (!{resultName}.IsSuccess)");
        textWriter.WriteLine("{");
        textWriter.Indent++;
        textWriter.WriteLine($"return Result.FromError(new PacketParameterSerializerError(this, \"{variableName}\", {resultName}), {resultName});");
        textWriter.Indent--;
        textWriter.WriteLine("}");
        return null;
    }

    /// <inheritdoc />
    public IError? GenerateDeserializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        string isLastString = packet.Parameters.IsLast ? "true" : "false";
        textWriter.WriteMultiline($@"
var {parameter.GetResultVariableName()} = _typeConverterRepository.Deserialize<{parameter.GetNullableType()}>(stringEnumerator);
var {parameter.GetErrorVariableName()} = CheckDeserializationResult({parameter.GetResultVariableName()}, ""{parameter.Name}"", stringEnumerator, {isLastString});
if ({parameter.GetErrorVariableName()} is not null)
{{
    return Result<{packet.Name}?>.FromError({parameter.GetErrorVariableName()}, {parameter.GetResultVariableName()});
}}
var {parameter.GetNullableVariableName()} = {parameter.GetResultVariableName()}.Entity;
");
        textWriter.WriteLine
        (
            $"_typeConverterRepository.Deserialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>(stringEnumerator);"
        );
        return null;
    }

    /// <inheritdoc />
    public void GenerateHelperMethods(IndentedTextWriter textWriter)
    {
        // ignore
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/IInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/IInlineConverterGenerator.cs +25 -7
@@ 5,6 5,8 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;



@@ 18,23 20,39 @@ public interface IInlineConverterGenerator
    /// <summary>
    /// Whether the given parameter should be handled by this.
    /// </summary>
    /// <param name="parameter">The parameter.</param>
    /// <returns>Whethet to handle.</returns>
    public bool ShouldHandle(ParameterInfo parameter);
    /// <param name="typeSyntax">The type syntax.</param>
    /// <param name="typeSymbol">The type symbol.</param>
    /// <returns>Whether to handle.</returns>
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol);

    /// <summary>
    /// Generate the serializer part.
    /// </summary>
    /// <param name="textWriter">The text writer to write to.</param>
    /// <param name="packet">The packet.</param>
    /// <param name="variableName">The name of the variable.</param>
    /// <param name="typeSyntax">The type syntax.</param>
    /// <param name="typeSymbol">The type symbol.</param>
    /// <returns>An error, if any.</returns>
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, PacketInfo packet);
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, string variableName, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol);

    /// <summary>
    /// Generate the deserializer part.
    /// </summary>
    /// <param name="textWriter">The text writer to write to.</param>
    /// <param name="packet">The packet.</param>
    /// <param name="typeSyntax">The type syntax.</param>
    /// <param name="typeSymbol">The type symbol.</param>
    /// <returns>An error, if any.</returns>
    public IError? GenerateDeserializerPart(IndentedTextWriter textWriter, PacketInfo packet);
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol);

    /// <summary>
    /// Generate helper methods to HelperClass.
    /// </summary>
    /// <remarks>
    /// These will be added to class <see cref="Constants.HelperClass"/>.
    /// This will be called after calling <see cref="CallDeserialize"/> and
    /// <see cref="GenerateSerializerPart"/> for all packets and parameters.
    /// The converter can group information it needs for the generations that way.
    /// </remarks>
    /// <param name="textWriter">The text writer to append full methods to.</param>
    public void GenerateHelperMethods(IndentedTextWriter textWriter);
}
\ No newline at end of file

A Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/ListInlineConverterGenerator.cs +162 -0
@@ 0,0 1,162 @@
//
//  ListInlineConverterGenerator.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.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;

namespace NosSmooth.PacketSerializersGenerator.InlineConverterGenerators;

/// <inheritdoc/>
public class ListInlineConverterGenerator : IInlineConverterGenerator
{
    private readonly InlineTypeConverterGenerator _inlineConverters;
    private readonly List<ITypeSymbol> _listTypes;

    /// <summary>
    /// Initializes a new instance of the <see cref="ListInlineConverterGenerator"/> class.
    /// </summary>
    /// <param name="inlineConverters">The inline converter that is used for converting the values of the list.</param>
    public ListInlineConverterGenerator(InlineTypeConverterGenerator inlineConverters)
    {
        _inlineConverters = inlineConverters;
        _listTypes = new List<ITypeSymbol>();
    }

    /// <inheritdoc />
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
        => typeSymbol?.Name == "IReadOnlyList";

    /// <inheritdoc />
    public IError? GenerateSerializerPart
    (
        IndentedTextWriter textWriter,
        string variableName,
        TypeSyntax? typeSyntax,
        ITypeSymbol? typeSymbol
    )
    {
        ITypeSymbol genericArgument = ((INamedTypeSymbol)typeSymbol!).TypeArguments[0];
        textWriter.WriteMultiline
        (
            @$"
        if ({variableName} is null)
        {{
            builder.Append('-');
        }}
        else
        {{
            foreach (var item in {variableName})
            {{
                if (!builder.PushPreparedLevel())
                {{
                    return new ArgumentInvalidError(nameof(builder), ""The string builder has to have a prepared level for all lists."");
                }}

"
        );
        var error = _inlineConverters.Serialize(textWriter, "item", null, genericArgument);
        if (error is not null)
        {
            return error;
        }

        textWriter.WriteMultiline(@"
                builder.PopLevel();
            }
        }
"
        );
        return null;
    }

    /// <inheritdoc />
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        ITypeSymbol genericArgument = ((INamedTypeSymbol)typeSymbol!).TypeArguments[0];
        if (_listTypes.All
            (
                x => x.ToString() != genericArgument!.ToString()
                    || ((x.IsNullable() ?? false) && (!genericArgument.IsNullable() ?? false))
            ))
        {
            _listTypes.Add(genericArgument!);
        }

        textWriter.WriteLine
            ($"{Constants.HelperClass}.{GetMethodName(genericArgument)}(typeConverter, _typeConverterRepository, stringEnumerator);");
        return null;
    }

    private string GetMethodName(ITypeSymbol genericArgumentType)
    {
        return
            $"ParseList{genericArgumentType.ToString().Replace('.', '_')}{((genericArgumentType.IsNullable() ?? false) ? "Nullable" : string.Empty)}";
    }

    /// <inheritdoc />
    public void GenerateHelperMethods(IndentedTextWriter textWriter)
    {
        foreach (var type in _listTypes)
        {
            textWriter.WriteLine
            (
                @$"
public static Result<IReadOnlyList<{type.GetActualType()}>> {GetMethodName(type)}(ITypeConverter typeConverter, ITypeConverterRepository _typeConverterRepository, PacketStringEnumerator stringEnumerator)
{{
    var data = new List<{type.GetActualType()}>();

    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 = "
            );
            /*var error = */_inlineConverters.CallDeserialize(textWriter, null, type); // TODO handle error

            textWriter.WriteMultiline(@$"

        // 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<IReadOnlyList<{type}>>.FromError(new ListSerializerError(result, data.Count), result);
        }}

"
            );

            if (!(type.IsNullable() ?? false))
            {
                textWriter.WriteMultiline
                (
                    $@"
if (result.Entity is null)
{{
    return new DeserializedValueNullError(typeof({type.ToString().TrimEnd('?')}));
}}
"
                );
            }
            textWriter.WriteLine($"data.Add(({type.GetActualType()})result.Entity);");
            textWriter.WriteLine("}");
            textWriter.WriteLine("return data;");
            textWriter.WriteLine("}");
        }
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/StringInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/StringInlineConverterGenerator.cs +27 -16
@@ 5,6 5,8 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;


@@ 15,31 17,40 @@ namespace NosSmooth.PacketSerializersGenerator.InlineConverterGenerators;
public class StringInlineConverterGenerator : IInlineConverterGenerator
{
    /// <inheritdoc />
    public bool ShouldHandle(ParameterInfo parameter)
        => parameter.Parameter.Type!.ToString() == "string";
    public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
        => typeSyntax?.ToString().TrimEnd('?') == "string" || typeSymbol?.ToString().TrimEnd('?') == "string";

    /// <inheritdoc />
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? GenerateSerializerPart(IndentedTextWriter textWriter, string variableName, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        textWriter.WriteLine($"builder.Append(obj.{parameter.Name} ?? \"-\");");
        textWriter.WriteLine($"builder.Append({variableName} ?? \"-\");");
        return null;
    }

    /// <inheritdoc />
    public IError? GenerateDeserializerPart(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
    {
        var parameter = packet.Parameters.Current;
        string isLastString = packet.Parameters.IsLast ? "true" : "false";
        textWriter.WriteMultiline($@"
var {parameter.GetResultVariableName()} = stringEnumerator.GetNextToken();
var {parameter.GetErrorVariableName()} = CheckDeserializationResult({parameter.GetResultVariableName()}, ""{parameter.Name}"", stringEnumerator, {isLastString});
if ({parameter.GetErrorVariableName()} is not null)
        textWriter.WriteLine($"{Constants.HelperClass}.ParseString(stringEnumerator);");
        return null;
    }

    /// <inheritdoc />
    public void GenerateHelperMethods(IndentedTextWriter textWriter)
    {
        textWriter.WriteLine
        (
            @"
public static Result<string?> ParseString(PacketStringEnumerator stringEnumerator)
{{
    return Result<{packet.Name}?>.FromError({parameter.GetErrorVariableName()}, {parameter.GetResultVariableName()});
    var tokenResult = stringEnumerator.GetNextToken();
    if (!tokenResult.IsSuccess)
    {{
        return Result<string?>.FromError(tokenResult);
    }}

    return tokenResult.Entity.Token;
}}
var {parameter.GetNullableVariableName()} = {parameter.GetResultVariableName()}.Entity.Token == ""-"" ? null : {parameter.GetResultVariableName()}.Entity.Token;
");
        return null;
"
        );
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/InlineTypeConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineTypeConverterGenerator.cs +71 -10
@@ 5,6 5,8 @@
//  Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CodeDom.Compiler;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;


@@ 32,48 34,107 @@ public class InlineTypeConverterGenerator
    }

    /// <summary>
    /// Generates deserialize and check code.
    /// Generates deserialize call.
    /// </summary>
    /// <param name="textWriter">The text writer.</param>
    /// <param name="packet">The packet.</param>
    /// <returns>An error, if any.</returns>
    public IError? DeserializeAndCheck(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? CallDeserialize(IndentedTextWriter textWriter, PacketInfo packet)
    {
        var parameter = packet.Parameters.Current;
        var shouldGenerateInline = packet.GenerateAttribute.GetIndexedValue<bool>(0);
        if (shouldGenerateInline)
        {
            foreach (var generator in _typeGenerators)
            {
                if (generator.ShouldHandle(packet.Parameters.Current))
                if (generator.ShouldHandle(parameter.Parameter.Type, parameter.Type))
                {
                    return generator.GenerateDeserializerPart(textWriter, packet);
                    return generator.CallDeserialize(textWriter, parameter.Parameter.Type, parameter.Type);
                }
            }
        }

        return _fallbackInlineConverterGenerator.GenerateDeserializerPart(textWriter, packet);
        return _fallbackInlineConverterGenerator.CallDeserialize(textWriter, parameter.Parameter.Type, parameter.Type);
    }

    /// <summary>
    /// Generates serialize and check code.
    /// Generates deserialize call.
    /// </summary>
    /// <param name="textWriter">The text writer.</param>
    /// <param name="typeSyntax">The type syntax.</param>
    /// <param name="typeSymbol">The type symbol.</param>
    /// <returns>An error, if any.</returns>
    public IError? CallDeserialize
    (
        IndentedTextWriter textWriter,
        TypeSyntax? typeSyntax,
        ITypeSymbol? typeSymbol
    )
    {
        foreach (var generator in _typeGenerators)
        {
            if (generator.ShouldHandle(typeSyntax, typeSymbol))
            {
                return generator.CallDeserialize(textWriter, typeSyntax, typeSymbol);
            }
        }

        return _fallbackInlineConverterGenerator.CallDeserialize(textWriter, typeSyntax, typeSymbol);
    }

    /// <summary>
    /// Generates serialize code.
    /// </summary>
    /// <param name="textWriter">The text writer.</param>
    /// <param name="packet">The packet.</param>
    /// <returns>An error, if any.</returns>
    public IError? SerializeAndCheck(IndentedTextWriter textWriter, PacketInfo packet)
    public IError? Serialize(IndentedTextWriter textWriter, PacketInfo packet)
    {
        var parameter = packet.Parameters.Current;
        var variableName = "obj." + parameter.Name;
        var shouldGenerateInline = packet.GenerateAttribute.GetIndexedValue<bool>(0);
        if (shouldGenerateInline)
        {
            foreach (var generator in _typeGenerators)
            {
                if (generator.ShouldHandle(packet.Parameters.Current))
                if (generator.ShouldHandle(parameter.Parameter.Type, parameter.Type))
                {
                    return generator.GenerateSerializerPart(textWriter, packet);
                    return generator.GenerateSerializerPart
                        (textWriter, variableName, parameter.Parameter.Type, parameter.Type);
                }
            }
        }

        return _fallbackInlineConverterGenerator.GenerateSerializerPart(textWriter, packet);
        return _fallbackInlineConverterGenerator.GenerateSerializerPart
            (textWriter, variableName, parameter.Parameter.Type, parameter.Type);
    }

    /// <summary>
    /// Generates serialize code.
    /// </summary>
    /// <param name="textWriter">The text writer.</param>
    /// <param name="variableName">The name of the variable.</param>
    /// <param name="typeSyntax">The type syntax.</param>
    /// <param name="typeSymbol">The type symbol.</param>
    /// <returns>An error, if any.</returns>
    public IError? Serialize
    (
        IndentedTextWriter textWriter,
        string variableName,
        TypeSyntax? typeSyntax,
        ITypeSymbol? typeSymbol
    )
    {
        foreach (var generator in _typeGenerators)
        {
            if (generator.ShouldHandle(typeSyntax, typeSymbol))
            {
                return generator.GenerateSerializerPart
                    (textWriter, variableName, typeSyntax, typeSymbol);
            }
        }

        return _fallbackInlineConverterGenerator.GenerateSerializerPart
            (textWriter, variableName, typeSyntax, typeSymbol);
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs +1 -10
@@ 97,6 97,7 @@ public override Result Serialize({_packetInfo.Name}? obj, PacketStringBuilder bu
/// <inheritdoc />
public override Result<{_packetInfo.Name}?> Deserialize(PacketStringEnumerator stringEnumerator)
{{
    var typeConverter = this;
"
        );



@@ 112,16 113,6 @@ public override Result<{_packetInfo.Name}?> Deserialize(PacketStringEnumerator s
        (
            $@"
    }}

private IResultError? CheckDeserializationResult<T>(Result<T> result, string property, PacketStringEnumerator stringEnumerator, bool last = false)
{{
    if (!result.IsSuccess)
    {{
        return new PacketParameterSerializerError(this, property, result);
    }}

    return null;
}}
}}"
        );
        return null;

M Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs +39 -3
@@ 32,7 32,7 @@ public class SourceGenerator : ISourceGenerator
    /// </summary>
    public SourceGenerator()
    {
        var typeGenerators = new List<IInlineConverterGenerator>
        _typeConverterGenerator = new List<IInlineConverterGenerator>
        (
            new IInlineConverterGenerator[]
            {


@@ 42,8 42,8 @@ public class SourceGenerator : ISourceGenerator
                new BoolInlineConverterGenerator(),
            }
        );

        var inlineTypeConverter = new InlineTypeConverterGenerator(typeGenerators);
        var inlineTypeConverter = new InlineTypeConverterGenerator(_typeConverterGenerator);
        _typeConverterGenerator.Add(new ListInlineConverterGenerator(inlineTypeConverter));

        _generators = new List<IParameterGenerator>
        (


@@ 59,6 59,7 @@ public class SourceGenerator : ISourceGenerator
    }

    private readonly List<IParameterGenerator> _generators;
    private readonly List<IInlineConverterGenerator> _typeConverterGenerator;

    /// <inheritdoc />
    public void Initialize(GeneratorInitializationContext context)


@@ 131,6 132,41 @@ public class SourceGenerator : ISourceGenerator
                );
            }
        }

        var helperClass = GenerateHelperMethods();
        context.AddSource
        (
            $"HelperClass.g.cs",
            helperClass
        );
    }

    private string GenerateHelperMethods()
    {
        using var stringWriter = new StringWriter();
        using var writer = new IndentedTextWriter(stringWriter, "    ");
        writer.WriteMultiline(@"// <auto-generated/>
#nullable enable
#pragma warning disable 1591

using System.Collections;
using System.Collections.Generic;
using NosSmooth.Packets.Converters;
using NosSmooth.Packets.Errors;
using NosSmooth.Packets;
using Remora.Results;

namespace NosSmooth.Generated;

internal static class HelperClass
{
");
        foreach (var inlineHelperGenerator in _typeConverterGenerator)
        {
            inlineHelperGenerator.GenerateHelperMethods(writer);
        }
        writer.WriteLine("}");
        return stringWriter.GetStringBuilder().ToString();
    }

    private IError? GeneratePacketSerializer

M Core/NosSmooth.Packets/Converters/Basic/BoolTypeConverter.cs => Core/NosSmooth.Packets/Converters/Basic/BoolTypeConverter.cs +1 -1
@@ 17,7 17,7 @@ public class BoolTypeConverter : BaseTypeConverter<bool>
    /// <inheritdoc />
    public override Result Serialize(bool obj, PacketStringBuilder builder)
    {
        builder.Append(obj ? "1" : "0");
        builder.Append(obj ? '1' : '0');
        return Result.FromSuccess();
    }


M Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs => Core/NosSmooth.Packets/Converters/Common/NameStringConverter.cs +1 -1
@@ 20,7 20,7 @@ public class NameStringConverter : BaseTypeConverter<NameString>
    {
        if (obj is null)
        {
            builder.Append("-");
            builder.Append('-');
            return Result.FromSuccess();
        }


M Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs => Core/NosSmooth.Packets/Converters/Packets/UpgradeRareSubPacketConverter.cs +1 -1
@@ 20,7 20,7 @@ public class UpgradeRareSubPacketConverter : BaseTypeConverter<UpgradeRareSubPac
    {
        if (obj is null)
        {
            builder.Append("-");
            builder.Append('-');
            return Result.FromSuccess();
        }
        builder.Append($"{obj.Upgrade}{obj.Rare}");

M Core/NosSmooth.Packets/Converters/Special/EnumTypeConverter.cs => Core/NosSmooth.Packets/Converters/Special/EnumTypeConverter.cs +2 -2
@@ 35,11 35,11 @@ public class EnumTypeConverter : ISpecialTypeConverter
    {
        if (obj is null)
        {
            builder.Append("-");
            builder.Append('-');
            return Result.FromSuccess();
        }

        builder.Append(Convert.ToInt64(obj).ToString());
        builder.Append(Convert.ToInt64(obj));
        return Result.FromSuccess();
    }
}
\ No newline at end of file

M Core/NosSmooth.Packets/Converters/Special/ListTypeConverter.cs => Core/NosSmooth.Packets/Converters/Special/ListTypeConverter.cs +1 -3
@@ 79,7 79,7 @@ public class ListTypeConverter : ISpecialTypeConverter
    {
        if (obj is null)
        {
            builder.Append("-");
            builder.Append('-');
            return Result.FromSuccess();
        }



@@ 94,14 94,12 @@ public class ListTypeConverter : ISpecialTypeConverter
            }

            var serializeResult = _typeConverterRepository.Serialize(genericType, item, builder);
            builder.ReplaceWithParentSeparator();
            builder.PopLevel();
            if (!serializeResult.IsSuccess)
            {
                return serializeResult;
            }
        }
        builder.ReplaceWithParentSeparator();

        return Result.FromSuccess();
    }

M Core/NosSmooth.Packets/PacketStringBuilder.cs => Core/NosSmooth.Packets/PacketStringBuilder.cs +143 -1
@@ 4,6 4,7 @@
//  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.Generic;
using System.Text;
using Remora.Results;


@@ 121,13 122,154 @@ public class PacketStringBuilder
    /// <param name="value">The value to append.</param>
    public void Append(string value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(int value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(uint value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(short value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(char value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(ushort value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(long value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(ulong value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(byte value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(sbyte value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(float value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(double value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    /// <summary>
    /// Appends the value to the string.
    /// </summary>
    /// <param name="value">The value to append.</param>
    public void Append(decimal value)
    {
        BeforeAppend();
        _builder.Append(value);
        AfterAppend();
    }

    private void BeforeAppend()
    {
        if (_insertSeparator is not null)
        {
            _builder.Append(_insertSeparator);
            _insertSeparator = null;
        }
    }

        _builder.Append(value);
    private void AfterAppend()
    {
        _insertSeparator = _currentLevel.SeparatorOnce ?? _currentLevel.Separator;
        _currentLevel.SeparatorOnce = null;
    }

Do not follow this link