~ruther/NosSmooth

5f2cfd781e31b595256023541771482c6457cf85 — František Boháček 3 years ago 321d3bf
feat: add inline list converter
M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/BasicInlineConverterGenerator.cs +1 -1
@@ 57,7 57,7 @@ public class BasicInlineConverterGenerator : IInlineConverterGenerator
            throw new Exception("TypeSyntax or TypeSymbol has to be non null.");
        }

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


M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/EnumInlineConverterGenerator.cs +1 -1
@@ 66,7 66,7 @@ public class EnumInlineConverterGenerator : IInlineConverterGenerator

        textWriter.WriteLine
        (
            $"{Constants.HelperClass}.ParseEnum{typeSymbol?.ToString().TrimEnd('?').Replace('.', '_')}(this, stringEnumerator);"
            $"{Constants.HelperClass}.ParseEnum{typeSymbol?.ToString().TrimEnd('?').Replace('.', '_')}(typeConverter, stringEnumerator);"
        );
        return null;
    }

M Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineConverterGenerators/FallbackInlineConverterGenerator.cs +8 -1
@@ 29,10 29,17 @@ public class FallbackInlineConverterGenerator : IInlineConverterGenerator
        ITypeSymbol? typeSymbol
    )
    {
        var resultName = $"{variableName.Replace(".", string.Empty)}Result";
        textWriter.WriteLine
        (
            $"_typeConverterRepository.Serialize<{(typeSyntax?.ToString() ?? typeSymbol!.ToString()).TrimEnd('?')}?>({variableName}, builder);"
            $"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;
    }


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/InlineTypeConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/InlineTypeConverterGenerator.cs +62 -4
@@ 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,7 34,7 @@ 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>


@@ 56,7 58,32 @@ public class InlineTypeConverterGenerator
    }

    /// <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>


@@ 72,11 99,42 @@ public class InlineTypeConverterGenerator
            {
                if (generator.ShouldHandle(parameter.Parameter.Type, parameter.Type))
                {
                    return generator.GenerateSerializerPart(textWriter, variableName, parameter.Parameter.Type, parameter.Type);
                    return generator.GenerateSerializerPart
                        (textWriter, variableName, parameter.Parameter.Type, parameter.Type);
                }
            }
        }

        return _fallbackInlineConverterGenerator.GenerateSerializerPart(textWriter, variableName, parameter.Parameter.Type, parameter.Type);
        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/NosSmooth.PacketSerializersGenerator.csproj => Core/NosSmooth.PacketSerializersGenerator/NosSmooth.PacketSerializersGenerator.csproj +0 -4
@@ 19,8 19,4 @@
        <EmbeddedResource Include="$(PkgRemora_Results)\lib\netstandard2.0\*.dll" Visible="false" />
    </ItemGroup>

    <ItemGroup>
      <Folder Include="HelperClassGenerators" />
    </ItemGroup>

</Project>

M Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/PacketConverterGenerator.cs +1 -0
@@ 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;
"
        );


M Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/SourceGenerator.cs +3 -11
@@ 42,8 42,8 @@ public class SourceGenerator : ISourceGenerator
                new BoolInlineConverterGenerator(),
            }
        );

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

        _generators = new List<IParameterGenerator>
        (


@@ 130,11 130,6 @@ public class SourceGenerator : ISourceGenerator
                    $"{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs",
                    stringWriter.GetStringBuilder().ToString()
                );
                File.WriteAllText
                (
                    $"/tmp/{packetRecord.GetPrefix()}.{packetRecord.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs",
                    stringWriter.GetStringBuilder().ToString()
                );
            }
        }



@@ 144,11 139,6 @@ public class SourceGenerator : ISourceGenerator
            $"HelperClass.g.cs",
            helperClass
        );
        File.WriteAllText
        (
            $"/tmp/HelperClass.g.cs",
            helperClass
        );
    }

    private string GenerateHelperMethods()


@@ 159,6 149,8 @@ public class SourceGenerator : ISourceGenerator
#nullable enable
#pragma warning disable 1591

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

Do not follow this link