Compare commits

..

No commits in common. "main" and "v1.0.0" have entirely different histories.
main ... v1.0.0

9 changed files with 32 additions and 143 deletions

View File

@ -10,9 +10,9 @@
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<IncludeBuildOutput>false</IncludeBuildOutput> <!-- Do not include build output in the package. Necessary for analyzers. --> <IncludeBuildOutput>false</IncludeBuildOutput> <!-- Do not include build output in the package. Necessary for analyzers. -->
<AssemblyVersion>1.1.0</AssemblyVersion> <AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion> <FileVersion>1.0.0</FileVersion>
<PackageVersion>1.1.0</PackageVersion> <PackageVersion>1.0.0</PackageVersion>
<Authors>Thorsten Sommer</Authors> <Authors>Thorsten Sommer</Authors>
<PackageProjectUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</PackageProjectUrl> <PackageProjectUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</PackageProjectUrl>
<RepositoryUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</RepositoryUrl> <RepositoryUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</RepositoryUrl>

View File

@ -29,9 +29,6 @@ public class CSVConverterGenerator : ISourceGenerator
private void ProcessStructure(GeneratorExecutionContext context, TypeDeclarationSyntax 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 accessModifiers = string.Join(' ', dataStructure.Modifiers.Select(n => n.Text));
var declarationType = dataStructure switch var declarationType = dataStructure switch
{ {
@ -40,27 +37,22 @@ public class CSVConverterGenerator : ISourceGenerator
_ => string.Empty _ => string.Empty
}; };
var namespaceName = this.GetNamespaceFrom(dataStructure);
var typeParameters = dataStructure switch var parameterProperties = dataStructure.ParameterList?.Parameters.Select(n => n.Identifier.ValueText).ToList() ?? [];
{ var bodyProperties = dataStructure.Members.OfType<PropertyDeclarationSyntax>().Select(n => n.Identifier.ValueText).ToList();
{ TypeParameterList: not null } => $"<{string.Join(", ", dataStructure.TypeParameterList.Parameters.Select(t => t.Identifier.Text))}>", var allProperties = new List<string>(parameterProperties);
_ => string.Empty allProperties.AddRange(bodyProperties);
};
var parameterProperties = dataStructure.ParameterList?.Parameters.Select(n => n.Identifier.ValueText) ?? [];
var bodyProperties = dataStructure.Members.OfType<PropertyDeclarationSyntax>().Select(n => n.Identifier.ValueText);
var allProperties = parameterProperties.Concat(bodyProperties).ToList();
var numberProperties = allProperties.Count; var numberProperties = allProperties.Count;
var header = string.Join(ARRAY_DELIMITER, allProperties.Select(n => $"\"{n}\"")); 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 allFieldsString = string.Join(ARRAY_DELIMITER, allProperties.Select(n => $"this.{n}.ToString(CultureInfo.InvariantCulture)"));
var sourceCode = SourceText.From( var sourceCode = SourceText.From(
$$""" $$"""
using System.Globalization; using System.Globalization;
using CSV_Metrics_Logger; using CSV_Metrics_Logger;
namespace {{namespaceName}}; namespace {{namespaceName}};
{{accessModifiers}} {{declarationType}} {{dataStructure.Identifier}}{{typeParameters}} : IConvertToCSV {{accessModifiers}} {{declarationType}} {{dataStructure.Identifier}} : IConvertToCSV
{ {
public uint GetCSVColumnCount() public uint GetCSVColumnCount()
{ {
@ -79,7 +71,7 @@ public class CSVConverterGenerator : ISourceGenerator
} }
""", Encoding.UTF8); """, Encoding.UTF8);
context.AddSource(generatedFileName, sourceCode); context.AddSource($"{dataStructure.Identifier}.Generated.cs", sourceCode);
} }
private string GetNamespaceFrom(SyntaxNode? node) => node switch private string GetNamespaceFrom(SyntaxNode? node) => node switch

View File

@ -18,7 +18,7 @@ List<TestData> testData =
]; ];
var filename = Path.GetTempFileName(); var filename = Path.GetTempFileName();
await using var storage = CSVStorage<TestData>.Create(filename); await using var storage = CSVStorage<TestData>.Create(fileName);
foreach (var data in testData) foreach (var data in testData)
storage.Write(data); storage.Write(data);
@ -26,37 +26,13 @@ foreach (var data in testData)
You might use `storage.Write` from multiple threads. The logger will handle the synchronization for you. You might use `storage.Write` from multiple threads. The logger will handle the synchronization for you.
CSV Metrics Logger uses a source generator. You need to use the following NuGet packages: CSV Metrics Logger uses a source generator. You have to use the following NuGet packages:
```xml ```xml
<PackageReference Include="CSVMetricsLoggerGenerator" Version="1.1.0" /> <PackageReference Include="CSVMetricsLoggerGenerator" Version="1.0.0" />
<PackageReference Include="CSVMetricsLogger" Version="1.1.0" /> <PackageReference Include="CSVMetricsLogger" Version="1.0.0" />
``` ```
Your data must be modeled as a structure; classes are not supported. It does not matter if you are using a (readonly) record struct or a regular struct. The only requirement is that the struct must be a partial struct. The source generator will generate the missing part of the struct for you. Each public property will be used as a column in the CSV file. The property type can be any type, as long as it supports the ToString() method. Your data must be modeled as a structure; classes are not supported. It does not matter if you are using a (readonly) record struct or a regular struct. The only requirement is that the struct must be a partial struct. The source generator will generate the missing part of the struct for you. Each public property will be used as a column in the CSV file. The property type might be any type, as long as it supports the `ToString(CultureInfo)` method.
Additionally, you can use generic type parameters in your struct, as long as there is a suitable ToString() overload. Here is an example:
```csharp
using CSV_Metrics_Logger;
[CSVRecord]
public readonly partial record struct GenericTestData<TNum>(string Name, sbyte Age, TNum Measure) where TNum : IFloatingPointIeee754<TNum>;
List<TestData> testData =
[
new TestData<float>("Name 1", 14, 47.53f),
new TestData<float>("Name 2", 25, 19.84f),
new TestData<float>("Name 3", 36, 38.78f),
new TestData<float>("Name 4", 47, 17.25f),
new TestData<float>("Name 5", 58, 73.89f),
];
var filename = Path.GetTempFileName();
await using var storage = CSVStorage<TestData<float>>.Create(filename);
foreach (var data in testData)
storage.Write(data);
```
For each data structure, you create a CSVStorage instance. The CSVStorage instance is a disposable object. You must dispose of it when you are done with it. When the CSV file already exists, the CSVStorage object will append data to the existing file; the header will not be written again. For each data structure, you create a CSVStorage instance. The CSVStorage instance is a disposable object. You must dispose of it when you are done with it. When the CSV file already exists, the CSVStorage object will append data to the existing file; the header will not be written again.

View File

@ -9,9 +9,9 @@
<WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors> <WarningsAsErrors>CS8600;CS8602;CS8603</WarningsAsErrors>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild> <GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<AssemblyVersion>1.1.0</AssemblyVersion> <AssemblyVersion>1.0.0</AssemblyVersion>
<FileVersion>1.1.0</FileVersion> <FileVersion>1.0.0</FileVersion>
<PackageVersion>1.1.0</PackageVersion> <PackageVersion>1.0.0</PackageVersion>
<Authors>Thorsten Sommer</Authors> <Authors>Thorsten Sommer</Authors>
<PackageProjectUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</PackageProjectUrl> <PackageProjectUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</PackageProjectUrl>
<RepositoryUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</RepositoryUrl> <RepositoryUrl>https://devops.tsommer.org/open-source/dotnet/csv-metrics-logger</RepositoryUrl>
@ -25,16 +25,10 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup Condition="'$(UseLocalProjects)' == 'true'"> <ItemGroup Condition="'$(UseLocalProjects)' == 'true'">
<ProjectReference Include="..\CSV Metrics Logger Generator\CSV Metrics Logger Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false"> <ProjectReference Include="..\CSV Metrics Logger Generator\CSV Metrics Logger Generator.csproj" OutputItemType="Analyzer" ReferenceOutputAssembly="false" />
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>build; analyzers; buildtransitive</IncludeAssets>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(UseLocalProjects)' == 'false'"> <ItemGroup Condition="'$(UseLocalProjects)' == 'false'">
<PackageReference Include="CSVMetricsLoggerGenerator" Version="1.0.0" OutputItemType="Analyzer" ReferenceOutputAssembly="false"> <PackageReference Include="CSVMetricsLoggerGenerator" Version="1.0.0" />
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' "> <PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">

View File

@ -18,7 +18,7 @@ List<TestData> testData =
]; ];
var filename = Path.GetTempFileName(); var filename = Path.GetTempFileName();
await using var storage = CSVStorage<TestData>.Create(filename); await using var storage = CSVStorage<TestData>.Create(fileName);
foreach (var data in testData) foreach (var data in testData)
storage.Write(data); storage.Write(data);
@ -26,37 +26,13 @@ foreach (var data in testData)
You might use `storage.Write` from multiple threads. The logger will handle the synchronization for you. You might use `storage.Write` from multiple threads. The logger will handle the synchronization for you.
CSV Metrics Logger uses a source generator. You need to use the following NuGet packages: CSV Metrics Logger uses a source generator. You have to use the following NuGet packages:
```xml ```xml
<PackageReference Include="CSVMetricsLoggerGenerator" Version="1.1.0" /> <PackageReference Include="CSVMetricsLoggerGenerator" Version="1.0.0" />
<PackageReference Include="CSVMetricsLogger" Version="1.1.0" /> <PackageReference Include="CSVMetricsLogger" Version="1.0.0" />
``` ```
Your data must be modeled as a structure; classes are not supported. It does not matter if you are using a (readonly) record struct or a regular struct. The only requirement is that the struct must be a partial struct. The source generator will generate the missing part of the struct for you. Each public property will be used as a column in the CSV file. The property type can be any type, as long as it supports the ToString() method. Your data must be modeled as a structure; classes are not supported. It does not matter if you are using a (readonly) record struct or a regular struct. The only requirement is that the struct must be a partial struct. The source generator will generate the missing part of the struct for you. Each public property will be used as a column in the CSV file. The property type might be any type, as long as it supports the `ToString(CultureInfo)` method.
Additionally, you can use generic type parameters in your struct, as long as there is a suitable ToString() overload. Here is an example:
```csharp
using CSV_Metrics_Logger;
[CSVRecord]
public readonly partial record struct GenericTestData<TNum>(string Name, sbyte Age, TNum Measure) where TNum : IFloatingPointIeee754<TNum>;
List<TestData> testData =
[
new TestData<float>("Name 1", 14, 47.53f),
new TestData<float>("Name 2", 25, 19.84f),
new TestData<float>("Name 3", 36, 38.78f),
new TestData<float>("Name 4", 47, 17.25f),
new TestData<float>("Name 5", 58, 73.89f),
];
var filename = Path.GetTempFileName();
await using var storage = CSVStorage<TestData<float>>.Create(filename);
foreach (var data in testData)
storage.Write(data);
```
For each data structure, you create a CSVStorage instance. The CSVStorage instance is a disposable object. You must dispose of it when you are done with it. When the CSV file already exists, the CSVStorage object will append data to the existing file; the header will not be written again. For each data structure, you create a CSVStorage instance. The CSVStorage instance is a disposable object. You must dispose of it when you are done with it. When the CSV file already exists, the CSVStorage object will append data to the existing file; the header will not be written again.

View File

@ -18,7 +18,7 @@ List<TestData> testData =
]; ];
var filename = Path.GetTempFileName(); var filename = Path.GetTempFileName();
await using var storage = CSVStorage<TestData>.Create(filename); await using var storage = CSVStorage<TestData>.Create(fileName);
foreach (var data in testData) foreach (var data in testData)
storage.Write(data); storage.Write(data);
@ -26,37 +26,13 @@ foreach (var data in testData)
You might use `storage.Write` from multiple threads. The logger will handle the synchronization for you. You might use `storage.Write` from multiple threads. The logger will handle the synchronization for you.
CSV Metrics Logger uses a source generator. You need to use the following NuGet packages: CSV Metrics Logger uses a source generator. You have to use the following NuGet packages:
```xml ```xml
<PackageReference Include="CSVMetricsLoggerGenerator" Version="1.1.0" /> <PackageReference Include="CSVMetricsLoggerGenerator" Version="1.0.0" />
<PackageReference Include="CSVMetricsLogger" Version="1.1.0" /> <PackageReference Include="CSVMetricsLogger" Version="1.0.0" />
``` ```
Your data must be modeled as a structure; classes are not supported. It does not matter if you are using a (readonly) record struct or a regular struct. The only requirement is that the struct must be a partial struct. The source generator will generate the missing part of the struct for you. Each public property will be used as a column in the CSV file. The property type can be any type, as long as it supports the ToString() method. Your data must be modeled as a structure; classes are not supported. It does not matter if you are using a (readonly) record struct or a regular struct. The only requirement is that the struct must be a partial struct. The source generator will generate the missing part of the struct for you. Each public property will be used as a column in the CSV file. The property type might be any type, as long as it supports the `ToString(CultureInfo)` method.
Additionally, you can use generic type parameters in your struct, as long as there is a suitable ToString() overload. Here is an example:
```csharp
using CSV_Metrics_Logger;
[CSVRecord]
public readonly partial record struct GenericTestData<TNum>(string Name, sbyte Age, TNum Measure) where TNum : IFloatingPointIeee754<TNum>;
List<TestData> testData =
[
new TestData<float>("Name 1", 14, 47.53f),
new TestData<float>("Name 2", 25, 19.84f),
new TestData<float>("Name 3", 36, 38.78f),
new TestData<float>("Name 4", 47, 17.25f),
new TestData<float>("Name 5", 58, 73.89f),
];
var filename = Path.GetTempFileName();
await using var storage = CSVStorage<TestData<float>>.Create(filename);
foreach (var data in testData)
storage.Write(data);
```
For each data structure, you create a CSVStorage instance. The CSVStorage instance is a disposable object. You must dispose of it when you are done with it. When the CSV file already exists, the CSVStorage object will append data to the existing file; the header will not be written again. For each data structure, you create a CSVStorage instance. The CSVStorage instance is a disposable object. You must dispose of it when you are done with it. When the CSV file already exists, the CSVStorage object will append data to the existing file; the header will not be written again.

View File

@ -1,8 +0,0 @@
using System.Numerics;
using CSV_Metrics_Logger;
namespace Tests;
[CSVRecord]
public readonly partial record struct TestGeneric<TNum>(string Name, sbyte Age, TNum Value) where TNum : IFloatingPointIeee754<TNum>;

View File

@ -60,23 +60,6 @@ public sealed class Tests
}); });
} }
[Test]
public void TestGeneric()
{
var testData = new TestGeneric<float>("Bob", 120, 4.78f);
Assert.Multiple(() =>
{
var numberColumns = testData.GetCSVColumnCount();
Assert.That(numberColumns, Is.EqualTo(3));
var header = testData.GetCSVHeaders();
Assert.That(header, Is.EquivalentTo(new[] { "Name", "Age", "Value" }));
var dataLine = testData.ConvertToCSVDataLine();
Assert.That(dataLine, Is.EquivalentTo(new[] { "Bob", "120", "4.78" }));
});
}
[Test] [Test]
public void TestRegularStruct() public void TestRegularStruct()
{ {

View File

@ -12,7 +12,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0"/> <PackageReference Include="coverlet.collector" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
<PackageReference Include="NUnit" Version="3.14.0"/> <PackageReference Include="NUnit" Version="3.14.0"/>
<PackageReference Include="NUnit.Analyzers" Version="3.9.0"/> <PackageReference Include="NUnit.Analyzers" Version="3.9.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>