//
// SourceGenerator.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.RegularExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NosSmooth.PacketSerializersGenerator.AttributeGenerators;
using NosSmooth.PacketSerializersGenerator.Data;
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 SourceGenerator : ISourceGenerator
{
///
/// Initializes a new instance of the class.
///
public SourceGenerator()
{
_generators = new List
(
new IParameterGenerator[]
{
new PacketIndexAttributeGenerator(),
new PacketListIndexAttributeGenerator(),
new PacketContextListAttributeGenerator(),
}
);
}
private readonly List _generators;
///
public void Initialize(GeneratorInitializationContext context)
{
}
private IEnumerable GetPacketRecords(Compilation compilation, SyntaxTree tree)
{
var semanticModel = compilation.GetSemanticModel(tree);
var root = tree.GetRoot();
return root
.DescendantNodes()
.OfType()
.Where
(
x => x.AttributeLists.Any
(y => y.ContainsAttribute(semanticModel, Constants.GenerateSourceAttributeClass))
);
}
///
public void Execute(GeneratorExecutionContext context)
{
var packetRecords = context.Compilation.SyntaxTrees
.SelectMany(x => GetPacketRecords(context.Compilation, x));
foreach (var packetRecord in packetRecords)
{
if (packetRecord is not null)
{
using var stringWriter = new StringWriter();
using var writer = new IndentedTextWriter(stringWriter, " ");
var generatedResult = GeneratePacketSerializer(writer, context.Compilation, packetRecord);
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
(
$"{packetRecord.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