diff --git a/CSV Metrics Logger Generator/CSV Metrics Logger Generator.csproj b/CSV Metrics Logger Generator/CSV Metrics Logger Generator.csproj
new file mode 100644
index 0000000..9c5d44a
--- /dev/null
+++ b/CSV Metrics Logger Generator/CSV Metrics Logger Generator.csproj
@@ -0,0 +1,20 @@
+
+
+
+ net8.0
+ latest
+ enable
+ enable
+ CS8600;CS8602;CS8603
+ true
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+
diff --git a/CSV Metrics Logger Generator/CSVConverterGenerator.cs b/CSV Metrics Logger Generator/CSVConverterGenerator.cs
new file mode 100644
index 0000000..43dcd67
--- /dev/null
+++ b/CSV Metrics Logger Generator/CSVConverterGenerator.cs
@@ -0,0 +1,86 @@
+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 accessModifiers = string.Join(' ', dataStructure.Modifiers.Select(n => n.Text));
+ var declarationType = dataStructure switch
+ {
+ RecordDeclarationSyntax => "record struct",
+ StructDeclarationSyntax => "struct",
+
+ _ => string.Empty
+ };
+ var namespaceName = this.GetNamespaceFrom(dataStructure);
+ var parameterProperties = dataStructure.ParameterList?.Parameters.Select(n => n.Identifier.ValueText).ToList() ?? [];
+ var bodyProperties = dataStructure.Members.OfType().Select(n => n.Identifier.ValueText).ToList();
+ var allProperties = new List(parameterProperties);
+ allProperties.AddRange(bodyProperties);
+
+ var numberProperties = allProperties.Count;
+ var header = string.Join(ARRAY_DELIMITER, allProperties.Select(n => $"\"{n}\""));
+ var allFieldsString = string.Join(ARRAY_DELIMITER, allProperties.Select(n => $"this.{n}.ToString(CultureInfo.InvariantCulture)"));
+
+ var sourceCode = SourceText.From(
+ $$"""
+ using System.Globalization;
+ using CSV_Metrics_Logger;
+ namespace {{namespaceName}};
+ {{accessModifiers}} {{declarationType}} {{dataStructure.Identifier}} : IConvertToCSV
+ {
+ public uint GetCSVColumnCount()
+ {
+ return (uint){{numberProperties}};
+ }
+
+ public IList GetCSVHeaders()
+ {
+ return [ {{header}} ];
+ }
+
+ public IList ConvertToCSVDataLine()
+ {
+ return [ {{allFieldsString}} ];
+ }
+ }
+ """, Encoding.UTF8);
+
+ context.AddSource($"{dataStructure.Identifier}.Generated.cs", sourceCode);
+ }
+
+ private string GetNamespaceFrom(SyntaxNode? node) => node switch
+ {
+ BaseNamespaceDeclarationSyntax namespaceDeclarationSyntax => namespaceDeclarationSyntax.Name.ToString(),
+ null => string.Empty,
+
+ _ => this.GetNamespaceFrom(node.Parent)
+ };
+
+ #endregion
+}
\ No newline at end of file
diff --git a/CSV Metrics Logger Generator/SyntaxReceiver.cs b/CSV Metrics Logger Generator/SyntaxReceiver.cs
new file mode 100644
index 0000000..ef96e9a
--- /dev/null
+++ b/CSV Metrics Logger Generator/SyntaxReceiver.cs
@@ -0,0 +1,39 @@
+using Microsoft.CodeAnalysis;
+using Microsoft.CodeAnalysis.CSharp.Syntax;
+
+namespace CSV_Metrics_Logger_Generator;
+
+internal sealed class SyntaxReceiver : ISyntaxReceiver
+{
+ public List DataStructures { get; } = [];
+
+ #region Implementation of ISyntaxReceiver
+
+ public void OnVisitSyntaxNode(SyntaxNode syntaxNode)
+ {
+ //
+ // The data structures we are looking for:
+ // - any kind of structs (record struct, readonly record struct, regular struct)
+ // - with the attribute [CSVRecord]
+ //
+
+ // Check if the syntax node is a struct declaration:
+ TypeDeclarationSyntax? structNode = syntaxNode switch
+ {
+ RecordDeclarationSyntax rds => rds,
+ StructDeclarationSyntax sds => sds,
+
+ _ => null
+ };
+
+ // No struct?
+ if (structNode is null)
+ return;
+
+ // Check if the struct has the attribute [CSVRecord]:
+ if (structNode.AttributeLists.Any(asl => asl.Attributes.Any(n => n.Name.ToString() == "CSVRecord")))
+ this.DataStructures.Add(structNode);
+ }
+
+ #endregion
+}
\ No newline at end of file
diff --git a/CSV Metrics Logger.sln b/CSV Metrics Logger.sln
index 7cbdcd0..fbc8414 100644
--- a/CSV Metrics Logger.sln
+++ b/CSV Metrics Logger.sln
@@ -2,6 +2,8 @@
Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSV Metrics Logger", "CSV Metrics Logger\CSV Metrics Logger.csproj", "{BFFEE7AC-0227-4FCA-BBE7-4F6E23CD5CD7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CSV Metrics Logger Generator", "CSV Metrics Logger Generator\CSV Metrics Logger Generator.csproj", "{559505B4-5322-4CFD-ABB9-D835C2A7EC09}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -12,5 +14,13 @@ Global
{BFFEE7AC-0227-4FCA-BBE7-4F6E23CD5CD7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BFFEE7AC-0227-4FCA-BBE7-4F6E23CD5CD7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BFFEE7AC-0227-4FCA-BBE7-4F6E23CD5CD7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {559505B4-5322-4CFD-ABB9-D835C2A7EC09}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {559505B4-5322-4CFD-ABB9-D835C2A7EC09}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {559505B4-5322-4CFD-ABB9-D835C2A7EC09}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {559505B4-5322-4CFD-ABB9-D835C2A7EC09}.Release|Any CPU.Build.0 = Release|Any CPU
+ {871708D2-633C-4D78-86EF-A1B72043B652}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {871708D2-633C-4D78-86EF-A1B72043B652}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {871708D2-633C-4D78-86EF-A1B72043B652}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {871708D2-633C-4D78-86EF-A1B72043B652}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal
diff --git a/CSV Metrics Logger/CSV Metrics Logger.csproj b/CSV Metrics Logger/CSV Metrics Logger.csproj
index ed5bd50..c4d0220 100644
--- a/CSV Metrics Logger/CSV Metrics Logger.csproj
+++ b/CSV Metrics Logger/CSV Metrics Logger.csproj
@@ -9,4 +9,8 @@
CS8600;CS8602;CS8603
+
+
+
+
diff --git a/CSV Metrics Logger/CSVRecordAttribute.cs b/CSV Metrics Logger/CSVRecordAttribute.cs
new file mode 100644
index 0000000..977b306
--- /dev/null
+++ b/CSV Metrics Logger/CSVRecordAttribute.cs
@@ -0,0 +1,4 @@
+namespace CSV_Metrics_Logger;
+
+[AttributeUsage(AttributeTargets.Struct)]
+public sealed class CSVRecordAttribute : Attribute;
\ No newline at end of file
diff --git a/CSV Metrics Logger/IConvertToCSV.cs b/CSV Metrics Logger/IConvertToCSV.cs
new file mode 100644
index 0000000..9f61e82
--- /dev/null
+++ b/CSV Metrics Logger/IConvertToCSV.cs
@@ -0,0 +1,10 @@
+namespace CSV_Metrics_Logger;
+
+public interface IConvertToCSV
+{
+ public uint GetCSVColumnCount();
+
+ public IList GetCSVHeaders();
+
+ public IList ConvertToCSVDataLine();
+}
\ No newline at end of file