//
// 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;
///
public class ListInlineConverterGenerator : IInlineConverterGenerator
{
private readonly InlineTypeConverterGenerator _inlineConverters;
private readonly List _listTypes;
///
/// Initializes a new instance of the class.
///
/// The inline converter that is used for converting the values of the list.
public ListInlineConverterGenerator(InlineTypeConverterGenerator inlineConverters)
{
_inlineConverters = inlineConverters;
_listTypes = new List();
}
///
public bool ShouldHandle(TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol)
=> typeSymbol?.Name == "IReadOnlyList";
///
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;
}
///
public IError? CallDeserialize(IndentedTextWriter textWriter, TypeSyntax? typeSyntax, ITypeSymbol? typeSymbol, bool nullable)
{
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, nullable)}(typeConverter, _stringSerializer, ref stringEnumerator);");
return null;
}
private string GetMethodName(ITypeSymbol genericArgumentType, bool nullable)
{
return
$"ParseList{genericArgumentType.ToString().TrimEnd('?').Replace('.', '_').Replace('<', '_').Replace('>', '_')}{((genericArgumentType.IsNullable() ?? false) ? "Nullable" : string.Empty)}{(nullable ? "NullableSupport" : string.Empty)}";
}
private void GenerateHelperMethods(IndentedTextWriter textWriter, bool nullable)
{
foreach (var type in _listTypes)
{
textWriter.WriteLine
(
@$"
public static Result> {GetMethodName(type, nullable)}(IStringConverter typeConverter, IStringSerializer _stringSerializer, ref PacketStringEnumerator stringEnumerator)
{{
var data = new List<{type.GetActualType()}>();
while (!(stringEnumerator.IsOnLastToken() ?? false))
{{
stringEnumerator.CaptureReadTokens();
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, nullable); // 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(out _);
}}
stringEnumerator.PopLevel();
stringEnumerator.IncrementReadTokens();
if (!result.IsSuccess)
{{
return Result>.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("}");
}
}
///
public void GenerateHelperMethods(IndentedTextWriter textWriter)
{
GenerateHelperMethods(textWriter, true);
GenerateHelperMethods(textWriter, false);
}
}