using System.Text; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.Text; namespace CSV_Metrics_Logger_Generator; [Generator] public class CSVConverterGenerator : ISourceGenerator { private const string ARRAY_DELIMITER = ", "; #region Implementation of ISourceGenerator public void Initialize(GeneratorInitializationContext context) { context.RegisterForSyntaxNotifications(() => new SyntaxReceiver()); } public void Execute(GeneratorExecutionContext context) { if (context.SyntaxReceiver is not SyntaxReceiver receiver) return; foreach (var dataStructure in receiver.DataStructures) this.ProcessStructure(context, dataStructure); } private void ProcessStructure(GeneratorExecutionContext context, TypeDeclarationSyntax dataStructure) { var generatedFileName = $"{dataStructure.Identifier}.Generated.cs"; var namespaceName = this.GetNamespaceFrom(dataStructure); var accessModifiers = string.Join(' ', dataStructure.Modifiers.Select(n => n.Text)); var declarationType = dataStructure switch { RecordDeclarationSyntax => "record struct", StructDeclarationSyntax => "struct", _ => string.Empty }; var typeParameters = dataStructure switch { { TypeParameterList: not null } => $"<{string.Join(", ", dataStructure.TypeParameterList.Parameters.Select(t => t.Identifier.Text))}>", _ => string.Empty }; var parameterProperties = dataStructure.ParameterList?.Parameters.Select(n => n.Identifier.ValueText) ?? []; var bodyProperties = dataStructure.Members.OfType().Select(n => n.Identifier.ValueText); var allProperties = parameterProperties.Concat(bodyProperties).ToList(); var numberProperties = allProperties.Count; var header = string.Join(ARRAY_DELIMITER, allProperties.Select(n => $"\"{n}\"")); var allFieldsString = string.Join(ARRAY_DELIMITER, allProperties.Select(n => $$"""string.Create(CultureInfo.InvariantCulture, $"{this.{{n}}}")""")); var sourceCode = SourceText.From( $$""" using System.Globalization; using CSV_Metrics_Logger; namespace {{namespaceName}}; {{accessModifiers}} {{declarationType}} {{dataStructure.Identifier}}{{typeParameters}} : IConvertToCSV { public uint GetCSVColumnCount() { return (uint){{numberProperties}}; } public IList GetCSVHeaders() { return [ {{header}} ]; } public IList ConvertToCSVDataLine() { return [ {{allFieldsString}} ]; } } """, Encoding.UTF8); context.AddSource(generatedFileName, sourceCode); } private string GetNamespaceFrom(SyntaxNode? node) => node switch { BaseNamespaceDeclarationSyntax namespaceDeclarationSyntax => namespaceDeclarationSyntax.Name.ToString(), null => string.Empty, _ => this.GetNamespaceFrom(node.Parent) }; #endregion }