Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Source generator - ColumnHeader attribute #37

Merged
merged 10 commits into from
Feb 9, 2024
2 changes: 1 addition & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ dotnet_diagnostic.CA1815.severity = warning
# CA2201: Do not raise reserved exception types
dotnet_diagnostic.CA2201.severity = warning

[{ColumnOrderAttribute.cs,WorksheetRowAttribute.cs}]
[{ColumnHeaderAttribute.cs,ColumnOrderAttribute.cs,WorksheetRowAttribute.cs}]
# CA1019: Define accessors for attribute arguments
dotnet_diagnostic.CA1019.severity = none
# CS9113: Parameter is unread
Expand Down
8 changes: 4 additions & 4 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@
</PropertyGroup>
<ItemGroup>
<GlobalPackageReference Include="ErrorProne.NET.CoreAnalyzers" Version="0.4.0-beta.1" />
<GlobalPackageReference Include="Meziantou.Analyzer" Version="2.0.138" />
<GlobalPackageReference Include="Meziantou.Analyzer" Version="2.0.140" />
<GlobalPackageReference Include="Microsoft.CodeAnalysis.BannedApiAnalyzers" Version="3.3.4" />
<GlobalPackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.8.14" />
<GlobalPackageReference Include="Microsoft.VisualStudio.Threading.Analyzers" Version="17.9.28" />
<GlobalPackageReference Include="Roslynator.Analyzers" Version="4.10.0" />
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="9.18.0.83559" />
<GlobalPackageReference Include="SonarAnalyzer.CSharp" Version="9.19.0.84025" />
</ItemGroup>
<ItemGroup>
<PackageVersion Include="BenchmarkDotNet" Version="0.13.12" />
Expand All @@ -31,7 +31,7 @@
<PackageVersion Include="System.IO.Compression" Version="4.3.0" />
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="Verify.SourceGenerators" Version="2.2.0" />
<PackageVersion Include="Verify.Xunit" Version="23.0.1" />
<PackageVersion Include="Verify.Xunit" Version="23.1.0" />
<PackageVersion Include="xunit" Version="2.6.6" />
<PackageVersion Include="xunit.runner.visualstudio" Version="2.5.6" />
</ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
using SpreadCheetah.SourceGeneration;

namespace SpreadCheetah.SourceGenerator.CSharp8Test.Models
{
public class ClassWithMultipleProperties
{
public string FirstName { get; }
[ColumnHeader("Last name")]
public string LastName { get; }
[ColumnOrder(1)]
public string FirstName { get; }
public int Age { get; }

public ClassWithMultipleProperties(string firstName, string lastName, int age)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using SpreadCheetah.SourceGeneration;

namespace SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader;

public class ClassWithColumnHeaderForAllProperties
{
[ColumnHeader("First name")]
public string FirstName { get; set; } = "";

[ColumnHeader("Middle name")]
public string? MiddleName { get; set; }

[ColumnHeader("Last name")]
public string LastName { get; set; } = "";

[ColumnHeader("Age")]
public int Age { get; set; }

[ColumnHeader("Employed (yes/no)")]
public bool Employed { get; set; }

[ColumnHeader("Score (decimal)")]
public double Score { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
using SpreadCheetah.SourceGeneration;

namespace SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader;

public class ClassWithSpecialCharacterColumnHeaders
{
[ColumnHeader("First name")]
public string? FirstName { get; set; }

[ColumnHeader("")]
public string? LastName { get; set; }

[ColumnHeader("Nationality (escaped characters \", \', \\)")]
public string? Nationality { get; set; }

[ColumnHeader("Address line 1 (escaped characters \r\n, \t)")]
public string? AddressLine1 { get; set; }

[ColumnHeader(@"Address line 2 (verbatim
string: "", \)")]
public string? AddressLine2 { get; set; }

[ColumnHeader("""
Age (
raw
string
literal
)
""")]
public int Age { get; set; }

[ColumnHeader("Note (unicode escape sequence 🌉, \ud83d\udc4d, \xE7)")]
public string? Note { get; set; }

private const string Constant = "This is a constant";

[ColumnHeader($"Note 2 (constant interpolated string: {Constant})")]
public string? Note2 { get; set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
//HintName: MyNamespace.MyGenRowContext.g.cs
// <auto-generated />
#nullable enable
using SpreadCheetah;
using SpreadCheetah.SourceGeneration;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace MyNamespace
{
public partial class MyGenRowContext
{
private static MyGenRowContext? _default;
public static MyGenRowContext Default => _default ??= new MyGenRowContext();

public MyGenRowContext()
{
}

private WorksheetRowTypeInfo<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties>? _ClassWithColumnHeaderForAllProperties;
public WorksheetRowTypeInfo<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties> ClassWithColumnHeaderForAllProperties => _ClassWithColumnHeaderForAllProperties
??= WorksheetRowMetadataServices.CreateObjectInfo<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties>(AddHeaderRow0Async, AddAsRowAsync, AddRangeAsRowsAsync);

private static async ValueTask AddHeaderRow0Async(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.Styling.StyleId? styleId, CancellationToken token)
{
var cells = ArrayPool<StyledCell>.Shared.Rent(6);
try
{
cells[0] = new StyledCell("First name", styleId);
cells[1] = new StyledCell("Middle name", styleId);
cells[2] = new StyledCell("Last name", styleId);
cells[3] = new StyledCell("Age", styleId);
cells[4] = new StyledCell("Employed (yes/no)", styleId);
cells[5] = new StyledCell("Score (decimal)", styleId);
await spreadsheet.AddRowAsync(cells.AsMemory(0, 6), token).ConfigureAwait(false);
}
finally
{
ArrayPool<StyledCell>.Shared.Return(cells, true);
}
}

private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties? obj, CancellationToken token)
{
if (spreadsheet is null)
throw new ArgumentNullException(nameof(spreadsheet));
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);
return AddAsRowInternalAsync(spreadsheet, obj, token);
}

private static ValueTask AddRangeAsRowsAsync(SpreadCheetah.Spreadsheet spreadsheet, IEnumerable<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties?> objs, CancellationToken token)
{
if (spreadsheet is null)
throw new ArgumentNullException(nameof(spreadsheet));
if (objs is null)
throw new ArgumentNullException(nameof(objs));
return AddRangeAsRowsInternalAsync(spreadsheet, objs, token);
}

private static async ValueTask AddAsRowInternalAsync(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties obj, CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(6);
try
{
await AddCellsAsRowAsync(spreadsheet, obj, cells, token).ConfigureAwait(false);
}
finally
{
ArrayPool<DataCell>.Shared.Return(cells, true);
}
}

private static async ValueTask AddRangeAsRowsInternalAsync(SpreadCheetah.Spreadsheet spreadsheet, IEnumerable<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties?> objs, CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(6);
try
{
await AddEnumerableAsRowsAsync(spreadsheet, objs, cells, token).ConfigureAwait(false);
}
finally
{
ArrayPool<DataCell>.Shared.Return(cells, true);
}
}

private static async ValueTask AddEnumerableAsRowsAsync(SpreadCheetah.Spreadsheet spreadsheet, IEnumerable<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties?> objs, DataCell[] cells, CancellationToken token)
{
foreach (var obj in objs)
{
await AddCellsAsRowAsync(spreadsheet, obj, cells, token).ConfigureAwait(false);
}
}

private static ValueTask AddCellsAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithColumnHeaderForAllProperties? obj, DataCell[] cells, CancellationToken token)
{
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);

cells[0] = new DataCell(obj.FirstName);
cells[1] = new DataCell(obj.MiddleName);
cells[2] = new DataCell(obj.LastName);
cells[3] = new DataCell(obj.Age);
cells[4] = new DataCell(obj.Employed);
cells[5] = new DataCell(obj.Score);
return spreadsheet.AddRowAsync(cells.AsMemory(0, 6), token);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
//HintName: MyNamespace.MyGenRowContext.g.cs
// <auto-generated />
#nullable enable
using SpreadCheetah;
using SpreadCheetah.SourceGeneration;
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;

namespace MyNamespace
{
public partial class MyGenRowContext
{
private static MyGenRowContext? _default;
public static MyGenRowContext Default => _default ??= new MyGenRowContext();

public MyGenRowContext()
{
}

private WorksheetRowTypeInfo<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders>? _ClassWithSpecialCharacterColumnHeaders;
public WorksheetRowTypeInfo<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders> ClassWithSpecialCharacterColumnHeaders => _ClassWithSpecialCharacterColumnHeaders
??= WorksheetRowMetadataServices.CreateObjectInfo<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders>(AddHeaderRow0Async, AddAsRowAsync, AddRangeAsRowsAsync);

private static async ValueTask AddHeaderRow0Async(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.Styling.StyleId? styleId, CancellationToken token)
{
var cells = ArrayPool<StyledCell>.Shared.Rent(8);
try
{
cells[0] = new StyledCell("First name", styleId);
cells[1] = new StyledCell("", styleId);
cells[2] = new StyledCell("Nationality (escaped characters \", ', \\)", styleId);
cells[3] = new StyledCell("Address line 1 (escaped characters \r\n, \t)", styleId);
cells[4] = new StyledCell("Address line 2 (verbatim\r\nstring: \", \\)", styleId);
cells[5] = new StyledCell(" Age (\r\n raw\r\n string\r\n literal\r\n )", styleId);
cells[6] = new StyledCell("Note (unicode escape sequence 🌉, 👍, ç)", styleId);
cells[7] = new StyledCell("Note 2 (constant interpolated string: This is a constant)", styleId);
await spreadsheet.AddRowAsync(cells.AsMemory(0, 8), token).ConfigureAwait(false);
}
finally
{
ArrayPool<StyledCell>.Shared.Return(cells, true);
}
}

private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders? obj, CancellationToken token)
{
if (spreadsheet is null)
throw new ArgumentNullException(nameof(spreadsheet));
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);
return AddAsRowInternalAsync(spreadsheet, obj, token);
}

private static ValueTask AddRangeAsRowsAsync(SpreadCheetah.Spreadsheet spreadsheet, IEnumerable<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders?> objs, CancellationToken token)
{
if (spreadsheet is null)
throw new ArgumentNullException(nameof(spreadsheet));
if (objs is null)
throw new ArgumentNullException(nameof(objs));
return AddRangeAsRowsInternalAsync(spreadsheet, objs, token);
}

private static async ValueTask AddAsRowInternalAsync(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders obj, CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(8);
try
{
await AddCellsAsRowAsync(spreadsheet, obj, cells, token).ConfigureAwait(false);
}
finally
{
ArrayPool<DataCell>.Shared.Return(cells, true);
}
}

private static async ValueTask AddRangeAsRowsInternalAsync(SpreadCheetah.Spreadsheet spreadsheet, IEnumerable<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders?> objs, CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(8);
try
{
await AddEnumerableAsRowsAsync(spreadsheet, objs, cells, token).ConfigureAwait(false);
}
finally
{
ArrayPool<DataCell>.Shared.Return(cells, true);
}
}

private static async ValueTask AddEnumerableAsRowsAsync(SpreadCheetah.Spreadsheet spreadsheet, IEnumerable<SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders?> objs, DataCell[] cells, CancellationToken token)
{
foreach (var obj in objs)
{
await AddCellsAsRowAsync(spreadsheet, obj, cells, token).ConfigureAwait(false);
}
}

private static ValueTask AddCellsAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader.ClassWithSpecialCharacterColumnHeaders? obj, DataCell[] cells, CancellationToken token)
{
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);

cells[0] = new DataCell(obj.FirstName);
cells[1] = new DataCell(obj.LastName);
cells[2] = new DataCell(obj.Nationality);
cells[3] = new DataCell(obj.AddressLine1);
cells[4] = new DataCell(obj.AddressLine2);
cells[5] = new DataCell(obj.Age);
cells[6] = new DataCell(obj.Note);
cells[7] = new DataCell(obj.Note2);
return spreadsheet.AddRowAsync(cells.AsMemory(0, 8), token);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using SpreadCheetah.SourceGenerator.SnapshotTest.Helpers;
using SpreadCheetah.SourceGenerators;

namespace SpreadCheetah.SourceGenerator.SnapshotTest.Tests;

public class WorksheetRowGeneratorColumnHeaderTests
{
[Fact]
public Task WorksheetRowGenerator_Generate_ClassWithColumnHeaderForAllProperties()
{
// Arrange
const string source = """
using SpreadCheetah.SourceGeneration;
using SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader;

namespace MyNamespace;

[WorksheetRow(typeof(ClassWithColumnHeaderForAllProperties))]
public partial class MyGenRowContext : WorksheetRowContext;
""";

// Act & Assert
return TestHelper.CompileAndVerify<WorksheetRowGenerator>(source);
}

[Fact]
public Task WorksheetRowGenerator_Generate_ClassWithSpecialCharacterColumnHeaders()
{
// Arrange
const string source = """
using SpreadCheetah.SourceGeneration;
using SpreadCheetah.SourceGenerator.SnapshotTest.Models.ColumnHeader;

namespace MyNamespace;

[WorksheetRow(typeof(ClassWithSpecialCharacterColumnHeaders))]
public partial class MyGenRowContext : WorksheetRowContext;
""";

// Act & Assert
return TestHelper.CompileAndVerify<WorksheetRowGenerator>(source);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using Xunit;
using Xunit;

namespace SpreadCheetah.SourceGenerator.Test.Helpers;

Expand Down
Loading
Loading