using InnovEnergy.Lib.Utils; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; namespace InnovEnergy.Lib.SrcGen; public static class GenerateAttributes { public static async Task Generate(Project project, Compilation compilation) { var syntaxTrees = await project .Documents .Where(d => d.Project == project) .Where(d => d.SupportsSyntaxTree) .Select(async d => await d.GetSyntaxRootAsync()) .WhenAll(); syntaxTrees.NotNull() .SelectMany(c => c.GetGenerateAttributes()) .ForEach(GenerateFile); String GenerateFile(AttributeSyntax attribute) { var derivedType = attribute.GetAncestor(); var defineArgs = attribute.ArgumentList?.Arguments ?? Enumerable.Empty(); var filePath = derivedType.SyntaxTree.FilePath; var derivedName = derivedType.Identifier.ValueText; var baseRef = attribute.GetGenericArgument(); var baseType = compilation.GetDefinition(baseRef); var baseName = baseType.Identifier.ValueText; var baseFile = baseType.GetAncestor(); var generatedPath = filePath.GetGeneratedPath(baseName); var defines = defineArgs .Select(n => n.ToString()) .Select(s => s.Replace("\"", "").ToUpper()) .Select(s => $"#define {s}"); Console.WriteLine($"Generating {generatedPath}"); var code = GenerateCode(); var fileContents = Utils.AutoGeneratedMessage(nameof(GenerateAttributes)) + defines.JoinLines() + "\n" + code; File.WriteAllText(generatedPath, fileContents); return generatedPath; String GenerateCode() { try { return baseFile .AddPartialModifier() .ReplaceIdentifiers(baseName, derivedName) .RemoveNotImplemented() .GetText() .ToString(); } catch (Exception e) { return $"Failed to generate source for {filePath}\n\n{e}" .SplitLines() .Select(l => $"// {l}") .JoinLines(); } } } } }