diff --git a/CHANGELOG.md b/CHANGELOG.md index b4c0fab52..e174558f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,9 +15,12 @@ This document is formatted according to the principles of [Keep A CHANGELOG](htt - [Ruby] update dependency messages up to v26 ([#267](https://github.com/cucumber/gherkin/pull/267)) ### Changed -- [.NET] Drop unsupported frameworks. Now supported target frameworks are .NET 8, .NET Framework 4.6.2, .NET Standard 2.0 ([#265](https://github.com/cucumber/gherkin/pull/265)) +- [.NET] Drop unsupported frameworks. Now supported target frameworks are .NET 8, .NET Standard 2.0 ([#265](https://github.com/cucumber/gherkin/pull/265)) - [.NET] Adopt File Scoped Namespaces c# feature ([#271](https://github.com/cucumber/gherkin/pull/271)) - [.NET] Adopt c# 12 primary constructors ([#272](https://github.com/cucumber/gherkin/pull/272)) +- [.NET] Adopt c# 10 ImplicitUsings +- [.NET] Replace TinyJson to System.Text.Json +- [.NET] Enable warnings as errors ## [29.0.0] - 2024-08-12 ### Added diff --git a/dotnet/Directory.Build.props b/dotnet/Directory.Build.props index 7f5ce1c47..e454fc19b 100644 --- a/dotnet/Directory.Build.props +++ b/dotnet/Directory.Build.props @@ -2,6 +2,8 @@ 12 + enable + true diff --git a/dotnet/Gherkin.Specs/AstBuildingTests.cs b/dotnet/Gherkin.Specs/AstBuildingTests.cs index 123ee8543..85f907cfc 100644 --- a/dotnet/Gherkin.Specs/AstBuildingTests.cs +++ b/dotnet/Gherkin.Specs/AstBuildingTests.cs @@ -1,4 +1,3 @@ -using System.Linq; using FluentAssertions; using Gherkin.CucumberMessages.Types; using Gherkin.Specs.Helper; diff --git a/dotnet/Gherkin.Specs/CLI/Program.cs b/dotnet/Gherkin.Specs/CLI/Program.cs index 0da59e6a6..b20253fa3 100644 --- a/dotnet/Gherkin.Specs/CLI/Program.cs +++ b/dotnet/Gherkin.Specs/CLI/Program.cs @@ -1,10 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Gherkin.CucumberMessages; using Gherkin.Specs.EventStubs; using Gherkin.Specs.Tokens; -using Utf8Json.Resolvers; +using System.Text.Json; +using System.Text.Json.Serialization; namespace Gherkin.Specs.CLI; @@ -102,7 +100,11 @@ private static int PrintEvents(PrintEventsArgs args) { foreach (var evt in gherkinEventsProvider.GetEvents(sourceEventEvent)) { - var jsonString = Utf8Json.JsonSerializer.ToJsonString((object)evt, StandardResolver.ExcludeNullCamelCase); + var jsonString = JsonSerializer.Serialize(evt, new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + Converters = { new JsonStringEnumConverter() }, + DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull + }); Console.WriteLine(jsonString); } } diff --git a/dotnet/Gherkin.Specs/DependencyValidationTests.cs b/dotnet/Gherkin.Specs/DependencyValidationTests.cs new file mode 100644 index 000000000..3de8556fe --- /dev/null +++ b/dotnet/Gherkin.Specs/DependencyValidationTests.cs @@ -0,0 +1,21 @@ +#if NETFRAMEWORK +using System.Reflection; +using System.Text.Json; +using Xunit; + +namespace Gherkin.Specs; + +public sealed class DependencyValidationTests +{ + [Fact] + public void SystemTextJsonUpgradeBlocker() + { + // this test was made intentionally to block upgrade for system.text.json nuget package for .NET standard version of library + // discussion: https://github.com/cucumber/messages/pull/237#issuecomment-2225649432 + + var version = typeof(JsonSerializer).Assembly.GetCustomAttribute().Version; + + Assert.Equal("6.0.21.52210", version); // System.Text.Json Version 6.0.0 + } +} +#endif \ No newline at end of file diff --git a/dotnet/Gherkin.Specs/EventStubs/GherkinEventsProvider.cs b/dotnet/Gherkin.Specs/EventStubs/GherkinEventsProvider.cs index bdbba383f..5621dd3bd 100644 --- a/dotnet/Gherkin.Specs/EventStubs/GherkinEventsProvider.cs +++ b/dotnet/Gherkin.Specs/EventStubs/GherkinEventsProvider.cs @@ -1,9 +1,6 @@ using Gherkin.CucumberMessages; using Gherkin.CucumberMessages.Pickles; using Gherkin.CucumberMessages.Types; -using System; -using System.Collections.Generic; -using System.IO; namespace Gherkin.Specs.EventStubs; diff --git a/dotnet/Gherkin.Specs/EventStubs/SourceProvider.cs b/dotnet/Gherkin.Specs/EventStubs/SourceProvider.cs index 5c2bc4bed..61a8416d4 100644 --- a/dotnet/Gherkin.Specs/EventStubs/SourceProvider.cs +++ b/dotnet/Gherkin.Specs/EventStubs/SourceProvider.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.IO; using Gherkin.CucumberMessages.Types; namespace Gherkin.Specs.EventStubs; diff --git a/dotnet/Gherkin.Specs/EventTestBase.cs b/dotnet/Gherkin.Specs/EventTestBase.cs index 693d9a1d0..c73639453 100644 --- a/dotnet/Gherkin.Specs/EventTestBase.cs +++ b/dotnet/Gherkin.Specs/EventTestBase.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; using System.Diagnostics; -using System.IO; using System.Text; using FluentAssertions; using Gherkin.CucumberMessages; diff --git a/dotnet/Gherkin.Specs/Gherkin.Specs.csproj b/dotnet/Gherkin.Specs/Gherkin.Specs.csproj index 803463e38..7b0e9bdbd 100644 --- a/dotnet/Gherkin.Specs/Gherkin.Specs.csproj +++ b/dotnet/Gherkin.Specs/Gherkin.Specs.csproj @@ -1,6 +1,7 @@ + - net8.0 + net8.0;net462 Exe Gherkin.Specs.CLI.Program @@ -8,7 +9,6 @@ - all diff --git a/dotnet/Gherkin.Specs/Helper/NDJsonParser.cs b/dotnet/Gherkin.Specs/Helper/NDJsonParser.cs index 027d622e4..17e187d29 100644 --- a/dotnet/Gherkin.Specs/Helper/NDJsonParser.cs +++ b/dotnet/Gherkin.Specs/Helper/NDJsonParser.cs @@ -1,6 +1,6 @@ -using System; -using System.Collections.Generic; -using Utf8Json; +using System.Text.Json; +using System.Text.Json.Serialization; + namespace Gherkin.Specs.Helper; @@ -14,7 +14,13 @@ public static List Deserialize(string ndjson) foreach (var line in lines) { - var deserializedObject = JsonSerializer.Deserialize(line); + var deserializedObject = JsonSerializer.Deserialize(line, new JsonSerializerOptions(JsonSerializerDefaults.Web) + { + Converters = + { + new JsonStringEnumConverter(JsonNamingPolicy.CamelCase) + } + }); result.Add(deserializedObject); } diff --git a/dotnet/Gherkin.Specs/Helper/TestFileProvider.cs b/dotnet/Gherkin.Specs/Helper/TestFileProvider.cs index 7d1165ec4..03dd13213 100644 --- a/dotnet/Gherkin.Specs/Helper/TestFileProvider.cs +++ b/dotnet/Gherkin.Specs/Helper/TestFileProvider.cs @@ -1,7 +1,3 @@ -using System.Collections.Generic; -using System.IO; -using System.Linq; - namespace Gherkin.Specs.Helper; public class TestFileProvider @@ -21,7 +17,7 @@ private static IEnumerable GetTestFiles(string category) string testFileFolder = GetTestFileFolder(category); return Directory.GetFiles(testFileFolder, "*.feature") - .Where(f => Path.GetFileName(f) != "escaped_pipes.feature") //currently failing, because of https://github.com/neuecc/Utf8Json/pull/96 + .Where(f => Path.GetFileName(f) != "escaped_pipes.feature") .Select(f => new object[] { Path.GetFileName(f) }); } diff --git a/dotnet/Gherkin.Specs/Helper/TestFolders.cs b/dotnet/Gherkin.Specs/Helper/TestFolders.cs index 8b3083b7a..597670473 100644 --- a/dotnet/Gherkin.Specs/Helper/TestFolders.cs +++ b/dotnet/Gherkin.Specs/Helper/TestFolders.cs @@ -1,7 +1,3 @@ -using System; -using System.IO; -using System.Reflection; - namespace Gherkin.Specs.Helper; internal static class TestFolders @@ -10,7 +6,7 @@ public static string InputFolder { get { - var inputFolder = Path.GetDirectoryName(typeof(TestFolders).GetTypeInfo().Assembly.Location); + var inputFolder = Environment.CurrentDirectory; inputFolder = Path.Combine(inputFolder, ".."); diff --git a/dotnet/Gherkin.Specs/PicklesTests.cs b/dotnet/Gherkin.Specs/PicklesTests.cs index 2bc689176..5b659f9d1 100644 --- a/dotnet/Gherkin.Specs/PicklesTests.cs +++ b/dotnet/Gherkin.Specs/PicklesTests.cs @@ -1,4 +1,3 @@ -using System.Linq; using FluentAssertions; using Gherkin.CucumberMessages.Types; using Gherkin.Specs.Helper; diff --git a/dotnet/Gherkin.Specs/SourceTests.cs b/dotnet/Gherkin.Specs/SourceTests.cs index 6ecc3bd19..0c8616782 100644 --- a/dotnet/Gherkin.Specs/SourceTests.cs +++ b/dotnet/Gherkin.Specs/SourceTests.cs @@ -1,4 +1,3 @@ -using System.Linq; using FluentAssertions; using Gherkin.CucumberMessages.Types; using Gherkin.Specs.Helper; diff --git a/dotnet/Gherkin.Specs/StringUtilsTests.cs b/dotnet/Gherkin.Specs/StringUtilsTests.cs index 7fafa6875..bb47d4761 100644 --- a/dotnet/Gherkin.Specs/StringUtilsTests.cs +++ b/dotnet/Gherkin.Specs/StringUtilsTests.cs @@ -1,4 +1,3 @@ -using System; using Xunit; namespace Gherkin.Specs; diff --git a/dotnet/Gherkin.Specs/SuccessfulParsingTests.cs b/dotnet/Gherkin.Specs/SuccessfulParsingTests.cs index 0a2e4690d..59671e1c7 100644 --- a/dotnet/Gherkin.Specs/SuccessfulParsingTests.cs +++ b/dotnet/Gherkin.Specs/SuccessfulParsingTests.cs @@ -1,4 +1,3 @@ -using System.IO; using Gherkin.Specs.Helper; using Xunit; diff --git a/dotnet/Gherkin.Specs/TokenizationTests.cs b/dotnet/Gherkin.Specs/TokenizationTests.cs index 12d35c9a4..d75736ae3 100644 --- a/dotnet/Gherkin.Specs/TokenizationTests.cs +++ b/dotnet/Gherkin.Specs/TokenizationTests.cs @@ -1,5 +1,4 @@ using System.Diagnostics; -using System.IO; using Gherkin.Specs.Helper; using Xunit; diff --git a/dotnet/Gherkin.Specs/Tokens/TestTokenFormatter.cs b/dotnet/Gherkin.Specs/Tokens/TestTokenFormatter.cs index 62146df86..1f55aea84 100644 --- a/dotnet/Gherkin.Specs/Tokens/TestTokenFormatter.cs +++ b/dotnet/Gherkin.Specs/Tokens/TestTokenFormatter.cs @@ -1,6 +1,3 @@ -using System; -using System.Linq; - namespace Gherkin.Specs.Tokens; class TestTokenFormatter diff --git a/dotnet/Gherkin.Specs/Tokens/TokenFormatterBuilder.cs b/dotnet/Gherkin.Specs/Tokens/TokenFormatterBuilder.cs index d02aa4a3c..52f43f411 100644 --- a/dotnet/Gherkin.Specs/Tokens/TokenFormatterBuilder.cs +++ b/dotnet/Gherkin.Specs/Tokens/TokenFormatterBuilder.cs @@ -1,4 +1,3 @@ -using System; using System.Text; namespace Gherkin.Specs.Tokens; diff --git a/dotnet/Gherkin.Specs/Tokens/TokensGenerator.cs b/dotnet/Gherkin.Specs/Tokens/TokensGenerator.cs index d8f4b49ed..79702e733 100644 --- a/dotnet/Gherkin.Specs/Tokens/TokensGenerator.cs +++ b/dotnet/Gherkin.Specs/Tokens/TokensGenerator.cs @@ -1,6 +1,3 @@ -using System; -using System.IO; - namespace Gherkin.Specs.Tokens; public class TokensGenerator diff --git a/dotnet/Gherkin/Ast/DataTable.cs b/dotnet/Gherkin/Ast/DataTable.cs index 7725274cb..3b9692416 100644 --- a/dotnet/Gherkin/Ast/DataTable.cs +++ b/dotnet/Gherkin/Ast/DataTable.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; - namespace Gherkin.Ast; public class DataTable : StepArgument, IHasRows, IHasLocation diff --git a/dotnet/Gherkin/Ast/Examples.cs b/dotnet/Gherkin/Ast/Examples.cs index c5eaa83bb..8520886b0 100644 --- a/dotnet/Gherkin/Ast/Examples.cs +++ b/dotnet/Gherkin/Ast/Examples.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Linq; - namespace Gherkin.Ast; public class Examples(Tag[] tags, Location location, string keyword, string name, string description, TableRow header, TableRow[] body) diff --git a/dotnet/Gherkin/Ast/Feature.cs b/dotnet/Gherkin/Ast/Feature.cs index b32ae177e..22676e205 100644 --- a/dotnet/Gherkin/Ast/Feature.cs +++ b/dotnet/Gherkin/Ast/Feature.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public class Feature(Tag[] tags, Location location, string language, string keyword, string name, string description, IHasLocation[] children) diff --git a/dotnet/Gherkin/Ast/GherkinDocument.cs b/dotnet/Gherkin/Ast/GherkinDocument.cs index 779e4bec6..87c7e7b54 100644 --- a/dotnet/Gherkin/Ast/GherkinDocument.cs +++ b/dotnet/Gherkin/Ast/GherkinDocument.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public class GherkinDocument(Feature feature, Comment[] comments) diff --git a/dotnet/Gherkin/Ast/IHasChildren.cs b/dotnet/Gherkin/Ast/IHasChildren.cs index f7faee4e8..af5a415b1 100644 --- a/dotnet/Gherkin/Ast/IHasChildren.cs +++ b/dotnet/Gherkin/Ast/IHasChildren.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public interface IHasChildren diff --git a/dotnet/Gherkin/Ast/IHasRows.cs b/dotnet/Gherkin/Ast/IHasRows.cs index 6e595044b..94474e978 100644 --- a/dotnet/Gherkin/Ast/IHasRows.cs +++ b/dotnet/Gherkin/Ast/IHasRows.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public interface IHasRows diff --git a/dotnet/Gherkin/Ast/IHasSteps.cs b/dotnet/Gherkin/Ast/IHasSteps.cs index 7645f2e04..b66e5011a 100644 --- a/dotnet/Gherkin/Ast/IHasSteps.cs +++ b/dotnet/Gherkin/Ast/IHasSteps.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public interface IHasSteps diff --git a/dotnet/Gherkin/Ast/IHasTags.cs b/dotnet/Gherkin/Ast/IHasTags.cs index 0fbe2310c..1c3ca0dc6 100644 --- a/dotnet/Gherkin/Ast/IHasTags.cs +++ b/dotnet/Gherkin/Ast/IHasTags.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public interface IHasTags diff --git a/dotnet/Gherkin/Ast/Rule.cs b/dotnet/Gherkin/Ast/Rule.cs index 4188e5345..fc9905ef8 100644 --- a/dotnet/Gherkin/Ast/Rule.cs +++ b/dotnet/Gherkin/Ast/Rule.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public class Rule(Tag[] tags, Location location, string keyword, string name, string description, IHasLocation[] children) diff --git a/dotnet/Gherkin/Ast/Scenario.cs b/dotnet/Gherkin/Ast/Scenario.cs index 480990062..be0cfb19a 100644 --- a/dotnet/Gherkin/Ast/Scenario.cs +++ b/dotnet/Gherkin/Ast/Scenario.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public class Scenario(Tag[] tags, Location location, string keyword, string name, string description, Step[] steps, Examples[] examples) diff --git a/dotnet/Gherkin/Ast/StepsContainer.cs b/dotnet/Gherkin/Ast/StepsContainer.cs index efcabe096..ec6985ddb 100644 --- a/dotnet/Gherkin/Ast/StepsContainer.cs +++ b/dotnet/Gherkin/Ast/StepsContainer.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public abstract class StepsContainer(Location location, string keyword, string name, string description, Step[] steps) diff --git a/dotnet/Gherkin/Ast/TableRow.cs b/dotnet/Gherkin/Ast/TableRow.cs index fb0340956..cdb7d55d9 100644 --- a/dotnet/Gherkin/Ast/TableRow.cs +++ b/dotnet/Gherkin/Ast/TableRow.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin.Ast; public class TableRow(Location location, TableCell[] cells) : IHasLocation diff --git a/dotnet/Gherkin/AstBuilder.cs b/dotnet/Gherkin/AstBuilder.cs index 3c9774575..433af8ad9 100644 --- a/dotnet/Gherkin/AstBuilder.cs +++ b/dotnet/Gherkin/AstBuilder.cs @@ -1,6 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; using Gherkin.Ast; namespace Gherkin; @@ -261,7 +258,7 @@ private Tag[] GetTags(AstNode node) { var tagsNode = node.GetSingle(RuleType.Tags); if (tagsNode == null) - return new Tag[0]; + return []; return tagsNode.GetTokens(TokenType.TagLine) .SelectMany(t => t.MatchedItems, (t, tagItem) => diff --git a/dotnet/Gherkin/AstNode.cs b/dotnet/Gherkin/AstNode.cs index 17663d7a5..42395401b 100644 --- a/dotnet/Gherkin/AstNode.cs +++ b/dotnet/Gherkin/AstNode.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Linq; - namespace Gherkin; public class AstNode(RuleType ruleType) diff --git a/dotnet/Gherkin/CucumberMessages/AstMessagesConverter.cs b/dotnet/Gherkin/CucumberMessages/AstMessagesConverter.cs index e81936fa3..229a5667a 100644 --- a/dotnet/Gherkin/CucumberMessages/AstMessagesConverter.cs +++ b/dotnet/Gherkin/CucumberMessages/AstMessagesConverter.cs @@ -1,8 +1,5 @@ using Gherkin.Ast; using Gherkin.CucumberMessages.Types; -using System; -using System.Collections.Generic; -using System.Linq; using Background = Gherkin.CucumberMessages.Types.Background; using Comment = Gherkin.CucumberMessages.Types.Comment; using DataTable = Gherkin.CucumberMessages.Types.DataTable; diff --git a/dotnet/Gherkin/CucumberMessages/EnumerableExtensions.cs b/dotnet/Gherkin/CucumberMessages/EnumerableExtensions.cs index 3cfabdb83..5e475c00a 100644 --- a/dotnet/Gherkin/CucumberMessages/EnumerableExtensions.cs +++ b/dotnet/Gherkin/CucumberMessages/EnumerableExtensions.cs @@ -1,6 +1,4 @@ -using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Linq; namespace Gherkin.CucumberMessages; diff --git a/dotnet/Gherkin/CucumberMessages/IdGenerator.cs b/dotnet/Gherkin/CucumberMessages/IdGenerator.cs index 0e8f217b1..dd077a6d9 100644 --- a/dotnet/Gherkin/CucumberMessages/IdGenerator.cs +++ b/dotnet/Gherkin/CucumberMessages/IdGenerator.cs @@ -1,5 +1,3 @@ -using System; - namespace Gherkin.CucumberMessages; public interface IIdGenerator diff --git a/dotnet/Gherkin/CucumberMessages/Pickles/PickleCompiler.cs b/dotnet/Gherkin/CucumberMessages/Pickles/PickleCompiler.cs index a9251837a..147f8f940 100644 --- a/dotnet/Gherkin/CucumberMessages/Pickles/PickleCompiler.cs +++ b/dotnet/Gherkin/CucumberMessages/Pickles/PickleCompiler.cs @@ -1,7 +1,4 @@ using Gherkin.CucumberMessages.Types; -using System; -using System.Collections.Generic; -using System.Linq; // ReSharper disable PossibleMultipleEnumeration diff --git a/dotnet/Gherkin/CucumberMessages/Types/Background.cs b/dotnet/Gherkin/CucumberMessages/Types/Background.cs index ce61109c9..586421091 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Background.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Background.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/DataTable.cs b/dotnet/Gherkin/CucumberMessages/Types/DataTable.cs index daa43b684..d02279cb7 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/DataTable.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/DataTable.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/Envelope.cs b/dotnet/Gherkin/CucumberMessages/Types/Envelope.cs index c0387a343..5c97dccb5 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Envelope.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Envelope.cs @@ -1,4 +1,3 @@ -using System; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/Examples.cs b/dotnet/Gherkin/CucumberMessages/Types/Examples.cs index 8bf8499c5..909563d38 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Examples.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Examples.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/Feature.cs b/dotnet/Gherkin/CucumberMessages/Types/Feature.cs index 9b3d9a003..35a37512e 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Feature.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Feature.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/GherkinDocument.cs b/dotnet/Gherkin/CucumberMessages/Types/GherkinDocument.cs index ea0068ca4..c0000d99e 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/GherkinDocument.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/GherkinDocument.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/Pickle.cs b/dotnet/Gherkin/CucumberMessages/Types/Pickle.cs index 34137f68e..e0eebf31d 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Pickle.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Pickle.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/PickleStep.cs b/dotnet/Gherkin/CucumberMessages/Types/PickleStep.cs index 1bbf304ad..715460124 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/PickleStep.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/PickleStep.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/PickleTable.cs b/dotnet/Gherkin/CucumberMessages/Types/PickleTable.cs index d741c9937..99b5d8daf 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/PickleTable.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/PickleTable.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/PickleTableRow.cs b/dotnet/Gherkin/CucumberMessages/Types/PickleTableRow.cs index 6b41231b1..8648c206b 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/PickleTableRow.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/PickleTableRow.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/Rule.cs b/dotnet/Gherkin/CucumberMessages/Types/Rule.cs index e51e537c2..3427a0690 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Rule.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Rule.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/Scenario.cs b/dotnet/Gherkin/CucumberMessages/Types/Scenario.cs index 9b0a1d701..076c16447 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/Scenario.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/Scenario.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/CucumberMessages/Types/TableRow.cs b/dotnet/Gherkin/CucumberMessages/Types/TableRow.cs index 80a66ca0a..1a10327ba 100644 --- a/dotnet/Gherkin/CucumberMessages/Types/TableRow.cs +++ b/dotnet/Gherkin/CucumberMessages/Types/TableRow.cs @@ -1,4 +1,3 @@ -using System.Collections.Generic; using System.Runtime.Serialization; namespace Gherkin.CucumberMessages.Types; diff --git a/dotnet/Gherkin/Gherkin.csproj b/dotnet/Gherkin/Gherkin.csproj index e4b7610d7..a8be9f321 100644 --- a/dotnet/Gherkin/Gherkin.csproj +++ b/dotnet/Gherkin/Gherkin.csproj @@ -1,6 +1,7 @@ + - net8.0;netstandard2.0;net462 + net8.0;netstandard2.0 true 1591 false @@ -18,7 +19,7 @@ Gherkin Parser Gherkin Cucumber Ltd, Gaspar Nagy - Copyright © Cucumber Ltd, Gaspar Nagy + Copyright © Cucumber Ltd, Gaspar Nagy Cross-platform parser for the Gherkin language, used by Cucumber, SpecFlow and other Cucumber-based tools to parse feature files. specflow gherkin cucumber https://github.com/cucumber/gherkin @@ -31,8 +32,12 @@ bin/$(Configuration)/NuGet + + + + - + diff --git a/dotnet/Gherkin/GherkinDialect.cs b/dotnet/Gherkin/GherkinDialect.cs index d54e8ffbc..992be5eb8 100644 --- a/dotnet/Gherkin/GherkinDialect.cs +++ b/dotnet/Gherkin/GherkinDialect.cs @@ -1,6 +1,3 @@ -using System.Collections.Generic; -using System.Linq; - namespace Gherkin; public class GherkinDialect( diff --git a/dotnet/Gherkin/GherkinDialectProvider.cs b/dotnet/Gherkin/GherkinDialectProvider.cs index 73e848fe1..ac3e17c57 100644 --- a/dotnet/Gherkin/GherkinDialectProvider.cs +++ b/dotnet/Gherkin/GherkinDialectProvider.cs @@ -1,8 +1,5 @@ using Gherkin.Ast; -using System; -using System.Collections.Generic; -using System.IO; -using TinyJson; +using System.Text.Json; namespace Gherkin; @@ -55,8 +52,7 @@ protected virtual Dictionary LoadLanguageSetting protected virtual Dictionary ParseJsonContent(string languagesFileContent) { - // ReSharper disable once InvokeAsExtensionMethod - return JSONParser.FromJson>(languagesFileContent); + return JsonSerializer.Deserialize>(languagesFileContent, new JsonSerializerOptions(JsonSerializerDefaults.Web)); } protected virtual bool TryGetDialect(string language, Dictionary gherkinLanguageSettings, Location location, out GherkinDialect dialect) @@ -75,17 +71,17 @@ protected GherkinDialect CreateGherkinDialect(string language, GherkinLanguageSe { return new GherkinDialect( language, - ParseTitleKeywords(languageSettings.feature), - ParseTitleKeywords(languageSettings.rule), - ParseTitleKeywords(languageSettings.background), - ParseTitleKeywords(languageSettings.scenario), - ParseTitleKeywords(languageSettings.scenarioOutline), - ParseTitleKeywords(languageSettings.examples), - ParseStepKeywords(languageSettings.given), - ParseStepKeywords(languageSettings.when), - ParseStepKeywords(languageSettings.then), - ParseStepKeywords(languageSettings.and), - ParseStepKeywords(languageSettings.but) + ParseTitleKeywords(languageSettings.Feature), + ParseTitleKeywords(languageSettings.Rule), + ParseTitleKeywords(languageSettings.Background), + ParseTitleKeywords(languageSettings.Scenario), + ParseTitleKeywords(languageSettings.ScenarioOutline), + ParseTitleKeywords(languageSettings.Examples), + ParseStepKeywords(languageSettings.Given), + ParseStepKeywords(languageSettings.When), + ParseStepKeywords(languageSettings.Then), + ParseStepKeywords(languageSettings.And), + ParseStepKeywords(languageSettings.But) ); } @@ -103,35 +99,33 @@ protected static GherkinDialect GetFactoryDefault() { return new GherkinDialect( "en", - new[] { "Feature" }, - new[] { "Rule" }, - new[] { "Background" }, - new[] { "Scenario" }, - new[] { "Scenario Outline", "Scenario Template" }, - new[] { "Examples", "Scenarios" }, - new[] { "* ", "Given " }, - new[] { "* ", "When " }, - new[] { "* ", "Then " }, - new[] { "* ", "And " }, - new[] { "* ", "But " }); + ["Feature"], + ["Rule"], + ["Background"], + ["Scenario"], + ["Scenario Outline", "Scenario Template"], + ["Examples", "Scenarios"], + ["* ", "Given "], + ["* ", "When "], + ["* ", "Then "], + ["* ", "And "], + ["* ", "But "]); } } public class GherkinLanguageSetting { - // ReSharper disable InconsistentNaming - public string name; - public string native; - public string[] feature; - public string[] rule; - public string[] background; - public string[] scenario; - public string[] scenarioOutline; - public string[] examples; - public string[] given; - public string[] when; - public string[] then; - public string[] and; - public string[] but; - // ReSharper restore InconsistentNaming + public string Name { get; set; } + public string Native { get; set; } + public string[] Feature { get; set; } + public string[] Rule { get; set; } + public string[] Background { get; set; } + public string[] Scenario { get; set; } + public string[] ScenarioOutline { get; set; } + public string[] Examples { get; set; } + public string[] Given { get; set; } + public string[] When { get; set; } + public string[] Then { get; set; } + public string[] And { get; set; } + public string[] But { get; set; } } diff --git a/dotnet/Gherkin/GherkinLine.cs b/dotnet/Gherkin/GherkinLine.cs index 20b137856..46f4f3afe 100644 --- a/dotnet/Gherkin/GherkinLine.cs +++ b/dotnet/Gherkin/GherkinLine.cs @@ -1,14 +1,11 @@ using Gherkin.Ast; -using System; -using System.Collections.Generic; -using System.Linq; using System.Text.RegularExpressions; namespace Gherkin; public class GherkinLine : IGherkinLine { - private static char[] inlineWhitespaceChars = new char[] { ' ', '\t', '\u00A0' }; + private static char[] inlineWhitespaceChars = [' ', '\t', '\u00A0']; private readonly string lineText; private readonly string trimmedLineText; diff --git a/dotnet/Gherkin/IGherkinLine.cs b/dotnet/Gherkin/IGherkinLine.cs index f61cc9770..5edd966da 100644 --- a/dotnet/Gherkin/IGherkinLine.cs +++ b/dotnet/Gherkin/IGherkinLine.cs @@ -1,5 +1,3 @@ -using System.Collections.Generic; - namespace Gherkin; /// diff --git a/dotnet/Gherkin/Parser.Extensions.cs b/dotnet/Gherkin/Parser.Extensions.cs index 3bc1939c5..50b8f0eb4 100644 --- a/dotnet/Gherkin/Parser.Extensions.cs +++ b/dotnet/Gherkin/Parser.Extensions.cs @@ -1,4 +1,3 @@ -using System.IO; using Gherkin.Ast; namespace Gherkin; diff --git a/dotnet/Gherkin/ParserException.cs b/dotnet/Gherkin/ParserException.cs index 8d9e0eea4..2afaa755f 100644 --- a/dotnet/Gherkin/ParserException.cs +++ b/dotnet/Gherkin/ParserException.cs @@ -1,7 +1,4 @@ using Gherkin.Ast; -using System; -using System.Collections.Generic; -using System.Linq; namespace Gherkin; diff --git a/dotnet/Gherkin/TinyJson/JSONParser.cs b/dotnet/Gherkin/TinyJson/JSONParser.cs deleted file mode 100644 index 399c90157..000000000 --- a/dotnet/Gherkin/TinyJson/JSONParser.cs +++ /dev/null @@ -1,372 +0,0 @@ -// source: https://raw.githubusercontent.com/zanders3/json/master/src/JSONParser.cs -// changes: class public -> internal -using System; -using System.Collections; -using System.Collections.Generic; -using System.Reflection; -using System.Runtime.Serialization; -using System.Text; - -namespace TinyJson; - -// Really simple JSON parser in ~300 lines -// - Attempts to parse JSON files with minimal GC allocation -// - Nice and simple "[1,2,3]".FromJson>() API -// - Classes and structs can be parsed too! -// class Foo { public int Value; } -// "{\"Value\":10}".FromJson() -// - Can parse JSON without type information into Dictionary and List e.g. -// "[1,2,3]".FromJson().GetType() == typeof(List) -// "{\"Value\":10}".FromJson().GetType() == typeof(Dictionary) -// - No JIT Emit support to support AOT compilation on iOS -// - Attempts are made to NOT throw an exception if the JSON is corrupted or invalid: returns null instead. -// - Only public fields and property setters on classes/structs will be written to -// -// Limitations: -// - No JIT Emit support to parse structures quickly -// - Limited to parsing <2GB JSON files (due to int.MaxValue) -// - Parsing of abstract classes or interfaces is NOT supported and will throw an exception. -internal static class JSONParser -{ - [ThreadStatic] static Stack> splitArrayPool; - [ThreadStatic] static StringBuilder stringBuilder; - [ThreadStatic] static Dictionary> fieldInfoCache; - [ThreadStatic] static Dictionary> propertyInfoCache; - - public static T FromJson(this string json) - { - // Initialize, if needed, the ThreadStatic variables - if (propertyInfoCache == null) propertyInfoCache = new Dictionary>(); - if (fieldInfoCache == null) fieldInfoCache = new Dictionary>(); - if (stringBuilder == null) stringBuilder = new StringBuilder(); - if (splitArrayPool == null) splitArrayPool = new Stack>(); - - //Remove all whitespace not within strings to make parsing simpler - stringBuilder.Length = 0; - for (int i = 0; i < json.Length; i++) - { - char c = json[i]; - if (c == '"') - { - i = AppendUntilStringEnd(true, i, json); - continue; - } - if (char.IsWhiteSpace(c)) - continue; - - stringBuilder.Append(c); - } - - //Parse the thing! - return (T)ParseValue(typeof(T), stringBuilder.ToString()); - } - - static int AppendUntilStringEnd(bool appendEscapeCharacter, int startIdx, string json) - { - stringBuilder.Append(json[startIdx]); - for (int i = startIdx + 1; i < json.Length; i++) - { - if (json[i] == '\\') - { - if (appendEscapeCharacter) - stringBuilder.Append(json[i]); - stringBuilder.Append(json[i + 1]); - i++;//Skip next character as it is escaped - } - else if (json[i] == '"') - { - stringBuilder.Append(json[i]); - return i; - } - else - stringBuilder.Append(json[i]); - } - return json.Length - 1; - } - - //Splits { :, : } and [ , ] into a list of strings - static List Split(string json) - { - List splitArray = splitArrayPool.Count > 0 ? splitArrayPool.Pop() : new List(); - splitArray.Clear(); - if (json.Length == 2) - return splitArray; - int parseDepth = 0; - stringBuilder.Length = 0; - for (int i = 1; i < json.Length - 1; i++) - { - switch (json[i]) - { - case '[': - case '{': - parseDepth++; - break; - case ']': - case '}': - parseDepth--; - break; - case '"': - i = AppendUntilStringEnd(true, i, json); - continue; - case ',': - case ':': - if (parseDepth == 0) - { - splitArray.Add(stringBuilder.ToString()); - stringBuilder.Length = 0; - continue; - } - break; - } - - stringBuilder.Append(json[i]); - } - - splitArray.Add(stringBuilder.ToString()); - - return splitArray; - } - - internal static object ParseValue(Type type, string json) - { - if (type == typeof(string)) - { - if (json.Length <= 2) - return string.Empty; - StringBuilder parseStringBuilder = new StringBuilder(json.Length); - for (int i = 1; i < json.Length - 1; ++i) - { - if (json[i] == '\\' && i + 1 < json.Length - 1) - { - int j = "\"\\nrtbf/".IndexOf(json[i + 1]); - if (j >= 0) - { - parseStringBuilder.Append("\"\\\n\r\t\b\f/"[j]); - ++i; - continue; - } - if (json[i + 1] == 'u' && i + 5 < json.Length - 1) - { - UInt32 c = 0; - if (UInt32.TryParse(json.Substring(i + 2, 4), System.Globalization.NumberStyles.AllowHexSpecifier, null, out c)) - { - parseStringBuilder.Append((char)c); - i += 5; - continue; - } - } - } - parseStringBuilder.Append(json[i]); - } - return parseStringBuilder.ToString(); - } - if (type.IsPrimitive) - { - var result = Convert.ChangeType(json, type, System.Globalization.CultureInfo.InvariantCulture); - return result; - } - if (type == typeof(decimal)) - { - decimal result; - decimal.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out result); - return result; - } - if (json == "null") - { - return null; - } - if (type.IsEnum) - { - if (json[0] == '"') - json = json.Substring(1, json.Length - 2); - try - { - return Enum.Parse(type, json, false); - } - catch - { - return 0; - } - } - if (type.IsArray) - { - Type arrayType = type.GetElementType(); - if (json[0] != '[' || json[json.Length - 1] != ']') - return null; - - List elems = Split(json); - Array newArray = Array.CreateInstance(arrayType, elems.Count); - for (int i = 0; i < elems.Count; i++) - newArray.SetValue(ParseValue(arrayType, elems[i]), i); - splitArrayPool.Push(elems); - return newArray; - } - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(List<>)) - { - Type listType = type.GetGenericArguments()[0]; - if (json[0] != '[' || json[json.Length - 1] != ']') - return null; - - List elems = Split(json); - var list = (IList)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count }); - for (int i = 0; i < elems.Count; i++) - list.Add(ParseValue(listType, elems[i])); - splitArrayPool.Push(elems); - return list; - } - if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Dictionary<,>)) - { - Type keyType, valueType; - { - Type[] args = type.GetGenericArguments(); - keyType = args[0]; - valueType = args[1]; - } - - //Refuse to parse dictionary keys that aren't of type string - if (keyType != typeof(string)) - return null; - //Must be a valid dictionary element - if (json[0] != '{' || json[json.Length - 1] != '}') - return null; - //The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON - List elems = Split(json); - if (elems.Count % 2 != 0) - return null; - - var dictionary = (IDictionary)type.GetConstructor(new Type[] { typeof(int) }).Invoke(new object[] { elems.Count / 2 }); - for (int i = 0; i < elems.Count; i += 2) - { - if (elems[i].Length <= 2) - continue; - string keyValue = elems[i].Substring(1, elems[i].Length - 2); - object val = ParseValue(valueType, elems[i + 1]); - dictionary[keyValue] = val; - } - return dictionary; - } - if (type == typeof(object)) - { - return ParseAnonymousValue(json); - } - if (json[0] == '{' && json[json.Length - 1] == '}') - { - return ParseObject(type, json); - } - - return null; - } - - static object ParseAnonymousValue(string json) - { - if (json.Length == 0) - return null; - if (json[0] == '{' && json[json.Length - 1] == '}') - { - List elems = Split(json); - if (elems.Count % 2 != 0) - return null; - var dict = new Dictionary(elems.Count / 2); - for (int i = 0; i < elems.Count; i += 2) - dict[elems[i].Substring(1, elems[i].Length - 2)] = ParseAnonymousValue(elems[i + 1]); - return dict; - } - if (json[0] == '[' && json[json.Length - 1] == ']') - { - List items = Split(json); - var finalList = new List(items.Count); - for (int i = 0; i < items.Count; i++) - finalList.Add(ParseAnonymousValue(items[i])); - return finalList; - } - if (json[0] == '"' && json[json.Length - 1] == '"') - { - string str = json.Substring(1, json.Length - 2); - return str.Replace("\\", string.Empty); - } - if (char.IsDigit(json[0]) || json[0] == '-') - { - if (json.Contains(".")) - { - double result; - double.TryParse(json, System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out result); - return result; - } - else - { - int result; - int.TryParse(json, out result); - return result; - } - } - if (json == "true") - return true; - if (json == "false") - return false; - // handles json == "null" as well as invalid JSON - return null; - } - - static Dictionary CreateMemberNameDictionary(T[] members) where T : MemberInfo - { - Dictionary nameToMember = new Dictionary(StringComparer.OrdinalIgnoreCase); - for (int i = 0; i < members.Length; i++) - { - T member = members[i]; - if (member.IsDefined(typeof(IgnoreDataMemberAttribute), true)) - continue; - - string name = member.Name; - if (member.IsDefined(typeof(DataMemberAttribute), true)) - { - DataMemberAttribute dataMemberAttribute = (DataMemberAttribute)Attribute.GetCustomAttribute(member, typeof(DataMemberAttribute), true); - if (!string.IsNullOrEmpty(dataMemberAttribute.Name)) - name = dataMemberAttribute.Name; - } - - nameToMember.Add(name, member); - } - - return nameToMember; - } - - static object ParseObject(Type type, string json) - { - object instance = FormatterServices.GetUninitializedObject(type); - - //The list is split into key/value pairs only, this means the split must be divisible by 2 to be valid JSON - List elems = Split(json); - if (elems.Count % 2 != 0) - return instance; - - Dictionary nameToField; - Dictionary nameToProperty; - if (!fieldInfoCache.TryGetValue(type, out nameToField)) - { - nameToField = CreateMemberNameDictionary(type.GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); - fieldInfoCache.Add(type, nameToField); - } - if (!propertyInfoCache.TryGetValue(type, out nameToProperty)) - { - nameToProperty = CreateMemberNameDictionary(type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.FlattenHierarchy)); - propertyInfoCache.Add(type, nameToProperty); - } - - for (int i = 0; i < elems.Count; i += 2) - { - if (elems[i].Length <= 2) - continue; - string key = elems[i].Substring(1, elems[i].Length - 2); - string value = elems[i + 1]; - - FieldInfo fieldInfo; - PropertyInfo propertyInfo; - if (nameToField.TryGetValue(key, out fieldInfo)) - fieldInfo.SetValue(instance, ParseValue(fieldInfo.FieldType, value)); - else if (nameToProperty.TryGetValue(key, out propertyInfo)) - propertyInfo.SetValue(instance, ParseValue(propertyInfo.PropertyType, value), null); - } - - return instance; - } -} diff --git a/dotnet/Gherkin/TokenMatcher.cs b/dotnet/Gherkin/TokenMatcher.cs index 15ae00317..93110834c 100644 --- a/dotnet/Gherkin/TokenMatcher.cs +++ b/dotnet/Gherkin/TokenMatcher.cs @@ -1,5 +1,3 @@ -using System; -using System.Linq; using System.Text.RegularExpressions; namespace Gherkin; diff --git a/dotnet/Gherkin/TokenScanner.cs b/dotnet/Gherkin/TokenScanner.cs index 6591d49e4..e3fdf9292 100644 --- a/dotnet/Gherkin/TokenScanner.cs +++ b/dotnet/Gherkin/TokenScanner.cs @@ -1,6 +1,3 @@ -using System.IO; -using Gherkin.Ast; - namespace Gherkin; ///