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() .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()); } } }