//
// PacketConverterGenerator.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.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.AttributeGenerators;
using NosSmooth.PacketSerializersGenerator.Data;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;
namespace NosSmooth.PacketSerializersGenerator;
///
/// Code generator of a packet converter.
///
public class PacketConverterGenerator
{
private readonly PacketInfo _packetInfo;
private readonly IReadOnlyList _parameterGenerators;
///
/// Initializes a new instance of the class.
///
/// The packet type information.
/// The converter parameter generators.
public PacketConverterGenerator(PacketInfo packetInfo, IReadOnlyList parameterGenerators)
{
_packetInfo = packetInfo;
_parameterGenerators = parameterGenerators;
}
///
/// Generate the converter class.
///
/// The text writer to write the class to.
/// An error, if any.
public IError? Generate(IndentedTextWriter textWriter)
{
var usings = _packetInfo.PacketRecord.SyntaxTree.GetRoot()
.DescendantNodes()
.OfType()
.Select(x => x.ToString())
.ToList();
usings.Add($"using {_packetInfo.Namespace};");
usings.Add("using NosSmooth.PacketSerializer.Abstractions.Errors;");
usings.Add("using NosSmooth.PacketSerializer.Abstractions;");
usings.Add("using Remora.Results;");
var usingsString = string.Join("\n", usings.Distinct());
textWriter.WriteLine
(
@$"//
#nullable enable
#pragma warning disable 1591
{usingsString}
namespace {_packetInfo.Namespace}.Generated;
public class {_packetInfo.Name}Converter : BaseStringConverter<{_packetInfo.Name}>
{{"
);
textWriter.Indent++;
textWriter.WriteLine
(
$@"
private readonly IStringSerializer _stringSerializer;
public {_packetInfo.Name}Converter(IStringSerializer stringSerializer)
{{
_stringSerializer = stringSerializer;
}}
///
public override Result Serialize({_packetInfo.Name}? obj, ref PacketStringBuilder builder)
{{
if (obj is null)
{{
return new ArgumentNullError(nameof(obj));
}}
"
);
textWriter.Indent++;
var serializerError = GenerateSerializer(textWriter);
if (serializerError is not null)
{
return serializerError;
}
textWriter.Indent--;
textWriter.WriteLine
(
$@"
}}
///
public override Result<{_packetInfo.Name}?> Deserialize(ref PacketStringEnumerator stringEnumerator, DeserializeOptions options)
{{
var typeConverter = this;
"
);
textWriter.Indent++;
var deserializerError = GenerateDeserializer(textWriter);
if (deserializerError is not null)
{
return deserializerError;
}
textWriter.Indent--;
textWriter.WriteLine
(
$@"
}}
}}"
);
return null;
}
private IError? GenerateSerializer(IndentedTextWriter textWriter)
{
_packetInfo.Parameters.CurrentIndex = 0;
foreach (var parameter in _packetInfo.Parameters.List)
{
bool handled = false;
foreach (var generator in _parameterGenerators)
{
if (generator.ShouldHandle(parameter))
{
var checkError = generator.CheckParameter(_packetInfo, parameter);
if (checkError is not null)
{
return checkError;
}
var serializerError = generator.GenerateSerializerPart(textWriter, _packetInfo);
if (serializerError is not null)
{
return serializerError;
}
handled = true;
break;
}
}
if (!handled)
{
throw new InvalidOperationException
(
$"Could not handle {_packetInfo.Namespace}.{_packetInfo.Name}.{parameter.Name}"
);
}
_packetInfo.Parameters.CurrentIndex++;
}
textWriter.WriteLine("return Result.FromSuccess();");
return null;
}
private IError? GenerateDeserializer
(IndentedTextWriter textWriter)
{
if (_packetInfo.Parameters.List.Count == 0)
{
textWriter.WriteLine
($"return new {_packetInfo.Name}();");
return null;
}
_packetInfo.Parameters.CurrentIndex = 0;
var lastIndex = _packetInfo.Parameters.Current.PacketIndex - 1;
bool skipped = false;
foreach (var parameter in _packetInfo.Parameters.List)
{
var skip = parameter.PacketIndex - lastIndex - 1;
if (skip > 0)
{
if (!skipped)
{
textWriter.WriteLine("Result skipResult;");
textWriter.WriteLine("IResultError? skipError;");
skipped = true;
}
textWriter.WriteLine($@"skipResult = stringEnumerator.GetNextToken(out _);");
textWriter.WriteLine
("skipError = CheckDeserializationResult(result, \"None\", stringEnumerator, false);");
textWriter.WriteMultiline
(
$@"if (skipError is not null) {{
return Result<{_packetInfo.Name}?>.FromError(skipError, skipResult);
}}"
);
}
else if (skip < 0)
{
return new DiagnosticError
(
"SGInd",
"Same packet index",
"There were two parameters of the same packet index {0} on property {1} in packet {2}, that is not supported.",
parameter.Attributes.First().Attribute.SyntaxTree,
parameter.Attributes.First().Attribute.FullSpan,
new List