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);