~ruther/NosSmooth

ce9e26d5b40e21a9a4f9432f4881c2625b8e0ba4 — František Boháček 3 years ago 6181c74
feat: add support for optional parameters
M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketContextListAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketContextListAttributeGenerator.cs +30 -0
@@ 33,6 33,13 @@ public class PacketContextListAttributeGenerator : IParameterGenerator
        var parameter = packetInfo.Parameters.Current;
        var attribute = parameter.Attributes.First(x => x.FullName == PacketListIndexAttributeFullName);

        if (parameter.IsOptional())
        {
            textWriter.WriteLine($"if (obj.{parameter.GetVariableName()} is not null)");
            textWriter.WriteLine("{");
            textWriter.Indent++;
        }

        var afterSeparator = attribute.GetNamedValue<char?>("AfterSeparator", null);
        if (afterSeparator is not null)
        {


@@ 49,6 56,13 @@ public class PacketContextListAttributeGenerator : IParameterGenerator
        generator.RemovePreparedLevel();
        generator.PopLevel();

        // end optional if
        if (parameter.IsOptional())
        {
            textWriter.Indent--;
            textWriter.WriteLine("}");
        }

        return null;
    }



@@ 59,6 73,16 @@ public class PacketContextListAttributeGenerator : IParameterGenerator
        var parameter = packetInfo.Parameters.Current;
        var attribute = parameter.Attributes.First(x => x.FullName == PacketListIndexAttributeFullName);

        // add optional if
        if (parameter.IsOptional())
        {
            var error = generator.StartOptionalCheck(parameter, packetInfo.Name);
            if (error is not null)
            {
                return error;
            }
        }

        var afterSeparator = attribute.GetNamedValue<char?>("AfterSeparator", null);
        if (afterSeparator is not null)
        {


@@ 82,6 106,12 @@ public class PacketContextListAttributeGenerator : IParameterGenerator

        generator.AssignLocalVariable(parameter);

        // end is last token if body
        if (parameter.IsOptional())
        {
            generator.EndOptionalCheck(parameter);
        }

        return null;
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketIndexAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketIndexAttributeGenerator.cs +39 -2
@@ 31,12 31,21 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
        var parameter = packetInfo.Parameters.Current;
        var attribute = parameter.Attributes.First(x => x.FullName == PacketIndexAttributeFullName);

        if (parameter.IsOptional())
        {
            textWriter.WriteLine($"if (obj.{parameter.GetVariableName()} is not null)");
            textWriter.WriteLine("{");
            textWriter.Indent++;
        }

        // register after separator
        var afterSeparator = attribute.GetNamedValue<char?>("AfterSeparator", null);
        if (afterSeparator is not null)
        {
            generator.SetAfterSeparatorOnce((char)afterSeparator);
        }

        // push inner separator level
        var innerSeparator = attribute.GetNamedValue<char?>("InnerSeparator", null);
        if (innerSeparator is not null)
        {


@@ 44,13 53,22 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
            pushedLevel = true;
        }

        // serialize, check the error.
        generator.SerializeAndCheck(parameter);

        // pop inner separator level
        if (pushedLevel)
        {
            generator.PopLevel();
        }

        // end optional if
        if (parameter.IsOptional())
        {
            textWriter.Indent--;
            textWriter.WriteLine("}");
        }

        return null;
    }



@@ 62,6 80,18 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
        var parameter = packetInfo.Parameters.Current;
        var attribute = parameter.Attributes.First();

        generator.DeclareLocalVariable(parameter);

        // add optional if
        if (parameter.IsOptional())
        {
            var error = generator.StartOptionalCheck(parameter, packetInfo.Name);
            if (error is not null)
            {
                return error;
            }
        }

        var afterSeparator = attribute.GetNamedValue<char?>("AfterSeparator", null);
        if (afterSeparator is not null)
        {


@@ 75,14 105,15 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
            pushedLevel = true;
        }

        generator.DeserializeAndCheck($"{packetInfo.Namespace}.{packetInfo.Name}", parameter, packetInfo.Parameters.IsLast);
        generator.DeserializeAndCheck
            ($"{packetInfo.Namespace}.{packetInfo.Name}", parameter, packetInfo.Parameters.IsLast);

        if (!parameter.Nullable)
        {
            generator.CheckNullError(parameter.GetResultVariableName(), parameter.Name);
        }

        generator.AssignLocalVariable(parameter);
        generator.AssignLocalVariable(parameter, false);

        if (pushedLevel)
        {


@@ 90,6 121,12 @@ public class PacketIndexAttributeGenerator : IParameterGenerator
            generator.PopLevel();
        }

        // end is last token if body
        if (parameter.IsOptional())
        {
            generator.EndOptionalCheck(parameter);
        }

        return null;
    }
}
\ No newline at end of file

M Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketListIndexAttributeGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/AttributeGenerators/PacketListIndexAttributeGenerator.cs +33 -1
@@ 35,6 35,13 @@ public class PacketListIndexAttributeGenerator : IParameterGenerator
        var parameter = packetInfo.Parameters.Current;
        var attribute = parameter.Attributes.First(x => x.FullName == PacketListIndexAttributeFullName);

        if (parameter.IsOptional())
        {
            textWriter.WriteLine($"if (obj.{parameter.GetVariableName()} is not null)");
            textWriter.WriteLine("{");
            textWriter.Indent++;
        }

        var afterSeparator = attribute.GetNamedValue<char?>("AfterSeparator", null);
        if (afterSeparator is not null)
        {


@@ 51,6 58,13 @@ public class PacketListIndexAttributeGenerator : IParameterGenerator
        generator.RemovePreparedLevel();
        generator.PopLevel();

        // end optional if
        if (parameter.IsOptional())
        {
            textWriter.Indent--;
            textWriter.WriteLine("}");
        }

        return null;
    }



@@ 61,6 75,18 @@ public class PacketListIndexAttributeGenerator : IParameterGenerator
        var parameter = packetInfo.Parameters.Current;
        var attribute = parameter.Attributes.First(x => x.FullName == PacketListIndexAttributeFullName);

        generator.DeclareLocalVariable(parameter);

        // add optional if
        if (parameter.IsOptional())
        {
            var error = generator.StartOptionalCheck(parameter, packetInfo.Name);
            if (error is not null)
            {
                return error;
            }
        }

        var afterSeparator = attribute.GetNamedValue<char?>("AfterSeparator", null);
        if (afterSeparator is not null)
        {


@@ 82,7 108,13 @@ public class PacketListIndexAttributeGenerator : IParameterGenerator
            generator.CheckNullError(parameter.GetResultVariableName(), parameter.Name);
        }

        generator.AssignLocalVariable(parameter);
        generator.AssignLocalVariable(parameter, false);

        // end is last token if body
        if (parameter.IsOptional())
        {
            generator.EndOptionalCheck(parameter);
        }

        return null;
    }

M Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs => Core/NosSmooth.PacketSerializersGenerator/ConverterDeserializationGenerator.cs +67 -2
@@ 6,6 6,7 @@

using System.CodeDom.Compiler;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;

namespace NosSmooth.PacketSerializersGenerator;


@@ 37,6 38,14 @@ public class ConverterDeserializationGenerator
    }

    /// <summary>
    /// Sets that the next token should be read to the last entry in the level.
    /// </summary>
    public void SetReadToLast()
    {
        _textWriter.WriteLine(@$"{_stringEnumeratorVariable}.SetReadToLast();");
    }

    /// <summary>
    /// Push level to the string enumerator.
    /// </summary>
    /// <param name="separator">The separator.</param>


@@ 128,8 137,64 @@ if ({resultVariableName}.Entity is null) {{
    /// Assign local variable with the result of the parameter deserialization.
    /// </summary>
    /// <param name="parameter">The parameter.</param>
    public void AssignLocalVariable(ParameterInfo parameter)
    public void DeclareLocalVariable(ParameterInfo parameter)
    {
        _textWriter.WriteLine($"{parameter.GetActualType()} {parameter.GetVariableName()};");
    }

    /// <summary>
    /// Assign local variable with the result of the parameter deserialization.
    /// </summary>
    /// <param name="parameter">The parameter.</param>
    /// <param name="declare">Whether to also declare the local variable.</param>
    public void AssignLocalVariable(ParameterInfo parameter, bool declare = true)
    {
        _textWriter.WriteLine($"{(declare ? "var " : string.Empty)}{parameter.Name} = ({parameter.GetActualType()}){parameter.GetResultVariableName()}.Entity;");
    }

    /// <summary>
    /// Begins the if for optionals, check if the parameter is not nullable.
    /// </summary>
    /// <param name="parameter">The parameter information.</param>
    /// <param name="packetName">The name of the packet.</param>
    /// <returns>An error, if any.</returns>
    public IError? StartOptionalCheck(ParameterInfo parameter, string packetName)
    {
        _textWriter.WriteLine($"var {parameter.Name} = ({parameter.GetActualType()}){parameter.GetResultVariableName()}.Entity;");
        if (!parameter.Nullable)
        {
            return new DiagnosticError
            (
                "SG0006",
                "Optional parameters must be nullable",
                "The parameter {0} in {1} has to be nullable, because it is optional.",
                parameter.Parameter.SyntaxTree,
                parameter.Parameter.FullSpan,
                new List<object?>(new[] { parameter.Name, packetName })
            );
        }

        // serialize this parameter only if we are not on the last token.
        _textWriter.WriteLine($"if (!(stringEnumerator.IsOnLastToken() ?? true))");
        _textWriter.WriteLine("{");
        _textWriter.Indent++;
        return null;
    }

    /// <summary>
    /// Ends the if for optionals.
    /// </summary>
    /// <param name="parameter">The parameter information.</param>
    public void EndOptionalCheck(ParameterInfo parameter)
    {
        _textWriter.Indent--;
        _textWriter.WriteLine("}");
        _textWriter.WriteLine("else");
        _textWriter.WriteLine("{");
        _textWriter.Indent++;

        _textWriter.WriteLine($"{parameter.GetVariableName()} = null;");

        _textWriter.Indent--;
        _textWriter.WriteLine("}");
    }
}
\ No newline at end of file

M Core/NosSmooth.Packets/Attributes/PacketIndexAttribute.cs => Core/NosSmooth.Packets/Attributes/PacketIndexAttribute.cs +14 -0
@@ 37,4 37,18 @@ public class PacketIndexAttribute : Attribute
    /// Gets the separator after this field.
    /// </summary>
    public char AfterSeparator { get; set; } = (char)0xFF;

    /// <summary>
    /// Gets or sets whether this parameter is optional.
    /// </summary>
    /// <remarks>
    /// Optional attributes have to be nullable,
    /// if the attribute is not in the string,
    /// it will be set to null. For serializer,
    /// if the parameter is null, it will be omitted.
    ///
    /// See <see cref="PacketConditionalIndexAttribute"/> for
    /// more complex decision making about using parameters.
    /// </remarks>
    public bool IsOptional { get; set; } = false;
}
\ No newline at end of file

M Core/NosSmooth.Packets/PacketStringEnumerator.cs => Core/NosSmooth.Packets/PacketStringEnumerator.cs +14 -1
@@ 21,6 21,7 @@ public struct PacketStringEnumerator
    private EnumeratorLevel _currentLevel;
    private (char Separator, uint? MaxTokens)? _preparedLevel;
    private PacketToken? _currentToken;
    private bool _readToLast;

    /// <summary>
    /// Initializes a new instance of the <see cref="PacketStringEnumerator"/> struct.


@@ 35,6 36,7 @@ public struct PacketStringEnumerator
        _numberOfSeparators.Add(separator, 1);
        _currentToken = null;
        _preparedLevel = null;
        _readToLast = false;
    }

    /// <summary>


@@ 52,6 54,7 @@ public struct PacketStringEnumerator
        _numberOfSeparators = new Dictionary<char, ushort>(numberOfSeparators);
        _currentToken = null;
        _preparedLevel = null;
        _readToLast = false;
    }

    /// <summary>


@@ 68,6 71,14 @@ public struct PacketStringEnumerator
    }

    /// <summary>
    /// Sets that the next token should be read to the last entry in the level.
    /// </summary>
    public void SetReadToLast()
    {
        _readToLast = true;
    }

    /// <summary>
    /// Prepare the given level that can be set when needed.
    /// </summary>
    /// <param name="separator">The separator of the prepared level.</param>


@@ 220,7 231,8 @@ public struct PacketStringEnumerator
        bool? isLast, encounteredUpperLevel;

        // If the current character is a separator, stop, else, add it to the builder.
        while (!IsSeparator(currentCharacter, out isLast, out encounteredUpperLevel))
        // If should read to last, then read until isLast is null or true.
        while (!IsSeparator(currentCharacter, out isLast, out encounteredUpperLevel) || (_readToLast && !(isLast ?? true)))
        {
            tokenString.Append(currentCharacter);
            currentIndex++;


@@ 235,6 247,7 @@ public struct PacketStringEnumerator
            currentCharacter = _data.Data[currentIndex];
        }

        _readToLast = false;
        currentIndex++;

        var token = new PacketToken(tokenString.ToString(), isLast, encounteredUpperLevel, _data.ReachedEnd);

Do not follow this link