Files
Polonium.Generators/src/Generators/FramePatchGenerator.cs

131 lines
7.4 KiB
C#

using System.Linq;
using System.Text;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Polonium.Generators.Generators;
[Generator]
public class FramePatchGenerator : AssetProcessGenerator
{
public override void Execute(GeneratorExecutionContext context)
{
if(context.SyntaxReceiver is not ClassSyntaxReceiver receiver)
return;
Compilation compilation = context.Compilation;
INamedTypeSymbol? autoPatch = compilation.GetTypeByMetadataName("Polonium.Attributes.AutoPatch");
INamedTypeSymbol? patchableProperty = compilation.GetTypeByMetadataName("Polonium.Attributes.PatchableProperty");
foreach (InterfaceInfo inf in GetInterfacesWithAttribute(context, autoPatch))
{
PropertyDeclarationSyntax? underlying = inf.DeclarationSyntax.Members
.OfType<PropertyDeclarationSyntax>()
.FirstOrDefault(m => m.AttributeLists
.SelectMany(a => a.Attributes)
.Any(a =>
{
SemanticModel model = compilation.GetSemanticModel(a.SyntaxTree);
INamedTypeSymbol? attributeSymbol = model.GetSymbolInfo(a).Symbol?.ContainingType;
return SymbolEqualityComparer.Default.Equals(attributeSymbol, patchableProperty);
}));
if (underlying is null)
continue;
SemanticModel model = compilation.GetSemanticModel(underlying.SyntaxTree);
ITypeSymbol? underlyingType = model.GetTypeInfo(underlying.Type).Type;
ITypeSymbol? hashSet = compilation
.GetTypeByMetadataName("Polonium.DataStructures.PatchableItems.PatchableHashSet`1");
ITypeSymbol? dictionary = compilation
.GetTypeByMetadataName("Polonium.DataStructures.PatchableItems.PatchableDictionary`2");
string typeString = "";
StringBuilder sb = new();
sb
.AppendLine("using System;")
.AppendLine("using System.Collections.Generic;")
.AppendLine("using System.Linq;")
.AppendLine("using Polonium.Interfaces;")
.AppendLine("using Polonium.DataStructures.PatchableItems;")
.AppendLine("using Polonium.Resources.FramePatches;")
.AppendLine("using Polonium.Resources.FramePatches.Generic;")
.AppendLine($"namespace {inf.Namespace};");
string lName = inf.InterfaceName[0] == 'I' ? inf.InterfaceName.Substring(1) : inf.InterfaceName;
if (SymbolEqualityComparer.Default.Equals(underlyingType?.OriginalDefinition, hashSet))
{
ITypeSymbol? x1 = (underlyingType as INamedTypeSymbol)?.TypeArguments[0];
sb
.AppendLine($"public partial class {lName}Patch : HashSetFramePatch<{x1?.ToDisplayString()}>")
.AppendLine("{")
.AppendLine($" public override void Patch(IPatchableFrame frame)")
.AppendLine(" {")
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
.AppendLine(" return;")
.AppendLine($" switch (sFrame.{underlying.Identifier.ToString()}.UpdateMethod )")
.AppendLine(" {")
.AppendLine(" case UpdateMethods.None:")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Update:")
.AppendLine($" foreach({x1?.ToDisplayString()} x in Updates)")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Add(x);")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Replace:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Data = Updates;")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Custom:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.CustomUpdate(Updates);")
.AppendLine(" return;")
.AppendLine(" default:")
.AppendLine(" return;")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}");
}
else if (SymbolEqualityComparer.Default.Equals(underlyingType?.OriginalDefinition, dictionary))
{
ITypeSymbol? x1 = (underlyingType as INamedTypeSymbol)?.TypeArguments[0];
ITypeSymbol? x2 = (underlyingType as INamedTypeSymbol)?.TypeArguments[1];
sb
.AppendLine($"public partial class {lName}Patch : DictionaryFramePatch<{x1?.ToDisplayString()}, {x2?.ToDisplayString()}>")
.AppendLine("{")
.AppendLine(" public override void Patch(IPatchableFrame frame)")
.AppendLine(" {")
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
.AppendLine(" return;")
.AppendLine($" switch (sFrame.{underlying.Identifier.ToString()}.UpdateMethod )")
.AppendLine(" {")
.AppendLine(" case UpdateMethods.None:")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Update:")
.AppendLine($" foreach({x1?.ToDisplayString()} x in Updates.Keys)")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}[x] = Updates[x];")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Replace:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.Data = Updates;")
.AppendLine(" return;")
.AppendLine(" case UpdateMethods.Custom:")
.AppendLine($" sFrame.{underlying.Identifier.ToString()}.CustomUpdate(Updates);")
.AppendLine(" return;")
.AppendLine(" default:")
.AppendLine(" return;")
.AppendLine(" }")
.AppendLine(" }")
.AppendLine("}");
}
else
{
sb
.AppendLine($"public partial class {lName}Patch : FramePatch<{underlyingType?.ToDisplayString()}>")
.AppendLine("{")
.AppendLine(" public override void Patch(IPatchableFrame frame)")
.AppendLine(" {")
.AppendLine($" if(frame is not {inf.InterfaceFillName} sFrame)")
.AppendLine(" return;")
.AppendLine($" sFrame.{underlying.Identifier.ToString()} = Updates;")
.AppendLine(" }")
.AppendLine("}");
}
context.AddSource($"{lName}Patch.g.cs", sb.ToString());
}
}
}