//
// PacketSerializerGenerator.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 System.Diagnostics;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.AttributeGenerators;
using NosSmooth.PacketSerializersGenerator.Errors;
using NosSmooth.PacketSerializersGenerator.Extensions;
namespace NosSmooth.PacketSerializersGenerator;
///
/// Generates ITypeGenerator for packets that are marked with NosSmooth.Packets.Attributes.GenerateSerializerAttribute.
///
///
/// The packets to create serializer for have to be records that specify PacketIndices in the constructor.
///
[Generator]
public class PacketSerializerGenerator : ISourceGenerator
{
///
/// Initializes a new instance of the class.
///
public PacketSerializerGenerator()
{
_generators = new List(new IParameterGenerator[]
{
new PacketIndexAttributeGenerator(),
new PacketListIndexAttributeGenerator(),
new PacketContextListAttributeGenerator(),
});
}
private readonly List _generators;
///
public void Initialize(GeneratorInitializationContext context)
{
context.RegisterForSyntaxNotifications(() => new PacketClassReceiver());
}
///
public void Execute(GeneratorExecutionContext context)
{
var syntaxReceiver = (PacketClassReceiver)context.SyntaxReceiver!;
foreach (var packetClass in syntaxReceiver.PacketClasses)
{
if (packetClass is not null)
{
using var stringWriter = new StringWriter();
using var writer = new IndentedTextWriter(stringWriter, " ");
var generatedResult = GeneratePacketSerializer(writer, context.Compilation, packetClass);
if (generatedResult is not null)
{
if (generatedResult is DiagnosticError diagnosticError)
{
context.ReportDiagnostic(Diagnostic.Create
(
new DiagnosticDescriptor
(
diagnosticError.Id,
diagnosticError.Title,
diagnosticError.MessageFormat,
"Serialization",
DiagnosticSeverity.Error,
true
),
Location.Create(diagnosticError.Tree, diagnosticError.Span),
diagnosticError.Parameters.ToArray()
)
);
}
else if (generatedResult is not null)
{
throw new Exception(generatedResult.Message);
}
continue;
}
context.AddSource($"{packetClass.Identifier.NormalizeWhitespace().ToFullString()}Converter.g.cs", stringWriter.GetStringBuilder().ToString());
}
}
}
private IError? GeneratePacketSerializer(IndentedTextWriter textWriter, Compilation compilation, RecordDeclarationSyntax packetClass)
{
var semanticModel = compilation.GetSemanticModel(packetClass.SyntaxTree);
var name = packetClass.Identifier.NormalizeWhitespace().ToFullString();
var @namespace = packetClass.GetPrefix();
var constructor = (ParameterListSyntax?)packetClass.ChildNodes()
.FirstOrDefault(x => x.IsKind(SyntaxKind.ParameterList));
if (constructor is null)
{
return new DiagnosticError(
"SG0001",
"Packet without constructor",
"The packet class {0} does not have any constructors to use for packet serializer.",
packetClass.SyntaxTree,
packetClass.FullSpan,
new List