diff --git a/.editorconfig b/.editorconfig index 4d0b015b..09f6ff08 100644 --- a/.editorconfig +++ b/.editorconfig @@ -16,126 +16,105 @@ indent_size = 2 ############################### # .NET Coding Conventions # ############################### -# Organize usings +[*.cs] +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const dotnet_sort_system_directives_first = false -# this. preferences -dotnet_style_qualification_for_field = false:suggestion -dotnet_style_qualification_for_property = false:suggestion -dotnet_style_qualification_for_method = false:suggestion -dotnet_style_qualification_for_event = false:suggestion -# Language keywords vs BCL types preferences -dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion -dotnet_style_predefined_type_for_member_access = true:suggestion -# Parentheses preferences -dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent -dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent -# Modifier preferences -dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning -dotnet_style_readonly_field = true:suggestion -# Expression-level preferences -dotnet_style_object_initializer = true:suggestion +dotnet_style_coalesce_expression = true:suggestion dotnet_style_collection_initializer = true:suggestion dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion dotnet_style_null_propagation = true:suggestion -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent -dotnet_style_prefer_inferred_tuple_names = true:suggestion -dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_object_initializer = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion dotnet_style_prefer_auto_properties = true:suggestion +dotnet_style_prefer_collection_expression = when_types_loosely_match:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion dotnet_style_prefer_conditional_expression_over_return = true:suggestion -dotnet_style_prefer_conditional_expression_over_assignment = true:suggestion -dotnet_style_prefer_conditional_expression_over_return = true:suggestion -dotnet_style_prefer_auto_properties = true:suggestion -dotnet_style_object_initializer = true:suggestion -dotnet_style_collection_initializer = true:suggestion -dotnet_style_prefer_simplified_boolean_expressions = true:suggestion -# Operator preferences -dotnet_style_operator_placement_when_wrapping = beginning_of_line -dotnet_style_coalesce_expression = true:suggestion -dotnet_style_null_propagation = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion - -############################### -# Naming Conventions # -############################### -# Style Definitions -dotnet_naming_style.pascal_case_style.capitalization = pascal_case -# Use PascalCase for constant fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion -dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields -dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style -dotnet_naming_symbols.constant_fields.applicable_kinds = field -dotnet_naming_symbols.constant_fields.applicable_accessibilities = * -dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_qualification_for_event = false:suggestion +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:warning ############################### # C# Coding Conventions # ############################### -# var preferences -csharp_style_var_for_built_in_types = true:suggestion -csharp_style_var_when_type_is_apparent = true:suggestion -csharp_style_var_elsewhere = true:suggestion -# Expression-bodied members -csharp_style_expression_bodied_methods = false:silent -csharp_style_expression_bodied_constructors = false:silent -csharp_style_expression_bodied_operators = false:silent -csharp_style_expression_bodied_properties = true:suggestion -csharp_style_expression_bodied_indexers = true:silent -csharp_style_expression_bodied_accessors = true:suggestion -# Pattern matching preferences -csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion -csharp_style_pattern_matching_over_as_with_null_check = true:suggestion -# Null-checking preferences -csharp_style_throw_expression = true:suggestion -csharp_style_conditional_delegate_call = true:suggestion -# Modifier preferences -csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion -# Expression-level preferences -csharp_prefer_braces = when-multiline:suggestion -csharp_style_deconstructed_variable_declaration = true:suggestion -csharp_prefer_simple_default_expression = true:suggestion -csharp_style_pattern_local_over_anonymous_function = true:suggestion -csharp_style_inlined_variable_declaration = true:suggestion -# Other -csharp_using_directive_placement = outside_namespace:warning -csharp_prefer_simple_using_statement = true:warning -csharp_style_namespace_declarations = file_scoped:warning -csharp_style_expression_bodied_lambdas = true:suggestion -csharp_style_expression_bodied_local_functions = true:suggestion - -############################### -# C# Formatting Rules # -############################### -# New line preferences -csharp_new_line_before_open_brace = all -csharp_new_line_before_else = true +[*.cs] +csharp_indent_case_contents = true +csharp_indent_labels = one_less_than_current +csharp_indent_switch_labels = true csharp_new_line_before_catch = true +csharp_new_line_before_else = true csharp_new_line_before_finally = true -csharp_new_line_before_members_in_object_initializers = true csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_open_brace = all csharp_new_line_between_query_expression_clauses = true -# Indentation preferences -csharp_indent_case_contents = true -csharp_indent_switch_labels = true -csharp_indent_labels = flush_left -# Space preferences +csharp_prefer_braces = false:silent +csharp_prefer_simple_default_expression = true:suggestion +csharp_prefer_simple_using_statement = true:warning +csharp_prefer_system_threading_lock = true:suggestion +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = true csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_around_binary_operators = before_and_after +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false csharp_space_between_method_declaration_parameter_list_parentheses = false csharp_space_between_parentheses = false -csharp_space_before_colon_in_inheritance_clause = true -csharp_space_after_colon_in_inheritance_clause = true -csharp_space_around_binary_operators = before_and_after -csharp_space_between_method_declaration_empty_parameter_list_parentheses = false -csharp_space_between_method_call_name_and_opening_parenthesis = false -csharp_space_between_method_call_empty_parameter_list_parentheses = false -# Wrapping preferences -csharp_preserve_single_line_statements = true -csharp_preserve_single_line_blocks = true +csharp_style_conditional_delegate_call = true:suggestion +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_style_expression_bodied_accessors = true:suggestion +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:suggestion +csharp_style_expression_bodied_local_functions = true:suggestion +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_namespace_declarations = file_scoped:warning +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_primary_constructors = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_prefer_top_level_statements = true:silent +csharp_style_throw_expression = true:suggestion +csharp_style_unused_value_expression_statement_preference = discard_variable:none +csharp_style_var_elsewhere = true:suggestion +csharp_style_var_for_built_in_types = true:suggestion +csharp_style_var_when_type_is_apparent = true:suggestion +csharp_using_directive_placement = outside_namespace:warning ############################### # .NET Analyzers Options # @@ -147,8 +126,12 @@ dotnet_diagnostic.CA1303.severity = none dotnet_diagnostic.CA1815.severity = warning # CA2201: Do not raise reserved exception types dotnet_diagnostic.CA2201.severity = warning +# IDE0010: Add missing cases to switch statement +dotnet_diagnostic.IDE0010.severity = none # IDE0028: Use collection initializers or expressions dotnet_diagnostic.IDE0028.severity = warning +# IDE0072: Add missing cases to switch expression +dotnet_diagnostic.IDE0072.severity = none # IDE0300: Use collection expression for array dotnet_diagnostic.IDE0300.severity = warning # IDE0301: Use collection expression for empty @@ -161,12 +144,20 @@ dotnet_diagnostic.IDE0305.severity = warning dotnet_diagnostic.CA1019.severity = none # CS9113: Parameter is unread dotnet_diagnostic.CS9113.severity = none +# IDE0060: Remove unused parameter +dotnet_diagnostic.IDE0060.severity = none + +[{*Test*,*.Benchmark,*Check,*.Cmd,*.SourceGenerator}/**.cs] +# IDE0005: Remove unnecessary using directives (requires GenerateDocumentationFile to be true) +dotnet_diagnostic.IDE0005.severity = none [{*Test,*.Benchmark,*.Cmd}/**.cs] # CA1305: Specify IFormatProvider dotnet_diagnostic.CA1305.severity = none # CA1062: Validate arguments of public methods dotnet_diagnostic.CA1062.severity = none +# CA1515: Consider making public types internal +dotnet_diagnostic.CA1515.severity = none # CA1707: Identifiers should not contain underscores dotnet_diagnostic.CA1707.severity = none # CA1720: Identifiers should not contain type names @@ -241,6 +232,8 @@ dotnet_diagnostic.MA0076.severity = none # Roslynator Options # ############################### [*.cs] +# RCS1036: Remove unnecessary blank line +dotnet_diagnostic.RCS1036.severity = warning # RCS1163: Unused parameter dotnet_diagnostic.RCS1163.severity = warning # RCS1205: Order named arguments according to the order of parameters diff --git a/Directory.Build.props b/Directory.Build.props index 5b8f0bd0..5fde3ca4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -1,10 +1,10 @@ - 12 + 13 enable enable true - 8-all + 9-all true true diff --git a/Directory.Packages.props b/Directory.Packages.props index a00a2e38..f89a6ad3 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,9 +5,9 @@ - + - + @@ -15,13 +15,13 @@ - + - - + + @@ -29,13 +29,16 @@ - + + + - - + + + - + diff --git a/README.md b/README.md index 55bc26f8..3387e811 100644 --- a/README.md +++ b/README.md @@ -11,8 +11,8 @@ SpreadCheetah is a high-performance .NET library for generating spreadsheet (Mic - Low memory allocation (see benchmarks below) - Async APIs - No dependency to Microsoft Excel -- Targeting .NET Standard 2.0 for .NET Framework and earlier versions of .NET Core -- Targeting .NET 6 and newer for more optimizations +- Supports .NET Standard 2.0 targets, such as .NET Framework +- Supports .NET 6 to .NET 9, with more optimizations enabled for later versions - Trimmable and NativeAOT compatible SpreadCheetah is designed to create spreadsheet files in a forward-only manner. @@ -24,7 +24,15 @@ Most basic spreadsheet functionality is supported, such as cells with different See [Releases](https://github.com/sveinungf/spreadcheetah/releases) for release notes. This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## How to install -SpreadCheetah is available as a [package on nuget.org](https://www.nuget.org/packages/SpreadCheetah). The NuGet package targets .NET Standard 2.0 as well as newer versions of .NET. The .NET Standard 2.0 version is intended for backwards compatibility (.NET Framework and earlier versions of .NET Core). More optimizations are enabled when targeting newer versions of .NET. +SpreadCheetah is available as a [package on nuget.org](https://www.nuget.org/packages/SpreadCheetah). +### .NET CLI +``` +dotnet add package SpreadCheetah +``` +### Package Manager +``` +Install-Package SpreadCheetah +``` ## Basic usage ```cs @@ -125,23 +133,23 @@ More features of the source generator can be seen in the [wiki](https://github.c The benchmark results here have been collected using [BenchmarkDotNet](https://github.com/dotnet/benchmarkdotnet) with the following system configuration: ``` ini -BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.4894/22H2/2022Update) +BenchmarkDotNet v0.14.0, Windows 10 (10.0.19045.5131/22H2/2022Update) Intel Core i5-8600K CPU 3.60GHz (Coffee Lake), 1 CPU, 6 logical and 6 physical cores -.NET SDK 9.0.100-preview.7.24407.12 - [Host] : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 - .NET 6.0 : .NET 6.0.33 (6.0.3324.36610), X64 RyuJIT AVX2 - .NET 8.0 : .NET 8.0.8 (8.0.824.36612), X64 RyuJIT AVX2 - .NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9261.0), X64 RyuJIT VectorSize=256 +.NET SDK 9.0.100 + [Host] : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 + .NET 8.0 : .NET 8.0.11 (8.0.1124.51707), X64 RyuJIT AVX2 + .NET 9.0 : .NET 9.0.0 (9.0.24.52809), X64 RyuJIT AVX2 + .NET Framework 4.8 : .NET Framework 4.8.1 (4.8.9282.0), X64 RyuJIT VectorSize=256 -InvocationCount=1 UnrollFactor=1 +InvocationCount=1 UnrollFactor=1 ``` These libraries have been used in the comparison benchmarks: | Library | Version | |--------------------------------------------------------|--------:| -| SpreadCheetah | 1.18.0 | -| [Open XML SDK](https://github.com/dotnet/Open-XML-SDK) | 2.20.0 | -| [ClosedXML](https://github.com/ClosedXML/ClosedXML) | 0.102.3 | +| SpreadCheetah | 1.19.0 | +| [Open XML SDK](https://github.com/dotnet/Open-XML-SDK) | 3.1.1 | +| [ClosedXML](https://github.com/ClosedXML/ClosedXML) | 0.104.2 | | [EPPlusFree](https://github.com/rimland/EPPlus) | 4.5.3.8 | > Disclaimer: The libraries have different feature sets compared to each other. @@ -157,28 +165,28 @@ but to make this a fair comparison the idea is to use the most efficient approac The code is available [in the Benchmark project](https://github.com/sveinungf/spreadcheetah/blob/main/SpreadCheetah.Benchmark/Benchmarks/StringCells.cs).
-

.NET 8

+

.NET 9

| Library | Mean | Allocated | |----------------------------|-------------:|--------------:| -| **SpreadCheetah** | **21.22 ms** | **6.33 KB** | -| Open XML (SAX approach) | 185.66 ms | 66 037.32 KB | -| EPPlusFree | 358.31 ms | 195 610.91 KB | -| Open XML (DOM approach) | 701.17 ms | 182 916.73 KB | -| ClosedXML | 739.16 ms | 529 203.20 KB | +| **SpreadCheetah** | **14.72 ms** | **6.25 KB** | +| Open XML (SAX approach) | 161.14 ms | 64 958.95 KB | +| EPPlusFree | 346.14 ms | 195 608.41 KB | +| Open XML (DOM approach) | 486.15 ms | 135 579.86 KB | +| ClosedXML | 562.77 ms | 265 742.71 KB |
-

.NET 6

+

.NET 8

| Library | Mean | Allocated | |----------------------------|-------------:|--------------:| -| **SpreadCheetah** | **25.05 ms** | **6.52 KB** | -| Open XML (SAX approach) | 234.12 ms | 66 052.24 KB | -| EPPlusFree | 407.38 ms | 195 791.84 KB | -| Open XML (DOM approach) | 803.30 ms | 182 926.09 KB | -| ClosedXML | 874.41 ms | 529 844.80 KB | +| **SpreadCheetah** | **21.51 ms** | **6.33 KB** | +| Open XML (SAX approach) | 179.69 ms | 64 958.03 KB | +| EPPlusFree | 351.35 ms | 195 610.91 KB | +| Open XML (DOM approach) | 515.54 ms | 135 585.72 KB | +| ClosedXML | 609.47 ms | 284 490.52 KB |
@@ -187,9 +195,9 @@ The code is available [in the Benchmark project](https://github.com/sveinungf/sp | Library | Mean | Allocated | |----------------------------|-------------:|--------------:| -| **SpreadCheetah** | **72.34 ms** | **152.23 KB** | +| **SpreadCheetah** | **71.08 ms** | **144.23 KB** | | Open XML (SAX approach) | 408.03 ms | 43 317.24 KB | -| EPPlusFree | 622.80 ms | 286 141.77 KB | -| Open XML (DOM approach) | 1,070.28 ms | 161 067.34 KB | -| ClosedXML | 1,319.16 ms | 509 205.80 KB | +| EPPlusFree | 604.59 ms | 286 141.34 KB | +| Open XML (DOM approach) | 777.62 ms | 113 059.66 KB | +| ClosedXML | 1,125.82 ms | 263 766.13 KB | diff --git a/SpreadCheetah.AotCompatibilityCheck/SpreadCheetah.AotCompatibilityCheck.csproj b/SpreadCheetah.AotCompatibilityCheck/SpreadCheetah.AotCompatibilityCheck.csproj index cb2c3dbd..3d3d4eee 100644 --- a/SpreadCheetah.AotCompatibilityCheck/SpreadCheetah.AotCompatibilityCheck.csproj +++ b/SpreadCheetah.AotCompatibilityCheck/SpreadCheetah.AotCompatibilityCheck.csproj @@ -2,7 +2,7 @@ Exe - net8.0 + net9.0 true false diff --git a/SpreadCheetah.Benchmark.Test/Helpers/SpreadsheetAssert.cs b/SpreadCheetah.Benchmark.Test/Helpers/SpreadsheetAssert.cs deleted file mode 100644 index 317026c5..00000000 --- a/SpreadCheetah.Benchmark.Test/Helpers/SpreadsheetAssert.cs +++ /dev/null @@ -1,20 +0,0 @@ -using DocumentFormat.OpenXml.Packaging; -using DocumentFormat.OpenXml.Validation; -using Xunit; - -namespace SpreadCheetah.Benchmark.Test.Helpers; - -internal static class SpreadsheetAssert -{ - public static void Valid(Stream stream) - { - stream.Position = 0; - - using var document = SpreadsheetDocument.Open(stream, false); - var validator = new OpenXmlValidator(); - var errors = validator.Validate(document); - Assert.Empty(errors); - - stream.Position = 0; - } -} diff --git a/SpreadCheetah.Benchmark.Test/SpreadCheetah.Benchmark.Test.csproj b/SpreadCheetah.Benchmark.Test/SpreadCheetah.Benchmark.Test.csproj index 58c60b2a..b0ccd16b 100644 --- a/SpreadCheetah.Benchmark.Test/SpreadCheetah.Benchmark.Test.csproj +++ b/SpreadCheetah.Benchmark.Test/SpreadCheetah.Benchmark.Test.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 false @@ -20,6 +20,7 @@ + diff --git a/SpreadCheetah.Benchmark.Test/Tests/StringCellsTests.cs b/SpreadCheetah.Benchmark.Test/Tests/StringCellsTests.cs index 07a7fdfe..35acbbca 100644 --- a/SpreadCheetah.Benchmark.Test/Tests/StringCellsTests.cs +++ b/SpreadCheetah.Benchmark.Test/Tests/StringCellsTests.cs @@ -1,7 +1,7 @@ -using ClosedXML.Excel; using SpreadCheetah.Benchmark.Benchmarks; -using SpreadCheetah.Benchmark.Test.Helpers; +using SpreadCheetah.TestHelpers.Assertions; using Xunit; +using Xunit.Abstractions; namespace SpreadCheetah.Benchmark.Test.Tests; @@ -10,9 +10,11 @@ public sealed class StringCellsTests : IDisposable private const int NumberOfColumns = 10; private const int NumberOfRows = 20000; private readonly StringCells _stringCells; + private readonly ITestOutputHelper _output; - public StringCellsTests() + public StringCellsTests(ITestOutputHelper output) { + _output = output; _stringCells = new StringCells { NumberOfColumns = NumberOfColumns, @@ -35,6 +37,7 @@ public async Task StringCells_SpreadCheetah_CorrectCellValues() await _stringCells.SpreadCheetah(); // Assert + WriteOutput(); AssertCellValuesEqual(); } @@ -45,6 +48,7 @@ public void StringCells_EpPlus4_CorrectCellValues() _stringCells.EpPlus4(); // Assert + WriteOutput(); AssertCellValuesEqual(); } @@ -55,6 +59,7 @@ public void StringCells_OpenXmlSax_CorrectCellValues() _stringCells.OpenXmlSax(); // Assert + WriteOutput(); AssertCellValuesEqual(); } @@ -65,6 +70,7 @@ public void StringCells_OpenXmlDom_CorrectCellValues() _stringCells.OpenXmlDom(); // Assert + WriteOutput(); AssertCellValuesEqual(); } @@ -75,27 +81,30 @@ public void StringCells_ClosedXml_CorrectCellValues() _stringCells.ClosedXml(); // Assert + WriteOutput(); AssertCellValuesEqual(); } + private void WriteOutput() + { + _output.WriteLine("Stream length: " + _stringCells.Stream.Length); + } + private void AssertCellValuesEqual() { - SpreadsheetAssert.Valid(_stringCells.Stream); + using var sheet = SpreadsheetAssert.SingleSheet(_stringCells.Stream); - _stringCells.Stream.Position = 0; - using var workbook = new XLWorkbook(_stringCells.Stream); - var worksheet = workbook.Worksheets.Single(); var values = _stringCells.Values; + Assert.Equal(values.Count, sheet.RowCount); - for (var r = 0; r < values.Count; ++r) + foreach (var (r, rowValues) in values.Index()) { - var row = worksheet.Row(r + 1); - var rowValues = values[r]; + var row = sheet.Row(r + 1).ToList(); + Assert.Equal(rowValues.Count, row.Count); - for (var c = 0; c < rowValues.Count; ++c) + foreach (var (c, cellValue) in rowValues.Index()) { - var cell = row.Cell(c + 1); - Assert.Equal(rowValues[c], cell.Value); + Assert.Equal(cellValue, row[c].StringValue); } } } diff --git a/SpreadCheetah.Benchmark/Benchmarks/GetColumnName.cs b/SpreadCheetah.Benchmark/Benchmarks/GetColumnName.cs index 1d54e93e..17ed3581 100644 --- a/SpreadCheetah.Benchmark/Benchmarks/GetColumnName.cs +++ b/SpreadCheetah.Benchmark/Benchmarks/GetColumnName.cs @@ -1,156 +1,53 @@ using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; -using System.Diagnostics.CodeAnalysis; namespace SpreadCheetah.Benchmark.Benchmarks; [SimpleJob(RuntimeMoniker.Net48)] -[SimpleJob(RuntimeMoniker.Net70)] [SimpleJob(RuntimeMoniker.Net80)] +[SimpleJob(RuntimeMoniker.Net90)] [MemoryDiagnoser(false)] public class GetColumnName { - private int[] _numbers = null!; + private int[] _numbers1 = null!; + private int[] _numbers2 = null!; + private int[] _numbers3 = null!; private string[] _names = null!; - [Params(26, 16384)] - public int MaxNumber { get; set; } + [Params(1, 2, 3)] + public int NumberOfCharacters { get; set; } [GlobalSetup] public void GlobalSetup() { var random = new Random(42); - _numbers = Enumerable.Range(0, 10000).Select(_ => random.Next(1, MaxNumber)).ToArray(); + _numbers1 = Enumerable.Range(0, 1000000).Select(_ => random.Next(1, 27)).ToArray(); + _numbers2 = Enumerable.Range(0, 1000000).Select(_ => random.Next(27, 703)).ToArray(); + _numbers3 = Enumerable.Range(0, 1000000).Select(_ => random.Next(703, 16384)).ToArray(); } [IterationSetup] public void IterationSetup() { - _names = new string[_numbers.Length]; + _names = new string[_numbers1.Length]; } [Benchmark(Baseline = true)] public string[] GetColumnNameBaseline() { - var names = _names; - for (var i = 0; i < _numbers.Length; i++) - { - names[i] = SpreadsheetUtility.GetColumnName(_numbers[i]); - } - return names; - } - - [Benchmark] - public string[] GetColumnNameWithLoop() - { - var names = _names; - for (var i = 0; i < _numbers.Length; i++) - { - names[i] = GetColumnNameWithLoop(_numbers[i]); - } - return names; - } - - [Benchmark] - public string[] GetColumnNameWithStringFormat() - { - var names = _names; - for (var i = 0; i < _numbers.Length; i++) + var numbers = NumberOfCharacters switch { - names[i] = GetColumnNameWithStringFormat(_numbers[i]); - } - return names; - } + 1 => _numbers1, + 2 => _numbers2, + 3 => _numbers3, + _ => throw new InvalidOperationException() + }; - [Benchmark] - public string[] GetColumnNameWithModulo() - { var names = _names; - for (var i = 0; i < _numbers.Length; i++) + for (var i = 0; i < numbers.Length; i++) { - names[i] = GetColumnNameWithModulo(_numbers[i]); + names[i] = SpreadsheetUtility.GetColumnName(numbers[i]); } return names; } - - private const int MaxNumberOfColumns = 16384; - - [DoesNotReturn] - public static void ThrowColumnNumberInvalid(string? paramName, int number) => throw new ArgumentOutOfRangeException(paramName, number, "The column number must be greater than 0 and can't be larger than 16384."); - - private static string GetColumnNameWithLoop(int columnNumber) - { - if (columnNumber is < 1 or > MaxNumberOfColumns) - ThrowColumnNumberInvalid(nameof(columnNumber), columnNumber); - - string columnName = ""; - - while (columnNumber > 0) - { - int modulo = (columnNumber - 1) % 26; -#pragma warning disable S1643 // Strings should not be concatenated using '+' in a loop - columnName = Convert.ToChar('A' + modulo) + columnName; -#pragma warning restore S1643 // Strings should not be concatenated using '+' in a loop - columnNumber = (columnNumber - modulo) / 26; - } - - return columnName; - } - - public static string GetColumnNameWithStringFormat(int columnNumber) - { - if (columnNumber is < 1 or > MaxNumberOfColumns) - ThrowColumnNumberInvalid(nameof(columnNumber), columnNumber); - - if (columnNumber <= 26) - return ((char)(columnNumber + 'A' - 1)).ToString(); - - if (columnNumber <= 702) - { - var quotient = Math.DivRem(columnNumber - 1, 26, out var remainder); - char firstChar = (char)('A' - 1 + quotient); - char secondChar = (char)('A' + remainder); - return string.Format("{0}{1}", firstChar, secondChar); - } - else - { - var quotient1 = Math.DivRem(columnNumber - 1, 26, out var remainder1); - var quotient2 = Math.DivRem(quotient1 - 1, 26, out var remainder2); - char firstChar = (char)('A' - 1 + quotient2); - char secondChar = (char)('A' + remainder2); - char thirdChar = (char)('A' + remainder1); - return string.Format("{0}{1}{2}", firstChar, secondChar, thirdChar); - } - } - - public static string GetColumnNameWithModulo(int columnNumber) - { - if (columnNumber is < 1 or > MaxNumberOfColumns) - ThrowColumnNumberInvalid(nameof(columnNumber), columnNumber); - - if (columnNumber <= 26) - return ((char)(columnNumber + 'A' - 1)).ToString(); - - if (columnNumber <= 702) - { - Span characters = stackalloc char[2]; - var quotient = (columnNumber - 1) / 26; - var remainder = (columnNumber - 1) % 26; - characters[0] = (char)('A' - 1 + quotient); - characters[1] = (char)('A' + remainder); - return characters.ToString(); - } - else - { - Span characters = stackalloc char[3]; - var quotient1 = (columnNumber - 1) / 26; - var remainder1 = (columnNumber - 1) % 26; - var quotient2 = (quotient1 - 1) / 26; - var remainder2 = (quotient1 - 1) % 26; - characters[0] = (char)('A' - 1 + quotient2); - characters[1] = (char)('A' + remainder2); - characters[2] = (char)('A' + remainder1); - return characters.ToString(); - } - } } diff --git a/SpreadCheetah.Benchmark/Benchmarks/StringCells.cs b/SpreadCheetah.Benchmark/Benchmarks/StringCells.cs index 14b639b5..3c6152e4 100644 --- a/SpreadCheetah.Benchmark/Benchmarks/StringCells.cs +++ b/SpreadCheetah.Benchmark/Benchmarks/StringCells.cs @@ -12,8 +12,8 @@ namespace SpreadCheetah.Benchmark.Benchmarks; [SimpleJob(RuntimeMoniker.Net48)] -[SimpleJob(RuntimeMoniker.Net60)] [SimpleJob(RuntimeMoniker.Net80)] +[SimpleJob(RuntimeMoniker.Net90)] [MemoryDiagnoser] public class StringCells : IDisposable { @@ -118,10 +118,11 @@ public void OpenXmlSax() var cellAttributes = new[] { new OpenXmlAttribute("t", "", "inlineStr") }; var cell = new OpenXmlCell(); var inlineString = new InlineString(); + var rowAttributes = new OpenXmlAttribute[1]; for (var row = 0; row < NumberOfRows; ++row) { - var rowAttributes = new[] { new OpenXmlAttribute("r", "", (row + 1).ToString()) }; + rowAttributes[0] = new OpenXmlAttribute("r", "", (row + 1).ToString()); oxw.WriteStartElement(rowObject, rowAttributes); var rowValues = Values[row]; diff --git a/SpreadCheetah.Benchmark/SpreadCheetah.Benchmark.csproj b/SpreadCheetah.Benchmark/SpreadCheetah.Benchmark.csproj index 9a71f2e7..6eb8911b 100644 --- a/SpreadCheetah.Benchmark/SpreadCheetah.Benchmark.csproj +++ b/SpreadCheetah.Benchmark/SpreadCheetah.Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net48;net6.0;net7.0;net8.0 + net48;net8.0;net9.0 AnyCPU @@ -15,7 +15,9 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + +
diff --git a/SpreadCheetah.SourceGenerator.SnapshotTest/SpreadCheetah.SourceGenerator.SnapshotTest.csproj b/SpreadCheetah.SourceGenerator.SnapshotTest/SpreadCheetah.SourceGenerator.SnapshotTest.csproj index 2a4c6376..e5c6ff44 100644 --- a/SpreadCheetah.SourceGenerator.SnapshotTest/SpreadCheetah.SourceGenerator.SnapshotTest.csproj +++ b/SpreadCheetah.SourceGenerator.SnapshotTest/SpreadCheetah.SourceGenerator.SnapshotTest.csproj @@ -1,7 +1,7 @@ - net8.0 + net9.0 false @@ -14,6 +14,7 @@ + diff --git a/SpreadCheetah.SourceGenerator.Test/Models/Accessibility/DefaultAccessibilityClassWithSingleProperty.cs b/SpreadCheetah.SourceGenerator.Test/Models/Accessibility/DefaultAccessibilityClassWithSingleProperty.cs index 68f8f3af..827bd198 100644 --- a/SpreadCheetah.SourceGenerator.Test/Models/Accessibility/DefaultAccessibilityClassWithSingleProperty.cs +++ b/SpreadCheetah.SourceGenerator.Test/Models/Accessibility/DefaultAccessibilityClassWithSingleProperty.cs @@ -1,6 +1,8 @@ namespace SpreadCheetah.SourceGenerator.Test.Models.Accessibility; +#pragma warning disable IDE0040 // Add accessibility modifiers class DefaultAccessibilityClassWithSingleProperty +#pragma warning restore IDE0040 // Add accessibility modifiers { public string? Name { get; set; } } diff --git a/SpreadCheetah.SourceGenerator.Test/Models/CellValueConverter/ClassWithCellValueConverterOnCustomType.cs b/SpreadCheetah.SourceGenerator.Test/Models/CellValueConverter/ClassWithCellValueConverterOnCustomType.cs index 4dd76f30..1cd82081 100644 --- a/SpreadCheetah.SourceGenerator.Test/Models/CellValueConverter/ClassWithCellValueConverterOnCustomType.cs +++ b/SpreadCheetah.SourceGenerator.Test/Models/CellValueConverter/ClassWithCellValueConverterOnCustomType.cs @@ -5,7 +5,7 @@ namespace SpreadCheetah.SourceGenerator.Test.Models.CellValueConverter; public class ClassWithCellValueConverterOnCustomType { public string Property { get; init; } = null!; - + [CellValueConverter(typeof(NullToDashValueConverter))] public object? ComplexProperty { get; init; } diff --git a/SpreadCheetah.SourceGenerator.Test/Models/ColumnOrdering/StructWithColumnOrdering.cs b/SpreadCheetah.SourceGenerator.Test/Models/ColumnOrdering/StructWithColumnOrdering.cs index b528b97b..5d41587b 100644 --- a/SpreadCheetah.SourceGenerator.Test/Models/ColumnOrdering/StructWithColumnOrdering.cs +++ b/SpreadCheetah.SourceGenerator.Test/Models/ColumnOrdering/StructWithColumnOrdering.cs @@ -2,7 +2,9 @@ namespace SpreadCheetah.SourceGenerator.Test.Models.ColumnOrdering; +#pragma warning disable IDE0250 // Make struct 'readonly' public struct StructWithColumnOrdering(string firstName, string lastName, decimal gpa, int age) +#pragma warning restore IDE0250 // Make struct 'readonly' { [ColumnOrder(2)] public string FirstName { get; } = firstName; diff --git a/SpreadCheetah.SourceGenerator.Test/Models/Contexts/DefaultAccessibilityContext.cs b/SpreadCheetah.SourceGenerator.Test/Models/Contexts/DefaultAccessibilityContext.cs index c6ad5570..0347dbdd 100644 --- a/SpreadCheetah.SourceGenerator.Test/Models/Contexts/DefaultAccessibilityContext.cs +++ b/SpreadCheetah.SourceGenerator.Test/Models/Contexts/DefaultAccessibilityContext.cs @@ -4,6 +4,8 @@ namespace SpreadCheetah.SourceGenerator.Test.Models.Contexts; [WorksheetRow(typeof(DefaultAccessibilityClassWithSingleProperty))] +#pragma warning disable IDE0040 // Add accessibility modifiers partial class DefaultAccessibilityContext : WorksheetRowContext +#pragma warning restore IDE0040 // Add accessibility modifiers { } diff --git a/SpreadCheetah.SourceGenerator.Test/Models/MultipleProperties/StructWithMultipleProperties.cs b/SpreadCheetah.SourceGenerator.Test/Models/MultipleProperties/StructWithMultipleProperties.cs index 81919842..5197ed8c 100644 --- a/SpreadCheetah.SourceGenerator.Test/Models/MultipleProperties/StructWithMultipleProperties.cs +++ b/SpreadCheetah.SourceGenerator.Test/Models/MultipleProperties/StructWithMultipleProperties.cs @@ -1,6 +1,8 @@ namespace SpreadCheetah.SourceGenerator.Test.Models.MultipleProperties; +#pragma warning disable IDE0250 // Make struct 'readonly' public struct StructWithMultipleProperties +#pragma warning restore IDE0250 // Make struct 'readonly' { public string FirstName { get; } public string LastName { get; } diff --git a/SpreadCheetah.SourceGenerator.Test/SpreadCheetah.SourceGenerator.Test.csproj b/SpreadCheetah.SourceGenerator.Test/SpreadCheetah.SourceGenerator.Test.csproj index 56d83ca3..b098336f 100644 --- a/SpreadCheetah.SourceGenerator.Test/SpreadCheetah.SourceGenerator.Test.csproj +++ b/SpreadCheetah.SourceGenerator.Test/SpreadCheetah.SourceGenerator.Test.csproj @@ -1,7 +1,7 @@ - net472;net6.0;net7.0;net8.0 + net472;net8.0;net9.0 false diff --git a/SpreadCheetah.SourceGenerator.Test/Tests/CellFormatTests.cs b/SpreadCheetah.SourceGenerator.Test/Tests/CellFormatTests.cs index 52731e3c..de316573 100644 --- a/SpreadCheetah.SourceGenerator.Test/Tests/CellFormatTests.cs +++ b/SpreadCheetah.SourceGenerator.Test/Tests/CellFormatTests.cs @@ -1,8 +1,8 @@ -using System.Reflection; using SpreadCheetah.SourceGeneration; using SpreadCheetah.SourceGenerator.Test.Models.CellFormat; using SpreadCheetah.Styling; using SpreadCheetah.TestHelpers.Assertions; +using System.Reflection; using Xunit; namespace SpreadCheetah.SourceGenerator.Test.Tests; @@ -105,7 +105,7 @@ public void CellFormat_ClassWithCellCustomFormat_CanReadCustomFormat() // Arrange var property = typeof(ClassWithCellCustomFormat).GetProperties(BindingFlags.Public | BindingFlags.Instance) .SingleOrDefault(p => string.Equals(p.Name, nameof(ClassWithCellCustomFormat.Price), StringComparison.Ordinal)); - + // Act var cellFormatAttr = property?.GetCustomAttribute(); diff --git a/SpreadCheetah.SourceGenerator.Test/Tests/ColumnHeaderTests.cs b/SpreadCheetah.SourceGenerator.Test/Tests/ColumnHeaderTests.cs index 7772439e..dd247b40 100644 --- a/SpreadCheetah.SourceGenerator.Test/Tests/ColumnHeaderTests.cs +++ b/SpreadCheetah.SourceGenerator.Test/Tests/ColumnHeaderTests.cs @@ -1,6 +1,6 @@ -using System.Reflection; using SpreadCheetah.SourceGeneration; using SpreadCheetah.SourceGenerator.Test.Models.ColumnHeader; +using System.Reflection; using Xunit; namespace SpreadCheetah.SourceGenerator.Test.Tests; @@ -84,28 +84,22 @@ public void ColumnHeader_ClassWithSpecialCharacterColumnHeaders_CanReadName() var note2ColHeaderAttr = note2Property?.GetCustomAttribute(); // Assert - Assert.NotNull(firstNameProperty); Assert.NotNull(firstNameColHeaderAttr); Assert.Equal("First name", firstNameColHeaderAttr.Name); - Assert.NotNull(lastNameProperty); Assert.NotNull(lastNameColHeaderAttr); Assert.Equal("", lastNameColHeaderAttr.Name); - Assert.NotNull(nationalityProperty); Assert.NotNull(nationalityColHeaderAttr); Assert.Equal("Nationality (escaped characters \", \', \\)", nationalityColHeaderAttr.Name); - Assert.NotNull(addressLine1Property); Assert.NotNull(addressLine1ColHeaderAttr); Assert.Equal("Address line 1 (escaped characters \r\n, \t)", addressLine1ColHeaderAttr.Name); - Assert.NotNull(addressLine2Property); Assert.NotNull(addressLine2ColHeaderAttr); Assert.Equal(@"Address line 2 (verbatim string: "", \)", addressLine2ColHeaderAttr.Name); - Assert.NotNull(ageProperty); Assert.NotNull(ageColHeaderAttr); Assert.Equal(""" Age ( @@ -115,11 +109,9 @@ public void ColumnHeader_ClassWithSpecialCharacterColumnHeaders_CanReadName() ) """, ageColHeaderAttr.Name); - Assert.NotNull(noteProperty); Assert.NotNull(noteColHeaderAttr); Assert.Equal("Note (unicode escape sequence 🌉, \ud83d\udc4d, \xE7)", noteColHeaderAttr.Name); - Assert.NotNull(note2Property); Assert.NotNull(note2ColHeaderAttr); Assert.Equal($"Note 2 (constant interpolated string: This is a constant)", note2ColHeaderAttr.Name); } diff --git a/SpreadCheetah.SourceGenerator.Test/Tests/WorksheetRowGeneratorTests.cs b/SpreadCheetah.SourceGenerator.Test/Tests/WorksheetRowGeneratorTests.cs index 8ca3d198..cc268edf 100644 --- a/SpreadCheetah.SourceGenerator.Test/Tests/WorksheetRowGeneratorTests.cs +++ b/SpreadCheetah.SourceGenerator.Test/Tests/WorksheetRowGeneratorTests.cs @@ -19,7 +19,6 @@ using Xunit; using OpenXmlCell = DocumentFormat.OpenXml.Spreadsheet.Cell; - #if NET472 using SpreadCheetah.SourceGenerator.Test.Helpers.Backporting; #endif diff --git a/SpreadCheetah.SourceGenerator/Helpers/HashCode.cs b/SpreadCheetah.SourceGenerator/Helpers/HashCode.cs index 5435febb..37e583a9 100644 --- a/SpreadCheetah.SourceGenerator/Helpers/HashCode.cs +++ b/SpreadCheetah.SourceGenerator/Helpers/HashCode.cs @@ -163,6 +163,7 @@ public readonly int ToHashCode() return (int)hash; } +#pragma warning disable CA1065 // Do not raise exceptions in unexpected locations #pragma warning disable CS0809 // Obsolete member overrides non-obsolete member #pragma warning disable S3877 // Exceptions should not be thrown from unexpected methods [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes. Use ToHashCode to retrieve the computed hash code.", error: true)] @@ -172,6 +173,7 @@ public readonly int ToHashCode() [Obsolete("HashCode is a mutable struct and should not be compared with other HashCodes.", error: true)] [EditorBrowsable(EditorBrowsableState.Never)] public override readonly bool Equals(object? obj) => throw new NotSupportedException(); +#pragma warning restore CA1065 // Do not raise exceptions in unexpected locations #pragma warning restore CS0809 // Obsolete member overrides non-obsolete member #pragma warning restore S3877 // Exceptions should not be thrown from unexpected methods diff --git a/SpreadCheetah.SourceGenerator/WorksheetRowGenerator.cs b/SpreadCheetah.SourceGenerator/WorksheetRowGenerator.cs index 471292cd..6178b313 100644 --- a/SpreadCheetah.SourceGenerator/WorksheetRowGenerator.cs +++ b/SpreadCheetah.SourceGenerator/WorksheetRowGenerator.cs @@ -591,9 +591,9 @@ private static string ConstructCell(RowTypeProperty property, int? styleIdIndex, var constructDataCell = (property.CellValueConverter, property.CellValueTruncate) switch { - (null, null) => $"new DataCell({value})", ({ } converter, _) => $"{valueConverters[converter.ConverterTypeName]}.ConvertToDataCell({value})", - (null, { } truncate) => FormattableString.Invariant($"ConstructTruncatedDataCell({value}, {truncate.Value})") + (null, { } truncate) => FormattableString.Invariant($"ConstructTruncatedDataCell({value}, {truncate.Value})"), + _ => $"new DataCell({value})" }; var styleId = (hasStyleAttributes, styleIdIndex) switch diff --git a/SpreadCheetah.Test/Helpers/AsyncWriteOnlyMemoryStream.cs b/SpreadCheetah.Test/Helpers/AsyncWriteOnlyMemoryStream.cs index 8e1c01c7..771af3a4 100644 --- a/SpreadCheetah.Test/Helpers/AsyncWriteOnlyMemoryStream.cs +++ b/SpreadCheetah.Test/Helpers/AsyncWriteOnlyMemoryStream.cs @@ -8,10 +8,10 @@ public override void Write(byte[] buffer, int offset, int count) } #if NETCOREAPP - public override void Write(ReadOnlySpan source) - { - throw new NotImplementedException(); - } + public override void Write(ReadOnlySpan source) + { + throw new NotImplementedException(); + } #endif public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback? callback, object? state) diff --git a/SpreadCheetah.Test/Helpers/CellFactory.cs b/SpreadCheetah.Test/Helpers/CellFactory.cs index 3bdb6a58..462a506b 100644 --- a/SpreadCheetah.Test/Helpers/CellFactory.cs +++ b/SpreadCheetah.Test/Helpers/CellFactory.cs @@ -60,10 +60,10 @@ internal static class CellFactory _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; - public static object Create(CellType type, DateTime? value, StyleId? styleId) => type switch + public static object Create(StyledCellType type, DateTime? value, StyleId? styleId) => type switch { - CellType.Cell => new Cell(value, styleId), - CellType.StyledCell => new StyledCell(value, styleId), + StyledCellType.Cell => new Cell(value, styleId), + StyledCellType.StyledCell => new StyledCell(value, styleId), _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; @@ -99,6 +99,13 @@ internal static class CellFactory _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) }; + public static object Create(StyledCellType type, string? value, StyleId? styleId) => type switch + { + StyledCellType.Cell => new Cell(value, styleId), + StyledCellType.StyledCell => new StyledCell(value, styleId), + _ => throw new ArgumentOutOfRangeException(nameof(type), type, null) + }; + public static object Create(CellType cellType, CellValueType valueType, bool isNull, StyleId? styleId, out object? value) => valueType switch { CellValueType.BoolFalse => CreateForBool(cellType, isNull ? null : false, styleId, out value), diff --git a/SpreadCheetah.Test/Helpers/EmbeddedResources.cs b/SpreadCheetah.Test/Helpers/EmbeddedResources.cs index 39f75e53..35050744 100644 --- a/SpreadCheetah.Test/Helpers/EmbeddedResources.cs +++ b/SpreadCheetah.Test/Helpers/EmbeddedResources.cs @@ -11,8 +11,8 @@ public static Stream GetStream(string filename) var stream = resourceNames switch { - [var resourceName] => assembly.GetManifestResourceStream(resourceName), - [] => throw new ArgumentException("Could not find embedded resource.", nameof(filename)), + [var resourceName] => assembly.GetManifestResourceStream(resourceName), + [] => throw new ArgumentException("Could not find embedded resource.", nameof(filename)), _ => throw new ArgumentException("Found multiple embedded resources with the same name.", nameof(filename)) }; diff --git a/SpreadCheetah.Test/Helpers/StyledCellType.cs b/SpreadCheetah.Test/Helpers/StyledCellType.cs new file mode 100644 index 00000000..ca57005f --- /dev/null +++ b/SpreadCheetah.Test/Helpers/StyledCellType.cs @@ -0,0 +1,7 @@ +namespace SpreadCheetah.Test.Helpers; + +public enum StyledCellType +{ + StyledCell, + Cell +} diff --git a/SpreadCheetah.Test/SpreadCheetah.Test.csproj b/SpreadCheetah.Test/SpreadCheetah.Test.csproj index c7daed5c..8496d29b 100644 --- a/SpreadCheetah.Test/SpreadCheetah.Test.csproj +++ b/SpreadCheetah.Test/SpreadCheetah.Test.csproj @@ -1,7 +1,7 @@ - net472;net6.0;net7.0;net8.0 + net472;net8.0;net9.0 false @@ -23,9 +23,12 @@ + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/SpreadCheetah.Test/Tests/NumberFormatTests.cs b/SpreadCheetah.Test/Tests/NumberFormatTests.cs index e08239ea..5c152829 100644 --- a/SpreadCheetah.Test/Tests/NumberFormatTests.cs +++ b/SpreadCheetah.Test/Tests/NumberFormatTests.cs @@ -1,56 +1,54 @@ using SpreadCheetah.Styling; -using Xunit; -namespace SpreadCheetah.Test.Tests -{ - public static class NumberFormatTests - { +namespace SpreadCheetah.Test.Tests; + +public static class NumberFormatTests +{ #pragma warning disable CS0618 // Type or member is obsolete - Testing for backwards compatibilty - [Theory] - [InlineData(NumberFormats.General, StandardNumberFormat.General)] - [InlineData(NumberFormats.Fraction, StandardNumberFormat.Fraction)] - [InlineData(NumberFormats.FractionTwoDenominatorPlaces, StandardNumberFormat.FractionTwoDenominatorPlaces)] - [InlineData(NumberFormats.NoDecimalPlaces, StandardNumberFormat.NoDecimalPlaces)] - [InlineData(NumberFormats.Percent, StandardNumberFormat.Percent)] - [InlineData(NumberFormats.PercentTwoDecimalPlaces, StandardNumberFormat.PercentTwoDecimalPlaces)] - [InlineData(NumberFormats.Scientific, StandardNumberFormat.Scientific)] - [InlineData(NumberFormats.ThousandsSeparator, StandardNumberFormat.ThousandsSeparator)] - [InlineData(NumberFormats.ThousandsSeparatorTwoDecimalPlaces, StandardNumberFormat.ThousandsSeparatorTwoDecimalPlaces)] - [InlineData(NumberFormats.TwoDecimalPlaces, StandardNumberFormat.TwoDecimalPlaces)] - [InlineData("mm-dd-yy", StandardNumberFormat.ShortDate)] - [InlineData("d-mmm-yy", StandardNumberFormat.LongDate)] - [InlineData("d-mmm", StandardNumberFormat.DayMonth)] - [InlineData("mmm-yy", StandardNumberFormat.MonthYear)] - [InlineData("h:mm AM/PM", StandardNumberFormat.ShortTime12hour)] - [InlineData("h:mm:ss AM/PM", StandardNumberFormat.LongTime12hour)] - [InlineData("h:mm", StandardNumberFormat.ShortTime)] - [InlineData("h:mm:ss", StandardNumberFormat.LongTime)] - [InlineData("m/d/yy h:mm", StandardNumberFormat.DateAndTime)] - [InlineData("#,##0 ;(#,##0)", StandardNumberFormat.NoDecimalPlacesNegativeParenthesis)] - [InlineData("#,##0 ;[Red](#,##0)", StandardNumberFormat.NoDecimalPlacesNegativeParenthesisRed)] - [InlineData("#,##0.00;(#,##0.00)", StandardNumberFormat.TwoDecimalPlacesNegativeParenthesis)] - [InlineData("#,##0.00;[Red](#,##0.00)", StandardNumberFormat.TwoDecimalPlacesNegativeParenthesisRed)] - [InlineData("mm:ss", StandardNumberFormat.MinutesAndSeconds)] - [InlineData("[h]:mm:ss", StandardNumberFormat.Duration)] - [InlineData("mmss.0", StandardNumberFormat.DecimalDuration)] - [InlineData("##0.0E+0", StandardNumberFormat.Exponential)] - [InlineData(NumberFormats.Text, StandardNumberFormat.Text)] - public static void NumberFormatFromCustomStringBackwardCompatibility(string customString, StandardNumberFormat expectedStandardFormat) - { - var numberFormatExplict = NumberFormat.Standard(expectedStandardFormat); - var numberFormatLegacyFromStyle = (new Style { NumberFormat = customString }).Format; - var numberFormatLegacyFromOptions = (new SpreadCheetahOptions { DefaultDateTimeNumberFormat = customString }).DefaultDateTimeFormat; + [Theory] + [InlineData(NumberFormats.General, StandardNumberFormat.General)] + [InlineData(NumberFormats.Fraction, StandardNumberFormat.Fraction)] + [InlineData(NumberFormats.FractionTwoDenominatorPlaces, StandardNumberFormat.FractionTwoDenominatorPlaces)] + [InlineData(NumberFormats.NoDecimalPlaces, StandardNumberFormat.NoDecimalPlaces)] + [InlineData(NumberFormats.Percent, StandardNumberFormat.Percent)] + [InlineData(NumberFormats.PercentTwoDecimalPlaces, StandardNumberFormat.PercentTwoDecimalPlaces)] + [InlineData(NumberFormats.Scientific, StandardNumberFormat.Scientific)] + [InlineData(NumberFormats.ThousandsSeparator, StandardNumberFormat.ThousandsSeparator)] + [InlineData(NumberFormats.ThousandsSeparatorTwoDecimalPlaces, StandardNumberFormat.ThousandsSeparatorTwoDecimalPlaces)] + [InlineData(NumberFormats.TwoDecimalPlaces, StandardNumberFormat.TwoDecimalPlaces)] + [InlineData("mm-dd-yy", StandardNumberFormat.ShortDate)] + [InlineData("d-mmm-yy", StandardNumberFormat.LongDate)] + [InlineData("d-mmm", StandardNumberFormat.DayMonth)] + [InlineData("mmm-yy", StandardNumberFormat.MonthYear)] + [InlineData("h:mm AM/PM", StandardNumberFormat.ShortTime12hour)] + [InlineData("h:mm:ss AM/PM", StandardNumberFormat.LongTime12hour)] + [InlineData("h:mm", StandardNumberFormat.ShortTime)] + [InlineData("h:mm:ss", StandardNumberFormat.LongTime)] + [InlineData("m/d/yy h:mm", StandardNumberFormat.DateAndTime)] + [InlineData("#,##0 ;(#,##0)", StandardNumberFormat.NoDecimalPlacesNegativeParenthesis)] + [InlineData("#,##0 ;[Red](#,##0)", StandardNumberFormat.NoDecimalPlacesNegativeParenthesisRed)] + [InlineData("#,##0.00;(#,##0.00)", StandardNumberFormat.TwoDecimalPlacesNegativeParenthesis)] + [InlineData("#,##0.00;[Red](#,##0.00)", StandardNumberFormat.TwoDecimalPlacesNegativeParenthesisRed)] + [InlineData("mm:ss", StandardNumberFormat.MinutesAndSeconds)] + [InlineData("[h]:mm:ss", StandardNumberFormat.Duration)] + [InlineData("mmss.0", StandardNumberFormat.DecimalDuration)] + [InlineData("##0.0E+0", StandardNumberFormat.Exponential)] + [InlineData(NumberFormats.Text, StandardNumberFormat.Text)] + public static void NumberFormatFromCustomStringBackwardCompatibility(string customString, StandardNumberFormat expectedStandardFormat) + { + var numberFormatExplict = NumberFormat.Standard(expectedStandardFormat); + var numberFormatLegacyFromStyle = new Style { NumberFormat = customString }.Format; + var numberFormatLegacyFromOptions = new SpreadCheetahOptions { DefaultDateTimeNumberFormat = customString }.DefaultDateTimeFormat; - Assert.Equal(numberFormatExplict, numberFormatLegacyFromStyle); - Assert.Equal(numberFormatExplict, numberFormatLegacyFromOptions); - } + Assert.Equal(numberFormatExplict, numberFormatLegacyFromStyle); + Assert.Equal(numberFormatExplict, numberFormatLegacyFromOptions); + } #pragma warning restore CS0618 // Type or member is obsolete - [Fact] - public static void NumberFormatThrowsExceptionOnInvalidFormat() - { - Assert.ThrowsAny(() => NumberFormat.Custom(new string('a', 256))); // Strings longer than 255 characters should throw. - Assert.ThrowsAny(() => NumberFormat.Standard((StandardNumberFormat)(-1))); // Numbers that are not in the enum should throw. - } + [Fact] + public static void NumberFormatThrowsExceptionOnInvalidFormat() + { + Assert.ThrowsAny(() => NumberFormat.Custom(new string('a', 256))); // Strings longer than 255 characters should throw. + Assert.ThrowsAny(() => NumberFormat.Standard((StandardNumberFormat)(-1))); // Numbers that are not in the enum should throw. } } diff --git a/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet7_0.verified.txt b/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet7_0.verified.txt deleted file mode 100644 index f89f1620..00000000 --- a/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet7_0.verified.txt +++ /dev/null @@ -1,545 +0,0 @@ -[assembly: System.CLSCompliant(true)] -[assembly: System.Reflection.AssemblyMetadata("IsTrimmable", "True")] -[assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/sveinungf/spreadcheetah")] -[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("SpreadCheetah.Test")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v7.0", FrameworkDisplayName=".NET 7.0")] -namespace SpreadCheetah -{ - public readonly struct Cell : System.IEquatable - { - public Cell(SpreadCheetah.DataCell value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(bool value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(System.DateTime value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(System.DateTime? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(decimal value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(double value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(int value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(long value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(System.ReadOnlyMemory value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(float value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(bool? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(decimal? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(double? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(float? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(int? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(long? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(string? value, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, SpreadCheetah.DataCell cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, bool cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, System.DateTime cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, System.DateTime? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, decimal cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, double cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, int cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, long cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, System.ReadOnlyMemory cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, float cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, bool? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, decimal? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, double? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, float? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, int? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, long? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - public Cell(SpreadCheetah.Formula formula, string? cachedValue, SpreadCheetah.Styling.StyleId? styleId = null) { } - } - public readonly struct DataCell : System.IEquatable - { - public DataCell(bool value) { } - public DataCell(System.DateTime value) { } - public DataCell(System.DateTime? value) { } - public DataCell(decimal value) { } - public DataCell(double value) { } - public DataCell(int value) { } - public DataCell(long value) { } - public DataCell(System.ReadOnlyMemory value) { } - public DataCell(float value) { } - public DataCell(bool? value) { } - public DataCell(decimal? value) { } - public DataCell(double? value) { } - public DataCell(float? value) { } - public DataCell(int? value) { } - public DataCell(long? value) { } - public DataCell(string? value) { } - } - public readonly struct Formula : System.IEquatable - { - public Formula(string? formulaText) { } - } - public enum SpreadCheetahCompressionLevel - { - Optimal = 0, - Fastest = 1, - } - public class SpreadCheetahException : System.Exception - { - public SpreadCheetahException() { } - public SpreadCheetahException(string message) { } - public SpreadCheetahException(string message, System.Exception exception) { } - } - public class SpreadCheetahOptions - { - public static readonly int DefaultBufferSize; - public static readonly SpreadCheetah.SpreadCheetahCompressionLevel DefaultCompressionLevel; - public static readonly int MinimumBufferSize; - public SpreadCheetahOptions() { } - public int BufferSize { get; set; } - public SpreadCheetah.SpreadCheetahCompressionLevel CompressionLevel { get; set; } - public SpreadCheetah.Styling.NumberFormat? DefaultDateTimeFormat { get; set; } - [System.Obsolete("Use SpreadCheetahOptions.DefaultDateTimeFormat instead")] - public string? DefaultDateTimeNumberFormat { get; set; } - public bool WriteCellReferenceAttributes { get; set; } - } - public sealed class Spreadsheet : System.IAsyncDisposable, System.IDisposable - { - public int NextRowNumber { get; } - public System.Threading.Tasks.ValueTask AddAsRowAsync(T obj, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo typeInfo, System.Threading.CancellationToken token = default) { } - public void AddDataValidation(string reference, SpreadCheetah.Validations.DataValidation validation) { } - public System.Threading.Tasks.ValueTask AddHeaderRowAsync(SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo typeInfo, SpreadCheetah.Styling.StyleId? styleId = null, System.Threading.CancellationToken token = default) { } - public void AddImage(SpreadCheetah.Images.ImageCanvas canvas, SpreadCheetah.Images.EmbeddedImage image, SpreadCheetah.Images.ImageOptions? options = null) { } - public void AddNote(string cellReference, string noteText) { } - public System.Threading.Tasks.ValueTask AddRangeAsRowsAsync(System.Collections.Generic.IEnumerable objs, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo typeInfo, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(SpreadCheetah.Cell[] cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(SpreadCheetah.DataCell[] cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(SpreadCheetah.StyledCell[] cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.Collections.Generic.IList cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.Collections.Generic.IList cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.Collections.Generic.IList cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory cells, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(SpreadCheetah.Cell[] cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(SpreadCheetah.DataCell[] cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(SpreadCheetah.StyledCell[] cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.Collections.Generic.IList cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.Collections.Generic.IList cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.Collections.Generic.IList cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask AddRowAsync(System.ReadOnlyMemory cells, SpreadCheetah.Worksheets.RowOptions? options, System.Threading.CancellationToken token = default) { } - public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style) { } - public SpreadCheetah.Styling.StyleId AddStyle(SpreadCheetah.Styling.Style style, string name, SpreadCheetah.Styling.StyleNameVisibility? nameVisibility = default) { } - public void Dispose() { } - public System.Threading.Tasks.ValueTask DisposeAsync() { } - public System.Threading.Tasks.ValueTask EmbedImageAsync(System.IO.Stream stream, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask FinishAsync(System.Threading.CancellationToken token = default) { } - public SpreadCheetah.SourceGeneration.WorksheetRowDependencyInfo GetOrCreateWorksheetRowDependencyInfo(SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo typeInfo) { } - public SpreadCheetah.Styling.StyleId GetStyleId(string name) { } - public void MergeCells(string cellRange) { } - public System.Threading.Tasks.ValueTask StartWorksheetAsync(string name, SpreadCheetah.Worksheets.WorksheetOptions? options = null, System.Threading.CancellationToken token = default) { } - public System.Threading.Tasks.ValueTask StartWorksheetAsync(string name, SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo typeInfo, System.Threading.CancellationToken token = default) { } - public bool TryAddDataValidation(string reference, SpreadCheetah.Validations.DataValidation validation) { } - public static System.Threading.Tasks.ValueTask CreateNewAsync(System.IO.Stream stream, SpreadCheetah.SpreadCheetahOptions? options = null, System.Threading.CancellationToken cancellationToken = default) { } - } - public static class SpreadsheetUtility - { - public static string GetColumnName(int columnNumber) { } - public static bool TryGetColumnNameUtf8(int columnNumber, System.Span destination, out int bytesWritten) { } - public static bool TryParseColumnName(System.ReadOnlySpan columnName, out int columnNumber) { } - } - public readonly struct StyledCell : System.IEquatable - { - public StyledCell(SpreadCheetah.DataCell value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(bool value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(System.DateTime value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(System.DateTime? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(decimal value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(double value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(int value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(long value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(System.ReadOnlyMemory value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(float value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(bool? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(decimal? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(double? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(float? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(int? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(long? value, SpreadCheetah.Styling.StyleId? styleId) { } - public StyledCell(string? value, SpreadCheetah.Styling.StyleId? styleId) { } - } -} -namespace SpreadCheetah.Images -{ - public sealed class EmbeddedImage - { - public int Height { get; } - public int Width { get; } - } - public readonly struct ImageCanvas : System.IEquatable - { - public static SpreadCheetah.Images.ImageCanvas Dimensions(System.ReadOnlySpan upperLeftReference, int width, int height, bool moveWithCells = true) { } - public static SpreadCheetah.Images.ImageCanvas FillCell(System.ReadOnlySpan cellReference, bool moveWithCells = true, bool resizeWithCells = true) { } - public static SpreadCheetah.Images.ImageCanvas FillCells(System.ReadOnlySpan upperLeftReference, System.ReadOnlySpan lowerRightReference, bool moveWithCells = true, bool resizeWithCells = true) { } - public static SpreadCheetah.Images.ImageCanvas OriginalSize(System.ReadOnlySpan upperLeftReference, bool moveWithCells = true) { } - public static SpreadCheetah.Images.ImageCanvas Scaled(System.ReadOnlySpan upperLeftReference, float scale, bool moveWithCells = true) { } - } - public readonly struct ImageOffset : System.IEquatable - { - public static SpreadCheetah.Images.ImageOffset Pixels(int left, int top, int right, int bottom) { } - } - public sealed class ImageOptions - { - public ImageOptions() { } - public SpreadCheetah.Images.ImageOffset? Offset { get; set; } - } -} -namespace SpreadCheetah.SourceGeneration -{ - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class CellFormatAttribute : System.Attribute - { - public CellFormatAttribute(SpreadCheetah.Styling.StandardNumberFormat format) { } - public CellFormatAttribute(string customFormat) { } - public string? CustomFormat { get; } - public SpreadCheetah.Styling.StandardNumberFormat? Format { get; } - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class CellStyleAttribute : System.Attribute - { - public CellStyleAttribute(string styleName) { } - public string? StyleName { get; } - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class CellValueConverterAttribute : System.Attribute - { - public CellValueConverterAttribute(System.Type converterType) { } - public System.Type? ConverterType { get; } - } - public abstract class CellValueConverter - { - protected CellValueConverter() { } - public abstract SpreadCheetah.DataCell ConvertToDataCell(T value); - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class CellValueTruncateAttribute : System.Attribute - { - public CellValueTruncateAttribute(int length) { } - public int? Length { get; } - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class ColumnHeaderAttribute : System.Attribute - { - public ColumnHeaderAttribute(string name) { } - public ColumnHeaderAttribute(System.Type type, string propertyName) { } - public string? Name { get; } - public string? PropertyName { get; } - public System.Type? Type { get; } - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class ColumnIgnoreAttribute : System.Attribute - { - public ColumnIgnoreAttribute() { } - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class ColumnOrderAttribute : System.Attribute - { - public ColumnOrderAttribute(int order) { } - public int? Order { get; } - } - [System.AttributeUsage(System.AttributeTargets.Property, AllowMultiple=false)] - public sealed class ColumnWidthAttribute : System.Attribute - { - public ColumnWidthAttribute(double width) { } - public double? Width { get; } - } - public static class EmptyWorksheetRowContext - { - public static SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo CreateTypeInfo() { } - } - [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=false, Inherited=false)] - public sealed class InheritColumnsAttribute : System.Attribute - { - public InheritColumnsAttribute() { } - public SpreadCheetah.SourceGeneration.InheritedColumnsOrder DefaultColumnOrder { get; set; } - } - public enum InheritedColumnsOrder - { - InheritedColumnsFirst = 0, - InheritedColumnsLast = 1, - } - [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=true)] - public sealed class WorksheetRowAttribute : System.Attribute - { - public WorksheetRowAttribute(System.Type type) { } - } - public abstract class WorksheetRowContext - { - protected WorksheetRowContext() { } - } - public sealed class WorksheetRowDependencyInfo : System.IEquatable - { - public WorksheetRowDependencyInfo(System.Collections.Generic.IReadOnlyList StyleIds) { } - public System.Collections.Generic.IReadOnlyList StyleIds { get; init; } - } - [System.AttributeUsage(System.AttributeTargets.Class, AllowMultiple=false)] - public sealed class WorksheetRowGenerationOptionsAttribute : System.Attribute - { - public WorksheetRowGenerationOptionsAttribute() { } - [System.Obsolete("Configure rule severity in an .editorconfig file instead (e.g. \'dotnet_diagnostic" + - ".SPCH1001.severity = none\').")] - public bool SuppressWarnings { get; set; } - } - public static class WorksheetRowMetadataServices - { - public static SpreadCheetah.SourceGeneration.WorksheetRowTypeInfo CreateObjectInfo(System.Func headerHandler, System.Func rowHandler, System.Func, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask> rowRangeHandler, System.Func? worksheetOptionsFactory = null, System.Func? createWorksheetRowDependencyInfo = null) { } - } - public abstract class WorksheetRowTypeInfo - { - public System.Func? CreateWorksheetRowDependencyInfo { get; } - public System.Func HeaderHandler { get; } - public System.Func RowHandler { get; } - public System.Func, System.Threading.CancellationToken, System.Threading.Tasks.ValueTask> RowRangeHandler { get; } - public SpreadCheetah.Worksheets.WorksheetOptions CreateWorksheetOptions() { } - } -} -namespace SpreadCheetah.Styling -{ - public sealed class Alignment : System.IEquatable - { - public Alignment() { } - public SpreadCheetah.Styling.HorizontalAlignment Horizontal { get; set; } - public int Indent { get; set; } - public SpreadCheetah.Styling.VerticalAlignment Vertical { get; set; } - public bool WrapText { get; set; } - } - public sealed class Border : System.IEquatable - { - public Border() { } - public SpreadCheetah.Styling.EdgeBorder Bottom { get; set; } - public SpreadCheetah.Styling.DiagonalBorder Diagonal { get; set; } - public SpreadCheetah.Styling.EdgeBorder Left { get; set; } - public SpreadCheetah.Styling.EdgeBorder Right { get; set; } - public SpreadCheetah.Styling.EdgeBorder Top { get; set; } - } - public enum BorderStyle - { - None = 0, - Thin = 1, - Medium = 2, - Dashed = 3, - Dotted = 4, - Thick = 5, - DoubleLine = 6, - Hair = 7, - MediumDashed = 8, - DashDot = 9, - MediumDashDot = 10, - DashDotDot = 11, - MediumDashDotDot = 12, - SlantDashDot = 13, - } - public sealed class DiagonalBorder : System.IEquatable - { - public DiagonalBorder() { } - public SpreadCheetah.Styling.BorderStyle BorderStyle { get; set; } - public System.Drawing.Color? Color { get; set; } - public SpreadCheetah.Styling.DiagonalBorderType Type { get; set; } - } - [System.Flags] - public enum DiagonalBorderType - { - None = 0, - DiagonalUp = 1, - DiagonalDown = 2, - CrissCross = 3, - } - public sealed class EdgeBorder : System.IEquatable - { - public EdgeBorder() { } - public SpreadCheetah.Styling.BorderStyle BorderStyle { get; set; } - public System.Drawing.Color? Color { get; set; } - } - public sealed class Fill : System.IEquatable - { - public Fill() { } - public System.Drawing.Color? Color { get; set; } - } - public sealed class Font : System.IEquatable - { - public Font() { } - public bool Bold { get; set; } - public System.Drawing.Color? Color { get; set; } - public bool Italic { get; set; } - public string? Name { get; set; } - public double Size { get; set; } - public bool Strikethrough { get; set; } - } - public enum HorizontalAlignment - { - None = 0, - Left = 1, - Center = 2, - Right = 3, - } - public readonly struct NumberFormat : System.IEquatable - { - public override string ToString() { } - public static SpreadCheetah.Styling.NumberFormat Custom(string formatString) { } - public static SpreadCheetah.Styling.NumberFormat Standard(SpreadCheetah.Styling.StandardNumberFormat format) { } - } - public static class NumberFormats - { - public const string DateTimeSortable = "yyyy\\-mm\\-dd\\ hh:mm:ss"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.Fraction)")] - public const string Fraction = "# ?/?"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.FractionTwoDenominatorPlaces)")] - public const string FractionTwoDenominatorPlaces = "# ??/??"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.General)")] - public const string General = "General"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.NoDecimalPlaces)")] - public const string NoDecimalPlaces = "0"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.Percent)")] - public const string Percent = "0%"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.PercentTwoDecimalPlaces)")] - public const string PercentTwoDecimalPlaces = "0.00%"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.Scientific)")] - public const string Scientific = "0.00E+00"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.Text)")] - public const string Text = "@"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.ThousandsSeparator)")] - public const string ThousandsSeparator = "#,##0"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.ThousandsSeparatorTwoDecimalPlaces" + - ")")] - public const string ThousandsSeparatorTwoDecimalPlaces = "#,##0.00"; - [System.Obsolete("Use NumberFormat.Standard(StandardNumberFormat.TwoDecimalPlaces)")] - public const string TwoDecimalPlaces = "0.00"; - } - public enum StandardNumberFormat - { - General = 0, - NoDecimalPlaces = 1, - TwoDecimalPlaces = 2, - ThousandsSeparator = 3, - ThousandsSeparatorTwoDecimalPlaces = 4, - Percent = 9, - PercentTwoDecimalPlaces = 10, - Scientific = 11, - Fraction = 12, - FractionTwoDenominatorPlaces = 13, - ShortDate = 14, - LongDate = 15, - DayMonth = 16, - MonthYear = 17, - ShortTime12hour = 18, - LongTime12hour = 19, - ShortTime = 20, - LongTime = 21, - DateAndTime = 22, - NoDecimalPlacesNegativeParenthesis = 37, - NoDecimalPlacesNegativeParenthesisRed = 38, - TwoDecimalPlacesNegativeParenthesis = 39, - TwoDecimalPlacesNegativeParenthesisRed = 40, - MinutesAndSeconds = 45, - Duration = 46, - DecimalDuration = 47, - Exponential = 48, - Text = 49, - } - public sealed class Style : System.IEquatable - { - public Style() { } - public SpreadCheetah.Styling.Alignment Alignment { get; set; } - public SpreadCheetah.Styling.Border Border { get; set; } - public SpreadCheetah.Styling.Fill Fill { get; set; } - public SpreadCheetah.Styling.Font Font { get; set; } - public SpreadCheetah.Styling.NumberFormat? Format { get; set; } - [System.Obsolete("Use Style.Format instead")] - public string? NumberFormat { get; set; } - } - public sealed class StyleId - { - public int Id { get; } - } - public enum StyleNameVisibility - { - Visible = 0, - Hidden = 1, - } - public enum VerticalAlignment - { - Bottom = 0, - Center = 1, - Top = 2, - } -} -namespace SpreadCheetah.Validations -{ - public sealed class DataValidation - { - public string? ErrorMessage { get; set; } - public string? ErrorTitle { get; set; } - public SpreadCheetah.Validations.ValidationErrorType ErrorType { get; set; } - public bool IgnoreBlank { get; set; } - public string? InputMessage { get; set; } - public string? InputTitle { get; set; } - public bool ShowErrorAlert { get; set; } - public bool ShowInputMessage { get; set; } - public static SpreadCheetah.Validations.DataValidation DecimalBetween(double min, double max) { } - public static SpreadCheetah.Validations.DataValidation DecimalEqualTo(double value) { } - public static SpreadCheetah.Validations.DataValidation DecimalGreaterThan(double value) { } - public static SpreadCheetah.Validations.DataValidation DecimalGreaterThanOrEqualTo(double value) { } - public static SpreadCheetah.Validations.DataValidation DecimalLessThan(double value) { } - public static SpreadCheetah.Validations.DataValidation DecimalLessThanOrEqualTo(double value) { } - public static SpreadCheetah.Validations.DataValidation DecimalNotBetween(double min, double max) { } - public static SpreadCheetah.Validations.DataValidation DecimalNotEqualTo(double value) { } - public static SpreadCheetah.Validations.DataValidation IntegerBetween(int min, int max) { } - public static SpreadCheetah.Validations.DataValidation IntegerEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation IntegerGreaterThan(int value) { } - public static SpreadCheetah.Validations.DataValidation IntegerGreaterThanOrEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation IntegerLessThan(int value) { } - public static SpreadCheetah.Validations.DataValidation IntegerLessThanOrEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation IntegerNotBetween(int min, int max) { } - public static SpreadCheetah.Validations.DataValidation IntegerNotEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation ListValues(System.Collections.Generic.IEnumerable values, bool showDropdown = true) { } - public static SpreadCheetah.Validations.DataValidation ListValuesFromCells(string cellRange, bool showDropdown = true) { } - public static SpreadCheetah.Validations.DataValidation ListValuesFromCells(string worksheetName, string cellRange, bool showDropdown = true) { } - public static SpreadCheetah.Validations.DataValidation TextLengthBetween(int min, int max) { } - public static SpreadCheetah.Validations.DataValidation TextLengthEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation TextLengthGreaterThan(int value) { } - public static SpreadCheetah.Validations.DataValidation TextLengthGreaterThanOrEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation TextLengthLessThan(int value) { } - public static SpreadCheetah.Validations.DataValidation TextLengthLessThanOrEqualTo(int value) { } - public static SpreadCheetah.Validations.DataValidation TextLengthNotBetween(int min, int max) { } - public static SpreadCheetah.Validations.DataValidation TextLengthNotEqualTo(int value) { } - public static bool TryCreateListValues(System.Collections.Generic.IEnumerable values, bool showDropdown, [System.Diagnostics.CodeAnalysis.NotNullWhen(true)] out SpreadCheetah.Validations.DataValidation? dataValidation) { } - } - public enum ValidationErrorType - { - Blocking = 0, - Warning = 1, - Information = 2, - } -} -namespace SpreadCheetah.Worksheets -{ - public class AutoFilterOptions - { - public AutoFilterOptions(string cellRange) { } - } - public class ColumnOptions - { - public ColumnOptions() { } - public bool Hidden { get; set; } - public double? Width { get; set; } - } - public class RowOptions - { - public RowOptions() { } - public double? Height { get; set; } - } - public class WorksheetOptions - { - public WorksheetOptions() { } - public SpreadCheetah.Worksheets.AutoFilterOptions? AutoFilter { get; set; } - public int? FrozenColumns { get; set; } - public int? FrozenRows { get; set; } - public SpreadCheetah.Worksheets.WorksheetVisibility Visibility { get; set; } - public SpreadCheetah.Worksheets.ColumnOptions Column(int columnNumber) { } - } - public enum WorksheetVisibility - { - Visible = 0, - Hidden = 1, - VeryHidden = 2, - } -} \ No newline at end of file diff --git a/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet6_0.verified.txt b/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet9_0.verified.txt similarity index 99% rename from SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet6_0.verified.txt rename to SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet9_0.verified.txt index fbde8c0c..641db920 100644 --- a/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet6_0.verified.txt +++ b/SpreadCheetah.Test/Tests/PublicApiTests.PublicApi_Generate.DotNet9_0.verified.txt @@ -2,7 +2,7 @@ [assembly: System.Reflection.AssemblyMetadata("IsTrimmable", "True")] [assembly: System.Reflection.AssemblyMetadata("RepositoryUrl", "https://github.com/sveinungf/spreadcheetah")] [assembly: System.Runtime.CompilerServices.InternalsVisibleTo("SpreadCheetah.Test")] -[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v6.0", FrameworkDisplayName=".NET 6.0")] +[assembly: System.Runtime.Versioning.TargetFramework(".NETCoreApp,Version=v8.0", FrameworkDisplayName=".NET 8.0")] namespace SpreadCheetah { public readonly struct Cell : System.IEquatable diff --git a/SpreadCheetah.Test/Tests/SpreadsheetImageTests.cs b/SpreadCheetah.Test/Tests/SpreadsheetImageTests.cs index f1c15f4d..75c69422 100644 --- a/SpreadCheetah.Test/Tests/SpreadsheetImageTests.cs +++ b/SpreadCheetah.Test/Tests/SpreadsheetImageTests.cs @@ -437,7 +437,6 @@ public async Task Spreadsheet_AddImage_MultiplePngUsedInMultipleWorksheets() embeddedImages.Enqueue(embeddedImage); } - // Act await spreadsheet.StartWorksheetAsync("Sheet 1"); foreach (var reference in worksheet1References) diff --git a/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs b/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs index 62e7af4b..87b2710f 100644 --- a/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs +++ b/SpreadCheetah.Test/Tests/SpreadsheetRowTests.cs @@ -612,7 +612,6 @@ public async Task Spreadsheet_AddRow_MultipleRowsWithCharactersToEscape(CellType await spreadsheet.FinishAsync(); - // Assert SpreadsheetAssert.Valid(stream); using var workbook = new XLWorkbook(stream); diff --git a/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs b/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs index 243a013e..a9dee318 100644 --- a/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs +++ b/SpreadCheetah.Test/Tests/SpreadsheetStyledRowTests.cs @@ -468,9 +468,8 @@ public async Task Spreadsheet_AddRow_MultipleStylesWithTheSameCustomNumberFormat Assert.Equal(styles.Length, actualCells.Count()); } - [Theory] - [MemberData(nameof(TrueAndFalse))] - public async Task Spreadsheet_AddRow_DateTimeCell(bool withDefaultDateTimeFormat, CellType type, RowCollectionType rowType) + [Theory, CombinatorialData] + public async Task Spreadsheet_AddRow_DateTimeCell(bool withDefaultDateTimeFormat, StyledCellType type, RowCollectionType rowType) { // Arrange var options = new SpreadCheetahOptions { BufferSize = SpreadCheetahOptions.MinimumBufferSize }; @@ -485,58 +484,75 @@ public async Task Spreadsheet_AddRow_DateTimeCell(bool withDefaultDateTimeFormat var style = new Style { Font = { Italic = true } }; var styleId = spreadsheet.AddStyle(style); var styledCell = CellFactory.Create(type, value, styleId); - var expectedNumberFormat = withDefaultDateTimeFormat ? @"yyyy\-mm\-dd\ hh:mm:ss" : ""; + var expectedNumberFormat = withDefaultDateTimeFormat ? NumberFormats.DateTimeSortable : null; // Act await spreadsheet.AddRowAsync(styledCell, rowType); await spreadsheet.FinishAsync(); // Assert - SpreadsheetAssert.Valid(stream); - using var workbook = new XLWorkbook(stream); - var worksheet = workbook.Worksheets.Single(); - var actualCell = worksheet.Cell(1, 1); - Assert.Equal(value.ToOADate(), actualCell.Value.GetUnifiedNumber(), 0.0005); - Assert.Equal(expectedNumberFormat, actualCell.Style.NumberFormat.Format); + using var sheet = SpreadsheetAssert.SingleSheet(stream); + var actualCell = sheet["A1"]; + Assert.Equal(value, actualCell.DateTimeValue); + Assert.Equal(expectedNumberFormat, actualCell.Style.NumberFormat.CustomFormat); Assert.True(actualCell.Style.Font.Italic); } - [Theory] - [MemberData(nameof(TrueAndFalse))] - public async Task Spreadsheet_AddRow_DateTimeNumberFormat(bool withExplicitNumberFormat, CellType type, RowCollectionType rowType) + [Theory, CombinatorialData] + public async Task Spreadsheet_AddRow_DateTimeNumberFormat(bool withExplicitNumberFormat, StyledCellType type, RowCollectionType rowType) { // Arrange const string explicitNumberFormat = "yyyy"; var expectedNumberFormat = withExplicitNumberFormat ? explicitNumberFormat : NumberFormats.DateTimeSortable; var value = new DateTime(2021, 2, 3, 4, 5, 6, DateTimeKind.Unspecified); using var stream = new MemoryStream(); - await using (var spreadsheet = await Spreadsheet.CreateNewAsync(stream)) - { - await spreadsheet.StartWorksheetAsync("Sheet"); + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream); + await spreadsheet.StartWorksheetAsync("Sheet"); - var style = new Style(); - style.Font.Italic = true; - if (withExplicitNumberFormat) - style.Format = NumberFormat.Custom(explicitNumberFormat); + var style = new Style(); + style.Font.Italic = true; + if (withExplicitNumberFormat) + style.Format = NumberFormat.Custom(explicitNumberFormat); - var styleId = spreadsheet.AddStyle(style); - var styledCell = CellFactory.Create(type, value, styleId); + var styleId = spreadsheet.AddStyle(style); + var styledCell = CellFactory.Create(type, value, styleId); - // Act - await spreadsheet.AddRowAsync(styledCell, rowType); - await spreadsheet.FinishAsync(); - } + // Act + await spreadsheet.AddRowAsync(styledCell, rowType); + await spreadsheet.FinishAsync(); // Assert - SpreadsheetAssert.Valid(stream); - using var workbook = new XLWorkbook(stream); - var worksheet = workbook.Worksheets.Single(); - var actualCell = worksheet.Cell(1, 1); - Assert.Equal(value, actualCell.GetDateTime()); - Assert.Equal(expectedNumberFormat, actualCell.Style.NumberFormat.Format); + using var sheet = SpreadsheetAssert.SingleSheet(stream); + var actualCell = sheet["A1"]; + Assert.Equal(value, actualCell.DateTimeValue); + Assert.Equal(expectedNumberFormat, actualCell.Style.NumberFormat.CustomFormat); Assert.True(actualCell.Style.Font.Italic); } + [Theory, CombinatorialData] + public async Task Spreadsheet_AddRow_LongCustomFormat(StyledCellType type, RowCollectionType rowType) + { + // Arrange + var options = new SpreadCheetahOptions { BufferSize = SpreadCheetahOptions.MinimumBufferSize }; + using var stream = new MemoryStream(); + await using var spreadsheet = await Spreadsheet.CreateNewAsync(stream, options); + await spreadsheet.StartWorksheetAsync("Sheet"); + + var format = new string('"', 255); + var style = new Style { Format = NumberFormat.Custom(format) }; + var styleId = spreadsheet.AddStyle(style); + var styledCell = CellFactory.Create(type, "Foo", styleId); + + // Act + await spreadsheet.AddRowAsync(styledCell, rowType); + await spreadsheet.FinishAsync(); + + // Assert + using var sheet = SpreadsheetAssert.SingleSheet(stream); + var actualCell = sheet["A1"]; + Assert.Equal(format, actualCell.Style.NumberFormat.CustomFormat); + } + public static IEnumerable BorderStyles() => TestData.CombineWithStyledCellTypes(EnumHelper.GetValues()); [Theory] diff --git a/SpreadCheetah.Test/Tests/XmlUtilityTests.cs b/SpreadCheetah.Test/Tests/XmlUtilityTests.cs index 73b05711..c7f0393d 100644 --- a/SpreadCheetah.Test/Tests/XmlUtilityTests.cs +++ b/SpreadCheetah.Test/Tests/XmlUtilityTests.cs @@ -1,7 +1,6 @@ #if DEBUG using SpreadCheetah.Helpers; using System.Text; -using Xunit; namespace SpreadCheetah.Test.Tests; diff --git a/SpreadCheetah/Helpers/XmlUtility.cs b/SpreadCheetah/Helpers/XmlUtility.cs index c3318eb5..5602a244 100644 --- a/SpreadCheetah/Helpers/XmlUtility.cs +++ b/SpreadCheetah/Helpers/XmlUtility.cs @@ -1,5 +1,4 @@ using System.Diagnostics.CodeAnalysis; -using System.Diagnostics.Tracing; using System.Runtime.CompilerServices; using System.Text; diff --git a/SpreadCheetah/Images/FileSignature.cs b/SpreadCheetah/Images/FileSignature.cs index 7e8d8c5d..7655d713 100644 --- a/SpreadCheetah/Images/FileSignature.cs +++ b/SpreadCheetah/Images/FileSignature.cs @@ -3,7 +3,7 @@ namespace SpreadCheetah.Images; internal static class FileSignature { // Will get an unwanted byte at the start if attempting to use a UTF-8 string literal here, e.g. "\u0089PNG\r\n\u001A\n"u8 - private static ReadOnlySpan PngHeader => [(byte)0x89, (byte)'P', (byte)'N', (byte)'G', (byte)'\r', (byte)'\n', (byte)0x1A, (byte)'\n']; + private static ReadOnlySpan PngHeader => [0x89, (byte)'P', (byte)'N', (byte)'G', (byte)'\r', (byte)'\n', 0x1A, (byte)'\n']; public static ImageType? GetImageTypeFromHeader(ReadOnlySpan header) { diff --git a/SpreadCheetah/MetadataXml/DataValidationXml.cs b/SpreadCheetah/MetadataXml/DataValidationXml.cs index 47a0aae3..80d3ebd5 100644 --- a/SpreadCheetah/MetadataXml/DataValidationXml.cs +++ b/SpreadCheetah/MetadataXml/DataValidationXml.cs @@ -1,28 +1,19 @@ using SpreadCheetah.CellReferences; using SpreadCheetah.Helpers; using SpreadCheetah.Validations; -using System.Net; namespace SpreadCheetah.MetadataXml; -internal struct DataValidationXml +internal struct DataValidationXml(SingleCellOrCellRangeReference reference, DataValidation validation) { - private readonly SingleCellOrCellRangeReference _reference; - private readonly DataValidation _validation; private Element _next; private int _nextIndex; - public DataValidationXml(SingleCellOrCellRangeReference reference, DataValidation validation) - { - _reference = reference; - _validation = validation; - } - #pragma warning disable MA0051 // Method is too long public bool TryWrite(Span bytes, ref int bytesWritten) #pragma warning restore MA0051 // Method is too long { - var valid = _validation; + var valid = validation; if (_next == Element.Header && !Advance(TryWriteHeader(bytes, ref bytesWritten))) return false; if (_next == Element.InputTitleStart && !Advance(TryWriteInputTitleStart(bytes, ref bytesWritten))) return false; @@ -61,7 +52,7 @@ private readonly bool TryWriteHeader(Span bytes, ref int bytesWritten) { var span = bytes.Slice(bytesWritten); var written = 0; - var valid = _validation; + var valid = validation; if (!" bytes, ref int bytesWritten) }; private readonly bool TryWriteInputTitleStart(Span bytes, ref int bytesWritten) - => string.IsNullOrEmpty(_validation.InputTitle) || "promptTitle=\""u8.TryCopyTo(bytes, ref bytesWritten); + => string.IsNullOrEmpty(validation.InputTitle) || "promptTitle=\""u8.TryCopyTo(bytes, ref bytesWritten); private readonly bool TryWriteInputMessageStart(Span bytes, ref int bytesWritten) - => string.IsNullOrEmpty(_validation.InputMessage) || "prompt=\""u8.TryCopyTo(bytes, ref bytesWritten); + => string.IsNullOrEmpty(validation.InputMessage) || "prompt=\""u8.TryCopyTo(bytes, ref bytesWritten); private readonly bool TryWriteErrorTitleStart(Span bytes, ref int bytesWritten) - => string.IsNullOrEmpty(_validation.ErrorTitle) || "errorTitle=\""u8.TryCopyTo(bytes, ref bytesWritten); + => string.IsNullOrEmpty(validation.ErrorTitle) || "errorTitle=\""u8.TryCopyTo(bytes, ref bytesWritten); private readonly bool TryWriteErrorMessageStart(Span bytes, ref int bytesWritten) - => string.IsNullOrEmpty(_validation.ErrorMessage) || "error=\""u8.TryCopyTo(bytes, ref bytesWritten); + => string.IsNullOrEmpty(validation.ErrorMessage) || "error=\""u8.TryCopyTo(bytes, ref bytesWritten); private bool TryWriteAttributeValue(string? value, Span bytes, ref int bytesWritten) { @@ -137,7 +128,7 @@ private readonly bool TryWriteReference(Span bytes, ref int bytesWritten) var written = 0; if (!"sqref=\""u8.TryCopyTo(span, ref written)) return false; - if (!SpanHelper.TryWrite(_reference.Reference, span, ref written)) return false; + if (!SpanHelper.TryWrite(reference.Reference, span, ref written)) return false; if (!"\" "u8.TryCopyTo(span, ref written)) return false; bytesWritten += written; @@ -145,18 +136,18 @@ private readonly bool TryWriteReference(Span bytes, ref int bytesWritten) } private readonly bool TryWriteValue1Start(Span bytes, ref int bytesWritten) - => _validation.Value1 is null + => validation.Value1 is null ? "/>"u8.TryCopyTo(bytes, ref bytesWritten) : ">"u8.TryCopyTo(bytes, ref bytesWritten); private readonly bool TryWriteValue1End(Span bytes, ref int bytesWritten) - => _validation.Value1 is null || ""u8.TryCopyTo(bytes, ref bytesWritten); + => validation.Value1 is null || ""u8.TryCopyTo(bytes, ref bytesWritten); private readonly bool TryWriteValue2Start(Span bytes, ref int bytesWritten) - => _validation.Value2 is null || ""u8.TryCopyTo(bytes, ref bytesWritten); + => validation.Value2 is null || ""u8.TryCopyTo(bytes, ref bytesWritten); private readonly bool TryWriteValue2End(Span bytes, ref int bytesWritten) - => _validation.Value2 is null || ""u8.TryCopyTo(bytes, ref bytesWritten); + => validation.Value2 is null || ""u8.TryCopyTo(bytes, ref bytesWritten); private bool TryWriteValue(string? value, Span bytes, ref int bytesWritten) { diff --git a/SpreadCheetah/MetadataXml/Styles/NumberFormatsXmlPart.cs b/SpreadCheetah/MetadataXml/Styles/NumberFormatsXmlPart.cs index b9aaa275..5f84405b 100644 --- a/SpreadCheetah/MetadataXml/Styles/NumberFormatsXmlPart.cs +++ b/SpreadCheetah/MetadataXml/Styles/NumberFormatsXmlPart.cs @@ -8,8 +8,12 @@ internal struct NumberFormatsXmlPart( { private Element _next; private int _nextIndex; + private string? _currentXmlEncodedFormat; + private int _currentXmlEncodedFormatIndex; +#pragma warning disable EPS12 // Member can be made readonly (false positive) public bool TryWrite() +#pragma warning restore EPS12 // Member can be made readonly (false positive) { while (MoveNext()) { @@ -64,16 +68,28 @@ private bool TryWriteNumberFormats() var span = buffer.GetSpan(); var written = 0; - if (!""u8.TryCopyTo(span, ref written)) return false; buffer.Advance(written); + + _currentXmlEncodedFormat = null; + _currentXmlEncodedFormatIndex = 0; } return true; diff --git a/SpreadCheetah/Properties/AssemblyInfo.cs b/SpreadCheetah/Properties/AssemblyInfo.cs index a2a3f20c..c313094d 100644 --- a/SpreadCheetah/Properties/AssemblyInfo.cs +++ b/SpreadCheetah/Properties/AssemblyInfo.cs @@ -1,7 +1,5 @@ -using System.Runtime.CompilerServices; - [assembly: CLSCompliant(true)] #if DEBUG -[assembly: InternalsVisibleTo("SpreadCheetah.Test")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("SpreadCheetah.Test")] #endif \ No newline at end of file diff --git a/SpreadCheetah/SourceGeneration/CellFormatAttribute.cs b/SpreadCheetah/SourceGeneration/CellFormatAttribute.cs index 391fc16a..8ae01a86 100644 --- a/SpreadCheetah/SourceGeneration/CellFormatAttribute.cs +++ b/SpreadCheetah/SourceGeneration/CellFormatAttribute.cs @@ -24,7 +24,6 @@ public CellFormatAttribute(StandardNumberFormat format) Format = format; } - /// /// When constructed with , returns the constructor's customFormat argument value. /// When constructed with , returns null. diff --git a/SpreadCheetah/SourceGeneration/ColumnHeaderAttribute.cs b/SpreadCheetah/SourceGeneration/ColumnHeaderAttribute.cs index 3f39b70c..5484e361 100644 --- a/SpreadCheetah/SourceGeneration/ColumnHeaderAttribute.cs +++ b/SpreadCheetah/SourceGeneration/ColumnHeaderAttribute.cs @@ -30,7 +30,6 @@ public ColumnHeaderAttribute(Type type, string propertyName) PropertyName = propertyName; } - /// /// When constructed with , returns the constructor's name argument value. /// When constructed with , returns null. @@ -48,6 +47,4 @@ public ColumnHeaderAttribute(Type type, string propertyName) /// When constructed with , returns null. /// public string? PropertyName { get; } - - } diff --git a/SpreadCheetah/SourceGeneration/EmptyWorksheetRowContext.cs b/SpreadCheetah/SourceGeneration/EmptyWorksheetRowContext.cs index b6e19bcd..03d3e32f 100644 --- a/SpreadCheetah/SourceGeneration/EmptyWorksheetRowContext.cs +++ b/SpreadCheetah/SourceGeneration/EmptyWorksheetRowContext.cs @@ -25,14 +25,14 @@ private static ValueTask AddHeaderRowAsync(Spreadsheet spreadsheet, StyleId? _, private static ValueTask AddAsRowAsync(Spreadsheet spreadsheet, T _, CancellationToken token) { - ArgumentNullException.ThrowIfNull(nameof(spreadsheet)); + ArgumentNullException.ThrowIfNull(spreadsheet); return spreadsheet.AddRowAsync(ReadOnlyMemory.Empty, token); } private static ValueTask AddRangeAsRowsAsync(Spreadsheet spreadsheet, IEnumerable objs, CancellationToken token) { - ArgumentNullException.ThrowIfNull(nameof(spreadsheet)); - ArgumentNullException.ThrowIfNull(nameof(objs)); + ArgumentNullException.ThrowIfNull(spreadsheet); + ArgumentNullException.ThrowIfNull(objs); return AddRangeAsEmptyRowsAsync(spreadsheet, objs, token); } diff --git a/SpreadCheetah/Spreadsheet.cs b/SpreadCheetah/Spreadsheet.cs index 9dc6e3db..79d36932 100644 --- a/SpreadCheetah/Spreadsheet.cs +++ b/SpreadCheetah/Spreadsheet.cs @@ -529,7 +529,6 @@ public void AddNote(string cellReference, string noteText) /// A5:XFD5References all cells in row 5. /// /// - /// public void MergeCells(string cellRange) { ArgumentNullException.ThrowIfNull(cellRange); diff --git a/SpreadCheetah/SpreadsheetBuffer.cs b/SpreadCheetah/SpreadsheetBuffer.cs index 3169b5d2..c16cbc75 100644 --- a/SpreadCheetah/SpreadsheetBuffer.cs +++ b/SpreadCheetah/SpreadsheetBuffer.cs @@ -137,7 +137,7 @@ public bool AppendFormatted(T value) { Debug.Fail("Create non-generic overloads to avoid allocations when running on .NET Framework"); - string? s = value is IFormattable f + var s = value is IFormattable f ? f.ToString(null, CultureInfo.InvariantCulture) : value?.ToString();