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 - CellValueConverter #72

Merged
merged 56 commits into from
Sep 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
56 commits
Select commit Hold shift + click to select a range
19227d1
add cell value mapper attribute, ICellValueMapper.cs for source gener…
Aug 22, 2024
48d7837
use constant instead of string
Aug 22, 2024
6ec6265
remove styleName from ICellValueMapper.cs arguments
Aug 22, 2024
7900947
WIP: template version how to use user mapper
Aug 23, 2024
87ef65c
don't generate _cellValueMappers if no mappers exists for a class
Aug 23, 2024
40cb247
rename ICellValueMapper.cs to CellValueConverter.cs
Aug 28, 2024
a0901e8
add constructor with DataCell in StyledCell.cs
Aug 28, 2024
2319350
emit error if property has CellValueConverterAttribute and TruncateVa…
Aug 28, 2024
a98624a
use dictionary instead of an array. allow only one instance per cell …
Aug 29, 2024
4c4f939
add test when cell value converter exists
Aug 29, 2024
f4e9293
add test when the same cell value converter exists
Aug 29, 2024
f6fc9f5
add diagnostic
Aug 29, 2024
5e1f28a
emit error if a type doesn't have a public parameterless constructor
Sep 1, 2024
beb216d
add test when class doesn't have a public parameterless constructor f…
Sep 1, 2024
85719a4
generate code for CellValueConverters only once
Sep 1, 2024
d8b12b2
Merge branch 'refs/heads/main' into dev/src-gen-cell_value_mapper
Sep 1, 2024
b997580
remove additional constructor from StyledCell.cs
Sep 1, 2024
deb1415
emit error if the CellValueConverter generic different from the prope…
Sep 1, 2024
6ec84eb
use ToDisplayString() to compare types and avoid bug when comparing n…
Sep 1, 2024
a13bf80
Merge branch 'refs/heads/dev/src-gen-cellstyle' into dev/src-gen-cell…
Sep 1, 2024
cc5080a
update tests
Sep 1, 2024
1334c03
Merge branch 'refs/heads/dev/src-gen-cellstyle' into dev/src-gen-cell…
Sep 1, 2024
75f01de
use styled cells with cell value converter
Sep 1, 2024
2586c5d
Merge branch 'refs/heads/main' into dev/src-gen-cell_value_mapper
Sep 1, 2024
8ba0f95
remove class usage from attribute and update tests
Sep 1, 2024
8fb20ba
fix tests
Sep 1, 2024
2280f40
add test for a case when CellValueConverter with CellStyle
Sep 2, 2024
e7c2b62
add missing test
Sep 2, 2024
95b6d8a
add primary constructor to CellValueConverterAttribute.cs fix checkin…
Sep 3, 2024
91cf9a2
update tests
Sep 3, 2024
25561d8
remove the dictionary with cell value converters
Sep 3, 2024
4459f8d
fix an error message
Sep 3, 2024
e3c7d3c
update tests
Sep 3, 2024
65d733c
fix tests
Sep 3, 2024
9dd9d02
fix tests and delete classes for CellValueConverter test
Sep 3, 2024
7615226
update public api tests
Sep 3, 2024
0c29c21
Merge pull request #69 from Ashymonth/dev/src-gen-cell_value_mapper
sveinungf Sep 4, 2024
0bd883b
Update XML comments and suppressions for CellValueConverter classes
sveinungf Sep 6, 2024
c5e234e
Refactor use of diagnostics
sveinungf Sep 6, 2024
83b7d32
Made diagnostic SPCH1007 more general, so it can be used on more attr…
sveinungf Sep 6, 2024
c93e2bc
Made diagnostic SPCH1008 more general, so it can be used for more att…
sveinungf Sep 6, 2024
7da7656
Update test snapshots
sveinungf Sep 6, 2024
4b21ddd
Use "onlyDiagnostics: true" for snapshot tests where we only need the…
sveinungf Sep 6, 2024
970e15a
Made diagnostic SPCH1009 more general, so it can be used for more att…
sveinungf Sep 6, 2024
55e1484
Remove redundant diagnostic
sveinungf Sep 6, 2024
f2eb2fa
Simplify ConstructCell in source generator
sveinungf Sep 7, 2024
9c33ce9
Update test snapshots
sveinungf Sep 7, 2024
9e47918
Simplify snapshot tests for CellValueConverter
sveinungf Sep 7, 2024
5b4cee0
Fix issue with reusing CellValueConverters not working as intended
sveinungf Sep 7, 2024
3ec9492
Add CellValueConverters to test for class with all src gen attributes
sveinungf Sep 7, 2024
2c4a9fc
Add CellValueConverters to C# 8 test for class with all src gen attri…
sveinungf Sep 7, 2024
fe9ea56
Regular tests for CellValueConverter
sveinungf Sep 7, 2024
690f362
Test for two classes using the same CellValueConverter
sveinungf Sep 7, 2024
57097f4
Test for property with CellValueConverter and CellStyle
sveinungf Sep 7, 2024
c630ed1
Update snapshots for public API
sveinungf Sep 7, 2024
115ce1b
Tests for diagnostics SPCH1008 and SPCH1009
sveinungf Sep 7, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ dotnet_diagnostic.IDE0301.severity = warning
# IDE0305: Use collection expression for fluent
dotnet_diagnostic.IDE0305.severity = warning

[{CellValueTruncateAttribute.cs,ColumnHeaderAttribute.cs,ColumnOrderAttribute.cs,CellStyleAttribute.cs,ColumnWidthAttribute.cs,WorksheetRowAttribute.cs}]
[{CellStyleAttribute.cs,CellValueConverterAttribute.cs,CellValueTruncateAttribute.cs,ColumnHeaderAttribute.cs,ColumnOrderAttribute.cs,ColumnWidthAttribute.cs,WorksheetRowAttribute.cs}]
# CA1019: Define accessors for attribute arguments
dotnet_diagnostic.CA1019.severity = none
# CS9113: Parameter is unread
Expand Down Expand Up @@ -266,6 +266,10 @@ dotnet_diagnostic.S3925.severity = none
# S4136: Method overloads should be grouped together
dotnet_diagnostic.S4136.severity = warning

[{CellValueConverter.cs}]
# S1694: An abstract class should have both abstract and concrete methods
dotnet_diagnostic.S1694.severity = none

[{*Test,*.Benchmark}/**.cs]
# S927: Parameter names should match base declaration
dotnet_diagnostic.S927.severity = none
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ namespace SpreadCheetah.SourceGenerator.CSharp8Test.Models
public class BaseClass
{
[CellStyle("Id style")]
[CellValueConverter(typeof(RemoveDashesValueConverter))]
public string Id { get; }

protected BaseClass(string id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class ClassWithMultipleProperties : BaseClass
public string LastName { get; }

[ColumnOrder(1)]
[CellValueConverter(typeof(UpperCaseValueConverter))]
public string FirstName { get; }

[ColumnWidth(5)]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using SpreadCheetah.SourceGeneration;

namespace SpreadCheetah.SourceGenerator.CSharp8Test.Models
{
internal class RemoveDashesValueConverter : CellValueConverter<string>
{
public override DataCell ConvertToDataCell(string value)
{
return new DataCell(value.Replace("-", ""));
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using SpreadCheetah.SourceGeneration;

namespace SpreadCheetah.SourceGenerator.CSharp8Test.Models
{
public class UpperCaseValueConverter : CellValueConverter<string>
{
public override DataCell ConvertToDataCell(string value)
{
return new DataCell(value.ToUpperInvariant());
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ public class WorksheetRowGeneratorTests
public async Task Spreadsheet_AddAsRow_ClassWithMultipleProperties()
{
// Arrange
const string id = "2cc56ae0-32b2-46c2-b6d2-27c6ce8568ec";
var obj = new ClassWithMultipleProperties(
id: Guid.NewGuid().ToString(),
id: id,
firstName: "Ola",
lastName: "Nordmann",
age: 30);
Expand All @@ -36,8 +37,8 @@ public async Task Spreadsheet_AddAsRow_ClassWithMultipleProperties()

// Assert
using var sheet = SpreadsheetAssert.SingleSheet(stream);
Assert.Equal(obj.FirstName, sheet["A1"].StringValue);
Assert.Equal(obj.Id, sheet["B1"].StringValue);
Assert.Equal(obj.FirstName.ToUpperInvariant(), sheet["A1"].StringValue);
Assert.Equal(obj.Id.Replace("-", ""), sheet["B1"].StringValue);
Assert.Equal(obj.LastName, sheet["C1"].StringValue);
Assert.Equal(obj.Age, sheet["D1"].IntValue);
Assert.Equal(4, sheet.CellCount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
Severity: Error,
WarningLevel: 0,
Location: : (6,5)-(6,18),
MessageFormat: '{0}' is an invalid argument for attribute '{1}',
Message: '' is an invalid argument for attribute 'SpreadCheetah.SourceGeneration.CellStyleAttribute',
MessageFormat: '{0}' is an invalid argument for {1},
Message: '' is an invalid argument for CellStyleAttribute,
Category: SpreadCheetah.SourceGenerator
}
]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,18 +20,28 @@ public partial class MyGenRowContext
public MyGenRowContext()
{
}
private static readonly MyNamespace.StringValueConverter _valueConverter0 = new MyNamespace.StringValueConverter();

private WorksheetRowTypeInfo<MyNamespace.ClassWithDuplicateColumnOrdering>? _ClassWithDuplicateColumnOrdering;
public WorksheetRowTypeInfo<MyNamespace.ClassWithDuplicateColumnOrdering> ClassWithDuplicateColumnOrdering => _ClassWithDuplicateColumnOrdering
??= WorksheetRowMetadataServices.CreateObjectInfo<MyNamespace.ClassWithDuplicateColumnOrdering>(
AddHeaderRow0Async, AddAsRowAsync, AddRangeAsRowsAsync, null);
private WorksheetRowTypeInfo<MyNamespace.ClassPropertyWithConverterAndCellStyle>? _ClassPropertyWithConverterAndCellStyle;
public WorksheetRowTypeInfo<MyNamespace.ClassPropertyWithConverterAndCellStyle> ClassPropertyWithConverterAndCellStyle => _ClassPropertyWithConverterAndCellStyle
??= WorksheetRowMetadataServices.CreateObjectInfo<MyNamespace.ClassPropertyWithConverterAndCellStyle>(
AddHeaderRow0Async, AddAsRowAsync, AddRangeAsRowsAsync, null, CreateWorksheetRowDependencyInfo0);

private static WorksheetRowDependencyInfo CreateWorksheetRowDependencyInfo0(Spreadsheet spreadsheet)
{
var styleIds = new[]
{
spreadsheet.GetStyleId("My style"),
};
return new WorksheetRowDependencyInfo(styleIds);
}

private static async ValueTask AddHeaderRow0Async(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.Styling.StyleId? styleId, CancellationToken token)
{
var cells = ArrayPool<StyledCell>.Shared.Rent(1);
try
{
cells[0] = new StyledCell("PropertyA", styleId);
cells[0] = new StyledCell("Property", styleId);
await spreadsheet.AddRowAsync(cells.AsMemory(0, 1), token).ConfigureAwait(false);
}
finally
Expand All @@ -40,17 +50,17 @@ private static async ValueTask AddHeaderRow0Async(SpreadCheetah.Spreadsheet spre
}
}

private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, MyNamespace.ClassWithDuplicateColumnOrdering? obj, CancellationToken token)
private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, MyNamespace.ClassPropertyWithConverterAndCellStyle? obj, CancellationToken token)
{
if (spreadsheet is null)
throw new ArgumentNullException(nameof(spreadsheet));
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);
return spreadsheet.AddRowAsync(ReadOnlyMemory<StyledCell>.Empty, token);
return AddAsRowInternalAsync(spreadsheet, obj, token);
}

private static ValueTask AddRangeAsRowsAsync(SpreadCheetah.Spreadsheet spreadsheet,
IEnumerable<MyNamespace.ClassWithDuplicateColumnOrdering?> objs,
IEnumerable<MyNamespace.ClassPropertyWithConverterAndCellStyle?> objs,
CancellationToken token)
{
if (spreadsheet is null)
Expand All @@ -61,48 +71,50 @@ private static ValueTask AddRangeAsRowsAsync(SpreadCheetah.Spreadsheet spreadshe
}

private static async ValueTask AddAsRowInternalAsync(SpreadCheetah.Spreadsheet spreadsheet,
MyNamespace.ClassWithDuplicateColumnOrdering obj,
MyNamespace.ClassPropertyWithConverterAndCellStyle obj,
CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(1);
var cells = ArrayPool<StyledCell>.Shared.Rent(1);
try
{
var styleIds = Array.Empty<StyleId>();
var worksheetRowDependencyInfo = spreadsheet.GetOrCreateWorksheetRowDependencyInfo(Default.ClassPropertyWithConverterAndCellStyle);
var styleIds = worksheetRowDependencyInfo.StyleIds;
await AddCellsAsRowAsync(spreadsheet, obj, cells, styleIds, token).ConfigureAwait(false);
}
finally
{
ArrayPool<DataCell>.Shared.Return(cells, true);
ArrayPool<StyledCell>.Shared.Return(cells, true);
}
}

private static async ValueTask AddRangeAsRowsInternalAsync(SpreadCheetah.Spreadsheet spreadsheet,
IEnumerable<MyNamespace.ClassWithDuplicateColumnOrdering?> objs,
IEnumerable<MyNamespace.ClassPropertyWithConverterAndCellStyle?> objs,
CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(1);
var cells = ArrayPool<StyledCell>.Shared.Rent(1);
try
{
var styleIds = Array.Empty<StyleId>();
var worksheetRowDependencyInfo = spreadsheet.GetOrCreateWorksheetRowDependencyInfo(Default.ClassPropertyWithConverterAndCellStyle);
var styleIds = worksheetRowDependencyInfo.StyleIds;
foreach (var obj in objs)
{
await AddCellsAsRowAsync(spreadsheet, obj, cells, styleIds, token).ConfigureAwait(false);
}
}
finally
{
ArrayPool<DataCell>.Shared.Return(cells, true);
ArrayPool<StyledCell>.Shared.Return(cells, true);
}
}

private static ValueTask AddCellsAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet,
MyNamespace.ClassWithDuplicateColumnOrdering? obj,
DataCell[] cells, IReadOnlyList<StyleId> styleIds, CancellationToken token)
MyNamespace.ClassPropertyWithConverterAndCellStyle? obj,
StyledCell[] cells, IReadOnlyList<StyleId> styleIds, CancellationToken token)
{
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);
return spreadsheet.AddRowAsync(ReadOnlyMemory<StyledCell>.Empty, token);

cells[0] = new DataCell(obj.PropertyA);
cells[0] = new StyledCell(_valueConverter0.ConvertToDataCell(obj.Property), styleIds[0]);
return spreadsheet.AddRowAsync(cells.AsMemory(0, 1), token);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
Diagnostics: [
{
Id: SPCH1008,
Title: Attribute combination not supported,
Severity: Error,
WarningLevel: 0,
Location: : (7,19)-(7,27),
MessageFormat: Having both the {0} and the {1} attributes on a property is not supported,
Message: Having both the CellValueConverter and the CellValueTruncate attributes on a property is not supported,
Category: SpreadCheetah.SourceGenerator
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
Diagnostics: [
{
Id: SPCH1007,
Title: Invalid attribute type argument,
Severity: Error,
WarningLevel: 0,
Location: : (5,5)-(5,54),
MessageFormat: Type '{0}' is an invalid argument for {1} because it does not inherit {2},
Message: Type 'MyNamespace.DecimalValueConverter' is an invalid argument for CellValueConverterAttribute because it does not inherit CellValueConverter<decimal>,
Category: SpreadCheetah.SourceGenerator
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
Diagnostics: [
{
Id: SPCH1007,
Title: Invalid attribute type argument,
Severity: Error,
WarningLevel: 0,
Location: : (5,5)-(5,54),
MessageFormat: Type '{0}' is an invalid argument for {1} because it does not inherit {2},
Message: Type 'MyNamespace.DecimalValueConverter' is an invalid argument for CellValueConverterAttribute because it does not inherit CellValueConverter<string>,
Category: SpreadCheetah.SourceGenerator
}
]
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,32 @@ public partial class MyGenRowContext
public MyGenRowContext()
{
}
private static readonly MyNamespace.StringValueConverter _valueConverter0 = new MyNamespace.StringValueConverter();
private static readonly MyNamespace.NullableIntValueConverter _valueConverter1 = new MyNamespace.NullableIntValueConverter();
private static readonly MyNamespace.DecimalValueConverter _valueConverter2 = new MyNamespace.DecimalValueConverter();

private WorksheetRowTypeInfo<MyNamespace.ClassWithDuplicateColumnOrdering>? _ClassWithDuplicateColumnOrdering;
public WorksheetRowTypeInfo<MyNamespace.ClassWithDuplicateColumnOrdering> ClassWithDuplicateColumnOrdering => _ClassWithDuplicateColumnOrdering
??= WorksheetRowMetadataServices.CreateObjectInfo<MyNamespace.ClassWithDuplicateColumnOrdering>(
private WorksheetRowTypeInfo<MyNamespace.ClassWithMultipleConverters>? _ClassWithMultipleConverters;
public WorksheetRowTypeInfo<MyNamespace.ClassWithMultipleConverters> ClassWithMultipleConverters => _ClassWithMultipleConverters
??= WorksheetRowMetadataServices.CreateObjectInfo<MyNamespace.ClassWithMultipleConverters>(
AddHeaderRow0Async, AddAsRowAsync, AddRangeAsRowsAsync, null);

private static async ValueTask AddHeaderRow0Async(SpreadCheetah.Spreadsheet spreadsheet, SpreadCheetah.Styling.StyleId? styleId, CancellationToken token)
{
var cells = ArrayPool<StyledCell>.Shared.Rent(1);
var cells = ArrayPool<StyledCell>.Shared.Rent(3);
try
{
cells[0] = new StyledCell("PropertyB", styleId);
await spreadsheet.AddRowAsync(cells.AsMemory(0, 1), token).ConfigureAwait(false);
cells[0] = new StyledCell("Property", styleId);
cells[1] = new StyledCell("Property1", styleId);
cells[2] = new StyledCell("Property2", styleId);
await spreadsheet.AddRowAsync(cells.AsMemory(0, 3), token).ConfigureAwait(false);
}
finally
{
ArrayPool<StyledCell>.Shared.Return(cells, true);
}
}

private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, MyNamespace.ClassWithDuplicateColumnOrdering? obj, CancellationToken token)
private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, MyNamespace.ClassWithMultipleConverters? obj, CancellationToken token)
{
if (spreadsheet is null)
throw new ArgumentNullException(nameof(spreadsheet));
Expand All @@ -50,7 +55,7 @@ private static ValueTask AddAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet, My
}

private static ValueTask AddRangeAsRowsAsync(SpreadCheetah.Spreadsheet spreadsheet,
IEnumerable<MyNamespace.ClassWithDuplicateColumnOrdering?> objs,
IEnumerable<MyNamespace.ClassWithMultipleConverters?> objs,
CancellationToken token)
{
if (spreadsheet is null)
Expand All @@ -61,10 +66,10 @@ private static ValueTask AddRangeAsRowsAsync(SpreadCheetah.Spreadsheet spreadshe
}

private static async ValueTask AddAsRowInternalAsync(SpreadCheetah.Spreadsheet spreadsheet,
MyNamespace.ClassWithDuplicateColumnOrdering obj,
MyNamespace.ClassWithMultipleConverters obj,
CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(1);
var cells = ArrayPool<DataCell>.Shared.Rent(3);
try
{
var styleIds = Array.Empty<StyleId>();
Expand All @@ -77,10 +82,10 @@ private static async ValueTask AddAsRowInternalAsync(SpreadCheetah.Spreadsheet s
}

private static async ValueTask AddRangeAsRowsInternalAsync(SpreadCheetah.Spreadsheet spreadsheet,
IEnumerable<MyNamespace.ClassWithDuplicateColumnOrdering?> objs,
IEnumerable<MyNamespace.ClassWithMultipleConverters?> objs,
CancellationToken token)
{
var cells = ArrayPool<DataCell>.Shared.Rent(1);
var cells = ArrayPool<DataCell>.Shared.Rent(3);
try
{
var styleIds = Array.Empty<StyleId>();
Expand All @@ -96,14 +101,16 @@ private static async ValueTask AddRangeAsRowsInternalAsync(SpreadCheetah.Spreads
}

private static ValueTask AddCellsAsRowAsync(SpreadCheetah.Spreadsheet spreadsheet,
MyNamespace.ClassWithDuplicateColumnOrdering? obj,
MyNamespace.ClassWithMultipleConverters? obj,
DataCell[] cells, IReadOnlyList<StyleId> styleIds, CancellationToken token)
{
if (obj is null)
return spreadsheet.AddRowAsync(ReadOnlyMemory<DataCell>.Empty, token);

cells[0] = new DataCell(obj.PropertyB);
return spreadsheet.AddRowAsync(cells.AsMemory(0, 1), token);
cells[0] = _valueConverter0.ConvertToDataCell(obj.Property);
cells[1] = _valueConverter1.ConvertToDataCell(obj.Property1);
cells[2] = _valueConverter2.ConvertToDataCell(obj.Property2);
return spreadsheet.AddRowAsync(cells.AsMemory(0, 3), token);
}

private static DataCell ConstructTruncatedDataCell(string? value, int truncateLength)
Expand Down
Loading
Loading