From 291f2954bf7421040b131d2188174dc8ae5fac20 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:32:29 +0000 Subject: [PATCH 01/10] chore(deps): update dotnet monorepo to v8 --- global.json | 2 +- src/DataFilters/DataFilters.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 8a59db38..b83a83ee 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.405", + "version": "8.0.101", "rollForward": "major" } } \ No newline at end of file diff --git a/src/DataFilters/DataFilters.csproj b/src/DataFilters/DataFilters.csproj index 960a3012..7588081b 100644 --- a/src/DataFilters/DataFilters.csproj +++ b/src/DataFilters/DataFilters.csproj @@ -20,18 +20,18 @@ - + - + - + From 4637c9d9561d63090be069ae71bfa17327309113 Mon Sep 17 00:00:00 2001 From: "renovate[bot]" <29139614+renovate[bot]@users.noreply.github.com> Date: Fri, 26 Jan 2024 09:32:29 +0000 Subject: [PATCH 02/10] chore(deps): update dotnet monorepo to v8 --- global.json | 2 +- src/DataFilters/DataFilters.csproj | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/global.json b/global.json index 8a59db38..b83a83ee 100644 --- a/global.json +++ b/global.json @@ -1,6 +1,6 @@ { "sdk": { - "version": "7.0.405", + "version": "8.0.101", "rollForward": "major" } } \ No newline at end of file diff --git a/src/DataFilters/DataFilters.csproj b/src/DataFilters/DataFilters.csproj index 960a3012..7588081b 100644 --- a/src/DataFilters/DataFilters.csproj +++ b/src/DataFilters/DataFilters.csproj @@ -20,18 +20,18 @@ - + - + - + From f45ab09ee94f2bbbdf8cfdbb6d83e7c1cf024cfa Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Fri, 26 Jan 2024 10:59:37 +0100 Subject: [PATCH 03/10] feat: add `net8.0` target add net8.0 target add code styles checks on CI pipeline --- .nuke/build.schema.json | 14 ++++++++++++++ .nuke/parameters.json | 4 +++- build/Build.cs | 15 +++++++-------- build/_build.csproj | 1 + .../DataFilters.Queries.csproj | 2 +- src/DataFilters/DataFilters.csproj | 14 ++++++++++---- .../DataFilters.Expressions.csproj | 2 +- .../DataFilters.Expressions.UnitTests.csproj | 12 ++++++------ .../DataFilters.PerformanceTests.csproj | 2 +- .../DataFilters.Queries.UnitTests.csproj | 2 +- .../DataFilters.TestObjects.csproj | 2 +- .../DataFilters.UnitTests.csproj | 2 +- 12 files changed, 47 insertions(+), 25 deletions(-) diff --git a/.nuke/build.schema.json b/.nuke/build.schema.json index a8a54ae2..0b5d4b70 100644 --- a/.nuke/build.schema.json +++ b/.nuke/build.schema.json @@ -43,6 +43,18 @@ "type": "boolean", "description": "Indicates to open the pull request as 'draft'" }, + "Formatters": { + "type": "array", + "description": "Sets of formatters that the tool must apply", + "items": { + "type": "string", + "enum": [ + "Analyzers", + "Style", + "Whitespace" + ] + } + }, "GitHubToken": { "type": "string", "description": "Token used to create a new release in GitHub", @@ -133,6 +145,7 @@ "Coldfix", "Compile", "Feature", + "Format", "Hotfix", "MutationTests", "Pack", @@ -166,6 +179,7 @@ "Coldfix", "Compile", "Feature", + "Format", "Hotfix", "MutationTests", "Pack", diff --git a/.nuke/parameters.json b/.nuke/parameters.json index 3c9e37ef..df1d35da 100644 --- a/.nuke/parameters.json +++ b/.nuke/parameters.json @@ -1,4 +1,6 @@ { "$schema": "./build.schema.json", - "Solution": "DataFilters.sln" + "Solution": "DataFilters.sln", + "NoLogo": true, + "Formatters": ["analyzers", "style"] } \ No newline at end of file diff --git a/build/Build.cs b/build/Build.cs index 95302909..e5a44d0a 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -1,20 +1,18 @@ namespace DataFilters.ContinuousIntegration { + using System; + using System.Collections.Generic; + using System.Linq; using Candoumbe.Pipelines.Components; + using Candoumbe.Pipelines.Components.Formatting; using Candoumbe.Pipelines.Components.GitHub; using Candoumbe.Pipelines.Components.NuGet; using Candoumbe.Pipelines.Components.Workflows; - using Nuke.Common; using Nuke.Common.CI.GitHubActions; using Nuke.Common.IO; using Nuke.Common.ProjectModel; - using System; - using System.Collections.Generic; - using System.Configuration; - using System.Linq; - [GitHubActions( "integration", GitHubActionsImage.UbuntuLatest, @@ -107,14 +105,13 @@ namespace DataFilters.ContinuousIntegration )] public class Build : NukeBuild, - IHaveArtifacts, - IHaveChangeLog, IHaveSolution, IHaveSourceDirectory, IHaveTestDirectory, IGitFlowWithPullRequest, IClean, IRestore, + IDotnetFormat, ICompile, IUnitTest, IMutationTest, @@ -179,6 +176,8 @@ IEnumerable IMutationTest.MutationTestsProjects () => this is ICreateGithubRelease && this.Get()?.GitHubToken is not null) }; + bool IDotnetFormat.VerifyNoChanges => IsServerBuild; + protected override void OnBuildCreated() { if (IsServerBuild) diff --git a/build/_build.csproj b/build/_build.csproj index 2429c1f0..51dc6ed1 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -12,6 +12,7 @@ + diff --git a/src/DataFilters.Queries/DataFilters.Queries.csproj b/src/DataFilters.Queries/DataFilters.Queries.csproj index 760e712c..258bebbc 100644 --- a/src/DataFilters.Queries/DataFilters.Queries.csproj +++ b/src/DataFilters.Queries/DataFilters.Queries.csproj @@ -1,7 +1,7 @@  - netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0 + netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 Provides extension methods to convert IFilter to IWhereClause and IOrder to ISort. bin\$(Configuration)\$(TargetFramework)\DataFilters.Queries.xml diff --git a/src/DataFilters/DataFilters.csproj b/src/DataFilters/DataFilters.csproj index 7588081b..b7268407 100644 --- a/src/DataFilters/DataFilters.csproj +++ b/src/DataFilters/DataFilters.csproj @@ -2,14 +2,14 @@ - netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0 + netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 Sets of classes to convert querystrings to strongly typed expressions. expressions, querystring bin\$(Configuration)\$(TargetFramework)\DataFilters.xml README.md - + STRING_SEGMENT @@ -20,12 +20,18 @@ - + - + + + + + + + diff --git a/src/Datafilters.Expressions/DataFilters.Expressions.csproj b/src/Datafilters.Expressions/DataFilters.Expressions.csproj index fc4c6514..692a9e5e 100644 --- a/src/Datafilters.Expressions/DataFilters.Expressions.csproj +++ b/src/Datafilters.Expressions/DataFilters.Expressions.csproj @@ -2,7 +2,7 @@ - netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0 + netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 Converts IFilter instance to strongly typed expressions. bin\$(Configuration)\$(TargetFramework)\DataFilters.Expressions.xml diff --git a/test/DataFilters.Expressions.UnitTests/DataFilters.Expressions.UnitTests.csproj b/test/DataFilters.Expressions.UnitTests/DataFilters.Expressions.UnitTests.csproj index 35fba93d..d667c2f6 100644 --- a/test/DataFilters.Expressions.UnitTests/DataFilters.Expressions.UnitTests.csproj +++ b/test/DataFilters.Expressions.UnitTests/DataFilters.Expressions.UnitTests.csproj @@ -2,7 +2,7 @@ - net6.0;net7.0 + net6.0;net7.0;net8.0 @@ -20,19 +20,19 @@ - + - + - + - + - + diff --git a/test/DataFilters.PerformanceTests/DataFilters.PerformanceTests.csproj b/test/DataFilters.PerformanceTests/DataFilters.PerformanceTests.csproj index a042d774..0a06a8cd 100644 --- a/test/DataFilters.PerformanceTests/DataFilters.PerformanceTests.csproj +++ b/test/DataFilters.PerformanceTests/DataFilters.PerformanceTests.csproj @@ -3,7 +3,7 @@ Exe latest - net6.0;net7.0; + net6.0;net7.0;net8.0 diff --git a/test/DataFilters.Queries.UnitTests/DataFilters.Queries.UnitTests.csproj b/test/DataFilters.Queries.UnitTests/DataFilters.Queries.UnitTests.csproj index 93390478..68bb508d 100644 --- a/test/DataFilters.Queries.UnitTests/DataFilters.Queries.UnitTests.csproj +++ b/test/DataFilters.Queries.UnitTests/DataFilters.Queries.UnitTests.csproj @@ -3,7 +3,7 @@ - net6.0;net7.0 + net6.0;net7.0;net8.0 diff --git a/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj b/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj index 5e7f0dca..3592cd93 100644 --- a/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj +++ b/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj @@ -1,7 +1,7 @@ - netstandard2.0;net6.0;net7.0 + netstandard2.0;net6.0;net7.0;net8.0 false diff --git a/test/DataFilters.UnitTests/DataFilters.UnitTests.csproj b/test/DataFilters.UnitTests/DataFilters.UnitTests.csproj index 81835384..b24a057e 100644 --- a/test/DataFilters.UnitTests/DataFilters.UnitTests.csproj +++ b/test/DataFilters.UnitTests/DataFilters.UnitTests.csproj @@ -2,7 +2,7 @@ - net6.0;net7.0 + net6.0;net7.0;net8.0 From 03dc42df09f019d13dbe12345cc405e09d5cb745 Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Fri, 26 Jan 2024 11:10:36 +0100 Subject: [PATCH 04/10] chore(ci): add default value for `Formatters` settings Add a default value for `Formatters` settings when running target locally --- .nuke/parameters.local.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.nuke/parameters.local.json b/.nuke/parameters.local.json index a864825c..37c2ce36 100644 --- a/.nuke/parameters.local.json +++ b/.nuke/parameters.local.json @@ -1,5 +1,6 @@ { "GitHubToken": "v1:OhvldXY34GSHTumJiuoWhHPHKwSoVb7B/X19pufOj7+Q2O5n0YnxMIowWmry2b4SbtIW2Ah4uwPP1HpOLvvfB9LO4QIggTf/dAVK00aG+Cp2ZeG/kqhbEsc9c6tV8lZn", "NugetApiKey": "v1:pqiQ7rGFSYoCihCFSrdaCRFpf0BoThUJFRbvzCqVjnWssX61zbm6Z7aZH9x1/fOg", - "CodecovToken": "v1:fD+Tl5VfvzhiKcjphPlwB+e7w7Dj7N645OTAjSoEtozxIae0KhwkqvHBi7RChHK3" + "CodecovToken": "v1:fD+Tl5VfvzhiKcjphPlwB+e7w7Dj7N645OTAjSoEtozxIae0KhwkqvHBi7RChHK3", + "Formatters": [] } From 77dbfe4eb36b762df30288c009f18771a786e25b Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Fri, 26 Jan 2024 15:53:44 +0100 Subject: [PATCH 05/10] chore: apply coding style --- .github/workflows/delivery.yml | 12 ++--- .github/workflows/integration.yml | 12 ++--- .github/workflows/nightly-manual.yml | 14 ++--- src/DataFilters.Queries/FilterToQueries.cs | 3 +- src/DataFilters.Queries/OrderExtensions.cs | 3 +- src/DataFilters/Filter.cs | 5 +- src/DataFilters/FilterOptions.cs | 2 +- .../Grammar/Parsing/FilterToken.cs | 4 +- .../Grammar/Parsing/FilterTokenizer.cs | 8 ++- .../Grammar/Syntax/AsteriskExpression.cs | 2 +- .../Grammar/Syntax/BinaryFilterExpression.cs | 10 ++-- .../Grammar/Syntax/ConstantValueExpression.cs | 2 +- .../Grammar/Syntax/DateExpression.cs | 4 +- .../Grammar/Syntax/FilterExpression.cs | 2 +- .../Grammar/Syntax/IntervalExpression.cs | 3 +- .../Grammar/Syntax/NumericValueExpression.cs | 4 ++ .../Grammar/Syntax/OffsetExpression.cs | 2 +- .../Grammar/Syntax/OneOfExpression.cs | 6 +-- .../Grammar/Syntax/StartsWithExpression.cs | 5 +- .../Grammar/Syntax/TextExpression.cs | 2 +- .../Grammar/Syntax/TimeExpression.cs | 2 +- src/DataFilters/Order.cs | 2 +- src/DataFilters/OrderValidator.cs | 3 +- src/DataFilters/StringExtensions.cs | 2 +- .../FilterExtensions.cs | 54 ++++++------------- .../OrderExtensions.cs | 3 +- .../QueryableExtensions.cs | 5 +- .../FilterToExpressionsTests.cs | 27 ++++------ .../DataFilters.Expressions.UnitTests/Hero.cs | 4 +- .../NodaTimeClass.cs | 3 +- .../QueryableExtensionsTests.cs | 4 +- .../ToOrderClauseTests.cs | 7 +-- .../BracketVsOr.cs | 5 +- test/DataFilters.PerformanceTests/Program.cs | 2 +- .../RawFilterVsFilterService.cs | 3 +- .../FilterToQueriesTests.cs | 10 ++-- .../OrderToQueriesTests.cs | 8 +-- test/DataFilters.TestObjects/SuperHero.cs | 2 +- .../Converters/DataFilterConvertersTests.cs | 10 ++-- .../FilterExtensionsTests.cs | 10 ++-- .../FilterOptionsTests.cs | 4 +- .../FilterServiceOptionsTests.cs | 8 +-- .../FilterServiceTests.cs | 10 ++-- test/DataFilters.UnitTests/FilterTests.cs | 20 +++---- test/DataFilters.UnitTests/Generators.cs | 6 +-- .../Grammar/Parsing/FilterTokenParserTests.cs | 17 +++--- .../Grammar/Parsing/FilterTokenizerTests.cs | 13 ++--- .../Grammar/Syntax/AndExpressionTests.cs | 10 ++-- .../Grammar/Syntax/AsteriskExpressionTests.cs | 12 +++-- .../Grammar/Syntax/BoundaryExpressionTests.cs | 8 +-- .../Grammar/Syntax/BracketExpressionTests.cs | 10 ++-- .../Syntax/ConstantBracketValueTests.cs | 7 +-- .../Grammar/Syntax/ContainsExpressionTests.cs | 8 +-- .../Grammar/Syntax/DateExpressionTests.cs | 6 +-- .../Grammar/Syntax/DateTimeExpressionTests.cs | 8 +-- .../Grammar/Syntax/DurationExpressionTests.cs | 8 +-- .../Grammar/Syntax/EndsWithExpressionTests.cs | 8 +-- .../Grammar/Syntax/GroupExpressionTests.cs | 8 +-- .../Grammar/Syntax/IntervalExpressionTests.cs | 8 +-- .../Grammar/Syntax/NotExpressionTests.cs | 8 +-- .../Syntax/NumericValueExpressionTests.cs | 6 +-- .../Grammar/Syntax/OneOfExpressionTests.cs | 10 ++-- .../Grammar/Syntax/OrExpressionTests.cs | 10 ++-- .../Syntax/PropertyNameExpressionTests.cs | 7 +-- .../Grammar/Syntax/RangeBracketValueTests.cs | 8 +-- .../Syntax/StartsWithExpressionTests.cs | 8 +-- .../Syntax/StringValueExpressionTests.cs | 8 +-- .../Grammar/Syntax/TimeExpressionTests.cs | 6 +-- .../Helpers/CultureSwitcher.cs | 2 +- .../Helpers/ExpressionsGenerators.cs | 11 ++-- .../Helpers/FilterGenerators.cs | 20 +++---- .../Helpers/GeneratorHelper.cs | 5 +- .../DataFilters.UnitTests/MultiFilterTests.cs | 18 +++---- test/DataFilters.UnitTests/MultiOrderTests.cs | 5 +- test/DataFilters.UnitTests/OrderTests.cs | 8 +-- .../StringExtensionsTests.cs | 8 +-- 76 files changed, 212 insertions(+), 386 deletions(-) diff --git a/.github/workflows/delivery.yml b/.github/workflows/delivery.yml index 2342d95f..827235c6 100644 --- a/.github/workflows/delivery.yml +++ b/.github/workflows/delivery.yml @@ -33,11 +33,11 @@ jobs: name: ubuntu-latest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: 'Cache: .nuke/temp, ~/.nuget/packages' - uses: actions/cache@v4 + uses: actions/cache@v3 with: path: | .nuke/temp @@ -50,22 +50,22 @@ jobs: CodecovToken: ${{ secrets.CODECOV_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: 'Publish: unit-tests' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: unit-tests path: output/artifacts/tests-results/unit-tests - name: 'Publish: coverage-report' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: coverage-report path: output/artifacts/reports/coverage-report - name: 'Publish: coverage-history' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: coverage-history path: output/artifacts/reports/coverage-history - name: 'Publish: packages' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: packages path: output/artifacts/packages diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index a7f6c294..de4010fa 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -32,11 +32,11 @@ jobs: name: ubuntu-latest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: 'Cache: .nuke/temp, ~/.nuget/packages' - uses: actions/cache@v4 + uses: actions/cache@v3 with: path: | .nuke/temp @@ -48,22 +48,22 @@ jobs: NugetApiKey: ${{ secrets.NUGET_API_KEY }} CodecovToken: ${{ secrets.CODECOV_TOKEN }} - name: 'Publish: unit-tests' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: unit-tests path: output/artifacts/tests-results/unit-tests - name: 'Publish: coverage-report' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: coverage-report path: output/artifacts/reports/coverage-report - name: 'Publish: coverage-history' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: coverage-history path: output/artifacts/reports/coverage-history - name: 'Publish: packages' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: packages path: output/artifacts/packages diff --git a/.github/workflows/nightly-manual.yml b/.github/workflows/nightly-manual.yml index 52d2ac08..781152d8 100644 --- a/.github/workflows/nightly-manual.yml +++ b/.github/workflows/nightly-manual.yml @@ -23,11 +23,11 @@ jobs: name: ubuntu-latest runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v3 with: fetch-depth: 0 - name: 'Cache: .nuke/temp, ~/.nuget/packages' - uses: actions/cache@v4 + uses: actions/cache@v3 with: path: | .nuke/temp @@ -41,27 +41,27 @@ jobs: StrykerDashboardApiKey: ${{ secrets.STRYKER_DASHBOARD_API_KEY }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: 'Publish: mutation-tests' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: mutation-tests path: output/artifacts/tests-results/mutation-tests - name: 'Publish: unit-tests' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: unit-tests path: output/artifacts/tests-results/unit-tests - name: 'Publish: packages' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: packages path: output/artifacts/packages - name: 'Publish: coverage-report' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: coverage-report path: output/artifacts/reports/coverage-report - name: 'Publish: coverage-history' - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v3 with: name: coverage-history path: output/artifacts/reports/coverage-history diff --git a/src/DataFilters.Queries/FilterToQueries.cs b/src/DataFilters.Queries/FilterToQueries.cs index 36b64b02..9ba861d5 100644 --- a/src/DataFilters.Queries/FilterToQueries.cs +++ b/src/DataFilters.Queries/FilterToQueries.cs @@ -1,9 +1,8 @@ namespace DataFilters { - using Queries.Core.Parts.Clauses; - using System; using System.Linq; + using Queries.Core.Parts.Clauses; /// /// Extensions method type. diff --git a/src/DataFilters.Queries/OrderExtensions.cs b/src/DataFilters.Queries/OrderExtensions.cs index 4a7ccc0a..21ca83de 100644 --- a/src/DataFilters.Queries/OrderExtensions.cs +++ b/src/DataFilters.Queries/OrderExtensions.cs @@ -1,9 +1,8 @@ namespace DataFilters { - using Queries.Core.Parts.Sorting; - using System; using System.Collections.Generic; + using Queries.Core.Parts.Sorting; /// /// Extensions method for types diff --git a/src/DataFilters/Filter.cs b/src/DataFilters/Filter.cs index 72bb8cd9..c3e31ef9 100644 --- a/src/DataFilters/Filter.cs +++ b/src/DataFilters/Filter.cs @@ -1,14 +1,13 @@ namespace DataFilters { - using DataFilters.Converters; - using System; using System.Collections.Generic; + using System.Text.RegularExpressions; + using DataFilters.Converters; using Newtonsoft.Json; using Newtonsoft.Json.Schema; using static Newtonsoft.Json.DefaultValueHandling; using static Newtonsoft.Json.Required; - using System.Text.RegularExpressions; #if !NETSTANDARD1_3 using System.Text.Json.Serialization; #endif diff --git a/src/DataFilters/FilterOptions.cs b/src/DataFilters/FilterOptions.cs index 5821c379..dd4116e8 100644 --- a/src/DataFilters/FilterOptions.cs +++ b/src/DataFilters/FilterOptions.cs @@ -21,7 +21,7 @@ public PropertyNameResolutionStrategy DefaultPropertyNameResolutionStrategy { get => _propertyNameResolutionStrategy; #if !NET6_0_OR_GREATER - set + set #else init #endif diff --git a/src/DataFilters/Grammar/Parsing/FilterToken.cs b/src/DataFilters/Grammar/Parsing/FilterToken.cs index 0c9e2d1e..33e99140 100644 --- a/src/DataFilters/Grammar/Parsing/FilterToken.cs +++ b/src/DataFilters/Grammar/Parsing/FilterToken.cs @@ -91,7 +91,7 @@ public enum FilterToken /// /// Bang sign /// - [Token(Example = "!", Description ="exclamation point")] + [Token(Example = "!", Description = "exclamation point")] Bang, /// @@ -133,7 +133,7 @@ public enum FilterToken /// /// The & character. /// - [Token(Example = "&", Description ="ampersand")] + [Token(Example = "&", Description = "ampersand")] Ampersand, /// diff --git a/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs b/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs index 516fafc5..4f72f898 100644 --- a/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs +++ b/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs @@ -1,11 +1,9 @@ namespace DataFilters.Grammar.Parsing { - using Superpower; - using Superpower.Model; - using System.Collections.Generic; using System.Linq; - + using Superpower; + using Superpower.Model; using static DataFilters.Grammar.Parsing.FilterToken; /// @@ -246,7 +244,7 @@ protected override IEnumerable> Tokenize(TextSpan span, Toke next.Remainder); next = next.Remainder.ConsumeChar(); break; - case DoubleQuote : + case DoubleQuote: yield return Result.Value(FilterToken.DoubleQuote, next.Location, next.Remainder); diff --git a/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs b/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs index 8957b94f..410e8787 100644 --- a/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs +++ b/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs @@ -36,7 +36,7 @@ private AsteriskExpression() { } /// /// /// - public static EndsWithExpression operator +(AsteriskExpression _, ConstantValueExpression right) => new (right.Value); + public static EndsWithExpression operator +(AsteriskExpression _, ConstantValueExpression right) => new(right.Value); /// /// Computes a by adding a to a . diff --git a/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs b/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs index df5d39cb..24c41af3 100644 --- a/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs +++ b/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs @@ -29,11 +29,11 @@ protected BinaryFilterExpression(FilterExpression left, FilterExpression right) { (null, _) => throw new ArgumentNullException(nameof(left)), (_, null) => throw new ArgumentNullException(nameof(right)), - (BinaryFilterExpression , BinaryFilterExpression ) => (new GroupExpression(left), new GroupExpression(right)), - (BinaryFilterExpression , not GroupExpression ) => (new GroupExpression(left), right), - (not GroupExpression , BinaryFilterExpression) => (left, new GroupExpression(right)), - (BinaryFilterExpression , _) => (new GroupExpression(left), right), - (_ , BinaryFilterExpression) => (left, new GroupExpression(right)), + (BinaryFilterExpression, BinaryFilterExpression) => (new GroupExpression(left), new GroupExpression(right)), + (BinaryFilterExpression, not GroupExpression) => (new GroupExpression(left), right), + (not GroupExpression, BinaryFilterExpression) => (left, new GroupExpression(right)), + (BinaryFilterExpression, _) => (new GroupExpression(left), right), + (_, BinaryFilterExpression) => (left, new GroupExpression(right)), _ => (left, right) }; } diff --git a/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs b/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs index a2277e63..5b1a05b7 100644 --- a/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs @@ -24,7 +24,7 @@ protected ConstantValueExpression(string value) Value = value switch { null => throw new ArgumentNullException(nameof(value)), - { Length : 0} => throw new ArgumentOutOfRangeException(nameof(value)), + { Length: 0 } => throw new ArgumentOutOfRangeException(nameof(value)), _ => value }; } diff --git a/src/DataFilters/Grammar/Syntax/DateExpression.cs b/src/DataFilters/Grammar/Syntax/DateExpression.cs index ffeb9823..955bac5f 100644 --- a/src/DataFilters/Grammar/Syntax/DateExpression.cs +++ b/src/DataFilters/Grammar/Syntax/DateExpression.cs @@ -63,7 +63,7 @@ public DateExpression(int year = 1, int month = 1, int day = 1) public override bool IsEquivalentTo(FilterExpression other) => other switch { DateExpression date => Equals(date), - DateTimeExpression { Date : var date, Time : null, Offset: null } => Equals(date), + DateTimeExpression { Date: var date, Time: null, Offset: null } => Equals(date), _ => Equals((other as ISimplifiable)?.Simplify() ?? other) }; @@ -81,6 +81,6 @@ public DateExpression(int year = 1, int month = 1, int day = 1) }; /// - public static bool operator !=(DateExpression left, DateExpression right) => ! (left == right); + public static bool operator !=(DateExpression left, DateExpression right) => !(left == right); } } diff --git a/src/DataFilters/Grammar/Syntax/FilterExpression.cs b/src/DataFilters/Grammar/Syntax/FilterExpression.cs index 19367a95..00f89fe3 100644 --- a/src/DataFilters/Grammar/Syntax/FilterExpression.cs +++ b/src/DataFilters/Grammar/Syntax/FilterExpression.cs @@ -38,6 +38,6 @@ public virtual bool IsEquivalentTo(FilterExpression other) /// /// Returns a that is the result of applying the NOT logical operator to the specified . /// - public static NotExpression operator !(FilterExpression expression) => new (expression); + public static NotExpression operator !(FilterExpression expression) => new(expression); } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/IntervalExpression.cs b/src/DataFilters/Grammar/Syntax/IntervalExpression.cs index 88564673..b87985f5 100644 --- a/src/DataFilters/Grammar/Syntax/IntervalExpression.cs +++ b/src/DataFilters/Grammar/Syntax/IntervalExpression.cs @@ -1,8 +1,7 @@ namespace DataFilters.Grammar.Syntax { - using Exceptions; - using System; + using Exceptions; /// /// A that holds an interval between and values. diff --git a/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs b/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs index 5e1d73ea..22771ac0 100644 --- a/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs @@ -43,5 +43,9 @@ public NumericValueExpression(string value) : base(value) /// public static bool operator !=(NumericValueExpression left, NumericValueExpression right) => !(left == right); + + /// + public override bool Equals(object obj) + => ReferenceEquals(this, obj) || Equals(obj.As()?.Value, Value); } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/OffsetExpression.cs b/src/DataFilters/Grammar/Syntax/OffsetExpression.cs index 24b12115..e4abe979 100644 --- a/src/DataFilters/Grammar/Syntax/OffsetExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OffsetExpression.cs @@ -47,7 +47,7 @@ public OffsetExpression(NumericSign sign = NumericSign.Plus, uint hours = 0, uin throw new ArgumentOutOfRangeException(nameof(minutes), $"{nameof(minutes)} must be between 0 and 59 inclusive"); } - Hours = (int) hours; + Hours = (int)hours; Minutes = (int)minutes; Sign = sign; diff --git a/src/DataFilters/Grammar/Syntax/OneOfExpression.cs b/src/DataFilters/Grammar/Syntax/OneOfExpression.cs index eb9ba9c7..006bea49 100644 --- a/src/DataFilters/Grammar/Syntax/OneOfExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OneOfExpression.cs @@ -43,7 +43,7 @@ public OneOfExpression(params FilterExpression[] values) _values = values.Where(x => x is not null) .ToArray(); - _lazyParseableString = new (() => $"{{{string.Join(",", Values.Select(v => v.EscapedParseableString))}}}"); + _lazyParseableString = new(() => $"{{{string.Join(",", Values.Select(v => v.EscapedParseableString))}}}"); } /// @@ -134,7 +134,7 @@ public FilterExpression Simplify() case 1: simplifiedResult = curatedExpressions.Single(); break; - case 2 : + case 2: FilterExpression first = curatedExpressions.First(); FilterExpression other = curatedExpressions.Last(); if (first is OneOfExpression oneOfFirst && other is OneOfExpression oneOfSecond) @@ -155,6 +155,6 @@ public FilterExpression Simplify() } /// - public override string EscapedParseableString =>_lazyParseableString.Value; + public override string EscapedParseableString => _lazyParseableString.Value; } } diff --git a/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs b/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs index eedd978c..0b6024ef 100644 --- a/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs +++ b/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs @@ -113,7 +113,7 @@ public StartsWithExpression(TextExpression text) /// /// a whose is and is /// - public static AndExpression operator +(StartsWithExpression left, EndsWithExpression right) => new (left, right); + public static AndExpression operator +(StartsWithExpression left, EndsWithExpression right) => new(left, right); /// /// Combines the specified and @@ -128,7 +128,7 @@ public StartsWithExpression(TextExpression text) /// exactly starts with 's value and contains 's value. /// /// - public static OneOfExpression operator +(StartsWithExpression left, StartsWithExpression right) => new (new StringValueExpression(left.Value + right.Value), + public static OneOfExpression operator +(StartsWithExpression left, StartsWithExpression right) => new(new StringValueExpression(left.Value + right.Value), new StartsWithExpression(left.Value + right.Value), new AndExpression(left, new ContainsExpression(right.Value))); @@ -156,6 +156,5 @@ public StartsWithExpression(TextExpression text) /// A that can match any that starts with and end /// public static AndExpression operator +(StartsWithExpression left, StringValueExpression right) => left + new EndsWithExpression(right.Value); - } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/TextExpression.cs b/src/DataFilters/Grammar/Syntax/TextExpression.cs index 282fe2da..8235e6dd 100644 --- a/src/DataFilters/Grammar/Syntax/TextExpression.cs +++ b/src/DataFilters/Grammar/Syntax/TextExpression.cs @@ -1,8 +1,8 @@ namespace DataFilters.Grammar.Syntax { using System; - using System.Text; using System.Collections.Generic; + using System.Text; /// /// An expression that holds a string value "as is". diff --git a/src/DataFilters/Grammar/Syntax/TimeExpression.cs b/src/DataFilters/Grammar/Syntax/TimeExpression.cs index 82e64141..da4f2c97 100644 --- a/src/DataFilters/Grammar/Syntax/TimeExpression.cs +++ b/src/DataFilters/Grammar/Syntax/TimeExpression.cs @@ -77,7 +77,7 @@ public bool Equals(TimeExpression other) => other is not null public override bool IsEquivalentTo(FilterExpression other) => other switch { TimeExpression time => Equals(time), - DateTimeExpression { Date: null, Time: var time, Offset: null} => Equals(time), + DateTimeExpression { Date: null, Time: var time, Offset: null } => Equals(time), _ => Equals((other as ISimplifiable)?.Simplify() ?? other) }; diff --git a/src/DataFilters/Order.cs b/src/DataFilters/Order.cs index 55e20b1f..b3df6677 100644 --- a/src/DataFilters/Order.cs +++ b/src/DataFilters/Order.cs @@ -47,7 +47,7 @@ public bool Equals(IOrder other) public override bool Equals(object obj) => Equals(obj as Order); /// -#if ! (NETSTANDARD1_0 || NETSTANDARD1_3 || NETSTANDARD2_0) +#if !(NETSTANDARD1_0 || NETSTANDARD1_3 || NETSTANDARD2_0) public override int GetHashCode() => HashCode.Combine(Expression, Direction); #else public override int GetHashCode() => (Expression, Direction).GetHashCode(); diff --git a/src/DataFilters/OrderValidator.cs b/src/DataFilters/OrderValidator.cs index b9627477..d5f6b717 100644 --- a/src/DataFilters/OrderValidator.cs +++ b/src/DataFilters/OrderValidator.cs @@ -1,10 +1,9 @@ namespace DataFilters { - using FluentValidation; - using System; using System.Linq; using System.Text.RegularExpressions; + using FluentValidation; /// /// Validates sort expression diff --git a/src/DataFilters/StringExtensions.cs b/src/DataFilters/StringExtensions.cs index f4a14ef7..debefca0 100644 --- a/src/DataFilters/StringExtensions.cs +++ b/src/DataFilters/StringExtensions.cs @@ -163,7 +163,7 @@ public static IFilter ToFilter(this StringSegment queryString, PropertyNameRe => ToFilter(queryString.Value, new FilterOptions() { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy}); #else public static IFilter ToFilter(this string queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) - => ToFilter(queryString, new FilterOptions () { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy}); + => ToFilter(queryString, new FilterOptions() { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy }); #endif /// diff --git a/src/Datafilters.Expressions/FilterExtensions.cs b/src/Datafilters.Expressions/FilterExtensions.cs index b297f1c5..adb6e77d 100644 --- a/src/Datafilters.Expressions/FilterExtensions.cs +++ b/src/Datafilters.Expressions/FilterExtensions.cs @@ -4,20 +4,18 @@ using DateOnlyTimeOnly.AspNet.Converters; #endif - using static Expressions.NullableValueBehavior; + using DataFilters.Expressions; using System; using System.Collections.Generic; - using System.ComponentModel; using System.Linq; using System.Linq.Expressions; using System.Reflection; using static DataFilters.FilterOperator; + using static Expressions.NullableValueBehavior; using static System.Linq.Expressions.Expression; - using DataFilters.Expressions; - /// /// The `FilterExtensions` class provides extension methods for building expression trees from `IFilter` instances. /// It allows for filtering data based on various conditions. @@ -323,25 +321,20 @@ private static Expression ComputeContains(MemberExpression property, object valu Type genericArgType = property.Type.GenericTypeArguments[0]; ParameterExpression pe = Parameter(genericArgType); - if (typeof(string).Equals(genericArgType)) - { - contains = Call(typeof(Enumerable), + contains = typeof(string).Equals(genericArgType) + ? Call(typeof(Enumerable), nameof(Enumerable.Any), new Type[] { typeof(string) }, property, Lambda( Call(pe, typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), - constantExpression), new[] { pe })); - } - else - { - contains = Call(typeof(Enumerable), + constantExpression), new[] { pe })) + : Call(typeof(Enumerable), nameof(Enumerable.Any), new Type[] { property.Type.GenericTypeArguments[0] }, property, Lambda(Equal(pe, constantExpression), new[] { pe })); - } } else { @@ -363,9 +356,8 @@ private static Expression ComputeNullSafeContains(MemberExpression property, obj Type genericArgType = property.Type.GenericTypeArguments[0]; ParameterExpression pe = Parameter(genericArgType); - if (typeof(string).Equals(genericArgType)) - { - contains = Call(typeof(Enumerable), + contains = typeof(string).Equals(genericArgType) + ? Call(typeof(Enumerable), nameof(Enumerable.Any), new Type[] { typeof(string) }, property, @@ -373,16 +365,12 @@ private static Expression ComputeNullSafeContains(MemberExpression property, obj AndAlso(NotEqual(pe, Constant(null)), Call(pe, typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), - constantExpression)), new[] { pe })); - } - else - { - contains = Call(typeof(Enumerable), + constantExpression)), new[] { pe })) + : Call(typeof(Enumerable), nameof(Enumerable.Any), new Type[] { property.Type.GenericTypeArguments[0] }, property, Lambda(Equal(pe, constantExpression), new[] { pe })); - } } else { @@ -490,14 +478,9 @@ private static Expression ComputeExpression(ParameterExpression pe, IEnumerable< fields = fields.Skip(i) .ToArray(); - if (fields.Any()) - { - localBody = ComputeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property); - } - else - { - localBody = ComputeBodyExpression(property, @operator, value); - } + localBody = fields.Any() + ? ComputeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property) + : ComputeBodyExpression(property, @operator, value); body = Call(typeof(Enumerable), nameof(Enumerable.Any), @@ -576,14 +559,9 @@ private static Expression ComputeNullSafeExpression(ParameterExpression pe, IEnu fields = fields.Skip(i) .ToArray(); - if (fields.Any()) - { - localBody = ComputeNullSafeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property); - } - else - { - localBody = ComputeNullSafeBodyExpression(property, @operator, value); - } + localBody = fields.Any() + ? ComputeNullSafeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property) + : ComputeNullSafeBodyExpression(property, @operator, value); body = AndAlso(NotEqual(property, Constant(null)), Call(typeof(Enumerable), diff --git a/src/Datafilters.Expressions/OrderExtensions.cs b/src/Datafilters.Expressions/OrderExtensions.cs index 630e4ddd..5ac6f548 100644 --- a/src/Datafilters.Expressions/OrderExtensions.cs +++ b/src/Datafilters.Expressions/OrderExtensions.cs @@ -1,9 +1,8 @@ namespace DataFilters { - using DataFilters.Expressions; - using System; using System.Collections.Generic; + using DataFilters.Expressions; /// /// Extension methods for instances. diff --git a/src/Datafilters.Expressions/QueryableExtensions.cs b/src/Datafilters.Expressions/QueryableExtensions.cs index 856e35af..a862bd87 100644 --- a/src/Datafilters.Expressions/QueryableExtensions.cs +++ b/src/Datafilters.Expressions/QueryableExtensions.cs @@ -1,10 +1,9 @@ namespace System.Linq { - using DataFilters; - using DataFilters.Expressions; - using System.Collections.Generic; using System.Linq.Expressions; + using DataFilters; + using DataFilters.Expressions; /// /// Provides extension methods for ordering a collection of entities. diff --git a/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs b/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs index 434e08fa..2de26825 100644 --- a/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs +++ b/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs @@ -1,22 +1,17 @@ namespace DataFilters.Expressions.UnitTests; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; using DataFilters.Expressions; using DataFilters.TestObjects; - using FluentAssertions; using FluentAssertions.Extensions; - using NodaTime; - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; - using static DataFilters.Expressions.NullableValueBehavior; using static DataFilters.FilterLogic; using static DataFilters.FilterOperator; @@ -105,8 +100,8 @@ public static IEnumerable EqualToTestCases (Expression>)(item => item.Nickname == "Batman" || item.Nickname == "Superman") }; - yield return new object[] - { + yield return new object[] + { new[] { new SuperHero { Firstname = "Bruce", Lastname = "Wayne", Height = 190, Nickname = "Batman" }, new SuperHero { Firstname = "Clark", Lastname = "Kent", Height = 190, Nickname = "Superman" }, @@ -128,11 +123,11 @@ public static IEnumerable EqualToTestCases }, NoAction, (Expression>)(item => item.Firstname.Contains("a") && (item.Nickname == "Batman" || item.Nickname == "Superman")) - }; + }; - yield return new object[] - { - new[] + yield return new object[] + { + new[] { #if NET6_0_OR_GREATER new SuperHero { Firstname = "Bruce", Lastname = "Wayne", Height = 190, Nickname = "Batman", LastBattleDate = DateOnly.FromDateTime(25.December(2012)) }, @@ -178,7 +173,7 @@ public static IEnumerable EqualToTestCases && item.LastBattleDate < 31.December(2012)) #endif - }; + }; yield return new object[] { diff --git a/test/DataFilters.Expressions.UnitTests/Hero.cs b/test/DataFilters.Expressions.UnitTests/Hero.cs index 845b854e..e346ecb9 100644 --- a/test/DataFilters.Expressions.UnitTests/Hero.cs +++ b/test/DataFilters.Expressions.UnitTests/Hero.cs @@ -12,10 +12,10 @@ public class Hero : IEquatable public DateTimeOffset FirstAppearance { get; set; } #if NET6_0_OR_GREATER - public DateOnly LastAppearance { get; set; } + public DateOnly LastAppearance { get; set; } #endif - public Hero Acolyte {get; set; } + public Hero Acolyte { get; set; } /// public override bool Equals(object obj) => Equals(obj as Hero); diff --git a/test/DataFilters.Expressions.UnitTests/NodaTimeClass.cs b/test/DataFilters.Expressions.UnitTests/NodaTimeClass.cs index b7d2ce68..49e88eef 100644 --- a/test/DataFilters.Expressions.UnitTests/NodaTimeClass.cs +++ b/test/DataFilters.Expressions.UnitTests/NodaTimeClass.cs @@ -1,8 +1,7 @@ namespace DataFilters.Expressions.UnitTests; -using NodaTime; - using System.Diagnostics.CodeAnalysis; +using NodaTime; [ExcludeFromCodeCoverage] public class NodaTimeClass diff --git a/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs b/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs index b469c0f5..fc313f84 100644 --- a/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs +++ b/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs @@ -1,11 +1,9 @@ namespace DataFilters.Expressions.UnitTests { - using FluentAssertions; - using System; using System.Collections.Generic; using System.Linq; - + using FluentAssertions; using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs b/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs index ae952ce8..1bdd0d90 100644 --- a/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs +++ b/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs @@ -1,16 +1,13 @@ namespace DataFilters.Expressions.UnitTests { - using FluentAssertions; - using FluentAssertions.Extensions; - using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - + using FluentAssertions; + using FluentAssertions.Extensions; using Xunit; using Xunit.Categories; - using static DataFilters.OrderDirection; [UnitTest] diff --git a/test/DataFilters.PerformanceTests/BracketVsOr.cs b/test/DataFilters.PerformanceTests/BracketVsOr.cs index adaa5c4b..51d003a1 100644 --- a/test/DataFilters.PerformanceTests/BracketVsOr.cs +++ b/test/DataFilters.PerformanceTests/BracketVsOr.cs @@ -1,16 +1,15 @@ namespace DataFilters.PerfomanceTests { + using System; using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Jobs; - using System; - [MemoryDiagnoser] [SimpleJob(RuntimeMoniker.CoreRt31)] [SimpleJob(RuntimeMoniker.Net50)] [SimpleJob(RuntimeMoniker.Net60)] public class BracketVsOr - { + { [Benchmark] [Arguments("Nickname=Br[a-f]")] public IFilter Bracket(string input) => input.ToFilter(); diff --git a/test/DataFilters.PerformanceTests/Program.cs b/test/DataFilters.PerformanceTests/Program.cs index ed591949..27cf0214 100644 --- a/test/DataFilters.PerformanceTests/Program.cs +++ b/test/DataFilters.PerformanceTests/Program.cs @@ -1,4 +1,4 @@ - using BenchmarkDotNet.Running; +using BenchmarkDotNet.Running; namespace DataFilters.PerfomanceTests { diff --git a/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs b/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs index a2545c9e..1201df0d 100644 --- a/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs +++ b/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs @@ -1,8 +1,7 @@ namespace DataFilters.PerfomanceTests; -using BenchmarkDotNet.Attributes; - using System; +using BenchmarkDotNet.Attributes; [MemoryDiagnoser] [RPlotExporter] diff --git a/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs b/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs index 1982a71e..fa8f3b62 100644 --- a/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs +++ b/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.Queries.UnitTests { + using System; + using System.Collections.Generic; using FluentAssertions; using FluentAssertions.Extensions; - using global::Queries.Core.Parts.Clauses; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; - using static DataFilters.FilterOperator; public class FilterToQueriesTests @@ -162,7 +158,7 @@ public void FilterToWhere(IFilter filter, IWhereClause expected) _outputHelper.WriteLine($"Expected : {expected.Jsonify()}"); // Act - IWhereClause actualWhere= filter.ToWhere(); + IWhereClause actualWhere = filter.ToWhere(); _outputHelper.WriteLine($"actualQuery : {actualWhere.Jsonify()}"); // Assert diff --git a/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs b/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs index 9f5a4dc8..ee72cc23 100644 --- a/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs +++ b/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.Queries.UnitTests { - using FluentAssertions; - - using global::Queries.Core.Parts.Sorting; - using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - + using FluentAssertions; + using global::Queries.Core.Parts.Sorting; using Xunit; using Xunit.Abstractions; - using static global::Queries.Core.Parts.Sorting.OrderDirection; public class OrderToQueriesTests diff --git a/test/DataFilters.TestObjects/SuperHero.cs b/test/DataFilters.TestObjects/SuperHero.cs index 1c399e69..bbf33ae8 100644 --- a/test/DataFilters.TestObjects/SuperHero.cs +++ b/test/DataFilters.TestObjects/SuperHero.cs @@ -11,7 +11,7 @@ public class SuperHero : Person #if NET6_0_OR_GREATER public DateOnly? LastBattleDate { get; init; } - public TimeOnly? PeakShape {get; init;} + public TimeOnly? PeakShape { get; init; } #else public DateTimeOffset? LastBattleDate { get; set; } #endif diff --git a/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs b/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs index 7a2a791e..d3f6b1aa 100644 --- a/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs +++ b/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs @@ -3,19 +3,19 @@ #endif namespace DataFilters.UnitTests.Converters { - using FluentAssertions; - using Newtonsoft.Json.Linq; using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using System.Linq.Expressions; + using FluentAssertions; + using Newtonsoft.Json.Linq; + using Newtonsoft.Json.Schema; using Xunit; using Xunit.Abstractions; - using static DataFilters.FilterOperator; - using static DataFilters.FilterLogic; using Xunit.Categories; - using Newtonsoft.Json.Schema; + using static DataFilters.FilterLogic; + using static DataFilters.FilterOperator; [UnitTest] [Feature("Converters")] diff --git a/test/DataFilters.UnitTests/FilterExtensionsTests.cs b/test/DataFilters.UnitTests/FilterExtensionsTests.cs index c2e7b1b5..d621ac15 100644 --- a/test/DataFilters.UnitTests/FilterExtensionsTests.cs +++ b/test/DataFilters.UnitTests/FilterExtensionsTests.cs @@ -1,20 +1,16 @@ namespace DataFilters.UnitTests { + using System; + using System.Collections.Generic; + using DataFilters; using DataFilters.Casing; using DataFilters.TestObjects; - using FluentAssertions; using FluentAssertions.Common; using FluentAssertions.Extensions; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; - - using DataFilters; using static DataFilters.FilterLogic; using static DataFilters.FilterOperator; diff --git a/test/DataFilters.UnitTests/FilterOptionsTests.cs b/test/DataFilters.UnitTests/FilterOptionsTests.cs index a9eb369f..ec6f7e3e 100644 --- a/test/DataFilters.UnitTests/FilterOptionsTests.cs +++ b/test/DataFilters.UnitTests/FilterOptionsTests.cs @@ -14,7 +14,7 @@ public class FilterOptionsTests public void Ctor_should_create_instance_with_default_values() { // Act - FilterOptions options = new (); + FilterOptions options = new(); // Assert options.DefaultPropertyNameResolutionStrategy.Should() @@ -31,7 +31,7 @@ public void Given_null_value_DefaultPropertyNameResolutionStrategy_should_be_Def // Act #if NET6_0_OR_GREATER - options = options with { DefaultPropertyNameResolutionStrategy = null }; + options = options with { DefaultPropertyNameResolutionStrategy = null }; #else options = new () { DefaultPropertyNameResolutionStrategy = null }; #endif diff --git a/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs b/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs index b7a313eb..0ee21ef9 100644 --- a/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs +++ b/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs @@ -1,14 +1,10 @@ namespace DataFilters.UnitTests; +using System; using DataFilters.Casing; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - -using System; - using Xunit.Categories; [UnitTest] @@ -25,7 +21,7 @@ public void Given_no_parameters_Cosntructor_should_set_properties_with_default_v _sut.PropertyNameResolutionStrategy.Should().Be(PropertyNameResolutionStrategy.Default); } - [Property(Arbitrary = new[] {typeof(Generators)})] + [Property(Arbitrary = new[] { typeof(Generators) })] public void Given_positive_integer_value_And_strategy_Constructor_should_set_properties(PositiveInt input, PropertyNameResolutionStrategy propertyNameResolutionStrategy) { // Act diff --git a/test/DataFilters.UnitTests/FilterServiceTests.cs b/test/DataFilters.UnitTests/FilterServiceTests.cs index 2df0ff6b..c2653057 100644 --- a/test/DataFilters.UnitTests/FilterServiceTests.cs +++ b/test/DataFilters.UnitTests/FilterServiceTests.cs @@ -1,19 +1,15 @@ #if NET5_0_OR_GREATER namespace DataFilters.UnitTests; +using System; +using System.Collections.Generic; + using DataFilters.TestObjects; using FluentAssertions; -using FsCheck.Xunit; -using FsCheck; - -using System; -using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; -using Xunit.Categories; public class FilterServiceTests { diff --git a/test/DataFilters.UnitTests/FilterTests.cs b/test/DataFilters.UnitTests/FilterTests.cs index 1a24171b..67b15a52 100644 --- a/test/DataFilters.UnitTests/FilterTests.cs +++ b/test/DataFilters.UnitTests/FilterTests.cs @@ -1,27 +1,21 @@ namespace DataFilters.UnitTests { + using System; + using System.Collections.Generic; + using System.Collections.Immutable; + using System.Linq; + using System.Linq.Expressions; + using System.Text.RegularExpressions; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - using Newtonsoft.Json.Linq; using Newtonsoft.Json.Schema; - - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Linq.Expressions; - using System.Text.RegularExpressions; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; - using static DataFilters.FilterLogic; using static DataFilters.FilterOperator; @@ -337,7 +331,7 @@ public void Ctor_should_build_valid_instance() .VerboseCheck(_output); } - [Property(Arbitrary = new[] {typeof(FilterGenerators)})] + [Property(Arbitrary = new[] { typeof(FilterGenerators) })] public void Given_filter_instance_Negate_should_work_as_expected(NonNull source) { // Arrange diff --git a/test/DataFilters.UnitTests/Generators.cs b/test/DataFilters.UnitTests/Generators.cs index 513ceef3..c3c80863 100644 --- a/test/DataFilters.UnitTests/Generators.cs +++ b/test/DataFilters.UnitTests/Generators.cs @@ -1,13 +1,11 @@ namespace DataFilters.UnitTests; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using DataFilters.Casing; - using FsCheck; using FsCheck.Fluent; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - // "Copyright (c) Cyrille NDOUMBE. // Licenced under Apache, version 2.0" diff --git a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs index d569acd5..fea8cfc4 100644 --- a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs @@ -1,26 +1,21 @@ namespace DataFilters.UnitTests.Grammar.Parsing { + using System; + using System.Collections.Generic; + using System.Data; + using System.Globalization; + using System.Linq; + using System.Linq.Expressions; using DataFilters.Grammar.Parsing; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; using FluentAssertions.Execution; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - using Superpower; using Superpower.Model; - - using System; - using System.Collections.Generic; - using System.Data; - using System.Globalization; - using System.Linq; - using System.Linq.Expressions; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs index 4bf60995..94b0cb6c 100644 --- a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs @@ -1,21 +1,16 @@ namespace DataFilters.UnitTests.Grammar.Parsing { - using DataFilters.Grammar.Parsing; - - using FluentAssertions; - - using Superpower; - using Superpower.Model; - using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - + using DataFilters.Grammar.Parsing; + using FluentAssertions; + using Superpower; + using Superpower.Model; using Xunit; using Xunit.Abstractions; using Xunit.Categories; - using static DataFilters.Grammar.Parsing.FilterToken; [UnitTest] diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs index 1673ef89..229cf34f 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs @@ -1,19 +1,15 @@  namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; + using System.Linq; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using System.Linq; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs index eebbeeec..581bb764 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs @@ -1,17 +1,19 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; + using DataFilters.Grammar.Syntax; + using DataFilters.UnitTests.Helpers; + using FluentAssertions; - using FsCheck.Xunit; + using FsCheck; + using FsCheck.Xunit; - using System; - using System.Collections.Generic; using Xunit; using Xunit.Abstractions; using Xunit.Categories; - using DataFilters.UnitTests.Helpers; - using FsCheck.Fluent; [UnitTest] [Feature(nameof(AsteriskExpression))] diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs index b9efd44c..a5760e16 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs index 66ec3eb0..fc9764eb 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs @@ -1,18 +1,14 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; + using System.Linq; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using System.Linq; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs index db27c368..54198fbe 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs @@ -1,17 +1,14 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Linq; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - using System; - using System.Linq; - public class ConstantBracketValueTests { [Property] diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs index 7433b442..ab23bbe1 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs index 157c9237..247f9e44 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs index 74225729..d2387679 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; using FluentAssertions.Extensions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs index 17241e7b..a918a2f1 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs index ca15fad8..a62a0e19 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs index 036b7102..e25b18af 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs index b7a5c746..845b8fc2 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs @@ -1,18 +1,14 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Exceptions; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; using FluentAssertions.Extensions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs index e26420be..273138c0 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs index c7ad46d7..bf675048 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs @@ -1,15 +1,11 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs index bdb31605..9128b59b 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; + using System.Linq; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using System.Linq; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs index 2402b5e0..6011106f 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs @@ -1,18 +1,14 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; + using System.Linq; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using System.Linq; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs index 2d270320..907c7569 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs @@ -1,12 +1,9 @@ namespace DataFilters.UnitTests.Grammar.Syntax { - using DataFilters.Grammar.Syntax; - - using FluentAssertions; - using System; using System.Collections.Generic; - + using DataFilters.Grammar.Syntax; + using FluentAssertions; using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs index 6a5253cb..7ce751c2 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Linq; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Linq; - using Xunit; using Xunit.Abstractions; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs index f062c5d9..f35cf280 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs index 789b6dec..4e4df7e7 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs @@ -1,17 +1,13 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; + using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs index a3b7c2ef..569c096c 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests.Grammar.Syntax { + using System; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; using FsCheck.Fluent; using FsCheck.Xunit; - - using System; - using Xunit; using Xunit.Abstractions; using Xunit.Categories; diff --git a/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs b/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs index a393d2d0..2ab4b2bc 100644 --- a/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs +++ b/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs @@ -1,8 +1,8 @@ namespace DataFilters.UnitTests.Helpers { + using System; using System.Diagnostics.CodeAnalysis; using System.Globalization; - using System; /// /// A helper class to control the value during tests. diff --git a/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs b/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs index f8e02899..28ebbf80 100644 --- a/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs +++ b/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs @@ -1,16 +1,13 @@ namespace DataFilters.UnitTests.Helpers { - using DataFilters.Grammar.Parsing; - using DataFilters.Grammar.Syntax; - - using FsCheck; - using FsCheck.Fluent; - using System; using System.Collections.Generic; using System.Globalization; using System.Linq; - + using DataFilters.Grammar.Parsing; + using DataFilters.Grammar.Syntax; + using FsCheck; + using FsCheck.Fluent; using static GeneratorHelper; public static class ExpressionsGenerators diff --git a/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs b/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs index 663fa24f..3e2bfffd 100644 --- a/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs +++ b/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs @@ -1,9 +1,9 @@ -using FsCheck.Fluent; -using FsCheck; -using System; +using System; using System.Collections.Generic; using System.Linq; using Bogus; +using FsCheck; +using FsCheck.Fluent; namespace DataFilters.UnitTests.Helpers { @@ -14,7 +14,7 @@ namespace DataFilters.UnitTests.Helpers /// internal static class FilterGenerators { - private readonly static Faker Faker = new (); + private readonly static Faker Faker = new(); /// /// Generates a where the value of the filter is a string @@ -29,7 +29,7 @@ internal static Arbitrary FiltersOverString() Gen genOperator = Gen.OneOf(operators.Select(op => Gen.Constant(op))); return genOperator.Zip(GetArbitraryFor().Generator) - .Select(tuple => (Op : tuple.Item1, Value: tuple.Item2)) + .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) .ToArbitrary(); } @@ -123,9 +123,9 @@ private static Gen SafeFilterGenerator(int size) case 0: { gen = GenerateFilters().Generator.Two() - .Select(tuple => new[] {tuple.Item1, tuple.Item2}) + .Select(tuple => new[] { tuple.Item1, tuple.Item2 }) .Zip(generateLogic) - .Select(tuple => new MultiFilter { Logic = tuple.Item2, Filters = tuple.Item1}); + .Select(tuple => new MultiFilter { Logic = tuple.Item2, Filters = tuple.Item1 }); break; } @@ -150,16 +150,16 @@ private static Gen GreaterThanFilter() private static Gen GreaterThanOrEqualFilter() => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.GreaterThanOrEqual); - + private static Gen LessThanOrEqualFilter() => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.LessThanOrEqualTo); - + private static Gen LessThanFilter() => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.LessThanOrEqualTo); private static Gen GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator op) => GetArbitraryFor().Generator - .Select(value => (IFilter) new Filter(Faker.Hacker.Noun(), op, value)); + .Select(value => (IFilter)new Filter(Faker.Hacker.Noun(), op, value)); } } diff --git a/test/DataFilters.UnitTests/Helpers/GeneratorHelper.cs b/test/DataFilters.UnitTests/Helpers/GeneratorHelper.cs index a151bc33..aac28d67 100644 --- a/test/DataFilters.UnitTests/Helpers/GeneratorHelper.cs +++ b/test/DataFilters.UnitTests/Helpers/GeneratorHelper.cs @@ -1,8 +1,7 @@ -using FsCheck; +using System; +using FsCheck; using FsCheck.Fluent; -using System; - namespace DataFilters.UnitTests.Helpers; internal static class GeneratorHelper diff --git a/test/DataFilters.UnitTests/MultiFilterTests.cs b/test/DataFilters.UnitTests/MultiFilterTests.cs index 3304a8f2..099bcd93 100644 --- a/test/DataFilters.UnitTests/MultiFilterTests.cs +++ b/test/DataFilters.UnitTests/MultiFilterTests.cs @@ -5,25 +5,19 @@ namespace DataFilters.UnitTests { - using DataFilters.UnitTests.Helpers; -using FluentAssertions; - - using FsCheck.Xunit; - - using FsCheck; - - using Newtonsoft.Json.Linq; - using Newtonsoft.Json.Schema; - using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; - + using DataFilters.UnitTests.Helpers; + using FluentAssertions; + using FsCheck; + using FsCheck.Xunit; + using Newtonsoft.Json.Linq; + using Newtonsoft.Json.Schema; using Xunit; using Xunit.Abstractions; using Xunit.Categories; - using static DataFilters.FilterLogic; using static DataFilters.FilterOperator; diff --git a/test/DataFilters.UnitTests/MultiOrderTests.cs b/test/DataFilters.UnitTests/MultiOrderTests.cs index ecf78761..94833d97 100644 --- a/test/DataFilters.UnitTests/MultiOrderTests.cs +++ b/test/DataFilters.UnitTests/MultiOrderTests.cs @@ -1,12 +1,11 @@ namespace DataFilters.UnitTests { - using FluentAssertions; using System.Collections.Generic; - + using DataFilters.TestObjects; + using FluentAssertions; using Xunit; using Xunit.Abstractions; using static DataFilters.OrderDirection; - using DataFilters.TestObjects; public class MultiOrderTests { diff --git a/test/DataFilters.UnitTests/OrderTests.cs b/test/DataFilters.UnitTests/OrderTests.cs index 683ebc9c..28f17221 100644 --- a/test/DataFilters.UnitTests/OrderTests.cs +++ b/test/DataFilters.UnitTests/OrderTests.cs @@ -1,15 +1,11 @@ namespace DataFilters.UnitTests { - using DataFilters.TestObjects; - - using FluentAssertions; - using System; using System.Collections.Generic; - + using DataFilters.TestObjects; + using FluentAssertions; using Xunit; using Xunit.Abstractions; - using static DataFilters.OrderDirection; public class OrderTests diff --git a/test/DataFilters.UnitTests/StringExtensionsTests.cs b/test/DataFilters.UnitTests/StringExtensionsTests.cs index 95c3523a..b419a201 100644 --- a/test/DataFilters.UnitTests/StringExtensionsTests.cs +++ b/test/DataFilters.UnitTests/StringExtensionsTests.cs @@ -1,16 +1,12 @@ namespace DataFilters.UnitTests { + using System; + using System.Collections.Generic; using DataFilters.Casing; using DataFilters.TestObjects; - using FluentAssertions; - - using System; - using System.Collections.Generic; - using Xunit; using Xunit.Abstractions; - using static DataFilters.OrderDirection; public class StringExtensionsTests From 25d38456fbf17a8718955492d840b614580acc12 Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Mon, 29 Jan 2024 10:47:59 +0100 Subject: [PATCH 06/10] chore(code style): use new C#12 syntax when possible use simplified collection syntax use newer constructor syntax --- .editorconfig | 48 ++++++- README.md | 10 +- build/Build.cs | 50 +++---- src/DataFilters/Filter.cs | 52 +++---- .../BoundariesTypeMismatchException.cs | 15 +- .../Grammar/Parsing/FilterTokenParser.cs | 2 +- .../Grammar/Parsing/FilterTokenizer.cs | 4 +- .../Grammar/Syntax/AndExpression.cs | 1 - .../Grammar/Syntax/AsteriskExpression.cs | 12 +- .../Grammar/Syntax/BinaryFilterExpression.cs | 2 +- .../Grammar/Syntax/BoundaryExpression.cs | 22 ++- .../Grammar/Syntax/BracketExpression.cs | 2 +- .../Grammar/Syntax/GroupExpression.cs | 16 +-- .../Grammar/Syntax/GuidValueExpression.cs | 13 +- .../Grammar/Syntax/IBoundaryExpression.cs | 4 +- .../Grammar/Syntax/NotExpression.cs | 2 +- .../Grammar/Syntax/NumericValueExpression.cs | 17 +-- .../Grammar/Syntax/OffsetExpression.cs | 2 +- .../Grammar/Syntax/OneOfExpression.cs | 47 +++---- .../Grammar/Syntax/RangeBracketValue.cs | 22 ++- .../Grammar/Syntax/StartsWithExpression.cs | 9 +- .../Grammar/Syntax/StringValueExpression.cs | 23 ++-- .../Grammar/Syntax/TextExpression.cs | 17 +-- src/DataFilters/MultiOrder.cs | 15 +- src/DataFilters/OrderValidator.cs | 2 +- .../Serialization/FilterOperatorConverter.cs | 1 - .../Serialization/MultiFilterConverter.cs | 4 +- src/DataFilters/StringExtensions.cs | 2 +- .../FilterExtensions.cs | 130 ++++++++++-------- .../OrderExpression.cs | 22 ++- .../OrderExtensions.cs | 1 - .../QueryableExtensions.cs | 6 +- .../FilterToExpressionsTests.cs | 67 ++++----- .../QueryableExtensionsTests.cs | 10 +- .../BracketVsOr.cs | 1 + .../RawFilterVsFilterService.cs | 3 +- .../FilterToQueriesTests.cs | 16 +-- .../OrderToQueriesTests.cs | 10 +- .../Converters/DataFilterConvertersTests.cs | 10 +- .../FilterExtensionsTests.cs | 10 +- .../FilterServiceOptionsTests.cs | 4 +- .../FilterServiceTests.cs | 11 +- test/DataFilters.UnitTests/FilterTests.cs | 24 ++-- .../Grammar/Parsing/FilterTokenParserTests.cs | 28 ++-- .../Grammar/Parsing/FilterTokenizerTests.cs | 15 +- .../Grammar/Syntax/AndExpressionTests.cs | 34 ++--- .../Grammar/Syntax/AsteriskExpressionTests.cs | 19 +-- .../Grammar/Syntax/BoundaryExpressionTests.cs | 12 +- .../Grammar/Syntax/BracketExpressionTests.cs | 17 +-- .../Syntax/ConstantBracketValueTests.cs | 12 +- .../Grammar/Syntax/ContainsExpressionTests.cs | 30 ++-- .../Grammar/Syntax/DateExpressionTests.cs | 30 ++-- .../Grammar/Syntax/DateTimeExpressionTests.cs | 30 ++-- .../Grammar/Syntax/DurationExpressionTests.cs | 36 +++-- .../Grammar/Syntax/EndsWithExpressionTests.cs | 22 ++- .../Grammar/Syntax/GroupExpressionTests.cs | 44 +++--- .../Grammar/Syntax/IntervalExpressionTests.cs | 43 +++--- .../Grammar/Syntax/NotExpressionTests.cs | 25 ++-- .../Syntax/NumericValueExpressionTests.cs | 24 ++-- .../Grammar/Syntax/OffsetExpressionTests.cs | 8 +- .../Grammar/Syntax/OneOfExpressionTests.cs | 43 +++--- .../Grammar/Syntax/OrExpressionTests.cs | 57 ++++---- .../Syntax/PropertyNameExpressionTests.cs | 13 +- .../Grammar/Syntax/RangeBracketValueTests.cs | 25 ++-- .../Syntax/StartsWithExpressionTests.cs | 56 ++------ .../Syntax/StringValueExpressionTests.cs | 22 ++- .../Grammar/Syntax/TextExpressionTests.cs | 20 ++- .../Grammar/Syntax/TimeExpressionTests.cs | 38 +++-- .../Helpers/CultureSwitcher.cs | 2 +- .../Helpers/ExpressionsGenerators.cs | 12 +- .../Helpers/FilterGenerators.cs | 5 +- .../DataFilters.UnitTests/MultiFilterTests.cs | 18 +-- test/DataFilters.UnitTests/MultiOrderTests.cs | 13 +- test/DataFilters.UnitTests/OrderTests.cs | 15 +- .../StringExtensionsTests.cs | 10 +- 75 files changed, 658 insertions(+), 861 deletions(-) diff --git a/.editorconfig b/.editorconfig index f2c9ca9d..10d12098 100644 --- a/.editorconfig +++ b/.editorconfig @@ -1,6 +1,26 @@ root = true [*] end_of_line = crlf +tab_width = 4 +indent_size = 4 +dotnet_style_readonly_field = true:suggestion +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_coalesce_expression = true:warning +dotnet_style_null_propagation = true:warning +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_object_initializer = true:warning +dotnet_style_prefer_collection_expression = true:suggestion +dotnet_style_collection_initializer = true:warning +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_prefer_conditional_expression_over_assignment = false:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_explicit_tuple_names = true:warning +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion [*.cs] # Style options @@ -156,4 +176,30 @@ dotnet_naming_rule.non_interface_types_must_be_pascal_case.style = pascal_case # Interfaces must be PascalCase and start with an 'I' dotnet_naming_rule.interface_types_must_be_prefixed_with_i.severity = warning dotnet_naming_rule.interface_types_must_be_prefixed_with_i.symbols = interface_types -dotnet_naming_rule.interface_types_must_be_prefixed_with_i.style = prefix_interface_interface_with_i \ No newline at end of file +dotnet_naming_rule.interface_types_must_be_prefixed_with_i.style = prefix_interface_interface_with_i +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:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = false:silent +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_pattern_matching = true:silent +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_prefer_not_pattern = true:suggestion +csharp_style_prefer_extended_property_pattern = true:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = block_scoped:silent +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_primary_constructors = true:suggestion +csharp_prefer_static_local_function = true:suggestion +csharp_style_prefer_readonly_struct = true:suggestion +csharp_style_prefer_readonly_struct_member = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_prefer_null_check_over_type_check = true:suggestion + +# RCS1179: Unnecessary assignment +dotnet_diagnostic.RCS1179.severity = none diff --git a/README.md b/README.md index 61dd1fc8..c15eb4b1 100644 --- a/README.md +++ b/README.md @@ -563,11 +563,11 @@ it to an equivalent [`IWhereClause`](https://github.com/candoumbe/Queries/blob/d can later be translated a secure SQL string. You can find more info on that directly in the Github repository. -| Package | Downloads | Description | -| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| [![Nuget](https://img.shields.io/nuget/v/Datafilters?label=Datafilters&color=blue)](https://www.nuget.org/packages/DataFilters) | ![DataFilters download count](https://img.shields.io/nuget/dt/Datafilters?label=&color=blue) | provides core functionalities of parsing strings and converting to [IFilter][class-ifilter] instances. | -| [![Nuget](https://img.shields.io/nuget/v/DataFilters.Expressions?label=Datafilters.Expressions&color=blue)](https://www.nuget.org/packages/DataFilters.Expressions) | ![DataFilters.Expressions download count](https://img.shields.io/nuget/dt/Datafilters.Expressions?label=&color=blue) | adds `ToExpression()` extension method on top of [IFilter][class-ifilter] instance to convert it to an equivalent `System.Linq.Expressions.Expression>` instance. | -| [![Nuget](https://img.shields.io/nuget/v/Datafilters.Queries?label=DataFilters.Queries&color=blue)](https://www.nuget.org/packages/DataFilters.Queries) | ![DataFilters.Queries download count](https://img.shields.io/nuget/dt/Datafilters.Queries?label=&color=blue) | adds `ToWhere()` extension method on top of [IFilter][class-ifilter] instance to convert it to an equivalent [`IWhereClause`](https://dev.azure.com/candoumbe/Queries) instance. | +| Package | Downloads | Description | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| [![Nuget](https://img.shields.io/nuget/v/Datafilters?label=Datafilters&color=blue)](https://www.nuget.org/packages/DataFilters) | ![DataFilters download count](https://img.shields.io/nuget/dt/Datafilters?label=&color=blue) | provides core functionalities of parsing strings and converting to [IFilter][class-ifilter] instances. | +| [![Nuget](https://img.shields.io/nuget/v/DataFilters.Expressions?label=Datafilters.Expressions&color=blue)](https://www.nuget.org/packages/DataFilters.Expressions) | ![DataFilters.Expressions download count](https://img.shields.io/nuget/dt/Datafilters.Expressions?label=&color=blue) | adds `ToExpression()` extension method on top of [IFilter][class-ifilter] instance to convert it to an equivalent `System.Linq.Expressions.Expression>` instance. | +| [![Nuget](https://img.shields.io/nuget/v/Datafilters.Queries?label=DataFilters.Queries&color=blue)](https://www.nuget.org/packages/DataFilters.Queries) | ![DataFilters.Queries download count](https://img.shields.io/nuget/dt/Datafilters.Queries?label=&color=blue) | adds `ToWhere()` extension method on top of [IFilter][class-ifilter] instance to convert it to an equivalent [`IWhereClause`](https://github.com/candoumbe/Queries/blob/develop/src/Queries.Core/Parts/Clauses/IWhereClause.cs) instance. | [class-multi-filter]: /src/DataFilters/MultiFilter.cs [class-ifilter]: /src/DataFilters/IFilter.cs diff --git a/build/Build.cs b/build/Build.cs index e5a44d0a..00cbccc0 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -17,44 +17,44 @@ namespace DataFilters.ContinuousIntegration "integration", GitHubActionsImage.UbuntuLatest, FetchDepth = 0, - OnPushBranchesIgnore = new[] { IHaveMainBranch.MainBranchName }, + OnPushBranchesIgnore = [IHaveMainBranch.MainBranchName], PublishArtifacts = true, - InvokedTargets = new[] { nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(IPack.Pack) }, - CacheKeyFiles = new[] { "global.json", "src/**/*.csproj" }, - ImportSecrets = new[] - { + InvokedTargets = [nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(IPack.Pack)], + CacheKeyFiles = ["global.json", "src/**/*.csproj"], + ImportSecrets = + [ nameof(NugetApiKey), nameof(IReportCoverage.CodecovToken), - }, - OnPullRequestExcludePaths = new[] - { + ], + OnPullRequestExcludePaths = + [ "docs/*", "README.md", "CHANGELOG.md", "LICENSE" - } + ] )] [GitHubActions( "delivery", GitHubActionsImage.UbuntuLatest, FetchDepth = 0, - OnPushBranches = new[] { IHaveMainBranch.MainBranchName, IGitFlow.ReleaseBranch + "/*" }, - InvokedTargets = new[] { nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(ICreateGithubRelease.AddGithubRelease) }, + OnPushBranches = [IHaveMainBranch.MainBranchName, IGitFlow.ReleaseBranch + "/*"], + InvokedTargets = [nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(ICreateGithubRelease.AddGithubRelease)], EnableGitHubToken = true, - CacheKeyFiles = new[] { "global.json", "src/**/*.csproj" }, + CacheKeyFiles = ["global.json", "src/**/*.csproj"], PublishArtifacts = true, - ImportSecrets = new[] - { + ImportSecrets = + [ nameof(NugetApiKey), nameof(IReportCoverage.CodecovToken) - }, - OnPullRequestExcludePaths = new[] - { + ], + OnPullRequestExcludePaths = + [ "docs/*", "README.md", "CHANGELOG.md", "LICENSE" - } + ] )] //[GitHubActions("nightly", GitHubActionsImage.UbuntuLatest, @@ -87,20 +87,20 @@ namespace DataFilters.ContinuousIntegration [GitHubActions("nightly-manual", GitHubActionsImage.UbuntuLatest, AutoGenerate = true, FetchDepth = 0, - On = new[] { GitHubActionsTrigger.WorkflowDispatch }, - InvokedTargets = new[] { nameof(IUnitTest.Compile), nameof(IMutationTest.MutationTests), nameof(IPushNugetPackages.Pack) }, - CacheKeyFiles = new[] { + On = [GitHubActionsTrigger.WorkflowDispatch], + InvokedTargets = [nameof(IUnitTest.Compile), nameof(IMutationTest.MutationTests), nameof(IPushNugetPackages.Pack)], + CacheKeyFiles = [ "src/**/*.csproj", "test/**/*.csproj", "stryker-config.json", - "test/**/*/xunit.runner.json" }, + "test/**/*/xunit.runner.json"], EnableGitHubToken = true, - ImportSecrets = new[] - { + ImportSecrets = + [ nameof(NugetApiKey), nameof(IReportCoverage.CodecovToken), nameof(IMutationTest.StrykerDashboardApiKey) - }, + ], PublishArtifacts = true )] diff --git a/src/DataFilters/Filter.cs b/src/DataFilters/Filter.cs index c3e31ef9..2f50313d 100644 --- a/src/DataFilters/Filter.cs +++ b/src/DataFilters/Filter.cs @@ -72,44 +72,33 @@ public sealed class Filter : IFilter, IEquatable /// public static JSchema Schema(FilterOperator op) { - JSchema schema; - switch (op) + JSchema schema = op switch { - case FilterOperator.Contains: - case FilterOperator.StartsWith: - case FilterOperator.EndsWith: - schema = new JSchema - { - Type = JSchemaType.Object, - Properties = + FilterOperator.Contains or FilterOperator.StartsWith or FilterOperator.EndsWith => new JSchema + { + Type = JSchemaType.Object, + Properties = { [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String }, [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }, [ValueJsonPropertyName] = new JSchema { Type = JSchemaType.String } }, - Required = { FieldJsonPropertyName, OperatorJsonPropertyName } - }; - break; - case FilterOperator.IsEmpty: - case FilterOperator.IsNotEmpty: - case FilterOperator.IsNotNull: - case FilterOperator.IsNull: - schema = new JSchema - { - Type = JSchemaType.Object, - Properties = + Required = { FieldJsonPropertyName, OperatorJsonPropertyName } + }, + FilterOperator.IsEmpty or FilterOperator.IsNotEmpty or FilterOperator.IsNotNull or FilterOperator.IsNull => new JSchema + { + Type = JSchemaType.Object, + Properties = { [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String }, [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String } }, - Required = { FieldJsonPropertyName, OperatorJsonPropertyName } - }; - break; - default: - schema = new JSchema - { - Type = JSchemaType.Object, - Properties = + Required = { FieldJsonPropertyName, OperatorJsonPropertyName } + }, + _ => new JSchema + { + Type = JSchemaType.Object, + Properties = { [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String, }, [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }, @@ -117,10 +106,9 @@ public static JSchema Schema(FilterOperator op) Not = new JSchema() { Type = JSchemaType.Null } } }, - Required = { FieldJsonPropertyName, OperatorJsonPropertyName, ValueJsonPropertyName } - }; - break; - } + Required = { FieldJsonPropertyName, OperatorJsonPropertyName, ValueJsonPropertyName } + }, + }; schema.AllowAdditionalProperties = false; return schema; diff --git a/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs b/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs index 0d97d864..565556c0 100644 --- a/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs +++ b/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs @@ -5,18 +5,15 @@ /// /// Exception thrown when a has incorrect /// + /// + /// Creates a new instance + /// + /// message of the exception + /// name of the argument which causes the exception to be thrown #pragma warning disable RCS1194 // Implement exception constructors. [Serializable] - public sealed class BoundariesTypeMismatchException : ArgumentException + public sealed class BoundariesTypeMismatchException(string message, string paramName) : ArgumentException(message, paramName) #pragma warning restore RCS1194 // Implement exception constructors. { - /// - /// Creates a new instance - /// - /// message of the exception - /// name of the argument which causes the exception to be thrown - public BoundariesTypeMismatchException(string message, string paramName) : base(message, paramName) - { - } } } diff --git a/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs b/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs index d8ac81f6..c6572b74 100644 --- a/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs +++ b/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs @@ -216,7 +216,7 @@ from ___ in Token.EqualTo(FilterToken.CloseSquaredBracket) NumericValueExpression constant => constant, DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") #else _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") #endif diff --git a/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs b/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs index 4f72f898..393214aa 100644 --- a/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs +++ b/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs @@ -99,7 +99,7 @@ public class FilterTokenizer : Tokenizer /// List of characters that have a special meaning and should be escaped /// public static readonly char[] SpecialCharacters = - { + [ Asterisk, EqualSign, LeftParenthesis, @@ -118,7 +118,7 @@ public class FilterTokenizer : Tokenizer '-', '.', ' ' - }; + ]; /// /// Custom implementation that serves as the foundation of parsing text. diff --git a/src/DataFilters/Grammar/Syntax/AndExpression.cs b/src/DataFilters/Grammar/Syntax/AndExpression.cs index 5198ebd0..2a6cfcdf 100644 --- a/src/DataFilters/Grammar/Syntax/AndExpression.cs +++ b/src/DataFilters/Grammar/Syntax/AndExpression.cs @@ -10,7 +10,6 @@ public sealed class AndExpression : BinaryFilterExpression, IEquatable _lazyToString; private readonly Lazy _lazyEscapedParseableString; - /// public override double Complexity => Right.Complexity * Left.Complexity; diff --git a/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs b/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs index 410e8787..c0592aa7 100644 --- a/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs +++ b/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs @@ -38,12 +38,18 @@ private AsteriskExpression() { } /// public static EndsWithExpression operator +(AsteriskExpression _, ConstantValueExpression right) => new(right.Value); +#if !NET6_0_OR_GREATER /// /// Computes a by adding a to a . /// - /// - /// The constant expression + /// Left operand + /// Right operand /// - public static StartsWithExpression operator +(ConstantValueExpression left, AsteriskExpression _) => new(left.Value); +#else + /// +#endif +#pragma warning disable IDE0060, RCS1163 + public static StartsWithExpression operator +(ConstantValueExpression left, AsteriskExpression right) => new(left.Value); +#pragma warning restore IDE0060, RCS1163 } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs b/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs index 24c41af3..bb9517bf 100644 --- a/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs +++ b/src/DataFilters/Grammar/Syntax/BinaryFilterExpression.cs @@ -8,7 +8,7 @@ namespace DataFilters.Grammar.Syntax; public abstract class BinaryFilterExpression : FilterExpression, ISimplifiable { /// - /// Left operand + /// Left operand /// public FilterExpression Left { get; } diff --git a/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs b/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs index d362155a..09b858ae 100644 --- a/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs +++ b/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs @@ -6,28 +6,22 @@ /// /// A that can be used to construct instances. /// - public sealed class BoundaryExpression : IEquatable + /// + /// Builds a new instance. + /// + /// an + /// true if should be included in the interval and false otherwise. + public sealed class BoundaryExpression(IBoundaryExpression expression, bool included) : IEquatable { /// /// Expression used as a boundary /// - public IBoundaryExpression Expression { get; } + public IBoundaryExpression Expression { get; } = expression ?? throw new ArgumentNullException(nameof(expression)); /// /// Should the be included or excluded in the /// - public bool Included { get; } - - /// - /// Builds a new instance. - /// - /// an - /// true if should be included in the interval and false otherwise. - public BoundaryExpression(IBoundaryExpression expression, bool included) - { - Expression = expression ?? throw new ArgumentNullException(nameof(expression)); - Included = included; - } + public bool Included { get; } = included; /// public bool Equals(BoundaryExpression other) => other is not null diff --git a/src/DataFilters/Grammar/Syntax/BracketExpression.cs b/src/DataFilters/Grammar/Syntax/BracketExpression.cs index ec00fb3c..07508e1d 100644 --- a/src/DataFilters/Grammar/Syntax/BracketExpression.cs +++ b/src/DataFilters/Grammar/Syntax/BracketExpression.cs @@ -19,7 +19,7 @@ /// public sealed class BracketExpression : FilterExpression, IEquatable { - private static readonly IEqualityComparer EqualityComparer = new ArrayEqualityComparer(); + private static readonly ArrayEqualityComparer EqualityComparer = new(); /// /// Builds a new instance. diff --git a/src/DataFilters/Grammar/Syntax/GroupExpression.cs b/src/DataFilters/Grammar/Syntax/GroupExpression.cs index 65b0a6db..d7ab58fb 100644 --- a/src/DataFilters/Grammar/Syntax/GroupExpression.cs +++ b/src/DataFilters/Grammar/Syntax/GroupExpression.cs @@ -14,19 +14,17 @@ /// plus a marginal overhead. /// /// - public sealed class GroupExpression : FilterExpression, IEquatable, ISimplifiable + /// + /// Builds a new that holds the specified . + /// + /// the expression to group + /// is null. + public sealed class GroupExpression(FilterExpression expression) : FilterExpression, IEquatable, ISimplifiable { /// /// that the current instance is applied onto. /// - public FilterExpression Expression { get; } - - /// - /// Builds a new that holds the specified . - /// - /// - /// is null. - public GroupExpression(FilterExpression expression) => Expression = expression ?? throw new ArgumentNullException(nameof(expression)); + public FilterExpression Expression { get; } = expression ?? throw new ArgumentNullException(nameof(expression)); /// public bool Equals(GroupExpression other) => Expression.Equals(other?.Expression); diff --git a/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs b/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs index eb160755..83d29b2b 100644 --- a/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs @@ -4,15 +4,12 @@ namespace DataFilters.Grammar.Syntax /// /// Wraps a string that represents a /// - public class GuidValueExpression : ConstantValueExpression + /// + /// Builds a new instance that can wrap a + /// + /// + public class GuidValueExpression(string value) : ConstantValueExpression(value) { - /// - /// Builds a new instance that can wrap a - /// - /// - public GuidValueExpression(string value) : base(value) - { } - /// public override string EscapedParseableString => Value; } diff --git a/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs b/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs index 426068f5..00d0ce77 100644 --- a/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs +++ b/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs @@ -3,7 +3,5 @@ /// /// Marker interface that identifies types that can be used as 's boundaries /// - public interface IBoundaryExpression : IHaveComplexity, IParseableString - { - } + public interface IBoundaryExpression : IHaveComplexity, IParseableString; } diff --git a/src/DataFilters/Grammar/Syntax/NotExpression.cs b/src/DataFilters/Grammar/Syntax/NotExpression.cs index 0a03ed58..00ed5c93 100644 --- a/src/DataFilters/Grammar/Syntax/NotExpression.cs +++ b/src/DataFilters/Grammar/Syntax/NotExpression.cs @@ -44,7 +44,7 @@ public NotExpression(FilterExpression expression) public override int GetHashCode() => Expression.GetHashCode(); /// - public override string ToString() => $"{{NotExpression [" + + public override string ToString() => "{NotExpression [" + $"Expression = {Expression.GetType().Name}, " + $"{nameof(Expression.EscapedParseableString)} = '{Expression.EscapedParseableString}', " + $"{nameof(Expression.OriginalString)} = '{Expression.OriginalString}']," + diff --git a/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs b/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs index 22771ac0..9ca2ba03 100644 --- a/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs @@ -6,17 +6,14 @@ namespace DataFilters.Grammar.Syntax /// /// Wraps a string that represents a numeric value of some sort /// - public class NumericValueExpression : ConstantValueExpression, IEquatable, IBoundaryExpression + /// + /// Builds a new instance that can wrap a numeric value of some sort + /// + /// + /// is null + /// is + public class NumericValueExpression(string value) : ConstantValueExpression(value), IEquatable, IBoundaryExpression { - /// - /// Builds a new instance that can wrap a numeric value of some sort - /// - /// - /// is null - /// is - public NumericValueExpression(string value) : base(value) - { } - /// public override string EscapedParseableString => Value; diff --git a/src/DataFilters/Grammar/Syntax/OffsetExpression.cs b/src/DataFilters/Grammar/Syntax/OffsetExpression.cs index e4abe979..d711301f 100644 --- a/src/DataFilters/Grammar/Syntax/OffsetExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OffsetExpression.cs @@ -73,7 +73,7 @@ public OffsetExpression(NumericSign sign = NumericSign.Plus, uint hours = 0, uin }; /// -#if NETSTANDARD2_1_OR_GREATER || NET5_0 +#if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER public override int GetHashCode() => HashCode.Combine(Hours, Minutes, Sign); #else public override int GetHashCode() diff --git a/src/DataFilters/Grammar/Syntax/OneOfExpression.cs b/src/DataFilters/Grammar/Syntax/OneOfExpression.cs index 006bea49..378154a1 100644 --- a/src/DataFilters/Grammar/Syntax/OneOfExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OneOfExpression.cs @@ -54,28 +54,20 @@ public override bool IsEquivalentTo(FilterExpression other) { equivalent = true; } - else if (other is OneOfExpression oneOfExpression) - { - if (EqualityComparer.Equals(oneOfExpression._values.ToArray(), _values.ToArray())) - { - equivalent = true; - } - else - { - equivalent = !(_values.Except(oneOfExpression._values).Any() || oneOfExpression._values.Except(_values).Any()); - } - } else { - equivalent = other switch - { - AsteriskExpression asterisk => Simplify().IsEquivalentTo(asterisk), - ConstantValueExpression constant => Simplify().IsEquivalentTo(constant), - DateExpression date => Simplify().IsEquivalentTo(date), - DateTimeExpression dateTime => Simplify().IsEquivalentTo(dateTime), - ISimplifiable simplifiable => Simplify().IsEquivalentTo(simplifiable.Simplify()), - _ => false - }; + equivalent = other is OneOfExpression oneOfExpression + ? EqualityComparer.Equals([.. oneOfExpression._values], [.. _values]) + || !(_values.Except(oneOfExpression._values).Any() || oneOfExpression._values.Except(_values).Any()) + : other switch + { + AsteriskExpression asterisk => Simplify().IsEquivalentTo(asterisk), + ConstantValueExpression constant => Simplify().IsEquivalentTo(constant), + DateExpression date => Simplify().IsEquivalentTo(date), + DateTimeExpression dateTime => Simplify().IsEquivalentTo(dateTime), + ISimplifiable simplifiable => Simplify().IsEquivalentTo(simplifiable.Simplify()), + _ => false + }; } return equivalent; @@ -102,7 +94,7 @@ public override bool IsEquivalentTo(FilterExpression other) /// public FilterExpression Simplify() { - ISet curatedExpressions = new HashSet(); + HashSet curatedExpressions = []; foreach (IGrouping expr in _values.ToLookup(x => x is OneOfExpression)) { @@ -137,17 +129,12 @@ public FilterExpression Simplify() case 2: FilterExpression first = curatedExpressions.First(); FilterExpression other = curatedExpressions.Last(); - if (first is OneOfExpression oneOfFirst && other is OneOfExpression oneOfSecond) - { - simplifiedResult = new OneOfExpression(oneOfFirst.Values.Concat(oneOfSecond.Values).ToArray()); - } - else - { - simplifiedResult = new OrExpression(first, other); - } + simplifiedResult = first is OneOfExpression oneOfFirst && other is OneOfExpression oneOfSecond + ? new OneOfExpression([.. oneOfFirst.Values, .. oneOfSecond.Values]) + : new OrExpression(first, other); break; default: - simplifiedResult = new OneOfExpression(curatedExpressions.ToArray()); + simplifiedResult = new OneOfExpression([.. curatedExpressions]); break; } diff --git a/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs b/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs index 6bd6310c..41cd07cf 100644 --- a/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs +++ b/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs @@ -6,28 +6,22 @@ /// /// stores a range value used in a bracket expression /// - public sealed class RangeBracketValue : BracketValue, IEquatable, IComparable + /// + /// Builds a new instance. + /// + /// + /// + public sealed class RangeBracketValue(char start, char end) : BracketValue, IEquatable, IComparable { - /// - /// Builds a new instance. - /// - /// - /// - public RangeBracketValue(char start, char end) - { - Start = start; - End = end; - } - /// /// Start of the regex range /// - public char Start { get; } + public char Start { get; } = start; /// /// Ends of the regex range /// - public char End { get; } + public char End { get; } = end; /// public bool Equals(RangeBracketValue other) => (Start, End) == (other?.Start, other?.End); diff --git a/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs b/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs index 0b6024ef..a8078a1a 100644 --- a/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs +++ b/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs @@ -1,11 +1,10 @@ namespace DataFilters.Grammar.Syntax { - using DataFilters.Grammar.Parsing; - using System; using System.Collections.Generic; using System.Linq; using System.Text; + using DataFilters.Grammar.Parsing; #if !NETSTANDARD1_3 using Ardalis.GuardClauses; #endif @@ -109,9 +108,9 @@ public StartsWithExpression(TextExpression text) /// /// Combines the specified and s into a new . /// - /// - /// - /// a whose is and is + /// The left operand + /// The right operand + /// a whose is and is /// public static AndExpression operator +(StartsWithExpression left, EndsWithExpression right) => new(left, right); diff --git a/src/DataFilters/Grammar/Syntax/StringValueExpression.cs b/src/DataFilters/Grammar/Syntax/StringValueExpression.cs index ab6d16bc..47814398 100644 --- a/src/DataFilters/Grammar/Syntax/StringValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/StringValueExpression.cs @@ -11,20 +11,16 @@ namespace DataFilters.Grammar.Syntax /// /// Wraps a string that represents a constant string value /// - public class StringValueExpression : ConstantValueExpression, IEquatable + /// + /// Builds a new instance that can wrap a string value + /// + /// value of the expression. + /// + /// The property automatically escapes from . + /// + public class StringValueExpression(string value) : ConstantValueExpression(value), IEquatable { - private readonly Lazy _lazyParseableString; - - /// - /// Builds a new instance that can wrap a string value - /// - /// value of the expression. - /// - /// The property automatically escapes from . - /// - public StringValueExpression(string value) : base(value) - { - _lazyParseableString = new(() => + private readonly Lazy _lazyParseableString = new(() => { // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) // Also we need an extra position for the final '*' that will be append in all cases @@ -50,7 +46,6 @@ public StringValueExpression(string value) : base(value) return parseableString.ToString(); }); - } /// public override string EscapedParseableString => _lazyParseableString.Value; diff --git a/src/DataFilters/Grammar/Syntax/TextExpression.cs b/src/DataFilters/Grammar/Syntax/TextExpression.cs index 8235e6dd..d4355d0e 100644 --- a/src/DataFilters/Grammar/Syntax/TextExpression.cs +++ b/src/DataFilters/Grammar/Syntax/TextExpression.cs @@ -7,17 +7,13 @@ /// /// An expression that holds a string value "as is". /// - public class TextExpression : StringValueExpression, IEquatable + /// + /// Builds a new instance. + /// + /// Value of the expression + public class TextExpression(string value) : StringValueExpression(value), IEquatable { - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Builds a new instance. - /// - /// Value of the expression - public TextExpression(string value) : base(value) - { - _lazyEscapedParseableString = new(() => + private readonly Lazy _lazyEscapedParseableString = new(() => { StringBuilder escapedParseableString; if (value.AtLeastOnce(chr => chr == '"' || chr == '\\')) @@ -40,7 +36,6 @@ public TextExpression(string value) : base(value) return escapedParseableString.Insert(0, '"').Append('"').ToString(); }); - } /// public override string EscapedParseableString => _lazyEscapedParseableString.Value; diff --git a/src/DataFilters/MultiOrder.cs b/src/DataFilters/MultiOrder.cs index b74f1d5a..fc9b9c63 100644 --- a/src/DataFilters/MultiOrder.cs +++ b/src/DataFilters/MultiOrder.cs @@ -10,23 +10,22 @@ /// Allow to sort by multiple expression /// /// Type onto which the sort will be applied - public class MultiOrder : IOrder, IEquatable> + /// + /// Builds a new instance. + /// + /// + public class MultiOrder(params Order[] orders) : IOrder, IEquatable> { /// /// items that the current instance carries. /// public IEnumerable> Orders => _orders; - private readonly Order[] _orders; + private readonly Order[] _orders = orders.Where(s => s is not null) + .ToArray(); private static readonly ArrayEqualityComparer> equalityComparer = new(); - /// - /// Builds a new instance. - /// - /// - public MultiOrder(params Order[] orders) => _orders = orders.Where(s => s is not null) - .ToArray(); /// public bool Equals(IOrder other) => Equals(other as MultiOrder); diff --git a/src/DataFilters/OrderValidator.cs b/src/DataFilters/OrderValidator.cs index d5f6b717..64f4e7a6 100644 --- a/src/DataFilters/OrderValidator.cs +++ b/src/DataFilters/OrderValidator.cs @@ -25,7 +25,7 @@ public OrderValidator() => RuleFor(x => x) .Matches(Pattern) .WithMessage(search => { - string[] incorrectExpresions = search.Split(new[] { _separator }) + string[] incorrectExpresions = search.Split([_separator]) .Where(x => !_orderRegex.IsMatch(x)) .Select(x => $@"""{x}""") .ToArray(); diff --git a/src/DataFilters/Serialization/FilterOperatorConverter.cs b/src/DataFilters/Serialization/FilterOperatorConverter.cs index cbde4afc..29224cbd 100644 --- a/src/DataFilters/Serialization/FilterOperatorConverter.cs +++ b/src/DataFilters/Serialization/FilterOperatorConverter.cs @@ -94,4 +94,3 @@ public override void Write(Utf8JsonWriter writer, FilterOperator value, JsonSeri #endif } } - diff --git a/src/DataFilters/Serialization/MultiFilterConverter.cs b/src/DataFilters/Serialization/MultiFilterConverter.cs index f589d7da..6091b2eb 100644 --- a/src/DataFilters/Serialization/MultiFilterConverter.cs +++ b/src/DataFilters/Serialization/MultiFilterConverter.cs @@ -47,7 +47,7 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist if (filtersProperty is JProperty prop && filtersProperty.Value.Type == JTokenType.Array) { JArray filtersArray = token[MultiFilter.FiltersJsonPropertyName].Value(); - int nbFilters = filtersArray.Count(); + int nbFilters = filtersArray.Count; if (nbFilters >= 2) { IList filters = new List(nbFilters); @@ -107,7 +107,7 @@ public override MultiFilter Read(ref Utf8JsonReader reader, Type typeToConvert, }; - IList filters = new List(); + List filters = new (); if (reader.Read() && (reader.TokenType != JsonTokenType.PropertyName || MultiFilter.FiltersJsonPropertyName != reader.GetString())) { throw new JsonException($@"Expected ""{MultiFilter.FiltersJsonPropertyName}"" property."); diff --git a/src/DataFilters/StringExtensions.cs b/src/DataFilters/StringExtensions.cs index debefca0..7ee85c9a 100644 --- a/src/DataFilters/StringExtensions.cs +++ b/src/DataFilters/StringExtensions.cs @@ -236,7 +236,7 @@ static IFilter ConvertExpressionToFilter(PropertyInfo propInfo, FilterExpression filter = ConvertExpressionToFilter(propInfo, group.Expression, tc); break; case OneOfExpression oneOf: - FilterExpression[] possibleValues = oneOf.Values.ToArray(); + FilterExpression[] possibleValues = [.. oneOf.Values]; if (oneOf.Values.Exactly(1)) { filter = ConvertExpressionToFilter(propInfo, possibleValues[0], tc); diff --git a/src/Datafilters.Expressions/FilterExtensions.cs b/src/Datafilters.Expressions/FilterExtensions.cs index adb6e77d..39759b18 100644 --- a/src/Datafilters.Expressions/FilterExtensions.cs +++ b/src/Datafilters.Expressions/FilterExtensions.cs @@ -2,19 +2,18 @@ { #if NET6_0 using DateOnlyTimeOnly.AspNet.Converters; + using System.ComponentModel; #endif - using DataFilters.Expressions; - using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; - + using DataFilters.Expressions; + using static System.Linq.Expressions.Expression; using static DataFilters.FilterOperator; using static Expressions.NullableValueBehavior; - using static System.Linq.Expressions.Expression; /// /// The `FilterExtensions` class provides extension methods for building expression trees from `IFilter` instances. @@ -48,26 +47,39 @@ public static class FilterExtensions /// List of all primitives types /// /// - private static readonly Type[] PrimitiveTypes = { + private static readonly Type[] PrimitiveTypes = [ typeof(string), - typeof(Guid), typeof(Guid?), - typeof(int), typeof(int?), - typeof(long?), typeof(long?), - typeof(short?), typeof(short?), - typeof(decimal?), typeof(decimal?), - typeof(double?), typeof(double?), - typeof(ushort?), typeof(ushort?), - typeof(uint?), typeof(uint?), - typeof(ulong?), typeof(ulong?), - typeof(DateTime), typeof(DateTime?), - typeof(DateTimeOffset), typeof(DateTimeOffset?), + typeof(Guid), + typeof(Guid?), + typeof(int), + typeof(int?), + typeof(long?), + typeof(long?), + typeof(short?), + typeof(short?), + typeof(decimal?), + typeof(decimal?), + typeof(double?), + typeof(double?), + typeof(ushort?), + typeof(ushort?), + typeof(uint?), + typeof(uint?), + typeof(ulong?), + typeof(ulong?), + typeof(DateTime), + typeof(DateTime?), + typeof(DateTimeOffset), + typeof(DateTimeOffset?), #if NET6_0_OR_GREATER typeof(DateOnly), typeof(DateOnly?), typeof(TimeOnly), typeof(TimeOnly?), #endif - typeof(bool), typeof(bool?), - typeof(char), typeof(char?) - }; + typeof(bool), + typeof(bool?), + typeof(char), + typeof(char?) + ]; /// /// Builds an tree from a instance. @@ -111,10 +123,13 @@ public static Expression> ToExpression(this IFilter filter, Nul Type type = typeof(T); ParameterExpression pe = Parameter(type, "item"); - string[] fields = df.Field.Replace(@"[""", ".") - .Replace(@"""]", string.Empty) - .Split(new[] { '.' }) - .ToArray(); + string[] fields = + [ + .. df.Field.Replace(@"[""", ".") + .Replace(@"""]", string.Empty) + .Split(['.']) +, + ]; Expression body = nullableValueBehavior switch { @@ -186,7 +201,6 @@ private static object ConvertObjectToDateTime(object source, Type targetType) return dateTime; } - private static Expression ComputeBodyExpression(MemberExpression property, FilterOperator @operator, object value) { ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(((PropertyInfo)property.Member).PropertyType, value); @@ -200,12 +214,12 @@ private static Expression ComputeBodyExpression(MemberExpression property, Filte FilterOperator.GreaterThan => GreaterThan(property, constantExpression), FilterOperator.GreaterThanOrEqual => GreaterThanOrEqual(property, constantExpression), LessThanOrEqualTo => LessThanOrEqual(property, constantExpression), - StartsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), new[] { typeof(string) }), constantExpression), - NotStartsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), new[] { typeof(string) }), constantExpression)), - EndsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), new[] { typeof(string) }), constantExpression), - NotEndsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), new[] { typeof(string) }), constantExpression)), + StartsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression), + NotStartsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression)), + EndsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression), + NotEndsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression)), Contains => ComputeContains(property, value), - NotContains => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), constantExpression)), + NotContains => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), constantExpression)), IsEmpty => ComputeIsEmpty(property), IsNotEmpty => ComputeIsNotEmpty(property), EqualTo => ComputeEquals(property, value), @@ -227,15 +241,15 @@ private static Expression ComputeNullSafeBodyExpression(MemberExpression propert FilterOperator.GreaterThanOrEqual => GreaterThanOrEqual(property, constantExpression), LessThanOrEqualTo => LessThanOrEqual(property, constantExpression), StartsWith => AndAlso(NotEqual(property, Constant(null)), - Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), new[] { typeof(string) }), constantExpression)), + Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression)), NotStartsWith => AndAlso(NotEqual(property, Constant(null)), - Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), new[] { typeof(string) }), constantExpression))), + Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression))), EndsWith => AndAlso(NotEqual(property, Constant(null)), Call(property, - typeof(string).GetRuntimeMethod(nameof(string.EndsWith), new[] { typeof(string) }), + typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression)), NotEndsWith => AndAlso(NotEqual(property, Constant(null)), - Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), new[] { typeof(string) }), constantExpression))), + Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression))), Contains => ComputeNullSafeContains(property, value), NotContains => Not(ComputeNullSafeContains(property, value)), IsEmpty => ComputeNullSafeIsEmpty(property), @@ -297,7 +311,7 @@ private static Expression ComputeNullSafeIsEmpty(MemberExpression property) right = Not(Call(typeof(Enumerable), nameof(Enumerable.Any), genericType is not null - ? new Type[] { genericType } + ? [genericType] : null, property)); @@ -324,22 +338,22 @@ private static Expression ComputeContains(MemberExpression property, object valu contains = typeof(string).Equals(genericArgType) ? Call(typeof(Enumerable), nameof(Enumerable.Any), - new Type[] { typeof(string) }, + [typeof(string)], property, Lambda( Call(pe, - typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), - constantExpression), new[] { pe })) + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), + constantExpression), [pe])) : Call(typeof(Enumerable), nameof(Enumerable.Any), - new Type[] { property.Type.GenericTypeArguments[0] }, + [property.Type.GenericTypeArguments[0]], property, - Lambda(Equal(pe, constantExpression), new[] { pe })); + Lambda(Equal(pe, constantExpression), [pe])); } else { contains = Call(property, - typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), constantExpression); } @@ -359,23 +373,23 @@ private static Expression ComputeNullSafeContains(MemberExpression property, obj contains = typeof(string).Equals(genericArgType) ? Call(typeof(Enumerable), nameof(Enumerable.Any), - new Type[] { typeof(string) }, + [typeof(string)], property, Lambda( AndAlso(NotEqual(pe, Constant(null)), Call(pe, - typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), - constantExpression)), new[] { pe })) + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), + constantExpression)), [pe])) : Call(typeof(Enumerable), nameof(Enumerable.Any), - new Type[] { property.Type.GenericTypeArguments[0] }, + [property.Type.GenericTypeArguments[0]], property, - Lambda(Equal(pe, constantExpression), new[] { pe })); + Lambda(Equal(pe, constantExpression), [pe])); } else { contains = Call(property, - typeof(string).GetRuntimeMethod(nameof(string.Contains), new[] { typeof(string) }), + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), constantExpression); } @@ -392,9 +406,9 @@ private static Expression ComputeEquals(MemberExpression property, object value) ParameterExpression pe = Parameter(property.Type.GenericTypeArguments[0]); equals = Call(typeof(Enumerable), nameof(Enumerable.Any), - new Type[] { property.Type.GenericTypeArguments[0] }, + [property.Type.GenericTypeArguments[0]], property, - Lambda(Equal(pe, constantExpression), new[] { pe })); + Lambda(Equal(pe, constantExpression), [pe])); } else { @@ -414,9 +428,9 @@ private static Expression ComputeNullSafeEquals(MemberExpression property, objec ParameterExpression pe = Parameter(property.Type.GenericTypeArguments[0]); equals = Call(typeof(Enumerable), nameof(Enumerable.Any), - new Type[] { property.Type.GenericTypeArguments[0] }, + [property.Type.GenericTypeArguments[0]], property, - Lambda(Equal(pe, constantExpression), new[] { pe })); + Lambda(Equal(pe, constantExpression), [pe])); } else { @@ -453,9 +467,9 @@ private static Expression ComputeExpression(ParameterExpression pe, IEnumerable< localBody = ComputeBodyExpression(localProperty, @operator, value); body = Call(typeof(Enumerable), nameof(Enumerable.Any), - new[] { enumerableGenericType }, + [enumerableGenericType], property, - Lambda(localBody, new[] { localParameter }) + Lambda(localBody, [localParameter]) ); } } @@ -484,9 +498,9 @@ private static Expression ComputeExpression(ParameterExpression pe, IEnumerable< body = Call(typeof(Enumerable), nameof(Enumerable.Any), - new[] { enumerableGenericType }, + [enumerableGenericType], property, - Lambda(localBody, new[] { localParameter }) + Lambda(localBody, [localParameter]) ); } else @@ -534,9 +548,9 @@ private static Expression ComputeNullSafeExpression(ParameterExpression pe, IEnu body = AndAlso(NotEqual(property, Constant(null)), Call(typeof(Enumerable), nameof(Enumerable.Any), - new[] { enumerableGenericType }, + [enumerableGenericType], property, - Lambda(localBody, new[] { localParameter })) + Lambda(localBody, [localParameter])) ); } } @@ -566,9 +580,9 @@ private static Expression ComputeNullSafeExpression(ParameterExpression pe, IEnu body = AndAlso(NotEqual(property, Constant(null)), Call(typeof(Enumerable), nameof(Enumerable.Any), - new[] { enumerableGenericType }, + [enumerableGenericType], property, - Lambda(localBody, new[] { localParameter }))); + Lambda(localBody, [localParameter]))); } else { diff --git a/src/Datafilters.Expressions/OrderExpression.cs b/src/Datafilters.Expressions/OrderExpression.cs index 0b532ebf..62618dc0 100644 --- a/src/Datafilters.Expressions/OrderExpression.cs +++ b/src/Datafilters.Expressions/OrderExpression.cs @@ -13,19 +13,13 @@ /// An only can be created by calling either /// or /// - public sealed class OrderExpression + /// + /// Builds a new instance + /// + /// Lambda expression to the property onto which the sort will be performed. + /// Direction of the sort. + public sealed class OrderExpression(LambdaExpression keySelector, OrderDirection direction) { - /// - /// Builds a new instance - /// - /// Lambda expression to the property onto which the sort will be performed. - /// Direction of the sort. - public OrderExpression(LambdaExpression keySelector, OrderDirection direction) - { - Expression = keySelector; - Direction = direction; - } - /// /// Creates a new instance of /// @@ -46,11 +40,11 @@ public OrderExpression(LambdaExpression keySelector, OrderDirection direction) /// /// The lambda expression to use when /// - public LambdaExpression Expression { get; } + public LambdaExpression Expression { get; } = keySelector; /// /// Order direction (either or /// - public OrderDirection Direction { get; } + public OrderDirection Direction { get; } = direction; } } diff --git a/src/Datafilters.Expressions/OrderExtensions.cs b/src/Datafilters.Expressions/OrderExtensions.cs index 5ac6f548..102497dc 100644 --- a/src/Datafilters.Expressions/OrderExtensions.cs +++ b/src/Datafilters.Expressions/OrderExtensions.cs @@ -45,4 +45,3 @@ static IEnumerable> BuildOrderClauses(IOrder sort) } } } - diff --git a/src/Datafilters.Expressions/QueryableExtensions.cs b/src/Datafilters.Expressions/QueryableExtensions.cs index a862bd87..1094863a 100644 --- a/src/Datafilters.Expressions/QueryableExtensions.cs +++ b/src/Datafilters.Expressions/QueryableExtensions.cs @@ -42,11 +42,11 @@ public static IOrderedQueryable OrderBy(this IQueryable entries, in IOr OrderDirection.Ascending => nameof(Queryable.OrderBy), _ => nameof(Queryable.OrderByDescending) }, - new Type[] { entries.ElementType, first.Expression.ReturnType }, + [entries.ElementType, first.Expression.ReturnType], entries.Expression, first.Expression); // Build the subsequent sorting expressions - foreach (var order in orders.Skip(1)) + foreach (OrderExpression order in orders.Skip(1)) { sortExpression = Expression.Call(typeof(Queryable), order.Direction switch @@ -54,7 +54,7 @@ public static IOrderedQueryable OrderBy(this IQueryable entries, in IOr OrderDirection.Ascending => nameof(Queryable.ThenBy), _ => nameof(Queryable.ThenByDescending) }, - new Type[] { entries.ElementType, order.Expression.ReturnType }, + [entries.ElementType, order.Expression.ReturnType], sortExpression, order.Expression); } diff --git a/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs b/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs index 2de26825..893055d1 100644 --- a/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs +++ b/test/DataFilters.Expressions.UnitTests/FilterToExpressionsTests.cs @@ -18,12 +18,8 @@ [Feature("Filters")] [UnitTest] -public class FilterToExpressionTests +public class FilterToExpressionTests(ITestOutputHelper output) { - private readonly ITestOutputHelper _output; - - public FilterToExpressionTests(ITestOutputHelper output) => _output = output; - public static IEnumerable EqualToTestCases { get @@ -122,7 +118,7 @@ public static IEnumerable EqualToTestCases } }, NoAction, - (Expression>)(item => item.Firstname.Contains("a") && (item.Nickname == "Batman" || item.Nickname == "Superman")) + (Expression>)(item => item.Firstname.Contains('a') && (item.Nickname == "Batman" || item.Nickname == "Superman")) }; yield return new object[] @@ -162,9 +158,9 @@ public static IEnumerable EqualToTestCases } }, NoAction, - + #if NET6_0_OR_GREATER - (Expression>)(item => item.Firstname.Contains("a") + (Expression>)(item => item.Firstname.Contains('a') && DateOnly.FromDateTime(1.January(2007)) < item.LastBattleDate && item.LastBattleDate < DateOnly.FromDateTime(31.December(2012))) #else @@ -210,7 +206,7 @@ public static IEnumerable EqualToTestCases }, NoAction, #if NET6_0_OR_GREATER - (Expression>)(item => item.Firstname.Contains("a") + (Expression>)(item => item.Firstname.Contains('a') && DateOnly.FromDateTime(1.January(2007)) < item.LastBattleDate && item.LastBattleDate < DateOnly.FromDateTime(31.December(2012))) #else @@ -486,7 +482,7 @@ public static IEnumerable StartsWithCases }, new Filter(field : nameof(SuperHero.Nickname), @operator : StartsWith, value: "B"), AddNullCheck, - (Expression>)(item => item != null && item.Nickname != null && item.Nickname.StartsWith("B")) + (Expression>)(item => item != null && item.Nickname != null && item.Nickname.StartsWith('B')) }; } } @@ -507,9 +503,9 @@ public static IEnumerable NotStartsWithCases new SuperHero { Firstname = "Clark", Lastname = "Kent", Height = 190, Nickname = "Superman" }, new SuperHero { Firstname = "Barry", Lastname = "Allen", Height = 190, Nickname = "Flash" } }, - new Filter(field : nameof(SuperHero.Nickname), @operator : NotStartsWith, value: "B"), + new Filter(field : nameof(SuperHero.Nickname), @operator : NotStartsWith, value: 'B'), NoAction, - (Expression>)(item => item != null && !item.Nickname.StartsWith("B")) + (Expression>)(item => item != null && !item.Nickname.StartsWith('B')) }; } } @@ -533,7 +529,7 @@ public static IEnumerable EndsWithCases }, new Filter(field: nameof(SuperHero.Nickname), @operator: EndsWith, value:"n"), NoAction, - (Expression>)(item => item.Nickname.EndsWith("n")) + (Expression>)(item => item.Nickname.EndsWith('n')) }; } } @@ -557,7 +553,7 @@ public static IEnumerable NotEndsWithCases }, new Filter(field: nameof(SuperHero.Nickname), @operator: NotEndsWith, value:"n"), NoAction, - (Expression>)(item => !item.Nickname.EndsWith("n")) + (Expression>)(item => !item.Nickname.EndsWith('n')) }; } } @@ -752,7 +748,7 @@ public static IEnumerable LessThanOrEqualToCases }, new Filter(field : nameof(SuperHero.LastBattleDate), @operator : LessThanOrEqualTo, value : DateOnly.FromDateTime(1.January(2007))), NullableValueBehavior.NoAction, - (Expression>)(item => item.Firstname.Contains("a") + (Expression>)(item => item.Firstname.Contains('a') && item.LastBattleDate <= DateOnly.FromDateTime(1.January(2007))) #else new[] @@ -783,17 +779,17 @@ public void BuildLessThanOrEqual(IEnumerable superHeroes, IFilter fil /// Expression the filter should match once built private void Build(IEnumerable elements, IFilter filter, NullableValueBehavior nullableValueBehavior, Expression> expression) { - _output.WriteLine($"Filtering {elements.Jsonify()}"); - _output.WriteLine($"Filter under test : {filter}"); - _output.WriteLine($"Behavior : {nullableValueBehavior}"); - _output.WriteLine($"Reference expression : {expression.Body}"); + output.WriteLine($"Filtering {elements.Jsonify()}"); + output.WriteLine($"Filter under test : {filter}"); + output.WriteLine($"Behavior : {nullableValueBehavior}"); + output.WriteLine($"Reference expression : {expression.Body}"); // Act Expression> filterExpression = filter.ToExpression(nullableValueBehavior); IEnumerable filteredResult = elements .Where(filterExpression.Compile()) .ToList(); - _output.WriteLine($"Current expression : {filterExpression.Body}"); + output.WriteLine($"Current expression : {filterExpression.Body}"); // Assert filteredResult.Should() @@ -891,18 +887,18 @@ public static IEnumerable DateTimeOffsetFilterCases [MemberData(nameof(DateTimeOffsetFilterCases))] public void Filter_DateTimeOffset(IEnumerable appointments, IFilter filter, Expression> expression) { - _output.WriteLine($"Filtering {appointments.Jsonify()}"); - _output.WriteLine($"Filter under test : {filter}"); - _output.WriteLine($"Reference expression : {expression.Body}"); + output.WriteLine($"Filtering {appointments.Jsonify()}"); + output.WriteLine($"Filter under test : {filter}"); + output.WriteLine($"Reference expression : {expression.Body}"); // Act Expression> buildResult = filter.ToExpression(); IEnumerable filteredResult = appointments.Where(buildResult.Compile()) .ToArray(); - _output.WriteLine($"Current expression : {buildResult.Body}"); + output.WriteLine($"Current expression : {buildResult.Body}"); // Assert - _output.WriteLine($"Filtered result : {filteredResult.Jsonify()}"); + output.WriteLine($"Filtered result : {filteredResult.Jsonify()}"); filteredResult.Should() .NotBeNull().And .BeEquivalentTo(appointments?.Where(expression.Compile())); @@ -941,20 +937,17 @@ public void Filter_LocalDate(IEnumerable elements, IFilter filter public void Given_property_contains_null_value_and_behaviour_is_set_operator_will_result_in_instance_method_call_Expression_should_throws_NullReferenceException(FilterOperator op, object value) { // Arrange - Hero[] heroes = new Hero[] - { - new() - }; + Hero[] heroes = [new()]; Filter filter = new(nameof(Hero.Name), op, value); // Act Expression> expression = filter.ToExpression(NoAction); - Action act = () => heroes.Where(expression.Compile()).ToArray(); + Action act = () => _ = heroes.Where(expression.Compile()).ToArray(); // Assert act.Should() - .Throw(); + .Throw("the resulting expression would apply to a property without checking if it's null"); } [Theory] @@ -967,17 +960,17 @@ public void Given_property_contains_null_value_and_behaviour_is_set_operator_wil public void Given_property_is_enumerable_and_behaviour_is_set_operator_will_result_in_instance_method_call_Expression_should_not_throws_NullReferenceException(FilterOperator op, object value) { // Arrange - SuperHero[] heroes = new SuperHero[] - { - new() { Acolytes = null} - }; + SuperHero[] heroes = + [ + new() { Acolytes = null } + ]; Filter filter = new($"{nameof(SuperHero.Acolytes)}.{nameof(SuperHero.Nickname)}", op, value); // Act Expression> expression = filter.ToExpression(AddNullCheck); - _output.WriteLine($"Computed expression : {expression.Body}"); - Action act = () => heroes.Where(expression.Compile()).ToArray(); + output.WriteLine($"Computed expression : {expression.Body}"); + Action act = () => _ = heroes.Where(expression.Compile()).ToArray(); // Assert act.Should() diff --git a/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs b/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs index fc313f84..1d78b8a2 100644 --- a/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs +++ b/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs @@ -9,12 +9,8 @@ using Xunit.Categories; [UnitTest] - public class QueryableExtensionsTests + public class QueryableExtensionsTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public QueryableExtensionsTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - public static IEnumerable ThrowsArgumentNullExceptionCases { get @@ -37,8 +33,8 @@ public static IEnumerable ThrowsArgumentNullExceptionCases [MemberData(nameof(ThrowsArgumentNullExceptionCases))] public void Should_Throws_ArgumentNullException_When_Parameter_IsNull(IQueryable heroes, IOrder orderBy) { - _outputHelper.WriteLine($"{nameof(heroes)} is null : {heroes == null}"); - _outputHelper.WriteLine($"{nameof(orderBy)} is null : {orderBy == null}"); + outputHelper.WriteLine($"{nameof(heroes)} is null : {heroes == null}"); + outputHelper.WriteLine($"{nameof(orderBy)} is null : {orderBy == null}"); // Act Action action = () => heroes.OrderBy(orderBy); diff --git a/test/DataFilters.PerformanceTests/BracketVsOr.cs b/test/DataFilters.PerformanceTests/BracketVsOr.cs index 51d003a1..6847d5c9 100644 --- a/test/DataFilters.PerformanceTests/BracketVsOr.cs +++ b/test/DataFilters.PerformanceTests/BracketVsOr.cs @@ -8,6 +8,7 @@ [SimpleJob(RuntimeMoniker.CoreRt31)] [SimpleJob(RuntimeMoniker.Net50)] [SimpleJob(RuntimeMoniker.Net60)] + [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Marquer les membres comme étant static")] public class BracketVsOr { [Benchmark] diff --git a/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs b/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs index 1201df0d..b1238eac 100644 --- a/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs +++ b/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs @@ -5,9 +5,10 @@ [MemoryDiagnoser] [RPlotExporter] +[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Marquer les membres comme étant static")] public class RawFilterVsFilterService { - private IFilterService _service; + private FilterService _service; [Params("Nickname=((Bat|Sup)|Wonder)*m[ae]n")] public string Input { get; set; } diff --git a/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs b/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs index fa8f3b62..d0cee984 100644 --- a/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs +++ b/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs @@ -9,12 +9,8 @@ namespace DataFilters.Queries.UnitTests using Xunit.Abstractions; using static DataFilters.FilterOperator; - public class FilterToQueriesTests + public class FilterToQueriesTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public FilterToQueriesTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - public static IEnumerable FilterToWhereCases { get @@ -106,8 +102,8 @@ public static IEnumerable FilterToWhereCases yield return new object[] { - new Filter(field: "XP", LessThanOrEqualTo, (int)10), - new WhereClause("XP".Field(), ClauseOperator.LessThanOrEqualTo, (int)10) + new Filter(field: "XP", LessThanOrEqualTo, 10), + new WhereClause("XP".Field(), ClauseOperator.LessThanOrEqualTo, 10) }; yield return new object[] @@ -154,12 +150,12 @@ public static IEnumerable FilterToWhereCases [MemberData(nameof(FilterToWhereCases))] public void FilterToWhere(IFilter filter, IWhereClause expected) { - _outputHelper.WriteLine($"Filter : {filter.Jsonify()}"); - _outputHelper.WriteLine($"Expected : {expected.Jsonify()}"); + outputHelper.WriteLine($"Filter : {filter.Jsonify()}"); + outputHelper.WriteLine($"Expected : {expected.Jsonify()}"); // Act IWhereClause actualWhere = filter.ToWhere(); - _outputHelper.WriteLine($"actualQuery : {actualWhere.Jsonify()}"); + outputHelper.WriteLine($"actualQuery : {actualWhere.Jsonify()}"); // Assert actualWhere.Should() diff --git a/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs b/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs index ee72cc23..0bb9f161 100644 --- a/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs +++ b/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs @@ -10,12 +10,8 @@ using Xunit.Abstractions; using static global::Queries.Core.Parts.Sorting.OrderDirection; - public class OrderToQueriesTests + public class OrderToQueriesTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public OrderToQueriesTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - public class Person { public string Firstname { get; set; } @@ -55,11 +51,11 @@ public static IEnumerable OrderToOrderCases [MemberData(nameof(OrderToOrderCases))] public void FilterToOrder(IOrder sort, Expression, bool>> expected) { - _outputHelper.WriteLine($"Sort : {sort.Jsonify()}"); + outputHelper.WriteLine($"Sort : {sort.Jsonify()}"); // Act IEnumerable actual = sort.ToOrder(); - _outputHelper.WriteLine($"actual result : {actual.Jsonify()}"); + outputHelper.WriteLine($"actual result : {actual.Jsonify()}"); // Assert actual.Should() diff --git a/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs b/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs index d3f6b1aa..c3232614 100644 --- a/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs +++ b/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs @@ -19,10 +19,8 @@ namespace DataFilters.UnitTests.Converters [UnitTest] [Feature("Converters")] - public class DataFilterConverterTests + public class DataFilterConverterTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - private static IImmutableDictionary Operators => new Dictionary { ["eq"] = EqualTo, @@ -38,8 +36,6 @@ public class DataFilterConverterTests ["isempty"] = IsEmpty }.ToImmutableDictionary(); - public DataFilterConverterTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - /// /// Deserialize tests cases /// @@ -236,7 +232,7 @@ public static IEnumerable DeserializeCases [MemberData(nameof(DeserializeCases))] public void Deserialize(string json, Type targetType, Expression> expectation) { - _outputHelper.WriteLine($"{nameof(json)} : {json}"); + outputHelper.WriteLine($"{nameof(json)} : {json}"); object result = System.Text.Json.JsonSerializer.Deserialize(json, targetType); @@ -254,7 +250,7 @@ public void Deserialize(string json, Type targetType, Expression> expectation) { - _outputHelper.WriteLine($"Serializing {filter}"); + outputHelper.WriteLine($"Serializing {filter}"); string result = System.Text.Json.JsonSerializer.Serialize(filter, filter.GetType()); diff --git a/test/DataFilters.UnitTests/FilterExtensionsTests.cs b/test/DataFilters.UnitTests/FilterExtensionsTests.cs index d621ac15..c65bd937 100644 --- a/test/DataFilters.UnitTests/FilterExtensionsTests.cs +++ b/test/DataFilters.UnitTests/FilterExtensionsTests.cs @@ -15,12 +15,8 @@ using static DataFilters.FilterOperator; [UnitTest] - public class FilterExtensionsTests + public class FilterExtensionsTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public FilterExtensionsTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - public static IEnumerable ToFilterCases { get @@ -453,7 +449,7 @@ public static IEnumerable ToFilterCases [MemberData(nameof(ToFilterCases))] public void ToFilter(string filter, IFilter expected) { - _outputHelper.WriteLine($"{nameof(filter)} : '{filter}'"); + outputHelper.WriteLine($"{nameof(filter)} : '{filter}'"); // Act IFilter actual = filter.ToFilter(); @@ -513,7 +509,7 @@ public static IEnumerable ToFilterWithFilterOptionsCases { get { - FilterLogic[] logics = { And, Or }; + FilterLogic[] logics = [And, Or]; foreach (FilterLogic logic in logics) { diff --git a/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs b/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs index 0ee21ef9..43847826 100644 --- a/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs +++ b/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs @@ -21,7 +21,7 @@ public void Given_no_parameters_Cosntructor_should_set_properties_with_default_v _sut.PropertyNameResolutionStrategy.Should().Be(PropertyNameResolutionStrategy.Default); } - [Property(Arbitrary = new[] { typeof(Generators) })] + [Property(Arbitrary = [typeof(Generators)])] public void Given_positive_integer_value_And_strategy_Constructor_should_set_properties(PositiveInt input, PropertyNameResolutionStrategy propertyNameResolutionStrategy) { // Act @@ -32,7 +32,7 @@ public void Given_positive_integer_value_And_strategy_Constructor_should_set_pro _sut.PropertyNameResolutionStrategy.Should().Be(propertyNameResolutionStrategy); } - [Property(Arbitrary = new[] { typeof(Generators) })] + [Property(Arbitrary = [typeof(Generators)])] public void Given_options_Validate_should_check_all_values_are_valid(int maxCacheSize, PropertyNameResolutionStrategy strategy) { // Arrange diff --git a/test/DataFilters.UnitTests/FilterServiceTests.cs b/test/DataFilters.UnitTests/FilterServiceTests.cs index c2653057..795da1dc 100644 --- a/test/DataFilters.UnitTests/FilterServiceTests.cs +++ b/test/DataFilters.UnitTests/FilterServiceTests.cs @@ -11,16 +11,9 @@ namespace DataFilters.UnitTests; using Xunit; using Xunit.Abstractions; -public class FilterServiceTests +public class FilterServiceTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - private readonly FilterService _sut; - - public FilterServiceTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - _sut = new FilterService(new FilterServiceOptions()); - } + private readonly FilterService _sut = new FilterService(new FilterServiceOptions()); public static IEnumerable ComputeCases { diff --git a/test/DataFilters.UnitTests/FilterTests.cs b/test/DataFilters.UnitTests/FilterTests.cs index 67b15a52..ae857b60 100644 --- a/test/DataFilters.UnitTests/FilterTests.cs +++ b/test/DataFilters.UnitTests/FilterTests.cs @@ -20,10 +20,8 @@ using static DataFilters.FilterOperator; [UnitTest] - public class FilterTests + public class FilterTests(ITestOutputHelper output) { - private readonly ITestOutputHelper _output; - private static readonly IImmutableDictionary _operators = new Dictionary { ["contains"] = Contains, @@ -165,8 +163,6 @@ public static IEnumerable FilterSchemaTestCases } } - public FilterTests(ITestOutputHelper output) => _output = output; - /// /// Serialization of instance of test cases /// @@ -193,13 +189,13 @@ public void FilterToJson(Filter filter, Expression> jsonMatch private void ToJson(IFilter filter, Expression> jsonMatcher) { - _output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); + output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); // Act string json = filter.ToJson(); // Assert - _output.WriteLine($"ToJson result is '{json}'"); + output.WriteLine($"ToJson result is '{json}'"); json.Should().Match(jsonMatcher); } @@ -207,8 +203,8 @@ private void ToJson(IFilter filter, Expression> jsonMatcher) [MemberData(nameof(FilterSchemaTestCases))] public void FilterSchema(string json, FilterOperator @operator, bool expectedValidity) { - _output.WriteLine($"{nameof(json)} : {json}"); - _output.WriteLine($"{nameof(FilterOperator)} : {@operator}"); + output.WriteLine($"{nameof(json)} : {json}"); + output.WriteLine($"{nameof(FilterOperator)} : {@operator}"); // Arrange JSchema schema = Filter.Schema(@operator); @@ -285,8 +281,8 @@ public static IEnumerable FilterEquatableCases [MemberData(nameof(FilterEquatableCases))] public void FilterImplementsEquatableProperly(Filter first, Filter second, bool expectedResult) { - _output.WriteLine($"first : {first}"); - _output.WriteLine($"second : {second}"); + output.WriteLine($"first : {first}"); + output.WriteLine($"second : {second}"); // Act bool result = first.Equals(second); @@ -326,12 +322,12 @@ public void Ctor_should_build_valid_instance() && filter.Operator == @operator && value.Equals(filter.Value); }).When(!Filter.UnaryOperators.Contains(@operator)).Label("Binary operator") - .VerboseCheck(_output); + .VerboseCheck(output); }) - .VerboseCheck(_output); + .VerboseCheck(output); } - [Property(Arbitrary = new[] { typeof(FilterGenerators) })] + [Property(Arbitrary = [typeof(FilterGenerators)])] public void Given_filter_instance_Negate_should_work_as_expected(NonNull source) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs index fea8cfc4..bfb50a7c 100644 --- a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs @@ -183,7 +183,7 @@ public void Given_int_as_string_Number_should_parse_to_a_ConstantValueExpression AssertThatShould_parse(actual, expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_StartsWith(StartsWithExpression expected) { // Arrange @@ -198,7 +198,7 @@ public void Should_parse_StartsWith(StartsWithExpression expected) AssertThatShould_parse(expression, expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_EndsWith(EndsWithExpression expected) { // Arrange @@ -238,7 +238,7 @@ public static IEnumerable ContainsCases new ContainsExpression("d3aa022d-ec52-47aa-be13-6823c478c60a") }; - string[] punctuations = { ".", "-", ":", "_" }; + string[] punctuations = [".", "-", ":", "_"]; foreach (string punctuation in punctuations) { @@ -372,7 +372,7 @@ public void Should_parse_AndExpression(string input, AndExpression expected) AssertThatShould_parse(expression, expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_NotExpression(NotExpression expected) { // Arrange @@ -568,7 +568,7 @@ public void Should_parse_OneOfExpression(string input, OneOfExpression expected) AssertThatShould_parse(expression, expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_bracket_expression_OneOf_can_parse_input(NonNull bracketExpression) { // Arrange @@ -592,7 +592,7 @@ public void Given_bracket_expression_OneOf_can_parse_input(NonNull expression.IsEquivalentTo(expected).ToProperty(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_Interval(CultureInfo culture, IntervalExpression expected) { _cultureSwitcher.Run(culture, () => @@ -614,7 +614,7 @@ public static IEnumerable IntervalCases { get { - string[] cultures = { "fr-FR", "en-GB", "en-US" }; + string[] cultures = ["fr-FR", "en-GB", "en-US"]; foreach (string culture in cultures) { yield return new object[] @@ -662,7 +662,7 @@ public void Given_interval_as_string_Parser_should_parse_to_IntervalExpression(s }); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_text(CultureInfo culture, TextExpression expected) { _cultureSwitcher.Run(culture, () => @@ -974,7 +974,7 @@ public void Should_parse_Criteria(string input, Expression DateCases } } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_DateCases(DateExpression expected) { // Arrange @@ -1014,7 +1014,7 @@ public void Should_parse_DateCases(DateExpression expected) AssertThatShould_parse(actual, expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_Groups(GroupExpression expected) { // Arrange @@ -1032,7 +1032,7 @@ public static IEnumerable ParseGroupCases { get { - string[] cultures = { "fr-FR", "en-GB", "en-US" }; + string[] cultures = ["fr-FR", "en-GB", "en-US"]; foreach (string culture in cultures) { yield return new object[] @@ -1068,7 +1068,7 @@ public void Given_GroupExpression_EscapedParseableString_as_input_Parser_should_ }); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_TimeExpression(NonNull expected) { // Arrange @@ -1082,7 +1082,7 @@ public void Should_parse_TimeExpression(NonNull expected) AssertThatShould_parse(actual, expected.Item); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Should_parse_DurationExpression(DurationExpression expected) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs index 94b0cb6c..17477b12 100644 --- a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs @@ -16,16 +16,9 @@ [UnitTest] [Feature(nameof(DataFilters.Grammar.Parsing))] [Feature(nameof(FilterTokenizer))] - public class FilterTokenizerTests + public class FilterTokenizerTests(ITestOutputHelper outputHelper) { - private readonly FilterTokenizer _sut; - private readonly ITestOutputHelper _outputHelper; - - public FilterTokenizerTests(ITestOutputHelper outputHelper) - { - _sut = new FilterTokenizer(); - _outputHelper = outputHelper; - } + private readonly FilterTokenizer _sut = new FilterTokenizer(); [Fact] public void IsTokenizer() => typeof(FilterTokenizer).Should() @@ -371,12 +364,12 @@ public static IEnumerable RecognizeTokensCases [MemberData(nameof(RecognizeTokensCases))] public void RecognizeTokens(string input, Expression, bool>> expectation) { - _outputHelper.WriteLine($"input : '{input}'"); + outputHelper.WriteLine($"input : '{input}'"); // Act TokenList tokens = _sut.Tokenize(input); // Assert - _outputHelper.WriteLine($"Tokens : {tokens.Select(token => new { Value = token.ToStringValue(), Kind = token.Kind.ToString(), token.Position.Column }).Jsonify()}"); + outputHelper.WriteLine($"Tokens : {tokens.Select(token => new { Value = token.ToStringValue(), Kind = token.Kind.ToString(), token.Position.Column }).Jsonify()}"); tokens.Should() .Match(expectation); diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs index 229cf34f..e1b6f529 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs @@ -16,12 +16,8 @@ namespace DataFilters.UnitTests.Grammar.Syntax [UnitTest] [Feature(nameof(AndExpression))] - public class AndExpressionTests + public class AndExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public AndExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(AndExpression).Should() .BeAssignableTo().And @@ -35,7 +31,7 @@ public static IEnumerable ArgumentNullExceptionCases { get { - FilterExpression[] left = { new StartsWithExpression("ce"), null }; + FilterExpression[] left = [new StartsWithExpression("ce"), null]; return left.CrossJoin(left, (left, right) => (left, right)) .Where(tuple => tuple.left == null || tuple.right is null) @@ -89,8 +85,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void Equals_should_work_as_expected(AndExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -100,7 +96,7 @@ public void Equals_should_work_as_expected(AndExpression first, object other, bo .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_AndExpression_GetComplexity_should_return_left_complexity_multiply_by_right_complexity(AndExpression and) { // Arrange @@ -158,7 +154,7 @@ public void Given_AndExpression_Simplify_should_return_the_expected_expression(A .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_AndExpression_instances_one_and_two_where_oneU002Eleft_eq_twoU002Eright_and_oneU002Eright_eq_twoU002Eleft_IsEquivalentTo_should_return_true(FilterExpression first, FilterExpression second) { // Arrange @@ -173,11 +169,11 @@ public void Given_two_AndExpression_instances_one_and_two_where_oneU002Eleft_eq_ .BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_reflexive(AndExpression and) => and.IsEquivalentTo(and).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_AndExpression_instances_first_and_second_and_firstU002ERight_eq_secondU002Eright_and_firstU002ELeft_eq_secondU002ELeft_Equals_should_returns_true(FilterExpression left, FilterExpression right) { // Arrange @@ -188,7 +184,7 @@ public void Given_two_AndExpression_instances_first_and_second_and_firstU002ERig first.Should().Be(second); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void An_AndExpression_Equals_should_neq_false(FilterExpression left, FilterExpression right) { // Arrange @@ -202,15 +198,15 @@ public void An_AndExpression_Equals_should_neq_false(FilterExpression left, Filt .BeFalse(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_AndExpression_instance_where_instanceU002ELeft_is_equivalent_to_instanceU002ERight_Simplify_should_return_the_expression_with_the_lowest_Complexity(FilterExpression expression, PositiveInt count) { // Arrange OneOfExpression oneOfExpression = new(Enumerable.Repeat(expression, count.Item + 1) // if count == 1 .ToArray()); - _outputHelper.WriteLine($"{nameof(oneOfExpression)} : '{oneOfExpression.EscapedParseableString}'"); - _outputHelper.WriteLine($"{nameof(oneOfExpression.Complexity)} : {oneOfExpression.Complexity}"); + outputHelper.WriteLine($"{nameof(oneOfExpression)} : '{oneOfExpression.EscapedParseableString}'"); + outputHelper.WriteLine($"{nameof(oneOfExpression.Complexity)} : {oneOfExpression.Complexity}"); AndExpression and = new(oneOfExpression, expression); @@ -220,14 +216,14 @@ public void Given_AndExpression_instance_where_instanceU002ELeft_is_equivalent_t double actualComplexity = actual.Complexity; // Assert - _outputHelper.WriteLine($"actual : {actual.EscapedParseableString} (Complexity : {actual.Complexity})"); - _outputHelper.WriteLine($"actual is equivalent to expression : {isEquivalent})"); + outputHelper.WriteLine($"actual : {actual.EscapedParseableString} (Complexity : {actual.Complexity})"); + outputHelper.WriteLine($"actual is equivalent to expression : {isEquivalent})"); isEquivalent.Should().BeTrue(); actualComplexity.Should().BeLessThan(oneOfExpression.Complexity); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_operand_is_a_AndFilterExpression_instance_When_right_is_not_null_Constructor_should_wrap_left_inside_a_GroupExpression_instance(NonNull left, NonNull right) { // Act diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs index 581bb764..8a0e184e 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs @@ -17,15 +17,8 @@ [UnitTest] [Feature(nameof(AsteriskExpression))] - public class AsteriskExpressionTests + public class AsteriskExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public AsteriskExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(AsteriskExpression).Should() .BeAssignableTo().And @@ -45,8 +38,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void TestEquals(AsteriskExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -67,10 +60,10 @@ public void TestEquals(AsteriskExpression first, object other, bool expected, st } } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_AsteriskExpression_GetComplexity_should_return_1() => AsteriskExpression.Instance.Complexity.Should().Be(1); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_AsteriskExpression_When_adding_ConstantValueExpression_Should_returns_EndsWithExpression(NonNull constantExpression) { // Arrange @@ -83,7 +76,7 @@ public void Given_AsteriskExpression_When_adding_ConstantValueExpression_Should_ actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_ConstantValueExpresion_When_adding_AsteriskExpression_Should_returns_StartsWithWithExpression(NonNull constantExpression) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs index a5760e16..1acad179 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs @@ -15,7 +15,7 @@ [Feature(nameof(IntervalExpression))] public class BoundaryExpressionTests { - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_a_BoundaryExpression_instance_should_be_equals_to_itself(BoundaryExpression input) { // Act @@ -25,7 +25,7 @@ public void Given_a_BoundaryExpression_instance_should_be_equals_to_itself(Bound actual.Should().BeTrue("'equals' implementation should be reflexive"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void A_BoundaryExpression_instance_should_not_be_equal_to_null(BoundaryExpression input) { // Act @@ -35,7 +35,7 @@ public void A_BoundaryExpression_instance_should_not_be_equal_to_null(BoundaryEx actual.Should().BeFalse("a not null boundaryexpression is not equal to null"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Two_BoundaryExpressions_instances_with_same_expression_and_included_should_be_equal(BoundaryExpression source) { // Arrange @@ -52,7 +52,7 @@ public void Two_BoundaryExpressions_instances_with_same_expression_and_included_ firstHashCode.Should().Be(otherHashCode); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Ctor_should_throw_ArgumentNullException_when_expression_is_null(bool included) { // Act @@ -63,7 +63,7 @@ public void Ctor_should_throw_ArgumentNullException_when_expression_is_null(bool .ThrowExactly(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) { // Act @@ -73,7 +73,7 @@ public void Equals_should_be_reflexive(NonNull expression) actual.Should().BeTrue("'equals' implementation must be reflexive"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull group, NonNull otherExpression) { // Act diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs index fc9764eb..cc0dea2d 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs @@ -12,15 +12,8 @@ using Xunit; using Xunit.Abstractions; - public class BracketExpressionTests + public class BracketExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public BracketExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(BracketExpression).Should() .BeAssignableTo().And @@ -72,7 +65,7 @@ public void Equals_should_behave_as_expected(BracketExpression expression, objec .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Two_BracketExpression_instances_built_with_different_inputs_should_not_be_equal(NonEmptyArray one, NonEmptyArray two) { @@ -80,10 +73,10 @@ public void Two_BracketExpression_instances_built_with_different_inputs_should_n BracketExpression first = new(one.Item); BracketExpression second = new(two.Item); - first.Equals(second).ToProperty().When(one.Item.Equals(two.Item)).VerboseCheck(_outputHelper); + first.Equals(second).ToProperty().When(one.Item.Equals(two.Item)).VerboseCheck(outputHelper); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Complexity_should_depends_on_input(NonEmptyArray values) { // Arrange @@ -126,7 +119,7 @@ public void Complexity_should_behave_as_expected(BracketExpression expression, d .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_BracketRangeValue_IsEquivalentTo_should_be_equivalent_to_many_OrExpression_where_each_expression_contains_one_charater(NonNull rangeBracketValue) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs index 54198fbe..c6b99534 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs @@ -22,7 +22,7 @@ public void Value_should_be_set_by_the_parameter_of_the_constructor(NonNull rangeBracketValue) { // Arrange @@ -36,15 +36,15 @@ public void Given_RangeBracketValue_Equals_should_returns_true_when_ConstantBrac actual.Should().BeTrue($"Range expression : {rangeBracketValue} and Constant expression is {constantBracketValue.Value}"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_ConstantBracketValues_left_eq_right_should_be_returns_same_value_as_Equals(ConstantBracketValue left, ConstantBracketValue right) => (left == right).When(left.Equals(right)).Label($"Left, Right : {(left, right)}"); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_ConstantBracketValues_left_neq_right_should_be_returns_same_value_as_Equals(ConstantBracketValue left, ConstantBracketValue right) => (left != right).When(!left.Equals(right)).Label($"Left, Right : {(left, right)}"); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) { // Act @@ -54,7 +54,7 @@ public void Equals_should_be_reflexive(NonNull expression) actual.Should().BeTrue("'equals' implementation is reflexive"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) { // Act @@ -64,7 +64,7 @@ public void Equals_should_be_symetric(NonNull expression, actual.Should().BeTrue("'equals' implementation should be symetric"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_ConstantBracketValue_Complexity_should_be_equal_to_inner_expression_complexity(ConstantBracketValue value) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs index ab23bbe1..2073a7c2 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs @@ -10,12 +10,8 @@ using Xunit; using Xunit.Abstractions; - public class ContainsExpressionTests + public class ContainsExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public ContainsExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(ContainsExpression).Should() .BeAssignableTo().And @@ -27,7 +23,7 @@ public void IsFilterExpression() => typeof(ContainsExpression).Should() public void Given_string_argument_is_null_Constructor_should_thros_ArgumentNullException() { // Act - Action action = () => new ContainsExpression((string)null); + Action action = () => _ = new ContainsExpression((string)null); // Assert action.Should() @@ -38,7 +34,7 @@ public void Given_string_argument_is_null_Constructor_should_thros_ArgumentNullE public void Given_TextExpression_is_null_Constructor_should_thros_ArgumentNullException() { // Act - Action action = () => new ContainsExpression((TextExpression)null); + Action action = () => _ = new ContainsExpression((TextExpression)null); // Assert action.Should() @@ -49,7 +45,7 @@ public void Given_TextExpression_is_null_Constructor_should_thros_ArgumentNullEx public void Given_TextExpression_argument_is_null_Constructor_should_thros_ArgumentNullException() { // Act - Action action = () => new ContainsExpression(string.Empty); + Action action = () => _ = new ContainsExpression(string.Empty); // Assert action.Should() @@ -60,7 +56,7 @@ public void Given_TextExpression_argument_is_null_Constructor_should_thros_Argum public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_WhitespaceOnly() { // Act - Action action = () => new ContainsExpression(" "); + Action action = () => _ = new ContainsExpression(" "); // Assert action.Should() @@ -95,8 +91,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void ImplementsEqualsCorrectly(ContainsExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -106,7 +102,7 @@ public void ImplementsEqualsCorrectly(ContainsExpression first, object other, bo .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_ContainsExpression_Complexity_eq_1U002E5(ContainsExpression contains) { // Act @@ -116,11 +112,11 @@ public void Given_ContainsExpression_Complexity_eq_1U002E5(ContainsExpression co actual.Should().Be(1.5); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_reflexive(ContainsExpression contains) => contains.IsEquivalentTo(contains).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) { // Arrange @@ -151,7 +147,7 @@ public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) { // Act @@ -162,7 +158,7 @@ public void Equals_should_be_commutative(NonNull first, Filt firstEqualsSecond.Should().Be(secondEqualsFirst, "'equals' implementation must be commutative"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) { // Act @@ -172,7 +168,7 @@ public void Equals_should_be_reflexive(NonNull expression) actual.Should().BeTrue("'equals' implementation must be reflexive"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) { // Act diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs index 247f9e44..b379e10e 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs @@ -10,12 +10,8 @@ using Xunit; using Xunit.Abstractions; - public class DateExpressionTests + public class DateExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public DateExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(DateExpression).Should() .BeAssignableTo().And @@ -67,50 +63,50 @@ public void Given_two_DateExpression_instances_first_and_other_with_same_data_fi firstHashCode.Should().Be(otherHashCode); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateExpression_GetComplexity_should_return_1(NonNull date) => date.Item.Complexity.Should().Be(1); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_commutative(NonNull first, FilterExpression second) => first.Item.IsEquivalentTo(second).Should().Be(second.IsEquivalentTo(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_reflexive(NonNull expression) => expression.Item.IsEquivalentTo(expression.Item).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.IsEquivalentTo(otherExpression.Item).Should().Be(otherExpression.Item.IsEquivalentTo(expression.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_operands_Equal_operator_should_return_same_result_as_using_Equals(NonNull left, NonNull right) => (left.Item == right.Item).Should().Be(left.Item.Equals(right.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_operands_NotEquals_operator_should_return_opposite_result_of_Equals(NonNull left, NonNull right) => (left.Item != right.Item).Should().Be(!left.Item.Equals(right.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_years_month_and_days_When_either_years_or_months_or_days_is_less_than_1_Then_constructor_should_throw_ArgumentOutOfRangeException(int years, int months, int days) { // Act - Action invokingConstructor = () => new DateExpression(years, months, days); + Action invokingConstructor = () => _ = new DateExpression(years, months, days); // Assert - object _ = (years, months, days) switch + object __ = (years, months, days) switch { (int y, int m, int d) when y < 1 || m < 1 || d < 1 => invokingConstructor.Should().Throw("because years, months and days must be greater than or equal to 1"), _ => invokingConstructor.Should().NotThrow("because years, months and days are greater than or equal to 1") diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs index d2387679..3e625060 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs @@ -13,12 +13,8 @@ using Xunit.Categories; [Feature(nameof(DataFilters.Grammar.Syntax))] - public class DateTimeExpressionTests + public class DateTimeExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public DateTimeExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(DateTimeExpression).Should() .BeAssignableTo().And @@ -42,7 +38,7 @@ public void Given_date_and_time_parameters_are_null_Constructor_should_throws_Ar .ThrowExactly("both date and time are null"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void A_non_null_DateTimeExpression_instance_should_never_be_equal_to_null(DateTimeExpression instance) { // Act @@ -53,7 +49,7 @@ public void A_non_null_DateTimeExpression_instance_should_never_be_equal_to_null .BeFalse(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Two_DateTimeExpression_instances_which_have_same_data_should_be_equal(DateExpression date, TimeExpression time, OffsetExpression offset) { // Arrange @@ -70,10 +66,10 @@ public void Two_DateTimeExpression_instances_which_have_same_data_should_be_equa .Be(other.GetHashCode(), "Two instances that are equal should have same hashcode"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void A_DateTimeExpression_built_from_variables_obtained_from_deconstructing_a_DateTimeExpression_should_equal_the_original_DateTimeExpression_value(DateTimeExpression source) { - _outputHelper.WriteLine(message: $"DateTimeExpression is {source}"); + outputHelper.WriteLine(message: $"DateTimeExpression is {source}"); (DateExpression date, TimeExpression time, OffsetExpression offset, _) = source; // Act @@ -84,7 +80,7 @@ public void A_DateTimeExpression_built_from_variables_obtained_from_deconstructi .Be(source); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateTimeExpression_Complexity_Should_be_the_sum_of_complexity_of_each_member(NonNull date, NonNull time, NonNull offset) { // Arrange @@ -100,7 +96,7 @@ public void Given_DateTimeExpression_Complexity_Should_be_the_sum_of_complexity_ .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateTimeExpression_where_only_date_part_is_set_When_comparing_to_DateExpression_IsEquivalentTo_should_be_true(NonNull dateExpression) { // Arrange @@ -113,7 +109,7 @@ public void Given_DateTimeExpression_where_only_date_part_is_set_When_comparing_ actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateTimeExpression_where_only_time_part_is_set_When_comparing_to_that_TimeExpression(NonNull timeExpression) { // Arrange @@ -169,15 +165,15 @@ public void Equals_should_work_as_expected(DateTimeExpression dateTime, object o .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); @@ -224,7 +220,7 @@ public void Equals_should_be_transitive(DateTimeExpression first, object second, .Be(firstEqualsSecond && secondEqualsThird); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_operands_Equal_operator_should_return_same_result_as_using_Equals(NonNull left, NonNull right) { // Act @@ -235,7 +231,7 @@ public void Given_left_and_right_operands_Equal_operator_should_return_same_resu .Be(left.Item.Equals(right.Item)); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_operands_NotEqual_operator_should_return_opposite_result_of_Equal_operator(NonNull left, NonNull right) { // Act diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs index a918a2f1..b8b6c5f8 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs @@ -12,12 +12,8 @@ namespace DataFilters.UnitTests.Grammar.Syntax using Xunit.Categories; [UnitTest] - public class DurationExpressionTests + public class DurationExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public DurationExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(DurationExpression).Should() .BeDerivedFrom(); @@ -25,9 +21,9 @@ public void IsFilterExpression() => typeof(DurationExpression).Should() [Property] public void Throws_ArgumentOutOfRangeException_when_any_ctor_input_is_negative(int years, int months, int weeks, int days, int hours, int minutes, int seconds) { - Action invokingConstructor = () => new DurationExpression(years, months, weeks, days, hours, minutes, seconds); + Action invokingConstructor = () => _ = new DurationExpression(years, months, weeks, days, hours, minutes, seconds); - object _ = (years, months, weeks, days, hours, minutes, seconds) switch + object __ = (years, months, weeks, days, hours, minutes, seconds) switch { var tuple when tuple.years < 0 || tuple.months < 0 @@ -113,29 +109,29 @@ public void Equals_to_null_always_returns_false(PositiveInt years, PositiveInt m durationEqualsToNull.Should().BeFalse(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Equals(expression.Item).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DurationExpression_GetComplexity_should_return_1(DurationExpression duration) => duration.Complexity.Should().Be(1); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_transitive(DurationExpression duration, NonNull other, NonNull third) { // Arrange - _outputHelper.WriteLine($"Duration : {duration}"); - _outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); - _outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); + outputHelper.WriteLine($"Duration : {duration}"); + outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); + outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); bool first = duration.IsEquivalentTo(other.Item); bool second = other.Item.IsEquivalentTo(third.Item); @@ -172,15 +168,15 @@ public static IEnumerable IsEquivalentToCases public void IsEquivalent_should_behave_as_expected(DurationExpression duration, FilterExpression other, FilterExpression third, (bool expected, string reason) expectation) { // Arrange - _outputHelper.WriteLine($"Duration : {duration}"); - _outputHelper.WriteLine($"Other : {other.EscapedParseableString}"); - _outputHelper.WriteLine($"Third : {third.EscapedParseableString}"); + outputHelper.WriteLine($"Duration : {duration}"); + outputHelper.WriteLine($"Other : {other.EscapedParseableString}"); + outputHelper.WriteLine($"Third : {third.EscapedParseableString}"); bool durationIsEquivalentToOther = duration.IsEquivalentTo(other); bool otherIsEquivalentToThird = other.IsEquivalentTo(third); - _outputHelper.WriteLine($"{duration} is equivalent to {other} : {durationIsEquivalentToOther}"); - _outputHelper.WriteLine($"{other} is equivalent to {third} : {otherIsEquivalentToThird}"); + outputHelper.WriteLine($"{duration} is equivalent to {other} : {durationIsEquivalentToOther}"); + outputHelper.WriteLine($"{other} is equivalent to {third} : {otherIsEquivalentToThird}"); // Act bool actual = duration.IsEquivalentTo(third); diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs index a62a0e19..75a55e89 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs @@ -12,12 +12,8 @@ using Xunit.Categories; [UnitTest] - public class EndsWithExpressionTests + public class EndsWithExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public EndsWithExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(EndsWithExpression).Should() .BeAssignableTo().And @@ -83,8 +79,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void ImplementsEqualsCorrectly(EndsWithExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -105,11 +101,11 @@ public void ImplementsEqualsCorrectly(EndsWithExpression first, object other, bo } } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_EndsWithExpression_Complexity_should_return_1U002E5(EndsWithExpression endsWith) => endsWith.Complexity.Should().Be(1.5); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) { // Arrange @@ -140,19 +136,19 @@ public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Equals(expression.Item).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_EndsWithExpression_When_adding_AsteriskExpression_should_returns_ContainsExpression(NonNull endsWith) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs index e25b18af..edcaead1 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs @@ -10,12 +10,8 @@ using Xunit; using Xunit.Abstractions; - public class GroupExpressionTests + public class GroupExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public GroupExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(GroupExpression).Should() .BeAssignableTo().And @@ -34,7 +30,7 @@ public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() .ThrowExactly($"The parameter of {nameof(GroupExpression)}'s constructor cannot be null"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_current_instance_is_not_null_and_other_is_null_Equals_should_return_false(NonNull group) { // Act @@ -45,18 +41,18 @@ public void Given_current_instance_is_not_null_and_other_is_null_Equals_should_r .BeFalse(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull group) => group.Item.Equals(group.Item).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull group, NonNull otherExpression) => group.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(group.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_GroupExpression_Complexity_should_be_linear_to_inner_expression_complexity(GroupExpression group) => group.Complexity.Should().Be(0.1 + group.Expression.Complexity); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_comparing_to_that_arbitrary_expression_IsEquivalent_should_return_true(NonNull filterExpression) { // Arrange @@ -69,7 +65,7 @@ public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_co actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_comparing_to_that_arbitrary_expression_IsEquivalentTo_should_return_true(NonNull filterExpression) { // Arrange @@ -82,7 +78,7 @@ public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_co actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_wrapping_that_group_inside_a_group_expression_Should_not_change_its_meaning(NonNull expression) { // Arrange @@ -95,7 +91,7 @@ public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_wr isEquivalent.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_GroupExpression_instances_that_wrap_equivalent_expressions_IsEquivalent_should_be_true(NonNull filterExpression) { // Arrange @@ -151,7 +147,7 @@ public void Equals_should_behave_as_expected(GroupExpression expression, object .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_GroupExpression_wraps_an_arbitrary_FilterExpression_When_that_group_is_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression) { // Arrange @@ -165,7 +161,7 @@ public void Given_GroupExpression_wraps_an_arbitrary_FilterExpression_When_that_ isEquivalent.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_any_BinaryExpression_When_that_expression_is_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression) { // Arrange @@ -178,26 +174,26 @@ public void Given_any_BinaryExpression_When_that_expression_is_wrapped_inside_a_ isEquivalent.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_reflexive(GroupExpression group) => group.IsEquivalentTo(group).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_symetric(GroupExpression group, NonNull other) { - _outputHelper.WriteLine($"Group : {group}"); - _outputHelper.WriteLine($"Other : {other}"); + outputHelper.WriteLine($"Group : {group}"); + outputHelper.WriteLine($"Other : {other}"); group.IsEquivalentTo(other.Item).Should().Be(other.Item.IsEquivalentTo(group)); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) }/*, Replay = "(1500570200792655435,5263687413201141279)"*/)] + [Property(Arbitrary = [typeof(ExpressionsGenerators)]/*, Replay = "(1500570200792655435,5263687413201141279)"*/)] public void IsEquivalent_should_be_transitive(GroupExpression group, NonNull other, NonNull third) { // Arrange - _outputHelper.WriteLine($"Group : {group}"); - _outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); - _outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); + outputHelper.WriteLine($"Group : {group}"); + outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); + outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); bool first = group.IsEquivalentTo(other.Item); bool second = other.Item.IsEquivalentTo(third.Item); @@ -212,7 +208,7 @@ public void IsEquivalent_should_be_transitive(GroupExpression group, NonNull filterExpression, PositiveInt count) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs index 845b8fc2..6a6e67f5 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs @@ -12,15 +12,8 @@ using Xunit; using Xunit.Abstractions; - public class IntervalExpressionTests + public class IntervalExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public IntervalExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(IntervalExpression).Should() .BeAssignableTo().And @@ -239,8 +232,8 @@ public static IEnumerable EqualCases [MemberData(nameof(EqualCases))] public void Equals_should_work_as_expected(IntervalExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -250,7 +243,7 @@ public void Equals_should_work_as_expected(IntervalExpression first, object othe .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_min_bound_is_DateTimeExpression_When_min_does_not_hold_TimeExpression_Constructor_should_converts_Min_to_DateExpression(DateExpression date, bool included) { // Arrange @@ -264,7 +257,7 @@ public void Given_min_bound_is_DateTimeExpression_When_min_does_not_hold_TimeExp sut.Min.Expression.Should().Be(date); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_Min_boundary_is_a_DateTimeExpression_with_only_time_specified_Ctor_should_convert_min_boundary_to_only_holds_the_specified_TimeExpression(NonNull time, bool included) { // Arrange @@ -279,7 +272,7 @@ public void Given_Min_boundary_is_a_DateTimeExpression_with_only_time_specified_ .Which.Should().Be(time.Item); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_Min_boundary_is_a_DateTimeExpression_with_only_date_specified_Ctor_should_convert_min_boundary_to_only_holds_the_specified_DateExpression(NonNull date, bool included) { // Arrange @@ -295,7 +288,7 @@ public void Given_Min_boundary_is_a_DateTimeExpression_with_only_date_specified_ .Which.Should().Be(date.Item); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Ctor_should_converts_Max_DateTimeExpression_to_TimeExpression_when_DateExpression_is_not_provided(PositiveInt hours, PositiveInt minutes, PositiveInt seconds, @@ -315,13 +308,13 @@ public void Ctor_should_converts_Max_DateTimeExpression_to_TimeExpression_when_D .Be(time); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_a_ConstantExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_ConstantExpression(NumericValueExpression constant) { CreateIsEquivalentPropeprty(constant, constant, minIncluded: true, maxIsIncluded: true); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_reflexive(NonNull interval) { // Arrange @@ -334,19 +327,19 @@ public void IsEquivalent_should_be_reflexive(NonNull interva actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_a_DateExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_DateExpression(DateExpression date) { CreateIsEquivalentPropeprty(date, date, minIncluded: true, maxIsIncluded: true); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_a_DateTimeExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_DateTimeExpression(DateTimeExpression dateTime) { CreateIsEquivalentPropeprty(dateTime, dateTime, minIncluded: true, maxIsIncluded: true); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_a_DateTimeExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_TimeExpression(TimeExpression dateTime) { CreateIsEquivalentPropeprty(dateTime, dateTime, minIncluded: true, maxIsIncluded: true); @@ -364,7 +357,7 @@ private static void CreateIsEquivalentPropeprty(FilterExpression filterExpressio actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_RangeExpression_GetComplexity_should_return_sum_of_min_and_max_complexity(NonNull rangeExpressionGenerator) { // Arrange @@ -421,7 +414,7 @@ public static IEnumerable SimplifyCases [MemberData(nameof(SimplifyCases))] public void Given_IntervalExpression_Simplify_should_return_expected_expression(IntervalExpression rangeExpression, FilterExpression expected, string reason) { - _outputHelper.WriteLine($"Range expression : {rangeExpression}"); + outputHelper.WriteLine($"Range expression : {rangeExpression}"); // Act FilterExpression actual = rangeExpression.Simplify(); @@ -431,7 +424,7 @@ public void Given_IntervalExpression_Simplify_should_return_expected_expression( .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void An_IntervalExpression_instance_built_from_data_from_a_previously_deconstructed_instance_should_be_equal(IntervalExpression expected) { // Arrange @@ -445,15 +438,15 @@ public void An_IntervalExpression_instance_built_from_data_from_a_previously_dec .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs index 273138c0..f0823601 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs @@ -11,15 +11,8 @@ using Xunit; using Xunit.Abstractions; - public class NotExpressionTests + public class NotExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public NotExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(NotExpression).Should() .BeAssignableTo().And @@ -38,19 +31,19 @@ public void Ctor_should_throws_ArgumentNullException_when_argument_is_null() .ThrowExactly($"The parameter of {nameof(NotExpression)}'s constructor cannot be null"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_NotExpression_GetComplexity_should_return_same_complexity_as_embedded_expression(NonNull notExpression) => notExpression.Item.Complexity.Should().Be(notExpression.Item.Expression.Complexity); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_NotExpression_Equals_should_be_reflexive(NonNull notExpression) => notExpression.Item.Should().Be(notExpression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_NotExpression_and_a_filter_expression_that_is_not_null_Equals_should_be_symetric(NonNull notExpression, NonNull filterExpression) => notExpression.Item.Equals(filterExpression.Item).Should().Be(filterExpression.Item.Equals(notExpression.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_instances_holding_same_Expressions_Equals_should_return_true(NonNull expression) { // Arrange @@ -67,7 +60,7 @@ public void Given_two_instances_holding_same_Expressions_Equals_should_return_tr firstHashCode.Should().Be(otherHashCode); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_argument_is_AndExpression_Constructor_should_wrap_it_inside_a_GroupExpression(NonNull expression) { // Act @@ -156,15 +149,15 @@ public void Equals_should_work_as_expected(NotExpression expression, object obj, .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Equals(expression.Item).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs index bf675048..61d25a06 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs @@ -11,12 +11,8 @@ using Xunit.Categories; [UnitTest] - public class NumericValueExpressionTests + public class NumericValueExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public NumericValueExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(NumericValueExpression).Should() .BeAssignableTo().And @@ -27,7 +23,7 @@ public void IsFilterExpression() => typeof(NumericValueExpression).Should() public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { // Act - Action action = () => new NumericValueExpression(null); + Action action = () => _ = new NumericValueExpression(null); // Assert action.Should() @@ -38,7 +34,7 @@ public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() { // Act - Action action = () => new NumericValueExpression(string.Empty); + Action action = () => _ = new NumericValueExpression(string.Empty); // Assert action.Should() @@ -49,7 +45,7 @@ public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() public void Given_parameter_is_whitespace_Ctor_should_not_throw() { // Act - Action action = () => new NumericValueExpression(" "); + Action action = () => _ = new NumericValueExpression(" "); // Assert action.Should() @@ -70,7 +66,7 @@ public void Two_instances_are_equals_when_holding_same_values(NonEmptyString val actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_is_commutative(NumericValueExpression first, NumericValueExpression second) => first.Equals(second).Should().Be(second.Equals(first)); @@ -85,11 +81,11 @@ public void Given_two_NumericValueExpressions_Equals_should_depends_on_string_in first.Equals(second).Should().Be(Equals(first.Value, second.Value)); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_NumericValueExpression_GetComplexity_should_return_1(NumericValueExpression constant) => constant.Complexity.Should().Be(1); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_IntervalExpression_with_min_equals_max_is_equivalent_should_return_true(NumericValueExpression input) { // Arrange @@ -104,15 +100,15 @@ public void Given_IntervalExpression_with_min_equals_max_is_equivalent_should_re .BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs index ab0beb8a..98709ea6 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs @@ -34,18 +34,18 @@ public void Given_input_EscapedParseableString_should_be_correct(NumericSign sig .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_an_OffsetExpression_instance_Equals_should_be_reflective(NonNull offset) => offset.Item.Equals(offset.Item).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs index 9128b59b..2236bf71 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs @@ -11,15 +11,8 @@ using Xunit; using Xunit.Abstractions; - public class OneOfExpressionTests + public class OneOfExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public OneOfExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(OneOfExpression).Should() .BeAssignableTo().And @@ -31,7 +24,7 @@ public void IsFilterExpression() => typeof(OneOfExpression).Should() public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { // Act - Action action = () => new OneOfExpression(null); + Action action = () => _ = new OneOfExpression(null); // Assert action.Should() @@ -42,7 +35,7 @@ public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() public void Constructor_throws_InvalidOperationException_when_values_is_empty() { // Act - Action ctorWithEmptyArray = () => new OneOfExpression(Array.Empty()); + Action ctorWithEmptyArray = () => _ = new OneOfExpression(Array.Empty()); // Assert ctorWithEmptyArray.Should() @@ -83,8 +76,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void ImplementsEqualsCorrectly(OneOfExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -182,19 +175,19 @@ public void Implements_IsEquivalentTo_Properly(OneOfExpression first, FilterExpr .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_ConstantExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(StringValueExpression filterExpression, PositiveInt n) => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateExpression filterExpression, PositiveInt n) => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateTimeExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateTimeExpression filterExpression, PositiveInt n) => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_AsteriskExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(PositiveInt n) => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(AsteriskExpression.Instance, n.Item); @@ -212,7 +205,7 @@ private static void Given_OneOfExpression_contains_the_same_epxression_repeated_ actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OneOfExpression_Complexity_should_return_sum_of_inner_expressions(NonEmptyArray expressions) { // Arrange @@ -226,7 +219,7 @@ public void Given_OneOfExpression_Complexity_should_return_sum_of_inner_expressi actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OneOfExpression_with_same_values_repetead_more_than_once_Simplify_should_filter_out_duplicated_values(NonEmptyArray expressions) { // Arrange @@ -285,17 +278,17 @@ public void Given_OneOfExpression_Simplify_should_output_expected_expression(One actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OneOfExpression_instance_which_contains_only_one_expression_Simplify_should_return_an_expression_that_is_requivalent_to_the_inner_expression(NonNull expression) { // Arrange - _outputHelper.WriteLine($"input : {expression.Item}"); + outputHelper.WriteLine($"input : {expression.Item}"); OneOfExpression oneOf = new(expression.Item); // Act FilterExpression simplified = oneOf.Simplify(); - _outputHelper.WriteLine($"Simplified : {simplified}"); + outputHelper.WriteLine($"Simplified : {simplified}"); // Assert simplified.IsEquivalentTo(expression.Item) @@ -303,7 +296,7 @@ public void Given_OneOfExpression_instance_which_contains_only_one_expression_Si .BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OneOfExpression_Simplify_should_return_an_expression_that_cannot_be_further_simplified(PositiveInt count, NonNull expression) { // Arrange @@ -322,13 +315,11 @@ public void Given_OneOfExpression_Simplify_should_return_an_expression_that_cann .BeLessThan(complexityBeforeFirstSimplification, "The expression must be simpler"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OneExpression_that_contains_inner_OneOfExpressions_Simplify_should_flatten_them(NonEmptyArray first, NonEmptyArray second, NonEmptyArray third) { // Arrange - FilterExpression expected = new OneOfExpression(first.Item.Concat(second.Item) - .Concat(third.Item) - .ToArray()).Simplify(); + FilterExpression expected = new OneOfExpression([.. first.Item, .. second.Item, .. third.Item]).Simplify(); OneOfExpression initial = new(new OneOfExpression(first.Item), new OneOfExpression(second.Item), diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs index 6011106f..3f8741e0 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs @@ -14,15 +14,8 @@ using Xunit.Categories; [UnitTest] - public class OrExpressionTests + public class OrExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public OrExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(OrExpression).Should() .BeAssignableTo().And @@ -35,7 +28,7 @@ public static IEnumerable ArgumentNullExceptionCases { get { - FilterExpression[] expression = { new StartsWithExpression("ce"), null }; + FilterExpression[] expression = [new StartsWithExpression("ce"), null]; return expression.CrossJoin(expression, (left, right) => (left, right)) .Where(tuple => tuple.left == null || tuple.right is null) @@ -89,8 +82,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void Equals_should_behave_has_expected(OrExpression first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); @@ -142,8 +135,8 @@ public static IEnumerable IsEquivalentToCases [MemberData(nameof(IsEquivalentToCases))] public void Implements_IsEquivalentTo_Correctly(OrExpression first, FilterExpression other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.IsEquivalentTo(other); @@ -153,7 +146,7 @@ public void Implements_IsEquivalentTo_Correctly(OrExpression first, FilterExpres .Be(expected, reason); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_OrExpression_instances_that_left_and_right_expressions_are_both_equals_IsEquivalentTo_should_return_true(FilterExpression left, FilterExpression right) { // Arrange @@ -167,7 +160,7 @@ public void Given_two_OrExpression_instances_that_left_and_right_expressions_are actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OrExpression_and_OneOfExpression_that_contains_the_same_two_expression_then_calling_IsEquivalentTo_with_that_OneOfExpression_should_return_true(FilterExpression left, FilterExpression right) { // Arrange @@ -181,23 +174,23 @@ public void Given_OrExpression_and_OneOfExpression_that_contains_the_same_two_ex actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_ConstantExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(ConstantValueExpression filterExpression) => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateExpression filterExpression) => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_DateTimeExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateTimeExpression filterExpression) => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_AsteriskExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true() => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(AsteriskExpression.Instance); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_eq_right_eq_OrExpression_Simplify_should_returns_the_inner_OrExpression(OrExpression filterExpression) { // Arrange @@ -223,11 +216,11 @@ private static void Given_FilterExpression_equals_left_and_right_IsEquivalentTo_ actual.Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_OrExpression_GetComplexity_should_return_sum_of_left_and_right_complexity(OrExpression orExpression) => (orExpression.Complexity == orExpression.Left.Complexity + orExpression.Right.Complexity).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_eq_right_Simplify_should_return_left(FilterExpression expected) { // Arrange @@ -236,7 +229,7 @@ public void Given_left_eq_right_Simplify_should_return_left(FilterExpression exp // Act FilterExpression actual = orExpression.Simplify(); - _outputHelper.WriteLine($"Simplifiied expression : {actual}"); + outputHelper.WriteLine($"Simplifiied expression : {actual}"); bool isEquivalent = actual.IsEquivalentTo(expected); @@ -275,7 +268,7 @@ public void Given_OrExpression_Simplify_should_return_expected_result(OrExpressi .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_OrExpression_instances_one_and_two_where_oneU002Eleft_eq_twoU002Eright_and_oneU002Eright_eq_twoU002Eleft_IsEquivalentTo_should_return_true(FilterExpression first, FilterExpression second) { // Arrange @@ -290,12 +283,12 @@ public void Given_two_OrExpression_instances_one_and_two_where_oneU002Eleft_eq_t .BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_reflexive(OrExpression or) => or.IsEquivalentTo(or).Should() .BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_symetric(OrExpression or) { // Arrange @@ -310,7 +303,7 @@ public void IsEquivalentTo_should_be_symetric(OrExpression or) .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_transitive(OrExpression or) { // Arrange @@ -326,7 +319,7 @@ public void IsEquivalent_should_be_transitive(OrExpression or) .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_right_and_left_are_OrExpression_Constructor_should_wrap_right_and_left_inside_a_GroupExpression_instance(NonNull left, NonNull right) { @@ -341,7 +334,7 @@ public void Given_right_and_left_are_OrExpression_Constructor_should_wrap_right_ .BeOfType($"Right instance is a '{nameof(BinaryFilterExpression)}'"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_is_ConstantValueExpression_and_right_is_ConstantValueExpression_Constructor_should_leave_right_and_left_untouched(NonNull left, NonNull right) { @@ -356,15 +349,15 @@ public void Given_left_is_ConstantValueExpression_and_right_is_ConstantValueExpr .NotBeOfType($"Left instance is a '{nameof(ConstantValueExpression)}'"); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs index 907c7569..de18ee4d 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs @@ -7,15 +7,8 @@ using Xunit; using Xunit.Abstractions; - public class PropertyNameTests + public class PropertyNameTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public PropertyNameTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(PropertyName).Should() .HaveConstructor(new[] { typeof(string) }).And @@ -71,8 +64,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void ImplementsEqualsCorrectly(PropertyName first, object other, bool expected, string reason) { - _outputHelper.WriteLine($"First instance : {first}"); - _outputHelper.WriteLine($"Second instance : {other}"); + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); // Act bool actual = first.Equals(other); diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs index 7ce751c2..ac2d4836 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs @@ -11,15 +11,8 @@ using Xunit; using Xunit.Abstractions; - public class RangeBracketValueTests + public class RangeBracketValueTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public RangeBracketValueTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Property] public void Value_should_be_set_by_the_parameter_of_the_constructor(char start, char end) { @@ -39,8 +32,8 @@ public void Given_ConstantBracketExpression_contains_consecutive_characters_When Prop.ForAll(arbMap.ArbFor().Filter(char.IsLetter).Filter(char.IsLower), arbMap.ArbFor().Filter(char.IsLetter).Filter(char.IsLower), (start, end) => { - char[] chrs = new[] { start, end }.OrderBy(x => x) - .ToArray(); + char[] chrs = [.. new[] { start, end }.OrderBy(x => x) +]; char head = chrs[0]; char tail = chrs[1]; ConstantBracketValue constantBracketValue = new(Enumerable.Range(head, tail - head + 1) @@ -52,10 +45,10 @@ public void Given_ConstantBracketExpression_contains_consecutive_characters_When return actual.When(start < end); }) - .QuickCheckThrowOnFailure(_outputHelper); + .QuickCheckThrowOnFailure(outputHelper); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_RangeBracketValues_left_ne_right_should_be_same_as_not_left_Equals_right(RangeBracketValue left, RangeBracketValue right) { // Arrange @@ -68,7 +61,7 @@ public void Given_left_and_right_RangeBracketValues_left_ne_right_should_be_same actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_RangeBracketValues_left_gt_right_should_be_same_as_left_Start_gt_right_Start(RangeBracketValue left, RangeBracketValue right) { // Arrange @@ -81,7 +74,7 @@ public void Given_left_and_right_RangeBracketValues_left_gt_right_should_be_same actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_RangeBracketValues_left_lte_right_should_be_same_as_left_lt_right_or_left_eq_right(RangeBracketValue left, RangeBracketValue right) { // Arrange @@ -94,7 +87,7 @@ public void Given_left_and_right_RangeBracketValues_left_lte_right_should_be_sam actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_left_and_right_RangeBracketValues_left_gte_right_should_be_same_as_left_gt_right_or_left_eq_right(RangeBracketValue left, RangeBracketValue right) { // Arrange @@ -122,7 +115,7 @@ public void Complexity_should_behave_as_expected(char start, char end, double ex .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_RangeBracketValue_Complexity_should_depends_on_start_and_end(RangeBracketValue bracketValue) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs index f35cf280..17af2a25 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs @@ -1,7 +1,6 @@ namespace DataFilters.UnitTests.Grammar.Syntax { using System; - using System.Collections.Generic; using DataFilters.Grammar.Syntax; using DataFilters.UnitTests.Helpers; using FluentAssertions; @@ -13,15 +12,8 @@ using Xunit.Categories; [UnitTest("StartsWith")] - public class StartsWithExpressionTests + public class StartsWithExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public StartsWithExpressionTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Fact] public void IsFilterExpression() => typeof(StartsWithExpression).Should() .NotBeAbstract().And @@ -34,7 +26,7 @@ public void IsFilterExpression() => typeof(StartsWithExpression).Should() public void Given_string_argument_is_null_Constructor_should_throw_ArgumentNullException() { // Act - Action action = () => new StartsWithExpression((string)null); + Action action = () => _ = new StartsWithExpression((string)null); // Assert action.Should() @@ -45,7 +37,7 @@ public void Given_string_argument_is_null_Constructor_should_throw_ArgumentNullE public void Given_TextExpression_argument_is_null_Constructor_should_throw_ArgumentNullException() { // Act - Action action = () => new StartsWithExpression((TextExpression)null); + Action action = () => _ = new StartsWithExpression((TextExpression)null); // Assert action.Should() @@ -56,7 +48,7 @@ public void Given_TextExpression_argument_is_null_Constructor_should_throw_Argum public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() { // Act - Action action = () => new StartsWithExpression(string.Empty); + Action action = () => _ = new StartsWithExpression(string.Empty); // Assert action.Should() @@ -67,7 +59,7 @@ public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_WhitespaceOnly() { // Act - Action action = () => new StartsWithExpression(" "); + Action action = () => _ = new StartsWithExpression(" "); // Assert action.Should() @@ -76,41 +68,19 @@ public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_Whi .NotThrow("The parameter of the constructor can be whitespace only"); } - public static IEnumerable EqualsCases - { - get - { - yield return new object[] - { - new StartsWithExpression("prop1"), - new StartsWithExpression("prop1"), - true, - "comparing two different instances with same property name" - }; - - yield return new object[] - { - new StartsWithExpression("prop1"), - new StartsWithExpression("prop2"), - false, - "comparing two different instances with different property name" - }; - } - } - - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) { // Arrange @@ -141,7 +111,7 @@ public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_StartsWithExpression_When_right_operand_is_EndsWithExpression_Plus_operator_should_return_expected_AndExpression(NonNull startsWithGen, NonNull endsWithGen) { // Arrange @@ -157,7 +127,7 @@ public void Given_StartsWithExpression_When_right_operand_is_EndsWithExpression_ actual.IsEquivalentTo(expected).Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_StartsWithExpression_When_right_operand_is_StartsWithExpression_Plus_operator_should_return_OneOfExpression(NonNull leftOperandGen, NonNull rightOperandGen) { // Arrange @@ -176,7 +146,7 @@ public void Given_StartsWithExpression_When_right_operand_is_StartsWithExpressio actual.IsEquivalentTo(expected).Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_StartsWithExpression_When_right_operand_is_Contains_Plus_operator_should_return_OneOfExpression(NonNull leftOperandGen, NonNull rightOperandGen) { // Arrange @@ -194,7 +164,7 @@ public void Given_StartsWithExpression_When_right_operand_is_Contains_Plus_opera actual.IsEquivalentTo(expected).Should().BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_StartsWithExpression_When_right_operand_is_StringValueExpression_Plus_operator_should_return_expected_AndExpression(NonNull leftOperandGen, NonNull rightOperandGen) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs index 4e4df7e7..ac91331d 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs @@ -13,12 +13,8 @@ using Xunit.Categories; [UnitTest] - public class StringValueExpressionTests + public class StringValueExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public StringValueExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(StringValueExpression).Should() .BeAssignableTo().And @@ -28,7 +24,7 @@ public void IsFilterExpression() => typeof(StringValueExpression).Should() public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { // Act - Action action = () => new StringValueExpression(null); + Action action = () => _ = new StringValueExpression(null); // Assert action.Should() @@ -39,7 +35,7 @@ public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() { // Act - Action action = () => new StringValueExpression(string.Empty); + Action action = () => _ = new StringValueExpression(string.Empty); // Assert action.Should() @@ -50,7 +46,7 @@ public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() public void Given_parameter_is_whitespace_Ctor_should_not_throw() { // Act - Action action = () => new StringValueExpression(" "); + Action action = () => _ = new StringValueExpression(" "); // Assert action.Should() @@ -97,15 +93,15 @@ public void Equals_should_behave_as_expected(StringValueExpression stringValue, .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(StringValueExpression first, FilterExpression second) => (first.Equals(second) == second.Equals(first)).ToProperty().QuickCheckThrowOnFailure(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Equals(expression.Item).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); @@ -119,10 +115,10 @@ public void Given_two_StringValueExpresssions_Equals_should_depends_on_string_in // Act (first.Equals(second) == Equals(first.Value, second.Value)) .ToProperty() - .QuickCheckThrowOnFailure(_outputHelper); + .QuickCheckThrowOnFailure(outputHelper); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_StringValueExpression_GetComplexity_should_return_1(StringValueExpression constant) => (constant.Complexity == 1).ToProperty(); [Property] diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs index 8f65f324..67db12f9 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs @@ -14,12 +14,8 @@ namespace DataFilters.UnitTests.Grammar.Syntax { [UnitTest] - public class TextExpressionTests + public class TextExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public TextExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Theory] [InlineData(@"<\\Y!A", @"""<\\\\Y!A""")] public void Given_input_EscapedParseableString_should_be_correct(string input, string expected) @@ -53,7 +49,7 @@ public void Equals_to_null_always_returns_false(NonEmptyString input) public void Given_two_instances_with_same_input_Equals_should_return_true(NonWhiteSpaceString input) { // Arrange - _outputHelper.WriteLine($"input : '{input.Item}'"); + outputHelper.WriteLine($"input : '{input.Item}'"); TextExpression first = new(input.Item); TextExpression other = new(input.Item); @@ -65,21 +61,21 @@ public void Given_two_instances_with_same_input_Equals_should_return_true(NonWhi .BeTrue(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty() - .QuickCheckThrowOnFailure(_outputHelper); + .QuickCheckThrowOnFailure(outputHelper); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) => expression.Item.Equals(expression.Item).ToProperty() - .QuickCheckThrowOnFailure(_outputHelper); + .QuickCheckThrowOnFailure(outputHelper); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_non_null_TextExpression_EscapedParseableString_should_be_correct(NonNull text) { // Arrange diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs index 569c096c..f8e8de71 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs @@ -12,12 +12,8 @@ using Xunit.Categories; [UnitTest] - public class TimeExpressionTests + public class TimeExpressionTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public TimeExpressionTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Fact] public void IsFilterExpression() => typeof(TimeExpression).Should() .BeAssignableTo().And @@ -34,10 +30,10 @@ public void Ctor_should_build_valid_instance(IntWithMinMax hours, IntWithMinMax seconds, IntWithMinMax milliseconds) { - _outputHelper.WriteLine($"hours : {hours.Item}"); - _outputHelper.WriteLine($"minutes : {minutes.Item}"); - _outputHelper.WriteLine($"seconds : {seconds.Item}"); - _outputHelper.WriteLine($"milliseconds : {milliseconds.Item}"); + outputHelper.WriteLine($"hours : {hours.Item}"); + outputHelper.WriteLine($"minutes : {minutes.Item}"); + outputHelper.WriteLine($"seconds : {seconds.Item}"); + outputHelper.WriteLine($"milliseconds : {milliseconds.Item}"); // Arrange Lazy timeExpressionBuilder = new(() => new TimeExpression(hours.Item, minutes.Item, @@ -61,23 +57,23 @@ public void Ctor_should_build_valid_instance(IntWithMinMax hours, && timeExpression.Minutes == minutes.Item && timeExpression.Seconds == seconds.Item; }) - .VerboseCheck(_outputHelper); + .VerboseCheck(outputHelper); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_commutative(NonNull first, FilterExpression second) => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty() - .QuickCheckThrowOnFailure(_outputHelper); + .QuickCheckThrowOnFailure(outputHelper); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).ToProperty().QuickCheckThrowOnFailure(_outputHelper); + => expression.Item.Equals(expression.Item).ToProperty().QuickCheckThrowOnFailure(outputHelper); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty().QuickCheckThrowOnFailure(_outputHelper); + => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty().QuickCheckThrowOnFailure(outputHelper); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_non_null_TimeExpression_instance_should_never_be_equal_to_null(TimeExpression instance) { // Act @@ -88,7 +84,7 @@ public void Given_non_null_TimeExpression_instance_should_never_be_equal_to_null .BeFalse(); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void Given_two_TimeExpression_instances_that_have_same_values_should_be_equal(NonNegativeInt hours, NonNegativeInt minutes, NonNegativeInt seconds, NonNegativeInt milliseconds) { // Arrange @@ -126,7 +122,7 @@ public void Given_TimeExpression_instance_EscapedParseableString_should_be_in_ex .Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalent_should_be_commutative(NonNull first, FilterExpression second) { // Act @@ -137,11 +133,11 @@ public void IsEquivalent_should_be_commutative(NonNull first, Fi actual.Should().Be(expected); } - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_reflexive(NonNull expression) => expression.Item.IsEquivalentTo(expression.Item).Should().BeTrue(); - [Property(Arbitrary = new[] { typeof(ExpressionsGenerators) })] + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] public void IsEquivalentTo_should_be_symetric(NonNull expression, NonNull otherExpression) => expression.Item.IsEquivalentTo(otherExpression.Item).Should().Be(otherExpression.Item.IsEquivalentTo(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs b/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs index 2ab4b2bc..83e24745 100644 --- a/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs +++ b/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs @@ -41,7 +41,7 @@ public CultureSwitcher() public void Run(string cultureToUse, Action action) => Run(CultureInfo.CreateSpecificCulture(cultureToUse), action); /// - /// Performs the specified AFTER switching and + /// Performs the specified AFTER switching and /// to the specified . /// /// The culture to use when running the specified . diff --git a/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs b/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs index 28ebbf80..0d3f2999 100644 --- a/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs +++ b/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs @@ -183,7 +183,7 @@ private static Gen SafeGroupExpressionGenerator(int size) public static Arbitrary GenerateFilterExpressions() { Gen[] generators = - { + [ EndsWithExpressions().Generator.Select(item => (FilterExpression) item), StartsWithExpressions().Generator.Select(item => (FilterExpression) item), ContainsExpressions().Generator.Select(item => (FilterExpression) item), @@ -194,7 +194,7 @@ public static Arbitrary GenerateFilterExpressions() DurationExpressions().Generator.Select(item => (FilterExpression) item), ConstantValueExpressions().Generator.Select(item => (FilterExpression) item), GroupExpressions().Generator.Select(item => (FilterExpression) item) - }; + ]; return Gen.OneOf(generators).ToArbitrary(); } @@ -206,10 +206,10 @@ public static Arbitrary GenerateFilterExpressions() public static Arbitrary BinaryFilterExpressions() { Gen[] generators = - { + [ AndExpressions().Generator.Select(item => (BinaryFilterExpression) item), OrExpressions().Generator.Select(item => (BinaryFilterExpression) item) - }; + ]; return Gen.OneOf(generators).ToArbitrary(); } @@ -274,7 +274,7 @@ public static Arbitrary NotExpressions() static Gen SafeNotExpressionGenerator(int size) { Gen[] generators = - { + [ AndExpressions().Generator.Select(expr => (FilterExpression)expr), OrExpressions().Generator.Select(expr => (FilterExpression)expr), ConstantValueExpressions().Generator.Select(expr => (FilterExpression)expr), @@ -283,7 +283,7 @@ static Gen SafeNotExpressionGenerator(int size) TimeExpressions().Generator.Select(expr => (FilterExpression)expr), DateTimeExpressions().Generator.Select(expr => (FilterExpression)expr), IntervalExpressions().Generator.Select(expr => (FilterExpression)expr) - }; + ]; Gen gen; switch (size) diff --git a/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs b/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs index 3e2bfffd..8d0b42c7 100644 --- a/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs +++ b/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs @@ -84,7 +84,7 @@ internal static Arbitrary FiltersOverGenericValues() public static Arbitrary GenerateFilters() { Gen[] generators = - { + [ EndsWithFilter().Generator.Select(filter => (IFilter) filter), StartsWithFilter().Generator.Select(filter => (IFilter) filter), ContainsFilter().Generator.Select(filter => (IFilter) filter), @@ -95,7 +95,7 @@ public static Arbitrary GenerateFilters() LessThanFilter(), LessThanOrEqualFilter(), FiltersOverNumericValues().Generator.Select(filter => (IFilter)filter) - }; + ]; return Gen.OneOf(generators).ToArbitrary(); } @@ -160,6 +160,5 @@ private static Gen LessThanFilter() private static Gen GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator op) => GetArbitraryFor().Generator .Select(value => (IFilter)new Filter(Faker.Hacker.Noun(), op, value)); - } } diff --git a/test/DataFilters.UnitTests/MultiFilterTests.cs b/test/DataFilters.UnitTests/MultiFilterTests.cs index 099bcd93..9bd5ce41 100644 --- a/test/DataFilters.UnitTests/MultiFilterTests.cs +++ b/test/DataFilters.UnitTests/MultiFilterTests.cs @@ -22,12 +22,8 @@ namespace DataFilters.UnitTests using static DataFilters.FilterOperator; [UnitTest] - public class MultiFilterTests + public class MultiFilterTests(ITestOutputHelper output) { - public MultiFilterTests(ITestOutputHelper output) => _output = output; - - private readonly ITestOutputHelper _output; - public class Person { public string Firstname { get; set; } @@ -149,12 +145,12 @@ public static IEnumerable CompositeFilterSchemaTestCases [MemberData(nameof(MultiFilterToJsonCases))] public void MultiFilterToJson(MultiFilter filter, Expression> jsonMatcher) { - _output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); + output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); // Act string json = filter.ToJson(); - _output.WriteLine($"{nameof(json)} : {json}"); + output.WriteLine($"{nameof(json)} : {json}"); // Assert json.Should().Match(jsonMatcher); @@ -289,8 +285,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void CompositeFilterImplementsEquatableProperly(MultiFilter first, object second, bool expectedResult, string reason) { - _output.WriteLine($"first : {first}"); - _output.WriteLine($"second : {second}"); + output.WriteLine($"first : {first}"); + output.WriteLine($"second : {second}"); // Act bool result = first.Equals(second); @@ -304,7 +300,7 @@ public void CompositeFilterImplementsEquatableProperly(MultiFilter first, object [MemberData(nameof(CompositeFilterSchemaTestCases))] public void CompositeFilterSchema(string json, bool expectedValidity) { - _output.WriteLine($"{nameof(json)} : {json}"); + output.WriteLine($"{nameof(json)} : {json}"); // Arrange JSchema schema = MultiFilter.Schema; @@ -316,7 +312,7 @@ public void CompositeFilterSchema(string json, bool expectedValidity) isValid.Should().Be(expectedValidity); } - [Property(Arbitrary = new[] { typeof(FilterGenerators) })] + [Property(Arbitrary = [typeof(FilterGenerators)])] public void Given_filter_instance_Negate_should_work_as_expected(FilterLogic logic, NonEmptyArray source) { // Arrange diff --git a/test/DataFilters.UnitTests/MultiOrderTests.cs b/test/DataFilters.UnitTests/MultiOrderTests.cs index 94833d97..f871d131 100644 --- a/test/DataFilters.UnitTests/MultiOrderTests.cs +++ b/test/DataFilters.UnitTests/MultiOrderTests.cs @@ -7,15 +7,8 @@ using Xunit.Abstractions; using static DataFilters.OrderDirection; - public class MultiOrderTests + public class MultiOrderTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public MultiOrderTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S1199:Nested code blocks should not be used")] public static IEnumerable EqualsCases { @@ -84,8 +77,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void EqualsTests(IOrder first, object second, bool expected, string reason) { - _outputHelper.WriteLine($"{nameof(first)} : '{first}'"); - _outputHelper.WriteLine($"{nameof(second)} : '{second}'"); + outputHelper.WriteLine($"{nameof(first)} : '{first}'"); + outputHelper.WriteLine($"{nameof(second)} : '{second}'"); // Act bool actual = first.Equals(second); diff --git a/test/DataFilters.UnitTests/OrderTests.cs b/test/DataFilters.UnitTests/OrderTests.cs index 28f17221..3fb7a1f5 100644 --- a/test/DataFilters.UnitTests/OrderTests.cs +++ b/test/DataFilters.UnitTests/OrderTests.cs @@ -8,15 +8,8 @@ using Xunit.Abstractions; using static DataFilters.OrderDirection; - public class OrderTests + public class OrderTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public OrderTests(ITestOutputHelper outputHelper) - { - _outputHelper = outputHelper; - } - [Theory] [InlineData("", "Expression is empty")] [InlineData(" ", "Expression is whitespace only")] @@ -24,7 +17,7 @@ public OrderTests(ITestOutputHelper outputHelper) public void Ctor_Throws_ArgumentException_If_Expression_Is_Null(string expression, string reason) { // Act - Action action = () => new Order(expression); + Action action = () => _ = new Order(expression); // Assert action.Should() @@ -68,8 +61,8 @@ public static IEnumerable EqualsCases [MemberData(nameof(EqualsCases))] public void EqualsTests(Order first, object second, bool expected, string reason) { - _outputHelper.WriteLine($"{nameof(first)} : '{first}'"); - _outputHelper.WriteLine($"{nameof(second)} : '{second}'"); + outputHelper.WriteLine($"{nameof(first)} : '{first}'"); + outputHelper.WriteLine($"{nameof(second)} : '{second}'"); // Act bool actual = first.Equals(second); diff --git a/test/DataFilters.UnitTests/StringExtensionsTests.cs b/test/DataFilters.UnitTests/StringExtensionsTests.cs index b419a201..bf96364f 100644 --- a/test/DataFilters.UnitTests/StringExtensionsTests.cs +++ b/test/DataFilters.UnitTests/StringExtensionsTests.cs @@ -9,12 +9,8 @@ using Xunit.Abstractions; using static DataFilters.OrderDirection; - public class StringExtensionsTests + public class StringExtensionsTests(ITestOutputHelper outputHelper) { - private readonly ITestOutputHelper _outputHelper; - - public StringExtensionsTests(ITestOutputHelper outputHelper) => _outputHelper = outputHelper; - [Theory] [InlineData(null, "sort expression cannot be null")] [InlineData(" ", "sort expression cannot be whitespace only")] @@ -100,12 +96,12 @@ public static IEnumerable ToSortCases [MemberData(nameof(ToSortCases))] public void ToSortTests(string sort, IOrder expected) { - _outputHelper.WriteLine($"{nameof(sort)} : '{sort}'"); + outputHelper.WriteLine($"{nameof(sort)} : '{sort}'"); // Act IOrder actual = sort.ToSort(); - _outputHelper.WriteLine($"actual sort : '{actual}'"); + outputHelper.WriteLine($"actual sort : '{actual}'"); // Assert actual.Should() From d3d7eacaf7d03b37b356c649fbc8c205cf94a7ac Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Mon, 29 Jan 2024 15:44:29 +0100 Subject: [PATCH 07/10] chore: disable `NU1608` disable `NU1608` set IDotNetFormat.Verbosity to `diagnostic` --- build/Build.cs | 8 +++++++- core.props | 5 +++-- tests.props | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/build/Build.cs b/build/Build.cs index 00cbccc0..e63966f6 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -12,6 +12,8 @@ namespace DataFilters.ContinuousIntegration using Nuke.Common.CI.GitHubActions; using Nuke.Common.IO; using Nuke.Common.ProjectModel; + using Nuke.Common.Tooling; + using Nuke.Common.Tools.DotNet; [GitHubActions( "integration", @@ -103,7 +105,7 @@ namespace DataFilters.ContinuousIntegration ], PublishArtifacts = true )] - + [DotNetVerbosityMapping] public class Build : NukeBuild, IHaveSolution, IHaveSourceDirectory, @@ -176,8 +178,12 @@ IEnumerable IMutationTest.MutationTestsProjects () => this is ICreateGithubRelease && this.Get()?.GitHubToken is not null) }; + /// bool IDotnetFormat.VerifyNoChanges => IsServerBuild; + /// + Configure IDotnetFormat.FormatSettings => settings => settings.SetVerbosity(DotNetVerbosity.diagnostic); + protected override void OnBuildCreated() { if (IsServerBuild) diff --git a/core.props b/core.props index 44c5f1f1..766f99d9 100644 --- a/core.props +++ b/core.props @@ -5,8 +5,9 @@ git https://github.com/candoumbe/DataFilters $(RepositoryUrl) - true + $(RepositoryUrl) Apache-2.0 + $(NoWarn);NU1608 @@ -17,7 +18,7 @@ true - true + $(RepositoryUrl) true diff --git a/tests.props b/tests.props index 75dbbe76..af626a9e 100644 --- a/tests.props +++ b/tests.props @@ -11,7 +11,7 @@ - + From 1d1e63fc1d6f828111c816958385991d181ae169 Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Mon, 29 Jan 2024 15:55:00 +0100 Subject: [PATCH 08/10] chore: force IDotNetFormat to run restore step --- build/Build.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/Build.cs b/build/Build.cs index e63966f6..46becd50 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -182,7 +182,8 @@ IEnumerable IMutationTest.MutationTestsProjects bool IDotnetFormat.VerifyNoChanges => IsServerBuild; /// - Configure IDotnetFormat.FormatSettings => settings => settings.SetVerbosity(DotNetVerbosity.diagnostic); + Configure IDotnetFormat.FormatSettings => settings => settings.SetVerbosity(DotNetVerbosity.diagnostic) + .SetNoRestore(false); protected override void OnBuildCreated() { From 8e45c7f2d52b840d212991859dfb0c48c92ee34f Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Tue, 30 Jan 2024 09:35:10 +0100 Subject: [PATCH 09/10] breaking: drop `netstandard1.3` support --- .../DataFilters.Queries.csproj | 2 +- src/DataFilters/DataFilters.csproj | 2 +- .../Serialization/MultiFilterConverter.cs | 19 ++++++++----------- .../DataFilters.Expressions.csproj | 2 +- .../FilterServiceTests.cs | 3 +-- .../Grammar/Syntax/DateExpressionTests.cs | 3 +-- .../Grammar/Syntax/NotExpressionTests.cs | 3 +-- .../Syntax/StartsWithExpressionTests.cs | 3 +-- 8 files changed, 15 insertions(+), 22 deletions(-) diff --git a/src/DataFilters.Queries/DataFilters.Queries.csproj b/src/DataFilters.Queries/DataFilters.Queries.csproj index 258bebbc..d0329bb9 100644 --- a/src/DataFilters.Queries/DataFilters.Queries.csproj +++ b/src/DataFilters.Queries/DataFilters.Queries.csproj @@ -1,7 +1,7 @@  - netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 + netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 Provides extension methods to convert IFilter to IWhereClause and IOrder to ISort. bin\$(Configuration)\$(TargetFramework)\DataFilters.Queries.xml diff --git a/src/DataFilters/DataFilters.csproj b/src/DataFilters/DataFilters.csproj index b7268407..3353ce0a 100644 --- a/src/DataFilters/DataFilters.csproj +++ b/src/DataFilters/DataFilters.csproj @@ -2,7 +2,7 @@ - netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 + netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 Sets of classes to convert querystrings to strongly typed expressions. expressions, querystring bin\$(Configuration)\$(TargetFramework)\DataFilters.xml diff --git a/src/DataFilters/Serialization/MultiFilterConverter.cs b/src/DataFilters/Serialization/MultiFilterConverter.cs index 6091b2eb..7e7b7076 100644 --- a/src/DataFilters/Serialization/MultiFilterConverter.cs +++ b/src/DataFilters/Serialization/MultiFilterConverter.cs @@ -107,7 +107,7 @@ public override MultiFilter Read(ref Utf8JsonReader reader, Type typeToConvert, }; - List filters = new (); + List filters = []; if (reader.Read() && (reader.TokenType != JsonTokenType.PropertyName || MultiFilter.FiltersJsonPropertyName != reader.GetString())) { throw new JsonException($@"Expected ""{MultiFilter.FiltersJsonPropertyName}"" property."); @@ -155,16 +155,13 @@ public override MultiFilter Read(ref Utf8JsonReader reader, Type typeToConvert, } reader.Read(); - if (reader.TokenType != JsonTokenType.EndObject) - { - throw new JsonException(@"Expected ""}""."); - } - - return new MultiFilter - { - Logic = logic, - Filters = filters - }; + return reader.TokenType != JsonTokenType.EndObject + ? throw new JsonException(@"Expected ""}"".") + : new MultiFilter + { + Logic = logic, + Filters = filters + }; } #endif /// diff --git a/src/Datafilters.Expressions/DataFilters.Expressions.csproj b/src/Datafilters.Expressions/DataFilters.Expressions.csproj index 692a9e5e..4657b17e 100644 --- a/src/Datafilters.Expressions/DataFilters.Expressions.csproj +++ b/src/Datafilters.Expressions/DataFilters.Expressions.csproj @@ -2,7 +2,7 @@ - netstandard1.3;netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 + netstandard2.0;netstandard2.1;net6.0;net7.0;net8.0 Converts IFilter instance to strongly typed expressions. bin\$(Configuration)\$(TargetFramework)\DataFilters.Expressions.xml diff --git a/test/DataFilters.UnitTests/FilterServiceTests.cs b/test/DataFilters.UnitTests/FilterServiceTests.cs index 795da1dc..7a7ab2e8 100644 --- a/test/DataFilters.UnitTests/FilterServiceTests.cs +++ b/test/DataFilters.UnitTests/FilterServiceTests.cs @@ -9,9 +9,8 @@ namespace DataFilters.UnitTests; using FluentAssertions; using Xunit; -using Xunit.Abstractions; -public class FilterServiceTests(ITestOutputHelper outputHelper) +public class FilterServiceTests { private readonly FilterService _sut = new FilterService(new FilterServiceOptions()); diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs index b379e10e..01587317 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs @@ -8,9 +8,8 @@ using FsCheck.Fluent; using FsCheck.Xunit; using Xunit; - using Xunit.Abstractions; - public class DateExpressionTests(ITestOutputHelper outputHelper) + public class DateExpressionTests { [Fact] public void IsFilterExpression() => typeof(DateExpression).Should() diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs index f0823601..5b64d5c3 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs @@ -9,9 +9,8 @@ using FsCheck.Fluent; using FsCheck.Xunit; using Xunit; - using Xunit.Abstractions; - public class NotExpressionTests(ITestOutputHelper outputHelper) + public class NotExpressionTests { [Fact] public void IsFilterExpression() => typeof(NotExpression).Should() diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs index 17af2a25..7cf596e1 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs @@ -8,11 +8,10 @@ using FsCheck.Fluent; using FsCheck.Xunit; using Xunit; - using Xunit.Abstractions; using Xunit.Categories; [UnitTest("StartsWith")] - public class StartsWithExpressionTests(ITestOutputHelper outputHelper) + public class StartsWithExpressionTests { [Fact] public void IsFilterExpression() => typeof(StartsWithExpression).Should() From 347cf90222a70576ff909f20c1c9004149e5a9ea Mon Sep 17 00:00:00 2001 From: Cyrille-Alexandre NDOUMBE Date: Tue, 30 Jan 2024 10:59:14 +0100 Subject: [PATCH 10/10] chore: use C#12 / net8.0 syntax --- build/Build.cs | 356 ++-- src/DataFilters.Queries/FilterToQueries.cs | 229 ++- src/DataFilters.Queries/OrderExtensions.cs | 69 +- ...CamelCasePropertyNameResolutionStrategy.cs | 21 +- .../DefaultPropertyNameResolutionStrategy.cs | 17 +- ...ascalCasePropertyNameResolutionStrategy.cs | 21 +- .../Casing/PropertyNameResolutionStrategy.cs | 61 +- ...SnakeCasePropertyNameResolutionStrategy.cs | 21 +- src/DataFilters/Filter.cs | 411 ++-- src/DataFilters/FilterLogic.cs | 27 +- src/DataFilters/FilterOperator.cs | 179 +- src/DataFilters/FilterOptions.cs | 163 +- src/DataFilters/FilterService.cs | 77 - src/DataFilters/FilterServiceOptions.cs | 92 - ...lterServiceOptionsInvalidValueException.cs | 33 - .../BoundariesTypeMismatchException.cs | 29 +- .../Exceptions/IncorrectBoundaryException.cs | 53 +- .../Grammar/Parsing/FilterToken.cs | 297 ++- .../Grammar/Parsing/FilterTokenParser.cs | 1509 +++++++------- .../Grammar/Parsing/FilterTokenizer.cs | 535 +++-- .../Grammar/Parsing/TokenizerMode.cs | 25 +- .../Grammar/Syntax/AndExpression.cs | 103 +- .../Grammar/Syntax/AsteriskExpression.cs | 87 +- .../Grammar/Syntax/BoundaryExpression.cs | 101 +- .../Grammar/Syntax/BracketExpression.cs | 157 +- .../Grammar/Syntax/BracketValue.cs | 29 +- .../Grammar/Syntax/ConstantBracketValue.cs | 85 +- .../Grammar/Syntax/ConstantValueExpression.cs | 103 +- .../Grammar/Syntax/ContainsExpression.cs | 163 +- .../Grammar/Syntax/DateExpression.cs | 141 +- .../Grammar/Syntax/DateTimeExpression.cs | 249 ++- .../Grammar/Syntax/DateTimeExpressionKind.cs | 33 +- .../Grammar/Syntax/DurationExpression.cs | 237 ++- .../Grammar/Syntax/EndsWithExpression.cs | 177 +- .../Grammar/Syntax/FilterExpression.cs | 67 +- .../Grammar/Syntax/GroupExpression.cs | 107 +- .../Grammar/Syntax/GuidValueExpression.cs | 25 +- .../Grammar/Syntax/IBoundaryExpression.cs | 13 +- .../Grammar/Syntax/IHaveComplexity.cs | 33 +- .../Grammar/Syntax/IParseableString.cs | 31 +- .../Grammar/Syntax/ISimplifiable.cs | 21 +- .../Grammar/Syntax/IntervalExpression.cs | 347 ++-- .../Grammar/Syntax/NotExpression.cs | 105 +- src/DataFilters/Grammar/Syntax/NumericSign.cs | 25 +- .../Grammar/Syntax/NumericValueExpression.cs | 71 +- .../Grammar/Syntax/OffsetExpression.cs | 179 +- .../Grammar/Syntax/OneOfExpression.cs | 224 +- .../Grammar/Syntax/OrExpression.cs | 93 +- .../Grammar/Syntax/PropertyName.cs | 80 +- .../Grammar/Syntax/RangeBracketValue.cs | 137 +- .../Grammar/Syntax/StartsWithExpression.cs | 272 +-- .../Grammar/Syntax/StringValueExpression.cs | 131 +- .../Grammar/Syntax/TextExpression.cs | 83 +- .../Grammar/Syntax/TimeExpression.cs | 181 +- src/DataFilters/IFIlter.cs | 37 +- src/DataFilters/IFilterService.cs | 22 - src/DataFilters/IOrder.cs | 35 +- .../InvalidOrderExpressionException.cs | 39 +- src/DataFilters/MultiFilter.cs | 183 +- src/DataFilters/MultiOrder.cs | 81 +- src/DataFilters/Order.cs | 87 +- src/DataFilters/OrderDirection.cs | 25 +- src/DataFilters/OrderValidator.cs | 61 +- .../CamelCaseEnumTypeConverter.cs | 23 +- .../Serialization/FilterConverter.cs | 267 ++- .../Serialization/FilterOperatorConverter.cs | 139 +- .../Serialization/MultiFilterConverter.cs | 297 ++- src/DataFilters/StringExtensions.cs | 603 +++--- .../FilterExtensions.cs | 1009 +++++---- .../OrderExpression.cs | 83 +- .../OrderExtensions.cs | 65 +- .../QueryableExtensions.cs | 110 +- .../DataFilters.Expressions.UnitTests/Hero.cs | 51 +- .../QueryableExtensionsTests.cs | 93 +- .../ToOrderClauseTests.cs | 303 +-- .../BracketVsOr.cs | 43 +- test/DataFilters.PerformanceTests/Program.cs | 13 +- .../RawFilterVsFilterService.cs | 63 - .../DataFilters.PerformanceTests/SuperHero.cs | 17 +- .../FilterToQueriesTests.cs | 265 ++- .../OrderToQueriesTests.cs | 97 +- test/DataFilters.TestObjects/Appointment.cs | 15 +- .../DataFilters.TestObjects.csproj | 1 + test/DataFilters.TestObjects/Henchman.cs | 13 +- test/DataFilters.TestObjects/Model.cs | 19 +- test/DataFilters.TestObjects/Person.cs | 31 +- test/DataFilters.TestObjects/SuperHero.cs | 35 +- test/DataFilters.TestObjects/Weapon.cs | 11 +- .../Converters/DataFilterConvertersTests.cs | 419 ++-- .../FilterExtensionsTests.cs | 833 ++++---- .../FilterServiceOptionsTests.cs | 69 - .../FilterServiceTests.cs | 52 - test/DataFilters.UnitTests/FilterTests.cs | 619 +++--- .../Grammar/Parsing/FilterTokenParserTests.cs | 1803 +++++++++-------- .../Grammar/Parsing/FilterTokenizerTests.cs | 681 ++++--- .../Grammar/Syntax/AndExpressionTests.cs | 387 ++-- .../Grammar/Syntax/AsteriskExpressionTests.cs | 143 +- .../Grammar/Syntax/BoundaryExpressionTests.cs | 201 +- .../Grammar/Syntax/BracketExpressionTests.cs | 227 ++- .../Syntax/ConstantBracketValueTests.cs | 125 +- .../Grammar/Syntax/ContainsExpressionTests.cs | 299 ++- .../Grammar/Syntax/DateExpressionTests.cs | 207 +- .../Grammar/Syntax/DateTimeExpressionTests.cs | 409 ++-- .../Grammar/Syntax/DurationExpressionTests.cs | 301 ++- .../Grammar/Syntax/EndsWithExpressionTests.cs | 269 ++- .../Grammar/Syntax/GroupExpressionTests.cs | 369 ++-- .../Grammar/Syntax/IntervalExpressionTests.cs | 727 ++++--- .../Grammar/Syntax/NotExpressionTests.cs | 275 ++- .../Syntax/NumericValueExpressionTests.cs | 221 +- .../Grammar/Syntax/OffsetExpressionTests.cs | 99 +- .../Grammar/Syntax/OneOfExpressionTests.cs | 557 +++-- .../Grammar/Syntax/OrExpressionTests.cs | 589 +++--- .../Syntax/PropertyNameExpressionTests.cs | 143 +- .../Grammar/Syntax/RangeBracketValueTests.cs | 253 ++- .../Syntax/StartsWithExpressionTests.cs | 355 ++-- .../Syntax/StringValueExpressionTests.cs | 249 ++- .../Grammar/Syntax/TextExpressionTests.cs | 153 +- .../Grammar/Syntax/TimeExpressionTests.cs | 279 ++- .../Helpers/CultureSwitcher.cs | 121 +- .../Helpers/ExpressionsGenerators.cs | 749 ++++--- .../Helpers/FilterGenerators.cs | 253 ++- .../DataFilters.UnitTests/MultiFilterTests.cs | 561 +++-- test/DataFilters.UnitTests/MultiOrderTests.cs | 164 +- test/DataFilters.UnitTests/OrderTests.cs | 141 +- .../OrderValidatorTests.cs | 57 +- .../StringExtensionsTests.cs | 275 ++- .../Validators/OrderValidatorTests.cs | 63 +- 127 files changed, 12782 insertions(+), 13266 deletions(-) delete mode 100644 src/DataFilters/FilterService.cs delete mode 100644 src/DataFilters/FilterServiceOptions.cs delete mode 100644 src/DataFilters/FilterServiceOptionsInvalidValueException.cs delete mode 100644 src/DataFilters/IFilterService.cs delete mode 100644 test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs delete mode 100644 test/DataFilters.UnitTests/FilterServiceOptionsTests.cs delete mode 100644 test/DataFilters.UnitTests/FilterServiceTests.cs diff --git a/build/Build.cs b/build/Build.cs index 46becd50..5579e5ae 100644 --- a/build/Build.cs +++ b/build/Build.cs @@ -1,196 +1,194 @@ -namespace DataFilters.ContinuousIntegration +namespace DataFilters.ContinuousIntegration; + +using System; +using System.Collections.Generic; +using System.Linq; +using Candoumbe.Pipelines.Components; +using Candoumbe.Pipelines.Components.Formatting; +using Candoumbe.Pipelines.Components.GitHub; +using Candoumbe.Pipelines.Components.NuGet; +using Candoumbe.Pipelines.Components.Workflows; +using Nuke.Common; +using Nuke.Common.CI.GitHubActions; +using Nuke.Common.IO; +using Nuke.Common.ProjectModel; +using Nuke.Common.Tooling; +using Nuke.Common.Tools.DotNet; + +[GitHubActions( + "integration", + GitHubActionsImage.UbuntuLatest, + FetchDepth = 0, + OnPushBranchesIgnore = [IHaveMainBranch.MainBranchName], + PublishArtifacts = true, + InvokedTargets = [nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(IPack.Pack)], + CacheKeyFiles = ["global.json", "src/**/*.csproj"], + ImportSecrets = + [ + nameof(NugetApiKey), + nameof(IReportCoverage.CodecovToken), + ], + OnPullRequestExcludePaths = + [ + "docs/*", + "README.md", + "CHANGELOG.md", + "LICENSE" + ] +)] +[GitHubActions( + "delivery", + GitHubActionsImage.UbuntuLatest, + FetchDepth = 0, + OnPushBranches = [IHaveMainBranch.MainBranchName, IGitFlow.ReleaseBranch + "/*"], + InvokedTargets = [nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(ICreateGithubRelease.AddGithubRelease)], + EnableGitHubToken = true, + CacheKeyFiles = ["global.json", "src/**/*.csproj"], + PublishArtifacts = true, + ImportSecrets = + [ + nameof(NugetApiKey), + nameof(IReportCoverage.CodecovToken) + ], + OnPullRequestExcludePaths = + [ + "docs/*", + "README.md", + "CHANGELOG.md", + "LICENSE" + ] +)] + +//[GitHubActions("nightly", GitHubActionsImage.UbuntuLatest, +// AutoGenerate = true, +// FetchDepth = 0, +// OnCronSchedule = "0 0 * * *", +// InvokedTargets = new[] { nameof(IUnitTest.Compile), nameof(IMutationTest.MutationTests), nameof(IPushNugetPackages.Pack) }, +// OnPushBranches = new[] { IHaveDevelopBranch.DevelopBranchName }, +// CacheKeyFiles = new[] { +// "src/**/*.csproj", +// "test/**/*.csproj", +// "stryker-config.json", +// "test/**/*/xunit.runner.json" }, +// EnableGitHubToken = true, +// ImportSecrets = new[] +// { +// nameof(NugetApiKey), +// nameof(IReportCoverage.CodecovToken), +// nameof(IMutationTest.StrykerDashboardApiKey) +// }, +// PublishArtifacts = true, +// OnPullRequestExcludePaths = new[] +// { +// "docs/*", +// "README.md", +// "CHANGELOG.md", +// "LICENSE" +// } +//)] +[GitHubActions("nightly-manual", GitHubActionsImage.UbuntuLatest, + AutoGenerate = true, + FetchDepth = 0, + On = [GitHubActionsTrigger.WorkflowDispatch], + InvokedTargets = [nameof(IUnitTest.Compile), nameof(IMutationTest.MutationTests), nameof(IPushNugetPackages.Pack)], + CacheKeyFiles = [ + "src/**/*.csproj", + "test/**/*.csproj", + "stryker-config.json", + "test/**/*/xunit.runner.json"], + EnableGitHubToken = true, + ImportSecrets = + [ + nameof(NugetApiKey), + nameof(IReportCoverage.CodecovToken), + nameof(IMutationTest.StrykerDashboardApiKey) + ], + PublishArtifacts = true +)] +[DotNetVerbosityMapping] +public class Build : NukeBuild, + IHaveSolution, + IHaveSourceDirectory, + IHaveTestDirectory, + IGitFlowWithPullRequest, + IClean, + IRestore, + IDotnetFormat, + ICompile, + IUnitTest, + IMutationTest, + IBenchmark, + IReportCoverage, + IPack, + IPushNugetPackages, + ICreateGithubRelease { - using System; - using System.Collections.Generic; - using System.Linq; - using Candoumbe.Pipelines.Components; - using Candoumbe.Pipelines.Components.Formatting; - using Candoumbe.Pipelines.Components.GitHub; - using Candoumbe.Pipelines.Components.NuGet; - using Candoumbe.Pipelines.Components.Workflows; - using Nuke.Common; - using Nuke.Common.CI.GitHubActions; - using Nuke.Common.IO; - using Nuke.Common.ProjectModel; - using Nuke.Common.Tooling; - using Nuke.Common.Tools.DotNet; - - [GitHubActions( - "integration", - GitHubActionsImage.UbuntuLatest, - FetchDepth = 0, - OnPushBranchesIgnore = [IHaveMainBranch.MainBranchName], - PublishArtifacts = true, - InvokedTargets = [nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(IPack.Pack)], - CacheKeyFiles = ["global.json", "src/**/*.csproj"], - ImportSecrets = - [ - nameof(NugetApiKey), - nameof(IReportCoverage.CodecovToken), - ], - OnPullRequestExcludePaths = - [ - "docs/*", - "README.md", - "CHANGELOG.md", - "LICENSE" - ] - )] - [GitHubActions( - "delivery", - GitHubActionsImage.UbuntuLatest, - FetchDepth = 0, - OnPushBranches = [IHaveMainBranch.MainBranchName, IGitFlow.ReleaseBranch + "/*"], - InvokedTargets = [nameof(IUnitTest.UnitTests), nameof(IPushNugetPackages.Publish), nameof(ICreateGithubRelease.AddGithubRelease)], - EnableGitHubToken = true, - CacheKeyFiles = ["global.json", "src/**/*.csproj"], - PublishArtifacts = true, - ImportSecrets = - [ - nameof(NugetApiKey), - nameof(IReportCoverage.CodecovToken) - ], - OnPullRequestExcludePaths = - [ - "docs/*", - "README.md", - "CHANGELOG.md", - "LICENSE" - ] - )] - - //[GitHubActions("nightly", GitHubActionsImage.UbuntuLatest, - // AutoGenerate = true, - // FetchDepth = 0, - // OnCronSchedule = "0 0 * * *", - // InvokedTargets = new[] { nameof(IUnitTest.Compile), nameof(IMutationTest.MutationTests), nameof(IPushNugetPackages.Pack) }, - // OnPushBranches = new[] { IHaveDevelopBranch.DevelopBranchName }, - // CacheKeyFiles = new[] { - // "src/**/*.csproj", - // "test/**/*.csproj", - // "stryker-config.json", - // "test/**/*/xunit.runner.json" }, - // EnableGitHubToken = true, - // ImportSecrets = new[] - // { - // nameof(NugetApiKey), - // nameof(IReportCoverage.CodecovToken), - // nameof(IMutationTest.StrykerDashboardApiKey) - // }, - // PublishArtifacts = true, - // OnPullRequestExcludePaths = new[] - // { - // "docs/*", - // "README.md", - // "CHANGELOG.md", - // "LICENSE" - // } - //)] - [GitHubActions("nightly-manual", GitHubActionsImage.UbuntuLatest, - AutoGenerate = true, - FetchDepth = 0, - On = [GitHubActionsTrigger.WorkflowDispatch], - InvokedTargets = [nameof(IUnitTest.Compile), nameof(IMutationTest.MutationTests), nameof(IPushNugetPackages.Pack)], - CacheKeyFiles = [ - "src/**/*.csproj", - "test/**/*.csproj", - "stryker-config.json", - "test/**/*/xunit.runner.json"], - EnableGitHubToken = true, - ImportSecrets = - [ - nameof(NugetApiKey), - nameof(IReportCoverage.CodecovToken), - nameof(IMutationTest.StrykerDashboardApiKey) - ], - PublishArtifacts = true - )] - [DotNetVerbosityMapping] - public class Build : NukeBuild, - IHaveSolution, - IHaveSourceDirectory, - IHaveTestDirectory, - IGitFlowWithPullRequest, - IClean, - IRestore, - IDotnetFormat, - ICompile, - IUnitTest, - IMutationTest, - IBenchmark, - IReportCoverage, - IPack, - IPushNugetPackages, - ICreateGithubRelease - { - [Parameter("API key used to publish artifacts to Nuget.org")] - [Secret] - public readonly string NugetApiKey; + [Parameter("API key used to publish artifacts to Nuget.org")] + [Secret] + public readonly string NugetApiKey; - [Solution] - [Required] - public readonly Solution Solution; + [Solution] + [Required] + public readonly Solution Solution; - /// - Solution IHaveSolution.Solution => Solution; + /// + Solution IHaveSolution.Solution => Solution; - /// - public static int Main() => Execute(x => ((ICompile)x).Compile); + /// + public static int Main() => Execute(x => ((ICompile)x).Compile); - /// - IEnumerable IClean.DirectoriesToDelete => this.Get().SourceDirectory.GlobDirectories("**/bin", "**/obj") - .Concat(this.Get().TestDirectory.GlobDirectories("**/bin", "**/obj")); + /// + IEnumerable IClean.DirectoriesToDelete => this.Get().SourceDirectory.GlobDirectories("**/bin", "**/obj") + .Concat(this.Get().TestDirectory.GlobDirectories("**/bin", "**/obj")); - /// - AbsolutePath IHaveSourceDirectory.SourceDirectory => RootDirectory / "src"; + /// + AbsolutePath IHaveSourceDirectory.SourceDirectory => RootDirectory / "src"; - /// - AbsolutePath IHaveTestDirectory.TestDirectory => RootDirectory / "test"; + /// + AbsolutePath IHaveTestDirectory.TestDirectory => RootDirectory / "test"; - /// - IEnumerable IUnitTest.UnitTestsProjects => this.Get().Solution.GetAllProjects("*UnitTests"); + /// + IEnumerable IUnitTest.UnitTestsProjects => this.Get().Solution.GetAllProjects("*UnitTests"); - /// - IEnumerable IMutationTest.MutationTestsProjects - => new[] { "DataFilters", "DataFilters.Expressions", "DataFilters.Queries" } - .Select(projectName => new MutationProjectConfiguration(sourceProject: Solution.AllProjects.Single(csproj => string.Equals(csproj.Name, projectName, StringComparison.InvariantCultureIgnoreCase)), - testProjects: Solution.AllProjects.Where(csproj => string.Equals(csproj.Name, $"{projectName}.UnitTests", StringComparison.InvariantCultureIgnoreCase)), - configurationFile: this.Get().TestDirectory / $"{projectName}.UnitTests" / "stryker-config.json")) - .ToArray(); + /// + IEnumerable IMutationTest.MutationTestsProjects + => new[] { "DataFilters", "DataFilters.Expressions", "DataFilters.Queries" } + .Select(projectName => new MutationProjectConfiguration(sourceProject: Solution.AllProjects.Single(csproj => string.Equals(csproj.Name, projectName, StringComparison.InvariantCultureIgnoreCase)), + testProjects: Solution.AllProjects.Where(csproj => string.Equals(csproj.Name, $"{projectName}.UnitTests", StringComparison.InvariantCultureIgnoreCase)), + configurationFile: this.Get().TestDirectory / $"{projectName}.UnitTests" / "stryker-config.json")) + .ToArray(); - /// - IEnumerable IBenchmark.BenchmarkProjects => this.Get().Solution.GetAllProjects("*.PerfomanceTests"); + /// + IEnumerable IBenchmark.BenchmarkProjects => this.Get().Solution.GetAllProjects("*.PerfomanceTests"); - /// - bool IReportCoverage.ReportToCodeCov => this.Get().CodecovToken is not null; + /// + bool IReportCoverage.ReportToCodeCov => this.Get().CodecovToken is not null; - /// - IEnumerable IPack.PackableProjects => this.Get().SourceDirectory.GlobFiles("**/*.csproj"); + /// + IEnumerable IPack.PackableProjects => this.Get().SourceDirectory.GlobFiles("**/*.csproj"); - /// - IEnumerable IPushNugetPackages.PublishConfigurations => new PushNugetPackageConfiguration[] - { - new NugetPushConfiguration(apiKey: NugetApiKey, - source: new Uri("https://api.nuget.org/v3/index.json"), - () => NugetApiKey is not null), - new GitHubPushNugetConfiguration(githubToken: this.Get().GitHubToken, - source: new Uri("https://nukpg.github.com/"), - () => this is ICreateGithubRelease && this.Get()?.GitHubToken is not null) - }; - - /// - bool IDotnetFormat.VerifyNoChanges => IsServerBuild; - - /// - Configure IDotnetFormat.FormatSettings => settings => settings.SetVerbosity(DotNetVerbosity.diagnostic) - .SetNoRestore(false); - - protected override void OnBuildCreated() + /// + IEnumerable IPushNugetPackages.PublishConfigurations => new PushNugetPackageConfiguration[] + { + new NugetPushConfiguration(apiKey: NugetApiKey, + source: new Uri("https://api.nuget.org/v3/index.json"), + () => NugetApiKey is not null), + new GitHubPushNugetConfiguration(githubToken: this.Get().GitHubToken, + source: new Uri("https://nukpg.github.com/"), + () => this is ICreateGithubRelease && this.Get()?.GitHubToken is not null) + }; + + /// + bool IDotnetFormat.VerifyNoChanges => IsServerBuild; + + /// + Configure IDotnetFormat.FormatSettings => settings => settings.SetVerbosity(DotNetVerbosity.diagnostic); + + protected override void OnBuildCreated() + { + if (IsServerBuild) { - if (IsServerBuild) - { - EnvironmentInfo.SetVariable("DOTNET_ROLL_FORWARD", "LatestMajor"); - } + EnvironmentInfo.SetVariable("DOTNET_ROLL_FORWARD", "LatestMajor"); } } } \ No newline at end of file diff --git a/src/DataFilters.Queries/FilterToQueries.cs b/src/DataFilters.Queries/FilterToQueries.cs index 9ba861d5..35741b28 100644 --- a/src/DataFilters.Queries/FilterToQueries.cs +++ b/src/DataFilters.Queries/FilterToQueries.cs @@ -1,131 +1,130 @@ -namespace DataFilters -{ - using System; - using System.Linq; - using Queries.Core.Parts.Clauses; +namespace DataFilters; + +using System; +using System.Linq; +using Queries.Core.Parts.Clauses; +/// +/// Extensions method type. +/// +public static class FilterToQueries +{ /// - /// Extensions method type. + /// Converts to . /// - public static class FilterToQueries + /// The filter to convert + /// A equivalent of the given . + public static IWhereClause ToWhere(this IFilter filter) { - /// - /// Converts to . - /// - /// The filter to convert - /// A equivalent of the given . - public static IWhereClause ToWhere(this IFilter filter) + IWhereClause clause = null; + + switch (filter) { - IWhereClause clause = null; + case Filter f: + { + ClauseOperator clauseOperator; + object value = f.Value; - switch (filter) - { - case Filter f: + switch (f.Operator) { - ClauseOperator clauseOperator; - object value = f.Value; - - switch (f.Operator) - { - case FilterOperator.EqualTo: - clauseOperator = ClauseOperator.EqualTo; - break; - case FilterOperator.NotEqualTo: - clauseOperator = ClauseOperator.NotEqualTo; - break; - case FilterOperator.IsNull: - clauseOperator = ClauseOperator.IsNull; - break; - case FilterOperator.IsNotNull: - clauseOperator = ClauseOperator.IsNotNull; - break; - case FilterOperator.LessThan: - clauseOperator = ClauseOperator.LessThan; - break; - case FilterOperator.LessThanOrEqualTo: - clauseOperator = ClauseOperator.LessThanOrEqualTo; - break; - case FilterOperator.GreaterThan: - clauseOperator = ClauseOperator.GreaterThan; - break; - case FilterOperator.GreaterThanOrEqual: - clauseOperator = ClauseOperator.GreaterThanOrEqualTo; - break; - case FilterOperator.StartsWith: - case FilterOperator.EndsWith: - case FilterOperator.Contains: - clauseOperator = ClauseOperator.Like; - if (f.Operator == FilterOperator.StartsWith) - { - value = $"{value}%"; - } - else if (f.Operator == FilterOperator.EndsWith) - { - value = $"%{value}"; - } - else - { - value = $"%{value}%"; - } - break; - case FilterOperator.NotStartsWith: - case FilterOperator.NotEndsWith: - case FilterOperator.NotContains: - clauseOperator = ClauseOperator.NotLike; - if (f.Operator == FilterOperator.NotStartsWith) - { - value = $"{value}%"; - } - else if (f.Operator == FilterOperator.NotEndsWith) - { - value = $"%{value}"; - } - else - { - value = $"%{value}%"; - } - break; - default: - throw new NotSupportedException($"Unsupported {f.Operator} operator"); - } + case FilterOperator.EqualTo: + clauseOperator = ClauseOperator.EqualTo; + break; + case FilterOperator.NotEqualTo: + clauseOperator = ClauseOperator.NotEqualTo; + break; + case FilterOperator.IsNull: + clauseOperator = ClauseOperator.IsNull; + break; + case FilterOperator.IsNotNull: + clauseOperator = ClauseOperator.IsNotNull; + break; + case FilterOperator.LessThan: + clauseOperator = ClauseOperator.LessThan; + break; + case FilterOperator.LessThanOrEqualTo: + clauseOperator = ClauseOperator.LessThanOrEqualTo; + break; + case FilterOperator.GreaterThan: + clauseOperator = ClauseOperator.GreaterThan; + break; + case FilterOperator.GreaterThanOrEqual: + clauseOperator = ClauseOperator.GreaterThanOrEqualTo; + break; + case FilterOperator.StartsWith: + case FilterOperator.EndsWith: + case FilterOperator.Contains: + clauseOperator = ClauseOperator.Like; + if (f.Operator == FilterOperator.StartsWith) + { + value = $"{value}%"; + } + else if (f.Operator == FilterOperator.EndsWith) + { + value = $"%{value}"; + } + else + { + value = $"%{value}%"; + } + break; + case FilterOperator.NotStartsWith: + case FilterOperator.NotEndsWith: + case FilterOperator.NotContains: + clauseOperator = ClauseOperator.NotLike; + if (f.Operator == FilterOperator.NotStartsWith) + { + value = $"{value}%"; + } + else if (f.Operator == FilterOperator.NotEndsWith) + { + value = $"%{value}"; + } + else + { + value = $"%{value}%"; + } + break; + default: + throw new NotSupportedException($"Unsupported {f.Operator} operator"); + } - clause = new WhereClause(f.Field.Field(), clauseOperator, value switch - { - bool b => b.Literal(), - DateTime date => date.Literal(), - string s => s.Literal(), - null => null, - long l => l.Literal(), - float floatValue => floatValue.Literal(), - decimal decimalValue => decimalValue.Literal(), - double doubleValue => doubleValue.Literal(), - int intValue => intValue.Literal(), + clause = new WhereClause(f.Field.Field(), clauseOperator, value switch + { + bool b => b.Literal(), + DateTime date => date.Literal(), + string s => s.Literal(), + null => null, + long l => l.Literal(), + float floatValue => floatValue.Literal(), + decimal decimalValue => decimalValue.Literal(), + double doubleValue => doubleValue.Literal(), + int intValue => intValue.Literal(), #if NET6_0_OR_GREATER - DateOnly date => date.Literal(), - TimeOnly time => time.Literal(), + DateOnly date => date.Literal(), + TimeOnly time => time.Literal(), #endif - _ => throw new NotSupportedException($"Unexpected '{value?.GetType().Name}' type when building WhereClause") - } - ); - break; + _ => throw new NotSupportedException($"Unexpected '{value?.GetType().Name}' type when building WhereClause") } + ); + break; + } - case MultiFilter cf: + case MultiFilter cf: + { + clause = new CompositeWhereClause { - clause = new CompositeWhereClause - { - Logic = cf.Logic == FilterLogic.And - ? ClauseLogic.And - : ClauseLogic.Or, - Clauses = cf.Filters.Select(item => item.ToWhere()) - }; - break; - } - default: - throw new NotSupportedException("Unsupported filter type"); - } - - return clause; + Logic = cf.Logic == FilterLogic.And + ? ClauseLogic.And + : ClauseLogic.Or, + Clauses = cf.Filters.Select(item => item.ToWhere()) + }; + break; + } + default: + throw new NotSupportedException("Unsupported filter type"); } + + return clause; } } diff --git a/src/DataFilters.Queries/OrderExtensions.cs b/src/DataFilters.Queries/OrderExtensions.cs index 21ca83de..08bf55c9 100644 --- a/src/DataFilters.Queries/OrderExtensions.cs +++ b/src/DataFilters.Queries/OrderExtensions.cs @@ -1,47 +1,46 @@ -namespace DataFilters -{ - using System; - using System.Collections.Generic; - using Queries.Core.Parts.Sorting; +namespace DataFilters; + +using System; +using System.Collections.Generic; +using Queries.Core.Parts.Sorting; +/// +/// Extensions method for types +/// +public static class OrderExtensions +{ /// - /// Extensions method for types + /// Converts to . /// - public static class OrderExtensions + /// Type of element the sort will be apply to. + /// + /// is neither nor . + public static IEnumerable ToOrder(this IOrder order) { - /// - /// Converts to . - /// - /// Type of element the sort will be apply to. - /// - /// is neither nor . - public static IEnumerable ToOrder(this IOrder order) + static OrderExpression CreateOrderExpressionFromOrder(in Order instance) { - static OrderExpression CreateOrderExpressionFromOrder(in Order instance) - { - return new OrderExpression(instance.Expression.Field(), direction: instance.Direction == OrderDirection.Ascending - ? Queries.Core.Parts.Sorting.OrderDirection.Ascending - : Queries.Core.Parts.Sorting.OrderDirection.Descending); - } + return new OrderExpression(instance.Expression.Field(), direction: instance.Direction == OrderDirection.Ascending + ? Queries.Core.Parts.Sorting.OrderDirection.Ascending + : Queries.Core.Parts.Sorting.OrderDirection.Descending); + } - switch (order) - { - case Order expression: - yield return CreateOrderExpressionFromOrder(expression); - break; - case MultiOrder multisort: + switch (order) + { + case Order expression: + yield return CreateOrderExpressionFromOrder(expression); + break; + case MultiOrder multisort: + { + foreach (Order item in multisort.Orders) { - foreach (Order item in multisort.Orders) - { - yield return CreateOrderExpressionFromOrder(item); - } - - break; + yield return CreateOrderExpressionFromOrder(item); } - default: - throw new NotSupportedException("Unsupported order type"); - } + break; + } + + default: + throw new NotSupportedException("Unsupported order type"); } } } diff --git a/src/DataFilters/Casing/CamelCasePropertyNameResolutionStrategy.cs b/src/DataFilters/Casing/CamelCasePropertyNameResolutionStrategy.cs index fb9e739a..43f38520 100644 --- a/src/DataFilters/Casing/CamelCasePropertyNameResolutionStrategy.cs +++ b/src/DataFilters/Casing/CamelCasePropertyNameResolutionStrategy.cs @@ -1,13 +1,12 @@ -namespace DataFilters.Casing -{ - using System; +namespace DataFilters.Casing; + +using System; - /// - /// that transform input to it camelCase equivalent. - /// - public class CamelCasePropertyNameResolutionStrategy : PropertyNameResolutionStrategy - { - /// - public override string Handle(string name) => name.ToCamelCase(); - } +/// +/// that transform input to it camelCase equivalent. +/// +public class CamelCasePropertyNameResolutionStrategy : PropertyNameResolutionStrategy +{ + /// + public override string Handle(string name) => name.ToCamelCase(); } \ No newline at end of file diff --git a/src/DataFilters/Casing/DefaultPropertyNameResolutionStrategy.cs b/src/DataFilters/Casing/DefaultPropertyNameResolutionStrategy.cs index 4ccbd676..82248545 100644 --- a/src/DataFilters/Casing/DefaultPropertyNameResolutionStrategy.cs +++ b/src/DataFilters/Casing/DefaultPropertyNameResolutionStrategy.cs @@ -1,11 +1,10 @@ -namespace DataFilters.Casing +namespace DataFilters.Casing; + +/// +/// implementation that leaves the name extracted from querystring unchanged. +/// +public class DefaultPropertyNameResolutionStrategy : PropertyNameResolutionStrategy { - /// - /// implementation that leaves the name extracted from querystring unchanged. - /// - public class DefaultPropertyNameResolutionStrategy : PropertyNameResolutionStrategy - { - /// - public override string Handle(string name) => name; - } + /// + public override string Handle(string name) => name; } \ No newline at end of file diff --git a/src/DataFilters/Casing/PascalCasePropertyNameResolutionStrategy.cs b/src/DataFilters/Casing/PascalCasePropertyNameResolutionStrategy.cs index fe9338df..e0fadee9 100644 --- a/src/DataFilters/Casing/PascalCasePropertyNameResolutionStrategy.cs +++ b/src/DataFilters/Casing/PascalCasePropertyNameResolutionStrategy.cs @@ -1,13 +1,12 @@ -namespace DataFilters.Casing -{ - using System; +namespace DataFilters.Casing; + +using System; - /// - /// that transform input to it PascalCase equivalent. - /// - public class PascalCasePropertyNameResolutionStrategy : PropertyNameResolutionStrategy - { - /// - public override string Handle(string name) => name.ToPascalCase(); - } +/// +/// that transform input to it PascalCase equivalent. +/// +public class PascalCasePropertyNameResolutionStrategy : PropertyNameResolutionStrategy +{ + /// + public override string Handle(string name) => name.ToPascalCase(); } \ No newline at end of file diff --git a/src/DataFilters/Casing/PropertyNameResolutionStrategy.cs b/src/DataFilters/Casing/PropertyNameResolutionStrategy.cs index 31578aeb..e072c606 100644 --- a/src/DataFilters/Casing/PropertyNameResolutionStrategy.cs +++ b/src/DataFilters/Casing/PropertyNameResolutionStrategy.cs @@ -1,39 +1,38 @@ -namespace DataFilters.Casing +namespace DataFilters.Casing; + +/// +/// +/// Base class to derive from to alter the way +/// and performs property names lookup. +/// +/// +public abstract class PropertyNameResolutionStrategy { /// - /// - /// Base class to derive from to alter the way - /// and performs property names lookup. - /// + /// A casing strategy that leave the fieldname casing unchanged /// - public abstract class PropertyNameResolutionStrategy - { - /// - /// A casing strategy that leave the fieldname casing unchanged - /// - public readonly static PropertyNameResolutionStrategy Default = new DefaultPropertyNameResolutionStrategy(); + public readonly static PropertyNameResolutionStrategy Default = new DefaultPropertyNameResolutionStrategy(); - /// - /// A resolution strategy that change each name case to conform to camelCasing. - /// - public readonly static PropertyNameResolutionStrategy CamelCase = new CamelCasePropertyNameResolutionStrategy(); + /// + /// A resolution strategy that change each name case to conform to camelCasing. + /// + public readonly static PropertyNameResolutionStrategy CamelCase = new CamelCasePropertyNameResolutionStrategy(); - /// - /// A resolution strategy that change each name case to conform to camelCasing. - /// - public readonly static PropertyNameResolutionStrategy PascalCase = new PascalCasePropertyNameResolutionStrategy(); + /// + /// A resolution strategy that change each name case to conform to camelCasing. + /// + public readonly static PropertyNameResolutionStrategy PascalCase = new PascalCasePropertyNameResolutionStrategy(); - /// - /// A casing strategy that change each fieldName case to conform to snake casing - /// - public readonly static PropertyNameResolutionStrategy SnakeCase = new SnakeCasePropertyNameResolutionStrategy(); + /// + /// A casing strategy that change each fieldName case to conform to snake casing + /// + public readonly static PropertyNameResolutionStrategy SnakeCase = new SnakeCasePropertyNameResolutionStrategy(); - /// - /// Performs the desired transformation. - /// The result of this method will BEFOREbe used instead of name - /// - /// The name of the key in the query string - /// The name to use when looking up for a corresponding property - public abstract string Handle(string name); - } + /// + /// Performs the desired transformation. + /// The result of this method will BEFOREbe used instead of name + /// + /// The name of the key in the query string + /// The name to use when looking up for a corresponding property + public abstract string Handle(string name); } diff --git a/src/DataFilters/Casing/SnakeCasePropertyNameResolutionStrategy.cs b/src/DataFilters/Casing/SnakeCasePropertyNameResolutionStrategy.cs index c8365c67..516273f3 100644 --- a/src/DataFilters/Casing/SnakeCasePropertyNameResolutionStrategy.cs +++ b/src/DataFilters/Casing/SnakeCasePropertyNameResolutionStrategy.cs @@ -1,13 +1,12 @@ -namespace DataFilters.Casing -{ - using System; +namespace DataFilters.Casing; + +using System; - /// - /// that transform input to its snake_case equivalent. - /// - public class SnakeCasePropertyNameResolutionStrategy : PropertyNameResolutionStrategy - { - /// - public override string Handle(string name) => name.ToSnakeCase(); - } +/// +/// that transform input to its snake_case equivalent. +/// +public class SnakeCasePropertyNameResolutionStrategy : PropertyNameResolutionStrategy +{ + /// + public override string Handle(string name) => name.ToSnakeCase(); } \ No newline at end of file diff --git a/src/DataFilters/Filter.cs b/src/DataFilters/Filter.cs index 2f50313d..f5738a59 100644 --- a/src/DataFilters/Filter.cs +++ b/src/DataFilters/Filter.cs @@ -1,247 +1,246 @@ -namespace DataFilters -{ - using System; - using System.Collections.Generic; - using System.Text.RegularExpressions; - using DataFilters.Converters; - using Newtonsoft.Json; - using Newtonsoft.Json.Schema; - using static Newtonsoft.Json.DefaultValueHandling; - using static Newtonsoft.Json.Required; +namespace DataFilters; + +using System; +using System.Collections.Generic; +using System.Text.RegularExpressions; +using DataFilters.Converters; +using Newtonsoft.Json; +using Newtonsoft.Json.Schema; +using static Newtonsoft.Json.DefaultValueHandling; +using static Newtonsoft.Json.Required; #if !NETSTANDARD1_3 - using System.Text.Json.Serialization; +using System.Text.Json.Serialization; #endif - /// - /// An instance of this class holds a filter - /// +/// +/// An instance of this class holds a filter +/// #if NETSTANDARD1_3 - [JsonObject] - [JsonConverter(typeof(FilterConverter))] +[JsonObject] +[JsonConverter(typeof(FilterConverter))] #else - [System.Text.Json.Serialization.JsonConverter(typeof(FilterConverter))] +[System.Text.Json.Serialization.JsonConverter(typeof(FilterConverter))] #endif - public sealed class Filter : IFilter, IEquatable - { - /// - /// Filter that always returns true - /// - public static Filter True => new(default, default); - - /// - /// Pattern that field name should respect. - /// - /// #lang : regex - public const string ValidFieldNamePattern = @"[a-zA-Z_]+((\[""[a-zA-Z0-9_]+""]|(\.[a-zA-Z0-9_]+))*)"; - - /// - /// Regular expression used to validate - /// - /// - public static readonly Regex ValidFieldNameRegex = new(ValidFieldNamePattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); - - /// - /// Name of the json property that holds the field name - /// - public const string FieldJsonPropertyName = "field"; - - /// - /// Name of the json property that holds the operator - /// - public const string OperatorJsonPropertyName = "op"; - - /// - /// Name of the json property that holds the value - /// - public const string ValueJsonPropertyName = "value"; - - /// - /// s that required to be null. - /// - public static ISet UnaryOperators { get; } = new HashSet{ - FilterOperator.IsEmpty, - FilterOperator.IsNotEmpty, - FilterOperator.IsNotNull, - FilterOperator.IsNull - }; +public sealed class Filter : IFilter, IEquatable +{ + /// + /// Filter that always returns true + /// + public static Filter True => new(default, default); - /// - /// Generates the for the specified . - /// - /// - /// - public static JSchema Schema(FilterOperator op) + /// + /// Pattern that field name should respect. + /// + /// #lang : regex + public const string ValidFieldNamePattern = @"[a-zA-Z_]+((\[""[a-zA-Z0-9_]+""]|(\.[a-zA-Z0-9_]+))*)"; + + /// + /// Regular expression used to validate + /// + /// + public static readonly Regex ValidFieldNameRegex = new(ValidFieldNamePattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + + /// + /// Name of the json property that holds the field name + /// + public const string FieldJsonPropertyName = "field"; + + /// + /// Name of the json property that holds the operator + /// + public const string OperatorJsonPropertyName = "op"; + + /// + /// Name of the json property that holds the value + /// + public const string ValueJsonPropertyName = "value"; + + /// + /// s that required to be null. + /// + public static ISet UnaryOperators { get; } = new HashSet{ + FilterOperator.IsEmpty, + FilterOperator.IsNotEmpty, + FilterOperator.IsNotNull, + FilterOperator.IsNull + }; + + /// + /// Generates the for the specified . + /// + /// + /// + public static JSchema Schema(FilterOperator op) + { + JSchema schema = op switch { - JSchema schema = op switch + FilterOperator.Contains or FilterOperator.StartsWith or FilterOperator.EndsWith => new JSchema { - FilterOperator.Contains or FilterOperator.StartsWith or FilterOperator.EndsWith => new JSchema - { - Type = JSchemaType.Object, - Properties = - { - [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String }, - [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }, - [ValueJsonPropertyName] = new JSchema { Type = JSchemaType.String } - }, - Required = { FieldJsonPropertyName, OperatorJsonPropertyName } - }, - FilterOperator.IsEmpty or FilterOperator.IsNotEmpty or FilterOperator.IsNotNull or FilterOperator.IsNull => new JSchema - { - Type = JSchemaType.Object, - Properties = - { - [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String }, - [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String } - }, - Required = { FieldJsonPropertyName, OperatorJsonPropertyName } - }, - _ => new JSchema - { - Type = JSchemaType.Object, - Properties = - { - [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String, }, - [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }, - [ValueJsonPropertyName] = new JSchema { - Not = new JSchema() { Type = JSchemaType.Null } - } - }, - Required = { FieldJsonPropertyName, OperatorJsonPropertyName, ValueJsonPropertyName } - }, - }; - schema.AllowAdditionalProperties = false; - - return schema; - } + Type = JSchemaType.Object, + Properties = + { + [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String }, + [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }, + [ValueJsonPropertyName] = new JSchema { Type = JSchemaType.String } + }, + Required = { FieldJsonPropertyName, OperatorJsonPropertyName } + }, + FilterOperator.IsEmpty or FilterOperator.IsNotEmpty or FilterOperator.IsNotNull or FilterOperator.IsNull => new JSchema + { + Type = JSchemaType.Object, + Properties = + { + [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String }, + [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String } + }, + Required = { FieldJsonPropertyName, OperatorJsonPropertyName } + }, + _ => new JSchema + { + Type = JSchemaType.Object, + Properties = + { + [FieldJsonPropertyName] = new JSchema { Type = JSchemaType.String, }, + [OperatorJsonPropertyName] = new JSchema { Type = JSchemaType.String }, + [ValueJsonPropertyName] = new JSchema { + Not = new JSchema() { Type = JSchemaType.Null } + } + }, + Required = { FieldJsonPropertyName, OperatorJsonPropertyName, ValueJsonPropertyName } + }, + }; + schema.AllowAdditionalProperties = false; + + return schema; + } - /// - /// Name of the field the filter will be applied to - /// + /// + /// Name of the field the filter will be applied to + /// #if NETSTANDARD1_3 - [JsonProperty(FieldJsonPropertyName, Required = Always)] + [JsonProperty(FieldJsonPropertyName, Required = Always)] #else - [JsonPropertyName(FieldJsonPropertyName)] + [JsonPropertyName(FieldJsonPropertyName)] #endif - public string Field { get; } + public string Field { get; } - /// - /// Operator to apply to the filter - /// + /// + /// Operator to apply to the filter + /// #if NETSTANDARD1_3 - [JsonProperty(OperatorJsonPropertyName, Required = Always)] - [JsonConverter(typeof(CamelCaseEnumTypeConverter))] + [JsonProperty(OperatorJsonPropertyName, Required = Always)] + [JsonConverter(typeof(CamelCaseEnumTypeConverter))] #else - [JsonPropertyName(OperatorJsonPropertyName)] + [JsonPropertyName(OperatorJsonPropertyName)] #endif - public FilterOperator Operator { get; } + public FilterOperator Operator { get; } - /// - /// Value of the filter - /// + /// + /// Value of the filter + /// #if NETSTANDARD1_3 - [JsonProperty(ValueJsonPropertyName, - Required = AllowNull, - DefaultValueHandling = IgnoreAndPopulate, - NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty(ValueJsonPropertyName, + Required = AllowNull, + DefaultValueHandling = IgnoreAndPopulate, + NullValueHandling = NullValueHandling.Ignore)] #else - [JsonPropertyName(ValueJsonPropertyName)] + [JsonPropertyName(ValueJsonPropertyName)] #endif - public object Value { get; } - - /// - /// Builds a new instance. - /// - /// name of the field - /// to apply - /// value of the filter - /// does not conform with - public Filter(string field, FilterOperator @operator, object value = null) - { - if (!string.IsNullOrEmpty(field) && !ValidFieldNameRegex.IsMatch(field)) - { - throw new ArgumentOutOfRangeException(nameof(field), field, $"field name is not valid ({ValidFieldNamePattern})."); - } + public object Value { get; } - Field = field; - switch (@operator) - { - case FilterOperator.EqualTo when value is null: - Operator = FilterOperator.IsNull; - break; - case FilterOperator.NotEqualTo when value is null: - Operator = FilterOperator.IsNotNull; - break; - default: - Operator = @operator; - Value = value; - break; - } + /// + /// Builds a new instance. + /// + /// name of the field + /// to apply + /// value of the filter + /// does not conform with + public Filter(string field, FilterOperator @operator, object value = null) + { + if (!string.IsNullOrEmpty(field) && !ValidFieldNameRegex.IsMatch(field)) + { + throw new ArgumentOutOfRangeException(nameof(field), field, $"field name is not valid ({ValidFieldNamePattern})."); } - /// -#if NETSTANDARD1_3 - public string ToJson() + Field = field; + switch (@operator) { - return this.Jsonify(new JsonSerializerSettings()); + case FilterOperator.EqualTo when value is null: + Operator = FilterOperator.IsNull; + break; + case FilterOperator.NotEqualTo when value is null: + Operator = FilterOperator.IsNotNull; + break; + default: + Operator = @operator; + Value = value; + break; } + } + + /// +#if NETSTANDARD1_3 + public string ToJson() + { + return this.Jsonify(new JsonSerializerSettings()); + } #else - public string ToJson() => this.Jsonify(); + public string ToJson() => this.Jsonify(); #endif - /// - public override string ToString() => ToJson(); + /// + public override string ToString() => ToJson(); - /// - public bool Equals(Filter other) - => other != null - && (ReferenceEquals(other, this) - || (Equals(other.Field, Field) && Equals(other.Operator, Operator) && Equals(other.Value, Value))); + /// + public bool Equals(Filter other) + => other != null + && (ReferenceEquals(other, this) + || (Equals(other.Field, Field) && Equals(other.Operator, Operator) && Equals(other.Value, Value))); - /// - public override bool Equals(object obj) => Equals(obj as Filter); + /// + public override bool Equals(object obj) => Equals(obj as Filter); - /// - public bool Equals(IFilter other) => Equals(other as Filter); + /// + public bool Equals(IFilter other) => Equals(other as Filter); - /// + /// #if NETSTANDARD1_3 || NETSTANDARD2_0 - public override int GetHashCode() => (Field, Operator, Value).GetHashCode(); + public override int GetHashCode() => (Field, Operator, Value).GetHashCode(); #else - public override int GetHashCode() => HashCode.Combine(Field, Operator, Value); + public override int GetHashCode() => HashCode.Combine(Field, Operator, Value); #endif - /// - public IFilter Negate() + /// + public IFilter Negate() + { + FilterOperator @operator = Operator switch { - FilterOperator @operator = Operator switch - { - FilterOperator.EqualTo => FilterOperator.NotEqualTo, - FilterOperator.NotEqualTo => FilterOperator.EqualTo, - FilterOperator.IsNull => FilterOperator.IsNotNull, - FilterOperator.IsNotNull => FilterOperator.IsNull, - FilterOperator.LessThan => FilterOperator.GreaterThan, - FilterOperator.GreaterThan => FilterOperator.LessThan, - FilterOperator.GreaterThanOrEqual => FilterOperator.LessThanOrEqualTo, - FilterOperator.StartsWith => FilterOperator.NotStartsWith, - FilterOperator.NotStartsWith => FilterOperator.StartsWith, - FilterOperator.EndsWith => FilterOperator.NotEndsWith, - FilterOperator.NotEndsWith => FilterOperator.EndsWith, - FilterOperator.Contains => FilterOperator.NotContains, - FilterOperator.NotContains => FilterOperator.Contains, - FilterOperator.IsEmpty => FilterOperator.IsNotEmpty, - FilterOperator.IsNotEmpty => FilterOperator.IsEmpty, - FilterOperator.LessThanOrEqualTo => FilterOperator.GreaterThanOrEqual, - _ => throw new NotSupportedException("Unknown operator"), - }; - return new Filter(Field, @operator, Value); - } + FilterOperator.EqualTo => FilterOperator.NotEqualTo, + FilterOperator.NotEqualTo => FilterOperator.EqualTo, + FilterOperator.IsNull => FilterOperator.IsNotNull, + FilterOperator.IsNotNull => FilterOperator.IsNull, + FilterOperator.LessThan => FilterOperator.GreaterThan, + FilterOperator.GreaterThan => FilterOperator.LessThan, + FilterOperator.GreaterThanOrEqual => FilterOperator.LessThanOrEqualTo, + FilterOperator.StartsWith => FilterOperator.NotStartsWith, + FilterOperator.NotStartsWith => FilterOperator.StartsWith, + FilterOperator.EndsWith => FilterOperator.NotEndsWith, + FilterOperator.NotEndsWith => FilterOperator.EndsWith, + FilterOperator.Contains => FilterOperator.NotContains, + FilterOperator.NotContains => FilterOperator.Contains, + FilterOperator.IsEmpty => FilterOperator.IsNotEmpty, + FilterOperator.IsNotEmpty => FilterOperator.IsEmpty, + FilterOperator.LessThanOrEqualTo => FilterOperator.GreaterThanOrEqual, + _ => throw new NotSupportedException("Unknown operator"), + }; + return new Filter(Field, @operator, Value); + } - /// - public void Deconstruct(out string field, out FilterOperator @operator, out object value) - { - field = Field; - @operator = Operator; - value = Value; - } + /// + public void Deconstruct(out string field, out FilterOperator @operator, out object value) + { + field = Field; + @operator = Operator; + value = Value; } } diff --git a/src/DataFilters/FilterLogic.cs b/src/DataFilters/FilterLogic.cs index 943d65cb..14127fc5 100644 --- a/src/DataFilters/FilterLogic.cs +++ b/src/DataFilters/FilterLogic.cs @@ -1,19 +1,18 @@ -namespace DataFilters +namespace DataFilters; + +/// +/// Logic that can be apply when combining several s together. +/// +/// +public enum FilterLogic { /// - /// Logic that can be apply when combining several s together. + /// Logical AND operator will be applied to all /// - /// - public enum FilterLogic - { - /// - /// Logical AND operator will be applied to all - /// - And, + And, - /// - /// Logicial OR operatior will be applied - /// - Or - } + /// + /// Logicial OR operatior will be applied + /// + Or } diff --git a/src/DataFilters/FilterOperator.cs b/src/DataFilters/FilterOperator.cs index 20f930a9..2dfa22fd 100644 --- a/src/DataFilters/FilterOperator.cs +++ b/src/DataFilters/FilterOperator.cs @@ -1,96 +1,95 @@ -namespace DataFilters -{ - using Converters; +namespace DataFilters; + +using Converters; #if NETSTANDARD1_3 - using Newtonsoft.Json; +using Newtonsoft.Json; #else - using System.Text.Json.Serialization; +using System.Text.Json.Serialization; #endif +/// +/// Operators that can be used when building instances. +/// +[JsonConverter(typeof(FilterOperatorConverter))] +public enum FilterOperator : short +{ /// - /// Operators that can be used when building instances. - /// - [JsonConverter(typeof(FilterOperatorConverter))] - public enum FilterOperator : short - { - /// - /// = operator - /// - EqualTo, - - /// - /// != operator - /// - NotEqualTo, - - /// - /// is null operator - /// - IsNull, - - /// - /// is not null operator - /// - IsNotNull, - - /// - /// < operator - /// - LessThan, - - /// - /// > operator - /// - GreaterThan, - - /// - /// >= operator - /// - GreaterThanOrEqual, - - /// - /// Applies only to string - /// - StartsWith, - - /// - /// Applies only to string - /// - NotStartsWith, - - /// - /// Applies only to string - /// - EndsWith, - - /// - /// Applies only to string - /// - NotEndsWith, - - /// - /// "Contains" operator - /// - Contains, - - /// - /// opposite of operator - /// - NotContains, - - /// - /// isempty operator - /// - IsEmpty, - - /// - /// Opposite of operator - /// - IsNotEmpty, - - /// - /// <= operator - /// - LessThanOrEqualTo - } + /// = operator + /// + EqualTo, + + /// + /// != operator + /// + NotEqualTo, + + /// + /// is null operator + /// + IsNull, + + /// + /// is not null operator + /// + IsNotNull, + + /// + /// < operator + /// + LessThan, + + /// + /// > operator + /// + GreaterThan, + + /// + /// >= operator + /// + GreaterThanOrEqual, + + /// + /// Applies only to string + /// + StartsWith, + + /// + /// Applies only to string + /// + NotStartsWith, + + /// + /// Applies only to string + /// + EndsWith, + + /// + /// Applies only to string + /// + NotEndsWith, + + /// + /// "Contains" operator + /// + Contains, + + /// + /// opposite of operator + /// + NotContains, + + /// + /// isempty operator + /// + IsEmpty, + + /// + /// Opposite of operator + /// + IsNotEmpty, + + /// + /// <= operator + /// + LessThanOrEqualTo } diff --git a/src/DataFilters/FilterOptions.cs b/src/DataFilters/FilterOptions.cs index dd4116e8..ca402021 100644 --- a/src/DataFilters/FilterOptions.cs +++ b/src/DataFilters/FilterOptions.cs @@ -1,98 +1,97 @@ using DataFilters.Casing; -namespace DataFilters -{ - /// - /// Allow to customize the computation of an instance. - /// +namespace DataFilters; + +/// +/// Allow to customize the computation of an instance. +/// #if NET6_0_OR_GREATER - public record FilterOptions +public record FilterOptions #else - public class FilterOptions +public class FilterOptions #endif - { - private PropertyNameResolutionStrategy _propertyNameResolutionStrategy; +{ + private PropertyNameResolutionStrategy _propertyNameResolutionStrategy; - /// - /// Gets or sets the default to use when matching property names whilst - /// computing instances. - /// - public PropertyNameResolutionStrategy DefaultPropertyNameResolutionStrategy - { - get => _propertyNameResolutionStrategy; + /// + /// Gets or sets the default to use when matching property names whilst + /// computing instances. + /// + public PropertyNameResolutionStrategy DefaultPropertyNameResolutionStrategy + { + get => _propertyNameResolutionStrategy; #if !NET6_0_OR_GREATER - set + set #else - init + init #endif - => _propertyNameResolutionStrategy = value ?? PropertyNameResolutionStrategy.Default; - } + => _propertyNameResolutionStrategy = value ?? PropertyNameResolutionStrategy.Default; + } - /// - /// to apply when converting a query that could result in a instance. - /// - /// This will only apply when the logic cannot be determined by the query from which the computation itself - /// - /// - /// FilterOptions options = new() { Logic = FilterLogic.And }; - /// string query = "Nickname=bat&Name=Grayson"; - /// IFilter filter = query.ToFilter<T>(options); - /// - /// - /// will result in a that is equivalent to - /// - /// MultiFilter multifilter = new () - /// { - /// Logic = FilterLogic.And, - /// Filters = new IFilter[] - /// { - /// new Filter("Nickname", EqualTo, "bat"), - /// new Filter("Name", EqualTo, "Grayson"), - /// } - /// } - /// - /// whereas - /// - /// - /// FilterOptions options = new() { Logic = FilterLogic.Or }; - /// string query = "Nickname=bat&Name=Grayson"; - /// IFilter filter = query.ToFilter<T>(options); - /// - /// - /// will result in a that is equivalent to - /// - /// MultiFilter multifilter = new () - /// { - /// Logic = FilterLogic.Or, - /// Filters = new IFilter[] - /// { - /// new Filter("Nickname", EqualTo, "bat"), - /// new Filter("Name", EqualTo, "Grayson"), - /// } - /// } - /// - /// - /// - /// - /// is by default. - /// - public FilterLogic Logic - { - get; + /// + /// to apply when converting a query that could result in a instance. + /// + /// This will only apply when the logic cannot be determined by the query from which the computation itself + /// + /// + /// FilterOptions options = new() { Logic = FilterLogic.And }; + /// string query = "Nickname=bat&Name=Grayson"; + /// IFilter filter = query.ToFilter<T>(options); + /// + /// + /// will result in a that is equivalent to + /// + /// MultiFilter multifilter = new () + /// { + /// Logic = FilterLogic.And, + /// Filters = new IFilter[] + /// { + /// new Filter("Nickname", EqualTo, "bat"), + /// new Filter("Name", EqualTo, "Grayson"), + /// } + /// } + /// + /// whereas + /// + /// + /// FilterOptions options = new() { Logic = FilterLogic.Or }; + /// string query = "Nickname=bat&Name=Grayson"; + /// IFilter filter = query.ToFilter<T>(options); + /// + /// + /// will result in a that is equivalent to + /// + /// MultiFilter multifilter = new () + /// { + /// Logic = FilterLogic.Or, + /// Filters = new IFilter[] + /// { + /// new Filter("Nickname", EqualTo, "bat"), + /// new Filter("Name", EqualTo, "Grayson"), + /// } + /// } + /// + /// + /// + /// + /// is by default. + /// + public FilterLogic Logic + { + get; #if !NET6_0_OR_GREATER - set; + set; #else - init; + init; #endif - } + } - /// - /// Builds a new instance. - /// - public FilterOptions() - { - Logic = FilterLogic.And; - DefaultPropertyNameResolutionStrategy = PropertyNameResolutionStrategy.Default; - } + /// + /// Builds a new instance. + /// + public FilterOptions() + { + Logic = FilterLogic.And; + DefaultPropertyNameResolutionStrategy = PropertyNameResolutionStrategy.Default; } } diff --git a/src/DataFilters/FilterService.cs b/src/DataFilters/FilterService.cs deleted file mode 100644 index 9a8a300c..00000000 --- a/src/DataFilters/FilterService.cs +++ /dev/null @@ -1,77 +0,0 @@ -#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER - -using DataFilters.Casing; - -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; - -namespace DataFilters; - -/// -/// -/// implementation with a local L.R.U cache. -/// This service can be used wherever you need to build an instance for a given input. -/// -/// -/// Replace the manual mapping of an input to -/// string input = "Nickname=*man&Town=Gotham" -/// (PropertyName propName, FilterExpression)[] parseResults = FilterTokenParser.Parse(input) -/// IList<IFilter> filters = new List<IFilter>(parseResults.Length); -/// -/// -/// -[Obsolete("This class will be removed in 0.12.0. Use the nuget package DataFilters.AspNetCore instead")] -public class FilterService : IFilterService -{ - private readonly FilterServiceOptions _options; - private readonly ConcurrentDictionary _cache; - - /// - /// Builds a new - /// - /// - /// is null. - public FilterService(FilterServiceOptions options) - { - if (options is null) - { - throw new ArgumentNullException(nameof(options)); - } - _options = options; - _cache = new(); - } - - /// - public IFilter Compute(string input) - { - string key = $"{typeof(T).FullName}_{input}"; - IFilter filter; - if (!_cache.TryGetValue(key, out (long TimeStamp, IFilter Filter) value)) - { - if ( _cache.Count == _options.MaxCacheSize) - { -#if NET6_0_OR_GREATER - string oldestKey = _cache.MinBy(entry => entry.Value.Timestamp).Key; -#else - string oldestKey = _cache.OrderBy(entry => entry.Value.Timestamp).First().Key; -#endif - - _cache.TryRemove(oldestKey, out _); - } - filter = input.ToFilter(_options.PropertyNameResolutionStrategy); - _cache.TryAdd(key, (DateTime.UtcNow.Ticks, filter)); - } - else - { - _cache.TryUpdate(key, (DateTime.UtcNow.Ticks, value.Filter), value); - filter = value.Filter; - } - - return filter; - } -} -#endif \ No newline at end of file diff --git a/src/DataFilters/FilterServiceOptions.cs b/src/DataFilters/FilterServiceOptions.cs deleted file mode 100644 index a7b4ddb4..00000000 --- a/src/DataFilters/FilterServiceOptions.cs +++ /dev/null @@ -1,92 +0,0 @@ -#if NETSTANDARD2_0_OR_GREATER || NET -using DataFilters.Casing; - -using System; -using System.Runtime.Serialization; -using Ardalis.GuardClauses; - -namespace DataFilters; - -/// -/// allows to customize the behavior of -/// -public class FilterServiceOptions -{ - public const int DefaultCacheSize = 1_000; - - /// - /// Defines how the IDataFilterService implementation will handle property names. - /// - /// - /// The default value is - /// - public PropertyNameResolutionStrategy PropertyNameResolutionStrategy - { - get => _strategy; -#if NET5_0_OR_GREATER - init => _strategy = value ?? PropertyNameResolutionStrategy.Default; -#else - set => _strategy = value ?? PropertyNameResolutionStrategy.Default; -#endif - } - - private PropertyNameResolutionStrategy _strategy; - - /// - /// Defines the number of elements to keep in the local cache - /// -#if NET5_0_OR_GREATER - public int MaxCacheSize { get; init; } -#else - public int MaxCacheSize { get; set; } -#endif - - /// - /// Builds a new instance with as size - /// and . - /// - public FilterServiceOptions() : this(DefaultCacheSize, PropertyNameResolutionStrategy.Default) - {} - - /// - /// Builds a new instance specifies - /// and used - /// - /// defines how many items the cache can contain at most - /// defines how the will behave when comparing - public FilterServiceOptions(int maxCacheSize, PropertyNameResolutionStrategy strategy) - { - MaxCacheSize = Guard.Against.OutOfRange(maxCacheSize, nameof(maxCacheSize), rangeFrom: 1, rangeTo: int.MaxValue); - PropertyNameResolutionStrategy = strategy ?? throw new ArgumentNullException(nameof(strategy)); - } - - /// - /// Validates all properties - /// - /// - /// This method should be called right after setting all properties. - /// - /// - /// The last line will throw a because must be a positive integer - /// - /// FilterServiceOptions options = new () - /// { - /// MaxCacheSize = -3, - /// Strategy = PropertyNameResolutionStrategy.CamelCase - /// }; - /// - /// options.Validate(); - /// - /// - /// - /// - /// when 's value is negative or zero - public void Validate() - { - if (MaxCacheSize < 1) - { - throw new FilterServiceOptionsInvalidValueException($"{MaxCacheSize} is not a valid value for {nameof(MaxCacheSize)}"); - } - } -} -#endif \ No newline at end of file diff --git a/src/DataFilters/FilterServiceOptionsInvalidValueException.cs b/src/DataFilters/FilterServiceOptionsInvalidValueException.cs deleted file mode 100644 index 8b7619d7..00000000 --- a/src/DataFilters/FilterServiceOptionsInvalidValueException.cs +++ /dev/null @@ -1,33 +0,0 @@ -#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER -using System.Runtime.Serialization; -using System; - -namespace DataFilters; - -/// -/// Exception thrown whenever a property of is set with a invalid value. -/// -[Serializable] -public class FilterServiceOptionsInvalidValueException : Exception -{ - /// - public FilterServiceOptionsInvalidValueException() - { - } - - /// - public FilterServiceOptionsInvalidValueException(string message) : base(message) - { - } - - /// - public FilterServiceOptionsInvalidValueException(string message, Exception innerException) : base(message, innerException) - { - } - - /// - protected FilterServiceOptionsInvalidValueException(SerializationInfo info, StreamingContext context) : base(info, context) - { - } -} -#endif \ No newline at end of file diff --git a/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs b/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs index 565556c0..c4174b12 100644 --- a/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs +++ b/src/DataFilters/Grammar/Exceptions/BoundariesTypeMismatchException.cs @@ -1,19 +1,18 @@ -namespace DataFilters.Grammar.Exceptions -{ - using System; +namespace DataFilters.Grammar.Exceptions; + +using System; - /// - /// Exception thrown when a has incorrect - /// - /// - /// Creates a new instance - /// - /// message of the exception - /// name of the argument which causes the exception to be thrown +/// +/// Exception thrown when a has incorrect +/// +/// +/// Creates a new instance +/// +/// message of the exception +/// name of the argument which causes the exception to be thrown #pragma warning disable RCS1194 // Implement exception constructors. - [Serializable] - public sealed class BoundariesTypeMismatchException(string message, string paramName) : ArgumentException(message, paramName) +[Serializable] +public sealed class BoundariesTypeMismatchException(string message, string paramName) : ArgumentException(message, paramName) #pragma warning restore RCS1194 // Implement exception constructors. - { - } +{ } diff --git a/src/DataFilters/Grammar/Exceptions/IncorrectBoundaryException.cs b/src/DataFilters/Grammar/Exceptions/IncorrectBoundaryException.cs index cbf0baa4..b0f2ad53 100644 --- a/src/DataFilters/Grammar/Exceptions/IncorrectBoundaryException.cs +++ b/src/DataFilters/Grammar/Exceptions/IncorrectBoundaryException.cs @@ -1,35 +1,34 @@ -namespace DataFilters.Grammar.Exceptions -{ - using System; +namespace DataFilters.Grammar.Exceptions; + +using System; - /// - /// Exception thrown when constructor is passed incorrect s. - /// - public class IncorrectBoundaryException : ArgumentException +/// +/// Exception thrown when constructor is passed incorrect s. +/// +public class IncorrectBoundaryException : ArgumentException +{ + /// + public IncorrectBoundaryException() : base() { - /// - public IncorrectBoundaryException() : base() - { - } + } - /// - public IncorrectBoundaryException(string message) : base(message) - { - } + /// + public IncorrectBoundaryException(string message) : base(message) + { + } - /// - public IncorrectBoundaryException(string message, Exception innerException) : base(message, innerException) - { - } + /// + public IncorrectBoundaryException(string message, Exception innerException) : base(message, innerException) + { + } - /// - public IncorrectBoundaryException(string message, string paramName) : base(message, paramName) - { - } + /// + public IncorrectBoundaryException(string message, string paramName) : base(message, paramName) + { + } - /// - public IncorrectBoundaryException(string message, string paramName, Exception innerException) : base(message, paramName, innerException) - { - } + /// + public IncorrectBoundaryException(string message, string paramName, Exception innerException) : base(message, paramName, innerException) + { } } diff --git a/src/DataFilters/Grammar/Parsing/FilterToken.cs b/src/DataFilters/Grammar/Parsing/FilterToken.cs index 33e99140..56673cea 100644 --- a/src/DataFilters/Grammar/Parsing/FilterToken.cs +++ b/src/DataFilters/Grammar/Parsing/FilterToken.cs @@ -1,151 +1,150 @@ -namespace DataFilters.Grammar.Parsing +namespace DataFilters.Grammar.Parsing; + +using Superpower.Display; + +/// +/// Enumeration of token used throughout the parsing process. +/// +/// +/// s acts as "markers" with special meaning. They can be combined to created a syntax tree with a higher meaning. +/// +public enum FilterToken { - using Superpower.Display; - - /// - /// Enumeration of token used throughout the parsing process. - /// - /// - /// s acts as "markers" with special meaning. They can be combined to created a syntax tree with a higher meaning. - /// - public enum FilterToken - { - /// - /// A token that has no specific meaning - /// - None, - - /// - /// Start of a group - /// - [Token(Example = "(")] - OpenParenthese, - - /// - /// Token that indicates the end of a group of token. - /// - /// - [Token(Example = ")")] - CloseParenthese, - - /// - /// Letter - /// - [Token(Example = "c", Description = "a letter")] - Letter, - - /// - /// Numeric value of some sort - /// - [Token(Example = "2", Description = "a digit")] - Digit, - - /// - /// [ character - /// - [Token(Example = "(", Description = "right parenthesis")] - OpenSquaredBracket, - - /// - /// ] character - /// - [Token(Example = ")", Description = "left parenthesis")] - CloseSquaredBracket, - - /// - /// Asterisk operator used in like expression - /// - [Token(Example = "*", Description = "asterisk")] - Asterisk, - - /// - /// Logical AND operator - /// - [Token(Example = ",", Description = "comma")] - And, - - /// - /// Logical OR operator - /// - [Token(Example = "|", Description = "pipe")] - Or, - - /// - /// Hyphen - /// - [Token(Example = "-", Description = "hyphen")] - Dash, - - /// - /// Equal sign - /// - [Token(Example = "=")] - Equal, - - /// - /// Underscore sign - /// - [Token(Example = "_")] - Underscore, - - /// - /// Bang sign - /// - [Token(Example = "!", Description = "exclamation point")] - Bang, - - /// - /// The whitespace - /// - [Token(Example = " ")] - Whitespace, - - /// - /// The : character - /// - [Token(Example = ":")] - Colon, - - /// - /// The . character. - /// - [Token(Example = ".")] - Dot, - - /// - /// The \ (backslash) character - /// - [Token(Example = @"\")] - Backslash, - - /// - /// Token that allow to escape the token that comes right after itself. - /// - [Token(Description = "Token that allow to escape character with a special meaning", Example = @"\\")] - Escaped, - - /// - /// The " charater - /// - [Token(Example = @"""", Description = "double quote")] - DoubleQuote, - - /// - /// The & character. - /// - [Token(Example = "&", Description = "ampersand")] - Ampersand, - - /// - /// The { character. - /// - [Token(Example = "{", Description = "left curly brace")] - LeftBrace, - - /// - /// The } character. - /// - [Token(Example = "}", Description = "right curly brace")] - RightBrace - } + /// + /// A token that has no specific meaning + /// + None, + + /// + /// Start of a group + /// + [Token(Example = "(")] + OpenParenthese, + + /// + /// Token that indicates the end of a group of token. + /// + /// + [Token(Example = ")")] + CloseParenthese, + + /// + /// Letter + /// + [Token(Example = "c", Description = "a letter")] + Letter, + + /// + /// Numeric value of some sort + /// + [Token(Example = "2", Description = "a digit")] + Digit, + + /// + /// [ character + /// + [Token(Example = "(", Description = "right parenthesis")] + OpenSquaredBracket, + + /// + /// ] character + /// + [Token(Example = ")", Description = "left parenthesis")] + CloseSquaredBracket, + + /// + /// Asterisk operator used in like expression + /// + [Token(Example = "*", Description = "asterisk")] + Asterisk, + + /// + /// Logical AND operator + /// + [Token(Example = ",", Description = "comma")] + And, + + /// + /// Logical OR operator + /// + [Token(Example = "|", Description = "pipe")] + Or, + + /// + /// Hyphen + /// + [Token(Example = "-", Description = "hyphen")] + Dash, + + /// + /// Equal sign + /// + [Token(Example = "=")] + Equal, + + /// + /// Underscore sign + /// + [Token(Example = "_")] + Underscore, + + /// + /// Bang sign + /// + [Token(Example = "!", Description = "exclamation point")] + Bang, + + /// + /// The whitespace + /// + [Token(Example = " ")] + Whitespace, + + /// + /// The : character + /// + [Token(Example = ":")] + Colon, + + /// + /// The . character. + /// + [Token(Example = ".")] + Dot, + + /// + /// The \ (backslash) character + /// + [Token(Example = @"\")] + Backslash, + + /// + /// Token that allow to escape the token that comes right after itself. + /// + [Token(Description = "Token that allow to escape character with a special meaning", Example = @"\\")] + Escaped, + + /// + /// The " charater + /// + [Token(Example = @"""", Description = "double quote")] + DoubleQuote, + + /// + /// The & character. + /// + [Token(Example = "&", Description = "ampersand")] + Ampersand, + + /// + /// The { character. + /// + [Token(Example = "{", Description = "left curly brace")] + LeftBrace, + + /// + /// The } character. + /// + [Token(Example = "}", Description = "right curly brace")] + RightBrace } diff --git a/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs b/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs index c6572b74..503d3df3 100644 --- a/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs +++ b/src/DataFilters/Grammar/Parsing/FilterTokenParser.cs @@ -1,825 +1,824 @@ -namespace DataFilters.Grammar.Parsing -{ - using DataFilters.Grammar.Syntax; +namespace DataFilters.Grammar.Parsing; + +using DataFilters.Grammar.Syntax; - using Superpower; - using Superpower.Model; - using Superpower.Parsers; +using Superpower; +using Superpower.Model; +using Superpower.Parsers; - using System; - using System.Collections.Generic; +using System; +using System.Collections.Generic; #if NET7_0_OR_GREATER - using System.Diagnostics; +using System.Diagnostics; #endif - using System.Globalization; - using System.Linq; +using System.Globalization; +using System.Linq; +/// +/// Parses a tree of into +/// +public static class FilterTokenParser +{ /// - /// Parses a tree of into + /// Parser for many /// - public static class FilterTokenParser - { - /// - /// Parser for many - /// - public static TokenListParser AlphaNumeric => from data in ( - from symbolBefore in Token.EqualTo(FilterToken.Escaped).Try() - .Or(Token.EqualTo(FilterToken.None).Try()) - .Or(Token.EqualTo(FilterToken.Dot).Try()).Many() - from digitsBefore in Digit.Many() - from alpha in Alpha.Many() - from digitsAfter in Digit.Many() - from symbolAfter in Token.EqualTo(FilterToken.Escaped).Try() - .Or(Token.EqualTo(FilterToken.None).Try()) - .Or(Token.EqualTo(FilterToken.Dot).Try()) - .Many() - where symbolBefore.AtLeastOnce() - || digitsBefore.AtLeastOnce() - || alpha.AtLeastOnce() - || digitsAfter.AtLeastOnce() - || symbolAfter.AtLeastOnce() - - let value = string.Concat(string.Concat(symbolBefore.Select(x => x.ToStringValue())), - string.Concat(digitsBefore.Select(x => x.ToStringValue())), - string.Concat(alpha.Select(item => item.ToStringValue())), - string.Concat(digitsAfter.Select(x => x.ToStringValue())), - string.Concat(symbolAfter.Select(x => x.ToStringValue()))) - select value).AtLeastOnce() - - let alphaNumericValue = string.Concat(data) - let textSpan = new TextSpan(alphaNumericValue) - select Numerics.Decimal.IsMatch(textSpan) || Numerics.Integer.IsMatch(textSpan) - ? new NumericValueExpression(alphaNumericValue) - : (ConstantValueExpression)new StringValueExpression(alphaNumericValue); - - private static TokenListParser> Alpha => Token.EqualTo(FilterToken.Letter).Try() - .Or(Token.EqualTo(FilterToken.Escaped)).Try() - .Or(Token.EqualTo(FilterToken.Underscore)).Try() - .Or(Token.EqualTo(FilterToken.None)); - - private static TokenListParser> Digit => Token.EqualTo(FilterToken.Digit); - - private static TokenListParser Bool => Token.EqualToValueIgnoreCase(FilterToken.Letter, "t") - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "r")) - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "u")) - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "e")) - .Select(_ => new StringValueExpression(bool.TrueString)).Try() - .Or( - Token.EqualToValueIgnoreCase(FilterToken.Letter, "f") - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "a")) - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "l")) - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "s")) - .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "e")) - .Select(_ => new StringValueExpression(bool.FalseString))); - - /// - /// Parser for '*' character - /// - private static TokenListParser Asterisk => from __ in Token.EqualTo(FilterToken.Asterisk) - select AsteriskExpression.Instance; - - /// - /// Parser for "starts with" expressions - /// - public static TokenListParser StartsWith => (from data in AlphaNumeric.AtLeastOnce() + public static TokenListParser AlphaNumeric => from data in ( + from symbolBefore in Token.EqualTo(FilterToken.Escaped).Try() + .Or(Token.EqualTo(FilterToken.None).Try()) + .Or(Token.EqualTo(FilterToken.Dot).Try()).Many() + from digitsBefore in Digit.Many() + from alpha in Alpha.Many() + from digitsAfter in Digit.Many() + from symbolAfter in Token.EqualTo(FilterToken.Escaped).Try() + .Or(Token.EqualTo(FilterToken.None).Try()) + .Or(Token.EqualTo(FilterToken.Dot).Try()) + .Many() + where symbolBefore.AtLeastOnce() + || digitsBefore.AtLeastOnce() + || alpha.AtLeastOnce() + || digitsAfter.AtLeastOnce() + || symbolAfter.AtLeastOnce() + + let value = string.Concat(string.Concat(symbolBefore.Select(x => x.ToStringValue())), + string.Concat(digitsBefore.Select(x => x.ToStringValue())), + string.Concat(alpha.Select(item => item.ToStringValue())), + string.Concat(digitsAfter.Select(x => x.ToStringValue())), + string.Concat(symbolAfter.Select(x => x.ToStringValue()))) + select value).AtLeastOnce() + + let alphaNumericValue = string.Concat(data) + let textSpan = new TextSpan(alphaNumericValue) + select Numerics.Decimal.IsMatch(textSpan) || Numerics.Integer.IsMatch(textSpan) + ? new NumericValueExpression(alphaNumericValue) + : (ConstantValueExpression)new StringValueExpression(alphaNumericValue); + + private static TokenListParser> Alpha => Token.EqualTo(FilterToken.Letter).Try() + .Or(Token.EqualTo(FilterToken.Escaped)).Try() + .Or(Token.EqualTo(FilterToken.Underscore)).Try() + .Or(Token.EqualTo(FilterToken.None)); + + private static TokenListParser> Digit => Token.EqualTo(FilterToken.Digit); + + private static TokenListParser Bool => Token.EqualToValueIgnoreCase(FilterToken.Letter, "t") + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "r")) + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "u")) + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "e")) + .Select(_ => new StringValueExpression(bool.TrueString)).Try() + .Or( + Token.EqualToValueIgnoreCase(FilterToken.Letter, "f") + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "a")) + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "l")) + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "s")) + .IgnoreThen(Token.EqualToValueIgnoreCase(FilterToken.Letter, "e")) + .Select(_ => new StringValueExpression(bool.FalseString))); + + /// + /// Parser for '*' character + /// + private static TokenListParser Asterisk => from __ in Token.EqualTo(FilterToken.Asterisk) + select AsteriskExpression.Instance; + + /// + /// Parser for "starts with" expressions + /// + public static TokenListParser StartsWith => (from data in AlphaNumeric.AtLeastOnce() + from _ in Asterisk + select new StartsWithExpression(string.Concat(data.Select(x => x.Value)))).Try() + .Or( + from text in Text from _ in Asterisk - select new StartsWithExpression(string.Concat(data.Select(x => x.Value)))).Try() - .Or( - from text in Text - from _ in Asterisk - select new StartsWithExpression(text) - ); - - /// - /// Parser for "starts with" expressions - /// - public static TokenListParser EndsWith => Asterisk.IgnoreThen(AlphaNumeric.AtLeastOnce()) - .Select(data => new EndsWithExpression(string.Concat(data.Select(item => item.Value)))).Try() - .Or(Asterisk.IgnoreThen(Text) - .Select(text => new EndsWithExpression(text))); - - /// - /// Parser for "contains" expression - /// - public static TokenListParser Contains => (from _ in Asterisk - from data in ( - from symbolBefore in Token.EqualTo(FilterToken.Escaped).Try().Or(Whitespace).Many() - from puncBefore in Punctuation.Many() - from alpha in AlphaNumeric.Many() - from puncAfter in Punctuation.Many() - from symbolAfter in Token.EqualTo(FilterToken.Escaped).Try().Or(Whitespace).Many() - where symbolBefore.AtLeastOnce() - || puncBefore.AtLeastOnce() - || alpha.AtLeastOnce() - || puncAfter.AtLeastOnce() - || symbolAfter.AtLeastOnce() - select new - { - value = string.Concat(string.Concat(symbolBefore.Select(x => x.ToStringValue())), - string.Concat(puncBefore.Select(x => x.Value)), - string.Concat(alpha.Select(item => item.Value)), - string.Concat(puncAfter.Select(x => x.Value)), - string.Concat(symbolAfter.Select(x => x.ToStringValue()))) - }).AtLeastOnce() - from escaped in Token.EqualTo(FilterToken.Backslash).Many() - from __ in Asterisk - select new ContainsExpression(string.Concat(data.Select(x => x.value)))).Try() - .Or(Text.Between(Asterisk, Asterisk) - .Select(text => new ContainsExpression(text))); - - /// - /// Parser for logical OR expression. - /// - public static TokenListParser Or => Parse.Chain(Token.EqualTo(FilterToken.Or), - UnaryExpression, - (_, left, right) => new OrExpression(left, right)); - - /// - /// Parser for logical AND expression - /// - public static TokenListParser And => (from left in UnaryExpression - from _ in Token.EqualTo(FilterToken.And) - from right in UnaryExpression - select new AndExpression(left, right)).Try() - .Or(from left in AlphaNumeric - from _ in Asterisk - from right in AlphaNumeric - select new AndExpression(new StartsWithExpression(left.Value), - new EndsWithExpression(right.Value))); - - /// - /// Parser for NOT expression - /// - public static TokenListParser Not => from _ in Token.EqualTo(FilterToken.Bang) - from expression in Parse.Ref(() => UnaryExpression) - select new NotExpression(expression); - - private static TokenListParser Bracket => ( - from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) - from rangeValues in BuildRangeBracketValuesParser(Alpha).Or(BuildRangeBracketValuesParser(Digit)).AtLeastOnce() - from __ in Token.EqualTo(FilterToken.CloseSquaredBracket) - select new BracketExpression(rangeValues) - ).Try() - .Or - ( - from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) - from alpha in Alpha.Try().Or(Digit).AtLeastOnce() - from __ in Token.EqualTo(FilterToken.CloseSquaredBracket) - select new BracketExpression(new ConstantBracketValue(alpha.Select(chr => chr.ToStringValue()) - .Aggregate((total, current) => $"{total}{current}")) - ) - ); - /// - /// Builds a parser that can extract bracket expression values - /// - /// The parser that can parse values inside a [ qnd ] - /// - private static TokenListParser BuildRangeBracketValuesParser(TokenListParser> token) - => (from rangeStart in token - from _ in Token.EqualTo(FilterToken.Dash) - from rangeEnd in token - select (BracketValue)new RangeBracketValue(rangeStart.ToStringValue()[0], rangeEnd.ToStringValue()[0])) - .Try() - .Or(from values in token.AtLeastOnce() - select (BracketValue)new ConstantBracketValue(string.Concat(values.Select(value => value.ToStringValue())))) - - ; - /// - /// Parses Range expressions - /// - public static TokenListParser Interval + select new StartsWithExpression(text) + ); + + /// + /// Parser for "starts with" expressions + /// + public static TokenListParser EndsWith => Asterisk.IgnoreThen(AlphaNumeric.AtLeastOnce()) + .Select(data => new EndsWithExpression(string.Concat(data.Select(item => item.Value)))).Try() + .Or(Asterisk.IgnoreThen(Text) + .Select(text => new EndsWithExpression(text))); + + /// + /// Parser for "contains" expression + /// + public static TokenListParser Contains => (from _ in Asterisk + from data in ( + from symbolBefore in Token.EqualTo(FilterToken.Escaped).Try().Or(Whitespace).Many() + from puncBefore in Punctuation.Many() + from alpha in AlphaNumeric.Many() + from puncAfter in Punctuation.Many() + from symbolAfter in Token.EqualTo(FilterToken.Escaped).Try().Or(Whitespace).Many() + where symbolBefore.AtLeastOnce() + || puncBefore.AtLeastOnce() + || alpha.AtLeastOnce() + || puncAfter.AtLeastOnce() + || symbolAfter.AtLeastOnce() + select new + { + value = string.Concat(string.Concat(symbolBefore.Select(x => x.ToStringValue())), + string.Concat(puncBefore.Select(x => x.Value)), + string.Concat(alpha.Select(item => item.Value)), + string.Concat(puncAfter.Select(x => x.Value)), + string.Concat(symbolAfter.Select(x => x.ToStringValue()))) + }).AtLeastOnce() + from escaped in Token.EqualTo(FilterToken.Backslash).Many() + from __ in Asterisk + select new ContainsExpression(string.Concat(data.Select(x => x.value)))).Try() + .Or(Text.Between(Asterisk, Asterisk) + .Select(text => new ContainsExpression(text))); + + /// + /// Parser for logical OR expression. + /// + public static TokenListParser Or => Parse.Chain(Token.EqualTo(FilterToken.Or), + UnaryExpression, + (_, left, right) => new OrExpression(left, right)); + + /// + /// Parser for logical AND expression + /// + public static TokenListParser And => (from left in UnaryExpression + from _ in Token.EqualTo(FilterToken.And) + from right in UnaryExpression + select new AndExpression(left, right)).Try() + .Or(from left in AlphaNumeric + from _ in Asterisk + from right in AlphaNumeric + select new AndExpression(new StartsWithExpression(left.Value), + new EndsWithExpression(right.Value))); + + /// + /// Parser for NOT expression + /// + public static TokenListParser Not => from _ in Token.EqualTo(FilterToken.Bang) + from expression in Parse.Ref(() => UnaryExpression) + select new NotExpression(expression); + + private static TokenListParser Bracket => ( + from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) + from rangeValues in BuildRangeBracketValuesParser(Alpha).Or(BuildRangeBracketValuesParser(Digit)).AtLeastOnce() + from __ in Token.EqualTo(FilterToken.CloseSquaredBracket) + select new BracketExpression(rangeValues) + ).Try() + .Or + ( + from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) + from alpha in Alpha.Try().Or(Digit).AtLeastOnce() + from __ in Token.EqualTo(FilterToken.CloseSquaredBracket) + select new BracketExpression(new ConstantBracketValue(alpha.Select(chr => chr.ToStringValue()) + .Aggregate((total, current) => $"{total}{current}")) + ) + ); + /// + /// Builds a parser that can extract bracket expression values + /// + /// The parser that can parse values inside a [ qnd ] + /// + private static TokenListParser BuildRangeBracketValuesParser(TokenListParser> token) + => (from rangeStart in token + from _ in Token.EqualTo(FilterToken.Dash) + from rangeEnd in token + select (BracketValue)new RangeBracketValue(rangeStart.ToStringValue()[0], rangeEnd.ToStringValue()[0])) + .Try() + .Or(from values in token.AtLeastOnce() + select (BracketValue)new ConstantBracketValue(string.Concat(values.Select(value => value.ToStringValue())))) + + ; + /// + /// Parses Range expressions + /// + public static TokenListParser Interval + { + get { - get - { - return // Case [ min TO max ] - ( - from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) - from min in Constant - from __ in RangeSeparator - from max in Constant - from ___ in Token.EqualTo(FilterToken.CloseSquaredBracket) - - where min != default || max != default - select new IntervalExpression( - min: new BoundaryExpression(min switch - { - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + return // Case [ min TO max ] + ( + from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) + from min in Constant + from __ in RangeSeparator + from max in Constant + from ___ in Token.EqualTo(FilterToken.CloseSquaredBracket) + + where min != default || max != default + select new IntervalExpression( + min: new BoundaryExpression(min switch + { + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") #else - _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") #endif - }, included: true), - max: new BoundaryExpression(max switch - { - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: true), + max: new BoundaryExpression(max switch + { + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") #else - _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") #endif - }, included: true) - ) - ).Try() - // Case ] min TO max ] : lower bound excluded from the interval - .Or( - from _ in Token.EqualTo(FilterToken.CloseSquaredBracket) - from min in Asterisk.Try().Cast() - .Or(Constant) - from __ in RangeSeparator - from max in Constant - from ___ in Token.EqualTo(FilterToken.CloseSquaredBracket) - - where min != default || max != default - select new IntervalExpression( - min: new BoundaryExpression(min switch - { - AsteriskExpression asterisk => asterisk, - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: true) + ) + ).Try() + // Case ] min TO max ] : lower bound excluded from the interval + .Or( + from _ in Token.EqualTo(FilterToken.CloseSquaredBracket) + from min in Asterisk.Try().Cast() + .Or(Constant) + from __ in RangeSeparator + from max in Constant + from ___ in Token.EqualTo(FilterToken.CloseSquaredBracket) + + where min != default || max != default + select new IntervalExpression( + min: new BoundaryExpression(min switch + { + AsteriskExpression asterisk => asterisk, + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") #else - _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") #endif - }, included: false), - max: new BoundaryExpression(max switch - { - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: false), + max: new BoundaryExpression(max switch + { + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") #else - _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") #endif - }, included: true) - ) - ).Try() - // Case syntax [ min TO max [ : upper bound excluded from the interval - .Or( - from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) - from min in Constant - from __ in RangeSeparator - from max in Asterisk.Try().Cast() - .Or(Constant) - - from ___ in Token.EqualTo(FilterToken.OpenSquaredBracket) - - where min != default || max != default - select new IntervalExpression( - min: new BoundaryExpression(min switch - { - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: true) + ) + ).Try() + // Case syntax [ min TO max [ : upper bound excluded from the interval + .Or( + from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) + from min in Constant + from __ in RangeSeparator + from max in Asterisk.Try().Cast() + .Or(Constant) + + from ___ in Token.EqualTo(FilterToken.OpenSquaredBracket) + + where min != default || max != default + select new IntervalExpression( + min: new BoundaryExpression(min switch + { + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") #else - _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") #endif - }, included: true), - max: new BoundaryExpression(max switch - { - AsteriskExpression asterisk => asterisk, - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: true), + max: new BoundaryExpression(max switch + { + AsteriskExpression asterisk => asterisk, + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") #else - _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") #endif - }, included: false) - ) - ).Try() - // Case ] min TO max [ : lower and upper bounds excluded from the interval - .Or( - from _ in Token.EqualTo(FilterToken.CloseSquaredBracket) - from min in Asterisk.Try().Cast() - .Or(Constant) - - from __ in RangeSeparator - from max in Asterisk.Try().Cast() - .Or(Constant) - from ___ in Token.EqualTo(FilterToken.OpenSquaredBracket) - - where min != default || max != default - select new IntervalExpression( - min: new BoundaryExpression(min switch - { - AsteriskExpression asterisk => asterisk, - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: false) + ) + ).Try() + // Case ] min TO max [ : lower and upper bounds excluded from the interval + .Or( + from _ in Token.EqualTo(FilterToken.CloseSquaredBracket) + from min in Asterisk.Try().Cast() + .Or(Constant) + + from __ in RangeSeparator + from max in Asterisk.Try().Cast() + .Or(Constant) + from ___ in Token.EqualTo(FilterToken.OpenSquaredBracket) + + where min != default || max != default + select new IntervalExpression( + min: new BoundaryExpression(min switch + { + AsteriskExpression asterisk => asterisk, + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new UnreachableException($"Unsupported '{min?.GetType()}' for min value") #else - _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") + _ => throw new NotSupportedException($"Unsupported '{min?.GetType()}' for min value") #endif - }, included: false), - max: new BoundaryExpression(max switch - { - AsteriskExpression asterisk => asterisk, - NumericValueExpression constant => constant, - DateTimeExpression dateTime => dateTime, + }, included: false), + max: new BoundaryExpression(max switch + { + AsteriskExpression asterisk => asterisk, + NumericValueExpression constant => constant, + DateTimeExpression dateTime => dateTime, #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new UnreachableException($"Unsupported '{max?.GetType()}' for max value") #else - _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") + _ => throw new NotSupportedException($"Unsupported '{max?.GetType()}' for max value") #endif - }, included: false) - ) - ); - } + }, included: false) + ) + ); } + } + + private static TokenListParser Constant => GlobalUniqueIdentifier.Try().Cast() + .Or(DateAndTime.Try().Cast()) + .Or(Time.Try().Cast()) + .Or(Date.Try().Cast()) + .Or(Duration.Try().Cast()) + .Or(Number.Try().Cast()) + .Or(Bool.Try().Cast()) + .Or(Text.Cast()) + .Or(AlphaNumeric.Cast()) + ; + + private static TokenListParser> RangeSeparator => from _ in Whitespace.AtLeastOnce() + from rangeSeparator in Token.EqualToValue(FilterToken.Letter, "T") + .Then(_ => Token.EqualToValue(FilterToken.Letter, "O")) + from __ in Whitespace.AtLeastOnce() + select rangeSeparator; + + private static TokenListParser> Whitespace => Token.EqualTo(FilterToken.Whitespace); + + /// + /// Group expression + /// + public static TokenListParser Group => from _ in Token.EqualTo(FilterToken.OpenParenthese) + from expression in BinaryOrUnaryExpression + from __ in Token.EqualTo(FilterToken.CloseParenthese) + select new GroupExpression(expression); + + private static TokenListParser BinaryOrUnaryExpression => Parse.Ref(() => Not.Try().Cast()) + .Or(Parse.Ref(() => And.Try().Cast())) + .Or(Parse.Ref(() => Or.Try())) + .Or(Parse.Ref(() => OneOf.Try().Cast())) + .Or(Parse.Ref(() => UnaryExpression)); + + /// + /// Property name parser + /// + public static TokenListParser Property => from prop in AlphaNumeric + from subProps in ( + from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) + from subProp in AlphaNumeric.Between(Token.EqualTo(FilterToken.DoubleQuote), Token.EqualTo(FilterToken.DoubleQuote)) + from __ in Token.EqualTo(FilterToken.CloseSquaredBracket) + select @$"[""{subProp.Value}""]" + ).Many() + select new PropertyName(string.Concat(prop.Value, string.Concat(subProps))); - private static TokenListParser Constant => GlobalUniqueIdentifier.Try().Cast() - .Or(DateAndTime.Try().Cast()) - .Or(Time.Try().Cast()) - .Or(Date.Try().Cast()) - .Or(Duration.Try().Cast()) - .Or(Number.Try().Cast()) - .Or(Bool.Try().Cast()) - .Or(Text.Cast()) - .Or(AlphaNumeric.Cast()) - ; - - private static TokenListParser> RangeSeparator => from _ in Whitespace.AtLeastOnce() - from rangeSeparator in Token.EqualToValue(FilterToken.Letter, "T") - .Then(_ => Token.EqualToValue(FilterToken.Letter, "O")) - from __ in Whitespace.AtLeastOnce() - select rangeSeparator; - - private static TokenListParser> Whitespace => Token.EqualTo(FilterToken.Whitespace); - - /// - /// Group expression - /// - public static TokenListParser Group => from _ in Token.EqualTo(FilterToken.OpenParenthese) - from expression in BinaryOrUnaryExpression - from __ in Token.EqualTo(FilterToken.CloseParenthese) - select new GroupExpression(expression); - - private static TokenListParser BinaryOrUnaryExpression => Parse.Ref(() => Not.Try().Cast()) - .Or(Parse.Ref(() => And.Try().Cast())) - .Or(Parse.Ref(() => Or.Try())) - .Or(Parse.Ref(() => OneOf.Try().Cast())) - .Or(Parse.Ref(() => UnaryExpression)); - - /// - /// Property name parser - /// - public static TokenListParser Property => from prop in AlphaNumeric - from subProps in ( - from _ in Token.EqualTo(FilterToken.OpenSquaredBracket) - from subProp in AlphaNumeric.Between(Token.EqualTo(FilterToken.DoubleQuote), Token.EqualTo(FilterToken.DoubleQuote)) - from __ in Token.EqualTo(FilterToken.CloseSquaredBracket) - select @$"[""{subProp.Value}""]" - ).Many() - select new PropertyName(string.Concat(prop.Value, string.Concat(subProps))); - - /// - /// Parser for any text between double quotes " - /// - public static TokenListParser Text => from _ in Token.EqualTo(FilterToken.DoubleQuote) + /// + /// Parser for any text between double quotes " + /// + public static TokenListParser Text => from _ in Token.EqualTo(FilterToken.DoubleQuote) #if NETSTANDARD1_3 - from text in (Token.EqualTo(FilterToken.Letter) - .Or(Token.EqualTo(FilterToken.Digit)) - .Or(Token.EqualTo(FilterToken.Escaped)) - .Or(Token.EqualTo(FilterToken.None))).AtLeastOnce() + from text in (Token.EqualTo(FilterToken.Letter) + .Or(Token.EqualTo(FilterToken.Digit)) + .Or(Token.EqualTo(FilterToken.Escaped)) + .Or(Token.EqualTo(FilterToken.None))).AtLeastOnce() #else - from text in Token.Matching(token => token != FilterToken.DoubleQuote, "Any character or symbol except double quote character") - .AtLeastOnce() + from text in Token.Matching(token => token != FilterToken.DoubleQuote, "Any character or symbol except double quote character") + .AtLeastOnce() #endif - from __ in Token.EqualTo(FilterToken.DoubleQuote) - select new TextExpression(TokensToString(text)); - - private static IEnumerable ConvertRegexToCharArray(IEnumerable values) - => values.Select(value => - value switch - { - RangeBracketValue rangeValue => Enumerable.Range(rangeValue.Start, rangeValue.End - rangeValue.Start + 1) - .Select(ascii => (char)ascii), - ConstantBracketValue constantValue => constantValue.Value.ToCharArray(), + from __ in Token.EqualTo(FilterToken.DoubleQuote) + select new TextExpression(TokensToString(text)); + + private static IEnumerable ConvertRegexToCharArray(IEnumerable values) + => values.Select(value => + value switch + { + RangeBracketValue rangeValue => Enumerable.Range(rangeValue.Start, rangeValue.End - rangeValue.Start + 1) + .Select(ascii => (char)ascii), + ConstantBracketValue constantValue => constantValue.Value.ToCharArray(), #if NET7_0_OR_GREATER - _ => throw new UnreachableException("Unexpected regex value") + _ => throw new UnreachableException("Unexpected regex value") #else - _ => throw new NotSupportedException("Unexpected regex value") + _ => throw new NotSupportedException("Unexpected regex value") #endif - }) - .SelectMany(x => x); - - /// - /// Parser for expressions that contains one or more regex parts. - /// - public static TokenListParser OneOf - => ( - from head in Bracket + }) + .SelectMany(x => x); + + /// + /// Parser for expressions that contains one or more regex parts. + /// + public static TokenListParser OneOf + => ( + from head in Bracket + from body in Asterisk.Try().Cast() + .Or(AlphaNumeric.Cast()) + .OptionalOrDefault() + from tail in Bracket + + select (head: (FilterExpression)head, (object)body, tail: (FilterExpression)tail) + ).Try() + .Or( + from head in EndsWith.Try().Cast() + .Or(StartsWith.Try().Cast()) + .Or(AlphaNumeric.Cast()) + .Or(Asterisk.Cast()) + .OptionalOrDefault() + from body in Bracket + from tail in EndsWith.Try().Cast() + .Or(StartsWith.Try().Cast()) + .Or(AlphaNumeric.Try().Cast()) + .Or(Asterisk.Try().Cast()) + .OptionalOrDefault() + + select (head, body: (object)body, tail) + ).Try() + .Or( + from head in Group from body in Asterisk.Try().Cast() .Or(AlphaNumeric.Cast()) .OptionalOrDefault() - from tail in Bracket + from tail in Group select (head: (FilterExpression)head, (object)body, tail: (FilterExpression)tail) - ).Try() - .Or( - from head in EndsWith.Try().Cast() - .Or(StartsWith.Try().Cast()) - .Or(AlphaNumeric.Cast()) - .Or(Asterisk.Cast()) - .OptionalOrDefault() - from body in Bracket - from tail in EndsWith.Try().Cast() + ).Try() + .Or( + from head in EndsWith.Try().Cast() .Or(StartsWith.Try().Cast()) - .Or(AlphaNumeric.Try().Cast()) - .Or(Asterisk.Try().Cast()) + .Or(AlphaNumeric.Cast()) + .Or(Asterisk.Cast()) .OptionalOrDefault() - - select (head, body: (object)body, tail) - ).Try() - .Or( - from head in Group - from body in Asterisk.Try().Cast() - .Or(AlphaNumeric.Cast()) - .OptionalOrDefault() - from tail in Group - - select (head: (FilterExpression)head, (object)body, tail: (FilterExpression)tail) - ).Try() - .Or( - from head in EndsWith.Try().Cast() - .Or(StartsWith.Try().Cast()) - .Or(AlphaNumeric.Cast()) - .Or(Asterisk.Cast()) - .OptionalOrDefault() - from body in Group - from tail in EndsWith.Try().Cast() + from body in Group + from tail in EndsWith.Try().Cast() + .Or(StartsWith.Try().Cast()) + .Or(AlphaNumeric.Try().Cast()) + .Or(Asterisk.Try().Cast()) + .OptionalOrDefault() + + select (head, body: (object)body, tail) + ).Try() + .Or( + from head in EndsWith.Try().Cast() .Or(StartsWith.Try().Cast()) - .Or(AlphaNumeric.Try().Cast()) - .Or(Asterisk.Try().Cast()) + .Or(AlphaNumeric.Cast()) + .Or(Asterisk.Cast()) + .OptionalOrDefault() + from _ in Token.EqualTo(FilterToken.LeftBrace) + from body in Constant.AtLeastOnceDelimitedBy(Token.EqualTo(FilterToken.Or)) + from __ in Token.EqualTo(FilterToken.RightBrace) + from tail in EndsWith.Try().Cast() + .Or(StartsWith.Try().Cast()) + .Or(AlphaNumeric.Cast()) + .Or(Asterisk.Cast()) .OptionalOrDefault() - select (head, body: (object)body, tail) - ).Try() - .Or( - from head in EndsWith.Try().Cast() - .Or(StartsWith.Try().Cast()) - .Or(AlphaNumeric.Cast()) - .Or(Asterisk.Cast()) - .OptionalOrDefault() - from _ in Token.EqualTo(FilterToken.LeftBrace) - from body in Constant.AtLeastOnceDelimitedBy(Token.EqualTo(FilterToken.Or)) - from __ in Token.EqualTo(FilterToken.RightBrace) - from tail in EndsWith.Try().Cast() - .Or(StartsWith.Try().Cast()) - .Or(AlphaNumeric.Cast()) - .Or(Asterisk.Cast()) - .OptionalOrDefault() - - select (head, body: (object)body.Select(item => item).OfType().ToArray(), tail) - ) - .Select(item => + select (head, body: (object)body.Select(item => item).OfType().ToArray(), tail) + ) + .Select(item => + { + return item switch { - return item switch - { - // * - (AsteriskExpression, BracketExpression bracket, null) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new EndsWithExpression(chr.ToString())) - .ToArray()), - // * - (AsteriskExpression, BracketExpression bracket, ConstantValueExpression tail) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new EndsWithExpression($"{chr}{tail.Value}")) - .ToArray()), - - // * - (null, BracketExpression bracket, AsteriskExpression) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StartsWithExpression(chr.ToString())) - .ToArray()), - - // - (EndsWithExpression head, BracketExpression body, null) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new EndsWithExpression($"{head.Value}{chr}")) - .ToArray()), - // - (EndsWithExpression head, BracketExpression body, ConstantValueExpression constant) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new EndsWithExpression($"{head.Value}{chr}{constant.Value}")) - .ToArray()), - // - (StartsWithExpression head, BracketExpression body, null) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new AndExpression(head, - new EndsWithExpression(chr.ToString()))) + // * + (AsteriskExpression, BracketExpression bracket, null) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new EndsWithExpression(chr.ToString())) + .ToArray()), + // * + (AsteriskExpression, BracketExpression bracket, ConstantValueExpression tail) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new EndsWithExpression($"{chr}{tail.Value}")) + .ToArray()), + + // * + (null, BracketExpression bracket, AsteriskExpression) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StartsWithExpression(chr.ToString())) + .ToArray()), + + // + (EndsWithExpression head, BracketExpression body, null) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new EndsWithExpression($"{head.Value}{chr}")) + .ToArray()), + // + (EndsWithExpression head, BracketExpression body, ConstantValueExpression constant) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new EndsWithExpression($"{head.Value}{chr}{constant.Value}")) .ToArray()), - // * - (null, BracketExpression bracket, StartsWithExpression body) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StartsWithExpression($"{chr}{body.Value}")).ToArray()), - - // - (ConstantValueExpression bracket, BracketExpression regex, ConstantValueExpression tail) => new OneOfExpression(ConvertRegexToCharArray(regex.Values).Select(chr => new StringValueExpression($"{bracket.Value}{chr}{tail.Value}")).ToArray()), - // - (ConstantValueExpression head, BracketExpression bracket, null) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StringValueExpression($"{head.Value}{chr}")).ToArray()), - - // - (null, BracketExpression bracket, ConstantValueExpression tail) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StringValueExpression($"{chr}{tail.Value}")).ToArray()), - - // - (null, BracketExpression bracket, null) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StringValueExpression(chr.ToString())).ToArray()), - - // - (BracketExpression head, ConstantValueExpression body, BracketExpression tail) => new(ConvertRegexToCharArray(head.Values).CrossJoin(ConvertRegexToCharArray(tail.Values)) - .Select(tuple => (start: tuple.Item1, end: tuple.Item2)) - .Select(tuple => new StringValueExpression($"{tuple.start}{body.Value}{tuple.end}")) - .ToArray()), - // - (BracketExpression head, null, BracketExpression tail) => new(ConvertRegexToCharArray(head.Values).CrossJoin(ConvertRegexToCharArray(tail.Values)) - .Select(tuple => (start: tuple.Item1, end: tuple.Item2)) - .Select(tuple => new StringValueExpression($"{tuple.start}{tuple.end}")) - .ToArray()), - // - (null, BracketExpression body, EndsWithExpression tail) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new AndExpression(new StartsWithExpression(chr.ToString()), tail)).ToArray()), - - // * - (AsteriskExpression, IEnumerable constants, null) => new OneOfExpression(constants.Select(constant => new EndsWithExpression(constant.Value)).ToArray()), - - // * - (null, IEnumerable constants, AsteriskExpression) => new OneOfExpression(constants.Select(constant => new StartsWithExpression(constant.Value)).ToArray()), - - // ** - (AsteriskExpression, IEnumerable constants, AsteriskExpression) => new OneOfExpression(constants.Select(constant => new ContainsExpression(constant.Value)).ToArray()), - // - (null, IEnumerable constants, EndsWithExpression endsWith) => new OneOfExpression(constants.Select(constant => constant + endsWith).ToArray()), - // - (null, IEnumerable constants, null) => new OneOfExpression(constants.ToArray()), + // + (StartsWithExpression head, BracketExpression body, null) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new AndExpression(head, + new EndsWithExpression(chr.ToString()))) + .ToArray()), + // * + (null, BracketExpression bracket, StartsWithExpression body) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StartsWithExpression($"{chr}{body.Value}")).ToArray()), + + // + (ConstantValueExpression bracket, BracketExpression regex, ConstantValueExpression tail) => new OneOfExpression(ConvertRegexToCharArray(regex.Values).Select(chr => new StringValueExpression($"{bracket.Value}{chr}{tail.Value}")).ToArray()), + // + (ConstantValueExpression head, BracketExpression bracket, null) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StringValueExpression($"{head.Value}{chr}")).ToArray()), + + // + (null, BracketExpression bracket, ConstantValueExpression tail) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StringValueExpression($"{chr}{tail.Value}")).ToArray()), + + // + (null, BracketExpression bracket, null) => new OneOfExpression(ConvertRegexToCharArray(bracket.Values).Select(chr => new StringValueExpression(chr.ToString())).ToArray()), + + // + (BracketExpression head, ConstantValueExpression body, BracketExpression tail) => new(ConvertRegexToCharArray(head.Values).CrossJoin(ConvertRegexToCharArray(tail.Values)) + .Select(tuple => (start: tuple.Item1, end: tuple.Item2)) + .Select(tuple => new StringValueExpression($"{tuple.start}{body.Value}{tuple.end}")) + .ToArray()), + // + (BracketExpression head, null, BracketExpression tail) => new(ConvertRegexToCharArray(head.Values).CrossJoin(ConvertRegexToCharArray(tail.Values)) + .Select(tuple => (start: tuple.Item1, end: tuple.Item2)) + .Select(tuple => new StringValueExpression($"{tuple.start}{tuple.end}")) + .ToArray()), + // + (null, BracketExpression body, EndsWithExpression tail) => new OneOfExpression(ConvertRegexToCharArray(body.Values).Select(chr => new AndExpression(new StartsWithExpression(chr.ToString()), tail)).ToArray()), + + // * + (AsteriskExpression, IEnumerable constants, null) => new OneOfExpression(constants.Select(constant => new EndsWithExpression(constant.Value)).ToArray()), + + // * + (null, IEnumerable constants, AsteriskExpression) => new OneOfExpression(constants.Select(constant => new StartsWithExpression(constant.Value)).ToArray()), + + // ** + (AsteriskExpression, IEnumerable constants, AsteriskExpression) => new OneOfExpression(constants.Select(constant => new ContainsExpression(constant.Value)).ToArray()), + // + (null, IEnumerable constants, EndsWithExpression endsWith) => new OneOfExpression(constants.Select(constant => constant + endsWith).ToArray()), + // + (null, IEnumerable constants, null) => new OneOfExpression(constants.ToArray()), #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported {nameof(OneOf)} expression : {item}") + _ => throw new UnreachableException($"Unsupported {nameof(OneOf)} expression : {item}") #else - _ => throw new NotSupportedException($"Unsupported {nameof(OneOf)} expression : {item}") + _ => throw new NotSupportedException($"Unsupported {nameof(OneOf)} expression : {item}") #endif - }; - }); - - /// - /// Parser for Date and Time - /// - public static TokenListParser DateAndTime => (from date in Date - from separator in Token.EqualToValue(FilterToken.Letter, "T") - .Or(Token.EqualTo(FilterToken.Whitespace)) - from time in Time - from offset in Offset.OptionalOrDefault() - select new DateTimeExpression(date, - new(hours: time.Hours, - minutes: time.Minutes, - seconds: time.Seconds, - milliseconds: time.Milliseconds), - offset)).Try() - // DATE - .Or(from date in Date - select new DateTimeExpression(date) - ).Try() - // TIME - .Or(from time in Time - select new DateTimeExpression(time) - ); + }; + }); + + /// + /// Parser for Date and Time + /// + public static TokenListParser DateAndTime => (from date in Date + from separator in Token.EqualToValue(FilterToken.Letter, "T") + .Or(Token.EqualTo(FilterToken.Whitespace)) + from time in Time + from offset in Offset.OptionalOrDefault() + select new DateTimeExpression(date, + new(hours: time.Hours, + minutes: time.Minutes, + seconds: time.Seconds, + milliseconds: time.Milliseconds), + offset)).Try() + // DATE + .Or(from date in Date + select new DateTimeExpression(date) + ).Try() + // TIME + .Or(from time in Time + select new DateTimeExpression(time) + ); - private static TokenListParser Offset => (from _ in Token.EqualToValue(FilterToken.Letter, "Z") - select OffsetExpression.Zero).Try() - .Or( - from sign in MinusOrPlusSign.OptionalOrDefault(NumericSign.Plus) - from hourDigits in IntDigits(2) - from _ in Colon - from minuteDigits in IntDigits(2) - select new OffsetExpression(sign, - uint.Parse(TokensToString(hourDigits), CultureInfo.InvariantCulture), - uint.Parse(TokensToString(minuteDigits), CultureInfo.InvariantCulture))); - - private static TokenListParser[]> IntDigits(int count) => Token.EqualTo(FilterToken.Digit).Repeat(count); - - /// - /// Parser for "date" expressions. - /// - public static TokenListParser Date => from year in IntDigits(4) - from _ in Dash - from month in IntDigits(2) - from __ in Dash - from day in IntDigits(2) - select new DateExpression(int.Parse(TokensToString(year), CultureInfo.InvariantCulture), - int.Parse(TokensToString(month), CultureInfo.InvariantCulture), - int.Parse(TokensToString(day), CultureInfo.InvariantCulture)); - - private static TokenListParser> Colon => Token.EqualTo(FilterToken.Colon); - - private static TokenListParser> Dash => Token.EqualTo(FilterToken.Dash); - - private static TokenListParser> MinusSign => Dash; - - private static TokenListParser> PlusSign => Token.EqualToValue(FilterToken.None, "+"); - - private static TokenListParser MinusOrPlusSign => MinusSign.Or(PlusSign).Optional() - .Select(token => token?.ToStringValue() switch - { - "-" => NumericSign.Minus, - _ => NumericSign.Plus - }); - - /// - /// Parser for time expression. - /// - public static TokenListParser Time => (from hourDigits in IntDigits(2) + private static TokenListParser Offset => (from _ in Token.EqualToValue(FilterToken.Letter, "Z") + select OffsetExpression.Zero).Try() + .Or( + from sign in MinusOrPlusSign.OptionalOrDefault(NumericSign.Plus) + from hourDigits in IntDigits(2) from _ in Colon from minuteDigits in IntDigits(2) - from __ in Colon - from secondDigits in IntDigits(2) - from ___ in Token.EqualTo(FilterToken.Dot) - from milliseconds in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - select new TimeExpression(int.Parse(TokensToString(hourDigits), CultureInfo.InvariantCulture), - int.Parse(TokensToString(minuteDigits), CultureInfo.InvariantCulture), - int.Parse(TokensToString(secondDigits), CultureInfo.InvariantCulture), - int.Parse(TokensToString(milliseconds), CultureInfo.InvariantCulture))) - .Try() - .Or(from hourDigits in IntDigits(2) - from _ in Colon - from minuteDigits in IntDigits(2) - from __ in Colon - from secondDigits in IntDigits(2) - select new TimeExpression(int.Parse(TokensToString(hourDigits), CultureInfo.InvariantCulture), - int.Parse(TokensToString(minuteDigits), CultureInfo.InvariantCulture), - int.Parse(TokensToString(secondDigits), CultureInfo.InvariantCulture))); - - /// - /// Parses all supported expressions - /// - private static TokenListParser UnaryExpression => Parse.Ref(() => Not.Try().Cast()) - .Or(Parse.Ref(() => Group.Try().Cast())) - .Or(Parse.Ref(() => OneOf.Try().Cast())) - .Or(Parse.Ref(() => Interval.Try().Cast())) - .Or(Parse.Ref(() => GlobalUniqueIdentifier.Try().Cast())) - .Or(Parse.Ref(() => Duration.Try().Cast())) - .Or(Parse.Ref(() => DateAndTime.Try().Cast())) - .Or(Parse.Ref(() => Time.Try().Cast())) - .Or(Parse.Ref(() => Date.Try().Cast())) - .Or(Parse.Ref(() => Contains.Try().Cast())) - .Or(Parse.Ref(() => StartsWith.Try().Cast())) - .Or(Parse.Ref(() => EndsWith.Try().Cast())) - .Or(Parse.Ref(() => Bool.Try().Cast())) - .Or(Parse.Ref(() => Text.Try().Cast())) - .Or(Parse.Ref(() => AlphaNumeric.Try().Cast())) - .Or(Parse.Ref(() => Number.Cast())) - ; - /// - /// Parser for property=value pair. - /// - public static TokenListParser Criterion => from property in Property - from _ in Token.EqualTo(FilterToken.Equal) - from expression in BinaryOrUnaryExpression - select (new PropertyName(property.Name), expression); - - /// - /// Parser for numeric expressions - /// - public static TokenListParser Number => FloatOrDouble.Try().Or(IntegerOrLong); - - private static TokenListParser FloatOrDouble - => (from sign in MinusOrPlusSign.Optional() + select new OffsetExpression(sign, + uint.Parse(TokensToString(hourDigits), CultureInfo.InvariantCulture), + uint.Parse(TokensToString(minuteDigits), CultureInfo.InvariantCulture))); + + private static TokenListParser[]> IntDigits(int count) => Token.EqualTo(FilterToken.Digit).Repeat(count); + + /// + /// Parser for "date" expressions. + /// + public static TokenListParser Date => from year in IntDigits(4) + from _ in Dash + from month in IntDigits(2) + from __ in Dash + from day in IntDigits(2) + select new DateExpression(int.Parse(TokensToString(year), CultureInfo.InvariantCulture), + int.Parse(TokensToString(month), CultureInfo.InvariantCulture), + int.Parse(TokensToString(day), CultureInfo.InvariantCulture)); + + private static TokenListParser> Colon => Token.EqualTo(FilterToken.Colon); + + private static TokenListParser> Dash => Token.EqualTo(FilterToken.Dash); + + private static TokenListParser> MinusSign => Dash; + + private static TokenListParser> PlusSign => Token.EqualToValue(FilterToken.None, "+"); + + private static TokenListParser MinusOrPlusSign => MinusSign.Or(PlusSign).Optional() + .Select(token => token?.ToStringValue() switch + { + "-" => NumericSign.Minus, + _ => NumericSign.Plus + }); + + /// + /// Parser for time expression. + /// + public static TokenListParser Time => (from hourDigits in IntDigits(2) + from _ in Colon + from minuteDigits in IntDigits(2) + from __ in Colon + from secondDigits in IntDigits(2) + from ___ in Token.EqualTo(FilterToken.Dot) + from milliseconds in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + select new TimeExpression(int.Parse(TokensToString(hourDigits), CultureInfo.InvariantCulture), + int.Parse(TokensToString(minuteDigits), CultureInfo.InvariantCulture), + int.Parse(TokensToString(secondDigits), CultureInfo.InvariantCulture), + int.Parse(TokensToString(milliseconds), CultureInfo.InvariantCulture))) + .Try() + .Or(from hourDigits in IntDigits(2) + from _ in Colon + from minuteDigits in IntDigits(2) + from __ in Colon + from secondDigits in IntDigits(2) + select new TimeExpression(int.Parse(TokensToString(hourDigits), CultureInfo.InvariantCulture), + int.Parse(TokensToString(minuteDigits), CultureInfo.InvariantCulture), + int.Parse(TokensToString(secondDigits), CultureInfo.InvariantCulture))); + + /// + /// Parses all supported expressions + /// + private static TokenListParser UnaryExpression => Parse.Ref(() => Not.Try().Cast()) + .Or(Parse.Ref(() => Group.Try().Cast())) + .Or(Parse.Ref(() => OneOf.Try().Cast())) + .Or(Parse.Ref(() => Interval.Try().Cast())) + .Or(Parse.Ref(() => GlobalUniqueIdentifier.Try().Cast())) + .Or(Parse.Ref(() => Duration.Try().Cast())) + .Or(Parse.Ref(() => DateAndTime.Try().Cast())) + .Or(Parse.Ref(() => Time.Try().Cast())) + .Or(Parse.Ref(() => Date.Try().Cast())) + .Or(Parse.Ref(() => Contains.Try().Cast())) + .Or(Parse.Ref(() => StartsWith.Try().Cast())) + .Or(Parse.Ref(() => EndsWith.Try().Cast())) + .Or(Parse.Ref(() => Bool.Try().Cast())) + .Or(Parse.Ref(() => Text.Try().Cast())) + .Or(Parse.Ref(() => AlphaNumeric.Try().Cast())) + .Or(Parse.Ref(() => Number.Cast())) + ; + /// + /// Parser for property=value pair. + /// + public static TokenListParser Criterion => from property in Property + from _ in Token.EqualTo(FilterToken.Equal) + from expression in BinaryOrUnaryExpression + select (new PropertyName(property.Name), expression); + + /// + /// Parser for numeric expressions + /// + public static TokenListParser Number => FloatOrDouble.Try().Or(IntegerOrLong); + + private static TokenListParser FloatOrDouble + => (from sign in MinusOrPlusSign.Optional() + from digitBeforeDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + from _ in Token.EqualTo(FilterToken.Dot) + from digitAfterDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + from __ in Token.EqualToValueIgnoreCase(FilterToken.Letter, "d") + select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digitBeforeDot)}.{TokensToString(digitAfterDot)}")) + .Try() + .Or( + from sign in MinusOrPlusSign.Optional() + from digitBeforeDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + from _ in Token.EqualTo(FilterToken.Dot) + from digitAfterDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + from __ in Token.EqualToValueIgnoreCase(FilterToken.Letter, "f") + select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digitBeforeDot)}.{TokensToString(digitAfterDot)}")) + .Try() + .Or( + from sign in MinusOrPlusSign.Optional() from digitBeforeDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() from _ in Token.EqualTo(FilterToken.Dot) from digitAfterDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digitBeforeDot)}.{TokensToString(digitAfterDot)}") + ) + .Try() + .Or( + from sign in MinusOrPlusSign.Optional() + from value in Token.EqualTo(FilterToken.Digit).AtLeastOnce() from __ in Token.EqualToValueIgnoreCase(FilterToken.Letter, "d") - select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digitBeforeDot)}.{TokensToString(digitAfterDot)}")) - .Try() - .Or( - from sign in MinusOrPlusSign.Optional() - from digitBeforeDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - from _ in Token.EqualTo(FilterToken.Dot) - from digitAfterDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - from __ in Token.EqualToValueIgnoreCase(FilterToken.Letter, "f") - select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digitBeforeDot)}.{TokensToString(digitAfterDot)}")) - .Try() - .Or( - from sign in MinusOrPlusSign.Optional() - from digitBeforeDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - from _ in Token.EqualTo(FilterToken.Dot) - from digitAfterDot in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digitBeforeDot)}.{TokensToString(digitAfterDot)}") - ) - .Try() - .Or( - from sign in MinusOrPlusSign.Optional() - from value in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - from __ in Token.EqualToValueIgnoreCase(FilterToken.Letter, "d") - select new NumericValueExpression($"{ConvertSignToChar(sign)}{value}") - ); - - private static TokenListParser IntegerOrLong - => (from sign in MinusOrPlusSign.Optional() + select new NumericValueExpression($"{ConvertSignToChar(sign)}{value}") + ); + + private static TokenListParser IntegerOrLong + => (from sign in MinusOrPlusSign.Optional() + from digits in Token.EqualTo(FilterToken.Digit).AtLeastOnce() + from hint in Token.EqualToValueIgnoreCase(FilterToken.Letter, "L") + select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digits)}")) + .Try() + .Or( + from sign in MinusOrPlusSign.Optional() from digits in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - from hint in Token.EqualToValueIgnoreCase(FilterToken.Letter, "L") - select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digits)}")) - .Try() - .Or( - from sign in MinusOrPlusSign.Optional() - from digits in Token.EqualTo(FilterToken.Digit).AtLeastOnce() - select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digits)}") - ) - ; - - private static string ConvertSignToChar(NumericSign? sign) => sign switch - { - NumericSign.Minus => "-", - _ => string.Empty, - }; - - /// - /// Parser for many separated by &. - /// - public static TokenListParser Criteria => from criteria in Criterion.ManyDelimitedBy(Token.EqualTo(FilterToken.Ampersand)) - select criteria; - - private static TokenListParser Punctuation => - ( - from c in Token.EqualTo(FilterToken.Dot) - select new StringValueExpression(c.ToStringValue()) - ).Or( - from c in Token.EqualTo(FilterToken.Colon) - select new StringValueExpression(c.ToStringValue()) - ).Or( - from c in Token.EqualTo(FilterToken.Dash) - select new StringValueExpression(c.ToStringValue()) - ).Or( - from c in Token.EqualTo(FilterToken.Underscore) + select new NumericValueExpression($"{ConvertSignToChar(sign)}{TokensToString(digits)}") + ) + ; + + private static string ConvertSignToChar(NumericSign? sign) => sign switch + { + NumericSign.Minus => "-", + _ => string.Empty, + }; + + /// + /// Parser for many separated by &. + /// + public static TokenListParser Criteria => from criteria in Criterion.ManyDelimitedBy(Token.EqualTo(FilterToken.Ampersand)) + select criteria; + + private static TokenListParser Punctuation => + ( + from c in Token.EqualTo(FilterToken.Dot) + select new StringValueExpression(c.ToStringValue()) + ).Or( + from c in Token.EqualTo(FilterToken.Colon) select new StringValueExpression(c.ToStringValue()) - ); + ).Or( + from c in Token.EqualTo(FilterToken.Dash) + select new StringValueExpression(c.ToStringValue()) + ).Or( + from c in Token.EqualTo(FilterToken.Underscore) + select new StringValueExpression(c.ToStringValue()) + ); + + private static TokenListParser GlobalUniqueIdentifier => from chr1 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr2 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr3 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr4 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr5 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr6 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr7 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr8 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from _ in Token.EqualTo(FilterToken.Dash) + from chr9 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr10 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr11 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr12 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from __ in Token.EqualTo(FilterToken.Dash) + from chr13 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr14 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr15 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr16 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from ___ in Token.EqualTo(FilterToken.Dash) + from chr17 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr18 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr19 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr20 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from ____ in Token.EqualTo(FilterToken.Dash) + from chr21 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr22 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr23 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr24 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr25 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr26 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr27 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr28 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr29 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr30 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr31 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + from chr32 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) + select new GuidValueExpression($"{TokensToString(new[] { chr1, chr2, chr3, chr4, chr5, chr6, chr7, chr8 })}-{TokensToString(new[] { chr9, chr10, chr11, chr12 })}-{TokensToString(new[] { chr13, chr14, chr15, chr16 })}-{TokensToString(new[] { chr17, chr18, chr19, chr20 })}-{TokensToString(new[] { chr21, chr22, chr23, chr24, chr25, chr26, chr27, chr28, chr29, chr30, chr31, chr32 })}"); - private static TokenListParser GlobalUniqueIdentifier => from chr1 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr2 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr3 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr4 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr5 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr6 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr7 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr8 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from _ in Token.EqualTo(FilterToken.Dash) - from chr9 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr10 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr11 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr12 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from __ in Token.EqualTo(FilterToken.Dash) - from chr13 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr14 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr15 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr16 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from ___ in Token.EqualTo(FilterToken.Dash) - from chr17 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr18 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr19 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr20 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from ____ in Token.EqualTo(FilterToken.Dash) - from chr21 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr22 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr23 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr24 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr25 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr26 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr27 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr28 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr29 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr30 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr31 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - from chr32 in Token.EqualTo(FilterToken.Letter).Try().Or(Token.EqualTo(FilterToken.Digit)) - select new GuidValueExpression($"{TokensToString(new[] { chr1, chr2, chr3, chr4, chr5, chr6, chr7, chr8 })}-{TokensToString(new[] { chr9, chr10, chr11, chr12 })}-{TokensToString(new[] { chr13, chr14, chr15, chr16 })}-{TokensToString(new[] { chr17, chr18, chr19, chr20 })}-{TokensToString(new[] { chr21, chr22, chr23, chr24, chr25, chr26, chr27, chr28, chr29, chr30, chr31, chr32 })}"); - - /// - /// Parser for duration - /// - public static TokenListParser Duration => from p in Token.EqualToValue(FilterToken.Letter, "P") - - from years in DurationPart("Y").OptionalOrDefault().Try() - from months in DurationPart("M").OptionalOrDefault().Try() - from weeks in DurationPart("W").OptionalOrDefault().Try() - from days in DurationPart("D").OptionalOrDefault().Try() - - from timeSeparator in Token.EqualToValue(FilterToken.Letter, "T") - - from hours in DurationPart("H").OptionalOrDefault().Try() - from minutes in DurationPart("M").OptionalOrDefault().Try() - from seconds in DurationPart("S").OptionalOrDefault().Try() - - where years is not null - || months is not null - || weeks is not null - || days is not null - || hours is not null - || minutes is not null - || seconds is not null - - select new DurationExpression(years: years is not null ? int.Parse(years.Value, CultureInfo.InvariantCulture) : 0, - months: months is not null ? int.Parse(months.Value, CultureInfo.InvariantCulture) : 0, - weeks: weeks is not null ? int.Parse(weeks.Value, CultureInfo.InvariantCulture) : 0, - days: days is not null ? int.Parse(days.Value, CultureInfo.InvariantCulture) : 0, - hours: hours is not null ? int.Parse(hours.Value, CultureInfo.InvariantCulture) : 0, - minutes: minutes is not null ? int.Parse(minutes.Value, CultureInfo.InvariantCulture) : 0, - seconds: seconds is not null ? int.Parse(seconds.Value, CultureInfo.InvariantCulture) : 0); - - private static TokenListParser DurationPart(string designator) => from n in IntegerOrLong - from _ in Token.EqualToValue(FilterToken.Letter, designator).Try() - select n; - - private static string TokensToString(IEnumerable> tokens) - { - static string TokenToString(Token token) => token.ToStringValue(); + /// + /// Parser for duration + /// + public static TokenListParser Duration => from p in Token.EqualToValue(FilterToken.Letter, "P") + + from years in DurationPart("Y").OptionalOrDefault().Try() + from months in DurationPart("M").OptionalOrDefault().Try() + from weeks in DurationPart("W").OptionalOrDefault().Try() + from days in DurationPart("D").OptionalOrDefault().Try() + + from timeSeparator in Token.EqualToValue(FilterToken.Letter, "T") + + from hours in DurationPart("H").OptionalOrDefault().Try() + from minutes in DurationPart("M").OptionalOrDefault().Try() + from seconds in DurationPart("S").OptionalOrDefault().Try() + + where years is not null + || months is not null + || weeks is not null + || days is not null + || hours is not null + || minutes is not null + || seconds is not null + + select new DurationExpression(years: years is not null ? int.Parse(years.Value, CultureInfo.InvariantCulture) : 0, + months: months is not null ? int.Parse(months.Value, CultureInfo.InvariantCulture) : 0, + weeks: weeks is not null ? int.Parse(weeks.Value, CultureInfo.InvariantCulture) : 0, + days: days is not null ? int.Parse(days.Value, CultureInfo.InvariantCulture) : 0, + hours: hours is not null ? int.Parse(hours.Value, CultureInfo.InvariantCulture) : 0, + minutes: minutes is not null ? int.Parse(minutes.Value, CultureInfo.InvariantCulture) : 0, + seconds: seconds is not null ? int.Parse(seconds.Value, CultureInfo.InvariantCulture) : 0); + + private static TokenListParser DurationPart(string designator) => from n in IntegerOrLong + from _ in Token.EqualToValue(FilterToken.Letter, designator).Try() + select n; + + private static string TokensToString(IEnumerable> tokens) + { + static string TokenToString(Token token) => token.ToStringValue(); - return string.Concat(tokens.Select(TokenToString)); - } + return string.Concat(tokens.Select(TokenToString)); } } diff --git a/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs b/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs index 393214aa..5cdb0b96 100644 --- a/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs +++ b/src/DataFilters/Grammar/Parsing/FilterTokenizer.cs @@ -1,310 +1,309 @@ -namespace DataFilters.Grammar.Parsing +namespace DataFilters.Grammar.Parsing; + +using System.Collections.Generic; +using System.Linq; +using Superpower; +using Superpower.Model; +using static DataFilters.Grammar.Parsing.FilterToken; + +/// +/// is the base class used to "tokenize" a string. +/// +/// +/// The tokenizer reads a string input and associated to one or more character a which is the first step in the process of building instances. +/// +public class FilterTokenizer : Tokenizer { - using System.Collections.Generic; - using System.Linq; - using Superpower; - using Superpower.Model; - using static DataFilters.Grammar.Parsing.FilterToken; + private TokenizerMode _mode; /// - /// is the base class used to "tokenize" a string. + /// The _ character /// - /// - /// The tokenizer reads a string input and associated to one or more character a which is the first step in the process of building instances. - /// - public class FilterTokenizer : Tokenizer - { - private TokenizerMode _mode; + public const char Underscore = '_'; - /// - /// The _ character - /// - public const char Underscore = '_'; + /// + /// The * character + /// + public const char Asterisk = '*'; - /// - /// The * character - /// - public const char Asterisk = '*'; + /// + /// The = character + /// + public const char EqualSign = '='; - /// - /// The = character - /// - public const char EqualSign = '='; + /// + /// The ( character + /// + public const char LeftParenthesis = '('; - /// - /// The ( character - /// - public const char LeftParenthesis = '('; + /// + /// The ) character + /// + public const char RightParenthesis = ')'; - /// - /// The ) character - /// - public const char RightParenthesis = ')'; + /// + /// The [ character + /// + public const char LeftSquareBracket = '['; - /// - /// The [ character - /// - public const char LeftSquareBracket = '['; + /// + /// The { character + /// + public const char LeftCurlyBracket = '{'; - /// - /// The { character - /// - public const char LeftCurlyBracket = '{'; + /// + /// The } character + /// + public const char RightCurlyBracket = '}'; - /// - /// The } character - /// - public const char RightCurlyBracket = '}'; + /// + /// The ] character + /// + public const char RightSquareBracket = ']'; - /// - /// The ] character - /// - public const char RightSquareBracket = ']'; + /// + /// The - character + /// + public const char Hyphen = '-'; - /// - /// The - character - /// - public const char Hyphen = '-'; + /// + /// The \ character + /// + public const char BackSlash = '\\'; - /// - /// The \ character - /// - public const char BackSlash = '\\'; + /// + /// The | character + /// + public const char Pipe = '|'; - /// - /// The | character - /// - public const char Pipe = '|'; + /// + /// The , character + /// + public const char Comma = ','; - /// - /// The , character - /// - public const char Comma = ','; + /// + /// The ! character + /// + public const char Bang = '!'; - /// - /// The ! character - /// - public const char Bang = '!'; + /// + /// The " character + /// + public const char DoubleQuote = '"'; + /// + /// The & character + /// + public const char Ampersand = '&'; - /// - /// The " character - /// - public const char DoubleQuote = '"'; - /// - /// The & character - /// - public const char Ampersand = '&'; + /// + /// List of characters that have a special meaning and should be escaped + /// + public static readonly char[] SpecialCharacters = + [ + Asterisk, + EqualSign, + LeftParenthesis, + RightParenthesis, + LeftSquareBracket, + RightSquareBracket, + BackSlash, + Pipe, + Comma, + Bang, + DoubleQuote, + Ampersand, + RightCurlyBracket, + LeftCurlyBracket, + ':', + '-', + '.', + ' ' + ]; - /// - /// List of characters that have a special meaning and should be escaped - /// - public static readonly char[] SpecialCharacters = - [ - Asterisk, - EqualSign, - LeftParenthesis, - RightParenthesis, - LeftSquareBracket, - RightSquareBracket, - BackSlash, - Pipe, - Comma, - Bang, - DoubleQuote, - Ampersand, - RightCurlyBracket, - LeftCurlyBracket, - ':', - '-', - '.', - ' ' - ]; + /// + /// Custom implementation that serves as the foundation of parsing text. + /// + public FilterTokenizer() => _mode = TokenizerMode.Normal; - /// - /// Custom implementation that serves as the foundation of parsing text. - /// - public FilterTokenizer() => _mode = TokenizerMode.Normal; + /// + protected override IEnumerable> Tokenize(TextSpan span, TokenizationState state) + { + Result next = span.ConsumeChar(); - /// - protected override IEnumerable> Tokenize(TextSpan span, TokenizationState state) + if (!next.HasValue) { - Result next = span.ConsumeChar(); + yield break; + } - if (!next.HasValue) + do + { + switch (next.Value) { - yield break; - } + case char c when char.IsLetter(c): + yield return Result.Value(Letter, next.Location, next.Remainder); + next = next.Remainder.ConsumeChar(); + break; - do - { - switch (next.Value) - { - case char c when char.IsLetter(c): - yield return Result.Value(Letter, next.Location, next.Remainder); + case char c when char.IsDigit(c): + yield return Result.Value(Digit, next.Location, next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Underscore: + yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Underscore, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Pipe: + yield return Result.Value(_mode switch { TokenizerMode.Normal => Or, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Comma: + yield return Result.Value(_mode switch { TokenizerMode.Normal => And, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case EqualSign: + yield return Result.Value(_mode switch { TokenizerMode.Normal => Equal, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Asterisk: + yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Asterisk, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case LeftCurlyBracket: + yield return Result.Value(_mode switch { TokenizerMode.Normal => LeftBrace, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case RightCurlyBracket: + yield return Result.Value(_mode switch { TokenizerMode.Normal => RightBrace, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Bang: + yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Bang, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case LeftSquareBracket: + yield return Result.Value(_mode switch { TokenizerMode.Normal => OpenSquaredBracket, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case RightSquareBracket: + yield return Result.Value(_mode switch { TokenizerMode.Normal => CloseSquaredBracket, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Hyphen: + yield return Result.Value(_mode switch { TokenizerMode.Normal => Dash, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case LeftParenthesis: + yield return Result.Value(_mode switch { TokenizerMode.Normal => OpenParenthese, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case RightParenthesis: + yield return Result.Value(_mode switch { TokenizerMode.Normal => CloseParenthese, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case ' ': + yield return Result.Value(_mode switch { TokenizerMode.Normal => Whitespace, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case ':': + yield return Result.Value(_mode switch { TokenizerMode.Normal => Colon, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case Ampersand: + yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Ampersand, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case DoubleQuote: + yield return Result.Value(FilterToken.DoubleQuote, + next.Location, + next.Remainder); + _mode = ToggleMode(_mode); + next = next.Remainder.ConsumeChar(); + break; + case '.': + yield return Result.Value(_mode switch { TokenizerMode.Normal => Dot, _ => Escaped }, + next.Location, + next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + case BackSlash: + TextSpan backSlashStart = next.Location; + if (_mode == TokenizerMode.Normal) + { next = next.Remainder.ConsumeChar(); - break; + yield return next.HasValue && SpecialCharacters.Contains(next.Value) + ? Result.Value(Escaped, next.Location, next.Remainder) + : Result.Value(Letter, backSlashStart, next.Remainder); - case char c when char.IsDigit(c): - yield return Result.Value(Digit, next.Location, next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Underscore: - yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Underscore, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Pipe: - yield return Result.Value(_mode switch { TokenizerMode.Normal => Or, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Comma: - yield return Result.Value(_mode switch { TokenizerMode.Normal => And, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case EqualSign: - yield return Result.Value(_mode switch { TokenizerMode.Normal => Equal, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Asterisk: - yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Asterisk, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case LeftCurlyBracket: - yield return Result.Value(_mode switch { TokenizerMode.Normal => LeftBrace, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case RightCurlyBracket: - yield return Result.Value(_mode switch { TokenizerMode.Normal => RightBrace, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Bang: - yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Bang, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case LeftSquareBracket: - yield return Result.Value(_mode switch { TokenizerMode.Normal => OpenSquaredBracket, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case RightSquareBracket: - yield return Result.Value(_mode switch { TokenizerMode.Normal => CloseSquaredBracket, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Hyphen: - yield return Result.Value(_mode switch { TokenizerMode.Normal => Dash, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case LeftParenthesis: - yield return Result.Value(_mode switch { TokenizerMode.Normal => OpenParenthese, _ => Escaped }, - next.Location, - next.Remainder); next = next.Remainder.ConsumeChar(); - break; - case RightParenthesis: - yield return Result.Value(_mode switch { TokenizerMode.Normal => CloseParenthese, _ => Escaped }, - next.Location, - next.Remainder); + } + else + { + TextSpan remainderAfterBackslah = next.Remainder; next = next.Remainder.ConsumeChar(); - break; - case ' ': - yield return Result.Value(_mode switch { TokenizerMode.Normal => Whitespace, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case ':': - yield return Result.Value(_mode switch { TokenizerMode.Normal => Colon, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case Ampersand: - yield return Result.Value(_mode switch { TokenizerMode.Normal => FilterToken.Ampersand, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case DoubleQuote: - yield return Result.Value(FilterToken.DoubleQuote, - next.Location, - next.Remainder); - _mode = ToggleMode(_mode); - next = next.Remainder.ConsumeChar(); - break; - case '.': - yield return Result.Value(_mode switch { TokenizerMode.Normal => Dot, _ => Escaped }, - next.Location, - next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - case BackSlash: - TextSpan backSlashStart = next.Location; - if (_mode == TokenizerMode.Normal) - { - next = next.Remainder.ConsumeChar(); - yield return next.HasValue && SpecialCharacters.Contains(next.Value) - ? Result.Value(Escaped, next.Location, next.Remainder) - : Result.Value(Letter, backSlashStart, next.Remainder); - - next = next.Remainder.ConsumeChar(); - } - else + // Only backslash and double quote need to be escaped when in Escaped mode + bool shouldEscape = next.Value == BackSlash || next.Value == DoubleQuote; + if (next.HasValue) { - TextSpan remainderAfterBackslah = next.Remainder; - next = next.Remainder.ConsumeChar(); - // Only backslash and double quote need to be escaped when in Escaped mode - bool shouldEscape = next.Value == BackSlash || next.Value == DoubleQuote; - if (next.HasValue) + if (shouldEscape) { - if (shouldEscape) - { - yield return Result.Value(Escaped, next.Location, next.Remainder); - next = next.Remainder.ConsumeChar(); - } - else - { - yield return Result.Value(Escaped, backSlashStart, remainderAfterBackslah); - } + yield return Result.Value(Escaped, next.Location, next.Remainder); + next = next.Remainder.ConsumeChar(); } else { yield return Result.Value(Escaped, backSlashStart, remainderAfterBackslah); } } + else + { + yield return Result.Value(Escaped, backSlashStart, remainderAfterBackslah); + } + } - break; - default: - yield return Result.Value(None, next.Location, next.Remainder); - next = next.Remainder.ConsumeChar(); - break; - } - } while (next.HasValue); + break; + default: + yield return Result.Value(None, next.Location, next.Remainder); + next = next.Remainder.ConsumeChar(); + break; + } + } while (next.HasValue); - static TokenizerMode ToggleMode(TokenizerMode currentMode) => currentMode switch - { - TokenizerMode.Normal => TokenizerMode.Escaped, - _ => TokenizerMode.Normal - }; - } + static TokenizerMode ToggleMode(TokenizerMode currentMode) => currentMode switch + { + TokenizerMode.Normal => TokenizerMode.Escaped, + _ => TokenizerMode.Normal + }; } } diff --git a/src/DataFilters/Grammar/Parsing/TokenizerMode.cs b/src/DataFilters/Grammar/Parsing/TokenizerMode.cs index 547cddbf..0895ea43 100644 --- a/src/DataFilters/Grammar/Parsing/TokenizerMode.cs +++ b/src/DataFilters/Grammar/Parsing/TokenizerMode.cs @@ -1,18 +1,17 @@ -namespace DataFilters.Grammar.Parsing +namespace DataFilters.Grammar.Parsing; + +/// +/// Defines the way "tokenizes" each read. +/// +public enum TokenizerMode { /// - /// Defines the way "tokenizes" each read. + /// Character are interpreted as-is /// - public enum TokenizerMode - { - /// - /// Character are interpreted as-is - /// - Normal, + Normal, - /// - /// Each character read is considered as already escaped - /// - Escaped - } + /// + /// Each character read is considered as already escaped + /// + Escaped } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/AndExpression.cs b/src/DataFilters/Grammar/Syntax/AndExpression.cs index 2a6cfcdf..ca257aa8 100644 --- a/src/DataFilters/Grammar/Syntax/AndExpression.cs +++ b/src/DataFilters/Grammar/Syntax/AndExpression.cs @@ -1,59 +1,58 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// A that combine two expressions using the logical AND operator +/// +public sealed class AndExpression : BinaryFilterExpression, IEquatable { - using System; + private readonly Lazy _lazyToString; + private readonly Lazy _lazyEscapedParseableString; + + /// + public override double Complexity => Right.Complexity * Left.Complexity; /// - /// A that combine two expressions using the logical AND operator + /// Builds a new that combines and using the logical + /// AND operator /// - public sealed class AndExpression : BinaryFilterExpression, IEquatable + /// + /// and/or will be wrapped inside a when either is a + /// or a instance. + /// + /// Left member + /// Right member + /// if either or is . + public AndExpression(FilterExpression left, FilterExpression right) : base(left, right) { - private readonly Lazy _lazyToString; - private readonly Lazy _lazyEscapedParseableString; - - /// - public override double Complexity => Right.Complexity * Left.Complexity; - - /// - /// Builds a new that combines and using the logical - /// AND operator - /// - /// - /// and/or will be wrapped inside a when either is a - /// or a instance. - /// - /// Left member - /// Right member - /// if either or is null. - public AndExpression(FilterExpression left, FilterExpression right) : base(left, right) - { - _lazyToString = new(() => $@"[""{nameof(Left)} ({Left.GetType().Name})"": {Left.EscapedParseableString}; ""{nameof(Right)} ({Right.GetType().Name})"": {Right.EscapedParseableString}]"); - _lazyEscapedParseableString = new(() => $"{Left.EscapedParseableString},{Right.EscapedParseableString}"); - } - - /// - public bool Equals(AndExpression other) => (Left.Equals(other?.Left) && Right.Equals(other?.Right)) || (Left.Equals(other?.Right) && Right.Equals(other?.Left)); - - /// - public override bool Equals(object obj) => Equals(obj as AndExpression); - - /// - public override int GetHashCode() => (Left, Right).GetHashCode(); - - /// - public override bool IsEquivalentTo(FilterExpression other) - => ReferenceEquals(this, other) - || other switch - { - AndExpression and => Equals(and) || (Left.IsEquivalentTo(and.Left) && Right.IsEquivalentTo(and.Right)) || (Left.IsEquivalentTo(and.Right) && Right.IsEquivalentTo(and.Left)), - ConstantValueExpression constant => Simplify().IsEquivalentTo(constant), - ISimplifiable simplifiable => IsEquivalentTo(simplifiable.Simplify()), - _ => false - }; - - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; - - /// - public override string ToString() => _lazyToString.Value; + _lazyToString = new(() => $@"[""{nameof(Left)} ({Left.GetType().Name})"": {Left.EscapedParseableString}; ""{nameof(Right)} ({Right.GetType().Name})"": {Right.EscapedParseableString}]"); + _lazyEscapedParseableString = new(() => $"{Left.EscapedParseableString},{Right.EscapedParseableString}"); } + + /// + public bool Equals(AndExpression other) => (Left.Equals(other?.Left) && Right.Equals(other?.Right)) || (Left.Equals(other?.Right) && Right.Equals(other?.Left)); + + /// + public override bool Equals(object obj) => Equals(obj as AndExpression); + + /// + public override int GetHashCode() => (Left, Right).GetHashCode(); + + /// + public override bool IsEquivalentTo(FilterExpression other) + => ReferenceEquals(this, other) + || other switch + { + AndExpression and => Equals(and) || (Left.IsEquivalentTo(and.Left) && Right.IsEquivalentTo(and.Right)) || (Left.IsEquivalentTo(and.Right) && Right.IsEquivalentTo(and.Left)), + ConstantValueExpression constant => Simplify().IsEquivalentTo(constant), + ISimplifiable simplifiable => IsEquivalentTo(simplifiable.Simplify()), + _ => false + }; + + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; + + /// + public override string ToString() => _lazyToString.Value; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs b/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs index c0592aa7..b423bc04 100644 --- a/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs +++ b/src/DataFilters/Grammar/Syntax/AsteriskExpression.cs @@ -1,55 +1,54 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// represent a * that can be used as a building block for more +/// complex s. +/// +public sealed class AsteriskExpression : FilterExpression, IEquatable, IBoundaryExpression { - using System; + /// + /// The unique instance of the . + /// + /// + /// This field is + /// + public static AsteriskExpression Instance { get; } = new(); + + private AsteriskExpression() { } + + /// + public bool Equals(AsteriskExpression other) => other is not null; + + /// + public override bool Equals(object obj) => Equals(obj as AsteriskExpression); + + /// + public override int GetHashCode() => 1; + + /// + public override string EscapedParseableString => "*"; /// - /// represent a * that can be used as a building block for more - /// complex s. + /// Computes a by adding a to a . /// - public sealed class AsteriskExpression : FilterExpression, IEquatable, IBoundaryExpression - { - /// - /// The unique instance of the . - /// - /// - /// This field is - /// - public static AsteriskExpression Instance { get; } = new(); - - private AsteriskExpression() { } - - /// - public bool Equals(AsteriskExpression other) => other is not null; - - /// - public override bool Equals(object obj) => Equals(obj as AsteriskExpression); - - /// - public override int GetHashCode() => 1; - - /// - public override string EscapedParseableString => "*"; - - /// - /// Computes a by adding a to a . - /// - /// - /// - /// - public static EndsWithExpression operator +(AsteriskExpression _, ConstantValueExpression right) => new(right.Value); + /// + /// + /// + public static EndsWithExpression operator +(AsteriskExpression _, ConstantValueExpression right) => new(right.Value); #if !NET6_0_OR_GREATER - /// - /// Computes a by adding a to a . - /// - /// Left operand - /// Right operand - /// + /// + /// Computes a by adding a to a . + /// + /// Left operand + /// Right operand + /// #else - /// + /// #endif #pragma warning disable IDE0060, RCS1163 - public static StartsWithExpression operator +(ConstantValueExpression left, AsteriskExpression right) => new(left.Value); + public static StartsWithExpression operator +(ConstantValueExpression left, AsteriskExpression right) => new(left.Value); #pragma warning restore IDE0060, RCS1163 - } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs b/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs index 09b858ae..1803d510 100644 --- a/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs +++ b/src/DataFilters/Grammar/Syntax/BoundaryExpression.cs @@ -1,67 +1,66 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; +/// +/// A that can be used to construct instances. +/// +/// +/// Builds a new instance. +/// +/// an +/// true if should be included in the interval and false otherwise. +public sealed class BoundaryExpression(IBoundaryExpression expression, bool included) : IEquatable +{ /// - /// A that can be used to construct instances. + /// Expression used as a boundary /// - /// - /// Builds a new instance. - /// - /// an - /// true if should be included in the interval and false otherwise. - public sealed class BoundaryExpression(IBoundaryExpression expression, bool included) : IEquatable - { - /// - /// Expression used as a boundary - /// - public IBoundaryExpression Expression { get; } = expression ?? throw new ArgumentNullException(nameof(expression)); + public IBoundaryExpression Expression { get; } = expression ?? throw new ArgumentNullException(nameof(expression)); - /// - /// Should the be included or excluded in the - /// - public bool Included { get; } = included; + /// + /// Should the be included or excluded in the + /// + public bool Included { get; } = included; - /// - public bool Equals(BoundaryExpression other) => other is not null - && Included == other.Included - && Expression.Equals(other.Expression); + /// + public bool Equals(BoundaryExpression other) => other is not null + && Included == other.Included + && Expression.Equals(other.Expression); - /// - public override bool Equals(object obj) => Equals(obj as BoundaryExpression); + /// + public override bool Equals(object obj) => Equals(obj as BoundaryExpression); - /// + /// #if !(NETSTANDARD1_3 || NETSTANDARD2_0) - public override int GetHashCode() => HashCode.Combine(Expression, Included); + public override int GetHashCode() => HashCode.Combine(Expression, Included); #else - public override int GetHashCode() - { - int hashCode = 1608575900; - hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(Expression); - hashCode = (hashCode * -1521134295) + Included.GetHashCode(); - return hashCode; - } + public override int GetHashCode() + { + int hashCode = 1608575900; + hashCode = (hashCode * -1521134295) + EqualityComparer.Default.GetHashCode(Expression); + hashCode = (hashCode * -1521134295) + Included.GetHashCode(); + return hashCode; + } #endif - /// - public static bool operator ==(BoundaryExpression left, BoundaryExpression right) => left switch - { - null => right is null, - _ => left.Equals(right), - }; + /// + public static bool operator ==(BoundaryExpression left, BoundaryExpression right) => left switch + { + null => right is null, + _ => left.Equals(right), + }; - /// - public static bool operator !=(BoundaryExpression left, BoundaryExpression right) => !(left == right); + /// + public static bool operator !=(BoundaryExpression left, BoundaryExpression right) => !(left == right); - /// - public override string ToString() => $"{{{Expression.GetType()}:{Expression.EscapedParseableString}, {nameof(Included)} : {Included}}}"; + /// + public override string ToString() => $"{{{Expression.GetType()}:{Expression.EscapedParseableString}, {nameof(Included)} : {Included}}}"; - /// - public void Deconstruct(out IBoundaryExpression expression, out bool included) - { - expression = Expression; - included = Included; - } + /// + public void Deconstruct(out IBoundaryExpression expression, out bool included) + { + expression = Expression; + included = Included; } } diff --git a/src/DataFilters/Grammar/Syntax/BracketExpression.cs b/src/DataFilters/Grammar/Syntax/BracketExpression.cs index 07508e1d..86880df8 100644 --- a/src/DataFilters/Grammar/Syntax/BracketExpression.cs +++ b/src/DataFilters/Grammar/Syntax/BracketExpression.cs @@ -1,101 +1,90 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; - using System.Linq; +namespace DataFilters.Grammar.Syntax; - using Utilities; +using System; +using System.Collections.Generic; +using System.Linq; - /// - /// A that holds a regex pattern as its . - /// - /// - /// As a regular expression can have many form, the constructor gives a way to define if it has : - /// - /// a range : [a-f] would be built like new RegularExpression(new RegularRangeValue('a', 'f')) - /// a set of values : [aMn] would be built by using as constructor parameters - /// a combination of both : [a-fMn] by using{ ('a', 'f', true), ('M', 'M', false), ('n', 'n', false), } to - /// - /// - public sealed class BracketExpression : FilterExpression, IEquatable - { - private static readonly ArrayEqualityComparer EqualityComparer = new(); +using Utilities; - /// - /// Builds a new instance. - /// - /// - public BracketExpression(params BracketValue[] values) - { - if (values is null) - { - throw new ArgumentNullException(nameof(values)); - } - - Values = values.Where(value => value is not null) - .ToArray(); - } +/// +/// A that holds a regex pattern as its . +/// +/// +/// As a regular expression can have many form, the constructor gives a way to define if it has : +/// +/// a range : [a-f] would be built like new RegularExpression(new RegularRangeValue('a', 'f')) +/// a set of values : [aMn] would be built by using as constructor parameters +/// a combination of both : [a-fMn] by using{ ('a', 'f', true), ('M', 'M', false), ('n', 'n', false), } to +/// +/// +/// +/// Builds a new instance. +/// +/// +/// if is . +public sealed class BracketExpression(params BracketValue[] values) : FilterExpression, IEquatable +{ + private static readonly ArrayEqualityComparer EqualityComparer = new(); - /// - /// Values of the original regex - /// - public IEnumerable Values { get; } + /// + /// Values of the original regex + /// + public IEnumerable Values { get; } = [.. values.Where(value => value is not null)]; - /// - public bool Equals(BracketExpression other) => other is not null - && EqualityComparer.Equals(Values.ToArray(), other.Values.ToArray()); + /// + public bool Equals(BracketExpression other) => other is not null + && EqualityComparer.Equals(Values.ToArray(), other.Values.ToArray()); - /// - public override bool Equals(object obj) => obj switch - { - BracketExpression bracket => Equals(bracket), - FilterExpression expression => IsEquivalentTo(expression), - _ => false - }; + /// + public override bool Equals(object obj) => obj switch + { + BracketExpression bracket => Equals(bracket), + FilterExpression expression => IsEquivalentTo(expression), + _ => false + }; - /// - public override int GetHashCode() => EqualityComparer.GetHashCode(Values.ToArray()); + /// + public override int GetHashCode() => EqualityComparer.GetHashCode(Values.ToArray()); - /// - public override string ToString() => $"{nameof(BracketExpression)} : [{string.Join(",", Values)}]"; + /// + public override string ToString() => $"{nameof(BracketExpression)} : [{string.Join(",", Values)}]"; - /// - public override double Complexity => Values.Select(x => x.Complexity) - .Aggregate((initial, current) => initial * current); + /// + public override double Complexity => Values.Select(x => x.Complexity) + .Aggregate((initial, current) => initial * current); - /// - public override bool IsEquivalentTo(FilterExpression other) + /// + public override bool IsEquivalentTo(FilterExpression other) + { + bool equivalent = false; + if (other is not null) { - bool equivalent = false; - if (other is not null) + if (ReferenceEquals(this, other)) { - if (ReferenceEquals(this, other)) - { - equivalent = true; - } - else if (other is BracketExpression bracketExpression) - { - equivalent = Equals(bracketExpression); - } - else if (other is OneOfExpression oneOf) - { - equivalent = oneOf.Values.Exactly(oneOf.Values.OfType().Count()) - && oneOf.Values.All(x => x is StringValueExpression constant) - && Values.All(value => value switch - { - ConstantBracketValue constant => constant.Value.All(chr => oneOf.Values.Any(expr => expr.As().Value.Equals(chr.ToString()))), - RangeBracketValue range => Enumerable.Range(range.Start, range.End - range.Start + 1) - .Select(ascii => (char)ascii) - .All(chr => oneOf.Values.Any(expr => expr.As().Value.Equals(chr.ToString()))), - _ => throw new NotSupportedException("Unsupported value") - }); - } + equivalent = true; + } + else if (other is BracketExpression bracketExpression) + { + equivalent = Equals(bracketExpression); + } + else if (other is OneOfExpression oneOf) + { + equivalent = oneOf.Values.Exactly(oneOf.Values.OfType().Count()) + && oneOf.Values.All(x => x is StringValueExpression constant) + && Values.All(value => value switch + { + ConstantBracketValue constant => constant.Value.All(chr => oneOf.Values.Any(expr => expr.As().Value.Equals(chr.ToString()))), + RangeBracketValue range => Enumerable.Range(range.Start, range.End - range.Start + 1) + .Select(ascii => (char)ascii) + .All(chr => oneOf.Values.Any(expr => expr.As().Value.Equals(chr.ToString()))), + _ => throw new NotSupportedException("Unsupported value") + }); } - - return equivalent; } - /// - public override string EscapedParseableString => $"[{string.Join(",", Values)}]"; + return equivalent; } + + /// + public override string EscapedParseableString => $"[{string.Join(",", Values)}]"; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/BracketValue.cs b/src/DataFilters/Grammar/Syntax/BracketValue.cs index a7460ba9..132c49be 100644 --- a/src/DataFilters/Grammar/Syntax/BracketValue.cs +++ b/src/DataFilters/Grammar/Syntax/BracketValue.cs @@ -1,20 +1,19 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Building block for . +/// +public abstract class BracketValue : IParseableString, IHaveComplexity { - /// - /// Building block for . - /// - public abstract class BracketValue : IParseableString, IHaveComplexity - { - /// - public abstract string EscapedParseableString { get; } + /// + public abstract string EscapedParseableString { get; } - /// - public abstract string OriginalString { get; } + /// + public abstract string OriginalString { get; } - /// - public abstract double Complexity { get; } + /// + public abstract double Complexity { get; } - /// - public override string ToString() => EscapedParseableString; - } + /// + public override string ToString() => EscapedParseableString; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/ConstantBracketValue.cs b/src/DataFilters/Grammar/Syntax/ConstantBracketValue.cs index d1ee1cf4..3add9d4f 100644 --- a/src/DataFilters/Grammar/Syntax/ConstantBracketValue.cs +++ b/src/DataFilters/Grammar/Syntax/ConstantBracketValue.cs @@ -1,59 +1,58 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; + +/// +/// Stores a constant value used in a bracket expression +/// +public sealed class ConstantBracketValue : BracketValue, IEquatable { - using System; - using System.Collections.Generic; + private readonly Lazy _lazyEscapedParseableString; /// - /// Stores a constant value used in a bracket expression + /// Builds a new instance. /// - public sealed class ConstantBracketValue : BracketValue, IEquatable + /// + public ConstantBracketValue(string value) { - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Builds a new instance. - /// - /// - public ConstantBracketValue(string value) - { - Value = value; - _lazyEscapedParseableString = new(() => $"[{Value}]"); - } + Value = value; + _lazyEscapedParseableString = new(() => $"[{Value}]"); + } - /// - /// Constant value of the regex - /// - public string Value { get; } + /// + /// Constant value of the regex + /// + public string Value { get; } - /// - public bool Equals(ConstantBracketValue other) => Value.Equals(other?.Value); + /// + public bool Equals(ConstantBracketValue other) => Value.Equals(other?.Value); - /// - public override bool Equals(object obj) + /// + public override bool Equals(object obj) + { + return obj switch { - return obj switch - { - RangeBracketValue rangeBracket => rangeBracket.Equals(this), - _ => Equals(obj as ConstantBracketValue), - }; - } + RangeBracketValue rangeBracket => rangeBracket.Equals(this), + _ => Equals(obj as ConstantBracketValue), + }; + } - /// - public static bool operator ==(ConstantBracketValue left, ConstantBracketValue right) => EqualityComparer.Default.Equals(left, right); + /// + public static bool operator ==(ConstantBracketValue left, ConstantBracketValue right) => EqualityComparer.Default.Equals(left, right); - /// - public static bool operator !=(ConstantBracketValue left, ConstantBracketValue right) => !(left == right); + /// + public static bool operator !=(ConstantBracketValue left, ConstantBracketValue right) => !(left == right); - /// - public override int GetHashCode() => Value.GetHashCode(); + /// + public override int GetHashCode() => Value.GetHashCode(); - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; - /// - public override string OriginalString => $"[{Value}]"; + /// + public override string OriginalString => $"[{Value}]"; - /// - public override double Complexity => 1 + Math.Pow(2, Value.Length); - } + /// + public override double Complexity => 1 + Math.Pow(2, Value.Length); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs b/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs index 5b1a05b7..35476f8b 100644 --- a/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/ConstantValueExpression.cs @@ -1,60 +1,59 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// An expression that holds a constant value +/// +public abstract class ConstantValueExpression : FilterExpression, IEquatable { - using System; + /// + /// Gets the "raw" value hold by the current instance. + /// + public string Value { get; } /// - /// An expression that holds a constant value + /// Builds a new that holds the specified . /// - public abstract class ConstantValueExpression : FilterExpression, IEquatable + /// + /// is or is not currently supported + /// + /// is . + protected ConstantValueExpression(string value) { - /// - /// Gets the "raw" value hold by the current instance. - /// - public string Value { get; } - - /// - /// Builds a new that holds the specified . - /// - /// - /// is or is not currently supported - /// - /// is null. - protected ConstantValueExpression(string value) + Value = value switch { - Value = value switch - { - null => throw new ArgumentNullException(nameof(value)), - { Length: 0 } => throw new ArgumentOutOfRangeException(nameof(value)), - _ => value - }; - } - - /// - public virtual bool Equals(ConstantValueExpression other) => Equals(Value, other?.Value); - - /// - public override bool Equals(object obj) => Equals(obj as ConstantValueExpression); - - /// - public override int GetHashCode() => Value.GetHashCode(); - - /// - public override double Complexity => 1; - - /// - public static bool operator ==(ConstantValueExpression left, ConstantValueExpression right) - => left?.Equals(right) ?? false; - - /// - public static bool operator !=(ConstantValueExpression left, ConstantValueExpression right) - => !(left == right); - - /// - /// Combines and into a . - /// - /// - /// - /// - public static AndExpression operator +(ConstantValueExpression left, EndsWithExpression right) => left + AsteriskExpression.Instance + new EndsWithExpression(right.Value); + null => throw new ArgumentNullException(nameof(value)), + { Length: 0 } => throw new ArgumentOutOfRangeException(nameof(value)), + _ => value + }; } + + /// + public virtual bool Equals(ConstantValueExpression other) => Equals(Value, other?.Value); + + /// + public override bool Equals(object obj) => Equals(obj as ConstantValueExpression); + + /// + public override int GetHashCode() => Value.GetHashCode(); + + /// + public override double Complexity => 1; + + /// + public static bool operator ==(ConstantValueExpression left, ConstantValueExpression right) + => left?.Equals(right) ?? false; + + /// + public static bool operator !=(ConstantValueExpression left, ConstantValueExpression right) + => !(left == right); + + /// + /// Combines and into a . + /// + /// + /// + /// + public static AndExpression operator +(ConstantValueExpression left, EndsWithExpression right) => left + AsteriskExpression.Instance + new EndsWithExpression(right.Value); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/ContainsExpression.cs b/src/DataFilters/Grammar/Syntax/ContainsExpression.cs index bcb35e15..d4306e23 100644 --- a/src/DataFilters/Grammar/Syntax/ContainsExpression.cs +++ b/src/DataFilters/Grammar/Syntax/ContainsExpression.cs @@ -1,109 +1,112 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; #if !NETSTANDARD1_3 - using Ardalis.GuardClauses; +using Ardalis.GuardClauses; #endif - using static DataFilters.Grammar.Parsing.FilterTokenizer; +using static DataFilters.Grammar.Parsing.FilterTokenizer; + +/// +/// A that holds a string value +/// +public sealed class ContainsExpression : FilterExpression, IEquatable +{ + /// + /// The value that was between two + /// + public string Value { get; } + + private readonly Lazy _lazyEscapedParseableString; /// - /// A that holds a string value + /// Builds a new that holds the specified . /// - public sealed class ContainsExpression : FilterExpression, IEquatable + /// + /// if is . + /// if is empty + public ContainsExpression(string value) { - /// - /// The value that was between two - /// - public string Value { get; } - - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Builds a new that holds the specified . - /// - /// - /// if is null - /// if is empty - public ContainsExpression(string value) +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNullOrEmpty(value); +#else + if (value is null) { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } + throw new ArgumentNullException(nameof(value)); + } +#endif - if (value.Length == 0) - { - throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} cannot be empty"); - } + if (value.Length == 0) + { + throw new ArgumentOutOfRangeException(nameof(value), $"{nameof(value)} cannot be empty"); + } - Value = value; + Value = value; - _lazyEscapedParseableString = new(() => - { - // The length of the final parseable string in worst case scenarios will double (1 backlash + the escaped character) - // Also we need an extra position for the final '*' that will be append in all cases - bool requireEscapingCharacters = Value.AtLeastOnce(chr => SpecialCharacters.Contains(chr)); - StringBuilder parseableString; + _lazyEscapedParseableString = new(() => + { + // The length of the final parseable string in worst case scenarios will double (1 backlash + the escaped character) + // Also we need an extra position for the final '*' that will be append in all cases + bool requireEscapingCharacters = Value.AtLeastOnce(chr => SpecialCharacters.Contains(chr)); + StringBuilder parseableString; - if (requireEscapingCharacters) + if (requireEscapingCharacters) + { + parseableString = new((Value.Length * 2) + 2); + foreach (char chr in Value) { - parseableString = new((Value.Length * 2) + 2); - foreach (char chr in Value) + if (SpecialCharacters.Contains(chr)) { - if (SpecialCharacters.Contains(chr)) - { - parseableString = parseableString.Append('\\'); - } - parseableString = parseableString.Append(chr); + parseableString = parseableString.Append('\\'); } + parseableString = parseableString.Append(chr); } - else - { - parseableString = new(Value, Value.Length + 2); - } + } + else + { + parseableString = new(Value, Value.Length + 2); + } - return parseableString.Insert(0, "*").Append('*').ToString(); - }); - } + return parseableString.Insert(0, "*").Append('*').ToString(); + }); + } - /// - /// Builds a new that holds the specified . - /// - /// - /// is null. - public ContainsExpression(TextExpression text) + /// + /// Builds a new that holds the specified . + /// + /// + /// is . + public ContainsExpression(TextExpression text) #if !NETSTANDARD1_3 - : this (Guard.Against.Null(text, nameof(text)).OriginalString) - { - _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}*"); - } + : this(Guard.Against.Null(text, nameof(text)).OriginalString) + { + _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}*"); + } #else + { + if (text is null) { - if (text is null) - { - throw new ArgumentNullException(nameof(text)); - } - _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}*"); + throw new ArgumentNullException(nameof(text)); } + _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}*"); + } #endif - /// - public override double Complexity => 1.5; + /// + public override double Complexity => 1.5; - /// - public override bool Equals(object obj) => Equals(obj as ContainsExpression); + /// + public override bool Equals(object obj) => Equals(obj as ContainsExpression); - /// - public bool Equals(ContainsExpression other) => Value == other?.Value; + /// + public bool Equals(ContainsExpression other) => Value == other?.Value; - /// - public override int GetHashCode() => Value.GetHashCode(); + /// + public override int GetHashCode() => Value.GetHashCode(); - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; - } + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/DateExpression.cs b/src/DataFilters/Grammar/Syntax/DateExpression.cs index 955bac5f..65243ed0 100644 --- a/src/DataFilters/Grammar/Syntax/DateExpression.cs +++ b/src/DataFilters/Grammar/Syntax/DateExpression.cs @@ -1,86 +1,91 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; +namespace DataFilters.Grammar.Syntax; + +using System; +/// +/// A that holds a date. +/// +public sealed class DateExpression : FilterExpression, IEquatable, IBoundaryExpression +{ /// - /// A that holds a date. + /// Year part of the date /// - public sealed class DateExpression : FilterExpression, IEquatable, IBoundaryExpression - { - /// - /// Year part of the date - /// - public int Year { get; } + public int Year { get; } - /// - /// Month part of the date - /// - public int Month { get; } + /// + /// Month part of the date + /// + public int Month { get; } - /// - /// Day part of the date - /// - public int Day { get; } + /// + /// Day part of the date + /// + public int Day { get; } - /// - /// Builds a new instance. - /// - /// - /// - /// - /// - /// either / or is less than 1. - /// - public DateExpression(int year = 1, int month = 1, int day = 1) + /// + /// Builds a new instance. + /// + /// + /// + /// + /// + /// either / or is less than 1. + /// + public DateExpression(int year = 1, int month = 1, int day = 1) + { +#if !NET8_0_OR_GREATER + if (year < 1) { - if (year < 1) - { - throw new ArgumentOutOfRangeException(nameof(year)); - } - - if (month < 1) - { - throw new ArgumentOutOfRangeException(nameof(month)); - } + throw new ArgumentOutOfRangeException(nameof(year)); + } - if (day < 1) - { - throw new ArgumentOutOfRangeException(nameof(day)); - } + if (month < 1) + { + throw new ArgumentOutOfRangeException(nameof(month)); + } - Year = year; - Month = month; - Day = day; + if (day < 1) + { + throw new ArgumentOutOfRangeException(nameof(day)); } +#else + ArgumentOutOfRangeException.ThrowIfLessThan(year, 1); + ArgumentOutOfRangeException.ThrowIfLessThan(month, 1); + ArgumentOutOfRangeException.ThrowIfLessThan(day, 1); +#endif - /// - public bool Equals(DateExpression other) => (Year, Month, Day) == (other?.Year, other?.Month, other?.Day); + Year = year; + Month = month; + Day = day; + } - /// - public override bool Equals(object obj) => Equals(obj as DateExpression); + /// + public bool Equals(DateExpression other) => (Year, Month, Day) == (other?.Year, other?.Month, other?.Day); - /// - public override bool IsEquivalentTo(FilterExpression other) => other switch - { - DateExpression date => Equals(date), - DateTimeExpression { Date: var date, Time: null, Offset: null } => Equals(date), - _ => Equals((other as ISimplifiable)?.Simplify() ?? other) - }; + /// + public override bool Equals(object obj) => Equals(obj as DateExpression); - /// - public override int GetHashCode() => (Year, Month, Day).GetHashCode(); + /// + public override bool IsEquivalentTo(FilterExpression other) => other switch + { + DateExpression date => Equals(date), + DateTimeExpression { Date: var date, Time: null, Offset: null } => Equals(date), + _ => Equals((other as ISimplifiable)?.Simplify() ?? other) + }; - /// - public override string EscapedParseableString => $"{Year:D4}-{Month:D2}-{Day:D2}"; + /// + public override int GetHashCode() => (Year, Month, Day).GetHashCode(); - /// - public static bool operator ==(DateExpression left, DateExpression right) => left switch - { - null => right is null, - _ => left.Equals(right) - }; + /// + public override string EscapedParseableString => $"{Year:D4}-{Month:D2}-{Day:D2}"; - /// - public static bool operator !=(DateExpression left, DateExpression right) => !(left == right); - } + /// + public static bool operator ==(DateExpression left, DateExpression right) => left switch + { + null => right is null, + _ => left.Equals(right) + }; + + /// + public static bool operator !=(DateExpression left, DateExpression right) => !(left == right); } diff --git a/src/DataFilters/Grammar/Syntax/DateTimeExpression.cs b/src/DataFilters/Grammar/Syntax/DateTimeExpression.cs index 8fe6c829..731c8a23 100644 --- a/src/DataFilters/Grammar/Syntax/DateTimeExpression.cs +++ b/src/DataFilters/Grammar/Syntax/DateTimeExpression.cs @@ -1,144 +1,143 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// A implementation that can holds a datetime value +/// +public sealed class DateTimeExpression : FilterExpression, IEquatable, IBoundaryExpression { - using System; + private readonly Lazy _lazyEscapedParseableString; + + /// + /// Date part of the expression + /// + public DateExpression Date { get; } + + /// + /// Time part of the expression + /// + public TimeExpression Time { get; } + + /// + /// Offset with the UTC. + /// + public OffsetExpression Offset { get; } /// - /// A implementation that can holds a datetime value + /// Definies the kind of the current instance. /// - public sealed class DateTimeExpression : FilterExpression, IEquatable, IBoundaryExpression + public DateTimeExpressionKind Kind { get; } + + /// + /// Builds a new instance where only part is set + /// + /// + /// if is . + public DateTimeExpression(DateExpression date) : this(date, null) { - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Date part of the expression - /// - public DateExpression Date { get; } - - /// - /// Time part of the expression - /// - public TimeExpression Time { get; } - - /// - /// Offset with the UTC. - /// - public OffsetExpression Offset { get; } - - /// - /// Definies the kind of the current instance. - /// - public DateTimeExpressionKind Kind { get; } - - /// - /// Builds a new instance where only part is set - /// - /// - /// if is null. - public DateTimeExpression(DateExpression date) : this(date, null) - { - } + } - /// - /// Builds a new where only the part is specified - /// - /// - /// if is null. - public DateTimeExpression(TimeExpression time) : this(null, time) { } - - /// - /// Builds a new with both and - /// specified. - /// - /// date part of the expression - /// time part of the expression - /// if both and are null. - public DateTimeExpression(DateExpression date, TimeExpression time) : this(date, time, null) - { - } + /// + /// Builds a new where only the part is specified + /// + /// + /// if is . + public DateTimeExpression(TimeExpression time) : this(null, time) { } - /// - /// Builds a new with both and - /// specified. - /// - /// date part of the expression - /// time part of the expression - /// offset with the UTC time - /// if both and are null. - public DateTimeExpression(DateExpression date, TimeExpression time, OffsetExpression offset) - { - if (date is null && time is null) - { - throw new ArgumentException($"Both {nameof(date)} and {nameof(time)} cannot be null and "); - } - - Date = date; - Time = time; - Offset = offset; - Kind = offset is not null - ? DateTimeExpressionKind.Utc - : DateTimeExpressionKind.Unspecified; - - _lazyEscapedParseableString = new(() => (date, time, offset) switch - { - ({ }, { }, { }) => $"{date.EscapedParseableString}T{time.EscapedParseableString}{offset.EscapedParseableString}", - ({ }, { }, null) => $"{date.EscapedParseableString}T{time.EscapedParseableString}", - (null, { }, _) => $"T{time.EscapedParseableString}", - _ => date.EscapedParseableString - }); - } + /// + /// Builds a new with both and + /// specified. + /// + /// date part of the expression + /// time part of the expression + /// if both and are . + public DateTimeExpression(DateExpression date, TimeExpression time) : this(date, time, null) + { + } - /// - /// Builds a new with both and values - /// extracted from . - /// - /// value to use as source of the current instance - public DateTimeExpression(DateTime localDateTime) : this(new DateExpression(localDateTime.Year, localDateTime.Month, localDateTime.Day), new TimeExpression(localDateTime.Hour, localDateTime.Minute, localDateTime.Second)) + /// + /// Builds a new with both and + /// specified. + /// + /// date part of the expression + /// time part of the expression + /// offset with the UTC time + /// if both and are . + public DateTimeExpression(DateExpression date, TimeExpression time, OffsetExpression offset) + { + if (date is null && time is null) { + throw new ArgumentException($"Both {nameof(date)} and {nameof(time)} cannot be null and "); } - /// - public void Deconstruct(out DateExpression date, out TimeExpression time, out OffsetExpression offset, out DateTimeExpressionKind kind) - { - date = Date; - time = Time; - offset = Offset; - kind = Offset is null - ? DateTimeExpressionKind.Unspecified - : DateTimeExpressionKind.Utc; - } + Date = date; + Time = time; + Offset = offset; + Kind = offset is not null + ? DateTimeExpressionKind.Utc + : DateTimeExpressionKind.Unspecified; + + _lazyEscapedParseableString = new(() => (date, time, offset) switch + { + ({ }, { }, { }) => $"{date.EscapedParseableString}T{time.EscapedParseableString}{offset.EscapedParseableString}", + ({ }, { }, null) => $"{date.EscapedParseableString}T{time.EscapedParseableString}", + (null, { }, _) => $"T{time.EscapedParseableString}", + _ => date.EscapedParseableString + }); + } - /// - public bool Equals(DateTimeExpression other) => Equals(Date, other?.Date) - && Equals(Time, other?.Time) - && Equals(Offset, other?.Offset) - ; + /// + /// Builds a new with both and values + /// extracted from . + /// + /// value to use as source of the current instance + public DateTimeExpression(DateTime localDateTime) : this(new DateExpression(localDateTime.Year, localDateTime.Month, localDateTime.Day), new TimeExpression(localDateTime.Hour, localDateTime.Minute, localDateTime.Second)) + { + } - /// - public override bool Equals(object obj) => obj switch - { - DateTimeExpression dateTime => Equals(dateTime), - DateExpression date => Time is null && Offset is null && Date.Equals(date), - TimeExpression time => Date is null && Offset is null && Time.Equals(time), - _ => false - }; + /// + public void Deconstruct(out DateExpression date, out TimeExpression time, out OffsetExpression offset, out DateTimeExpressionKind kind) + { + date = Date; + time = Time; + offset = Offset; + kind = Offset is null + ? DateTimeExpressionKind.Unspecified + : DateTimeExpressionKind.Utc; + } - /// - public override int GetHashCode() => (Date, Time, Offset).GetHashCode(); + /// + public bool Equals(DateTimeExpression other) => Equals(Date, other?.Date) + && Equals(Time, other?.Time) + && Equals(Offset, other?.Offset) + ; - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; + /// + public override bool Equals(object obj) => obj switch + { + DateTimeExpression dateTime => Equals(dateTime), + DateExpression date => Time is null && Offset is null && Date.Equals(date), + TimeExpression time => Date is null && Offset is null && Time.Equals(time), + _ => false + }; - /// - public override double Complexity => (Date?.Complexity ?? 1) + (Time?.Complexity ?? 1) + (Offset?.Complexity ?? 1); + /// + public override int GetHashCode() => (Date, Time, Offset).GetHashCode(); - /// - public static bool operator ==(DateTimeExpression left, DateTimeExpression right) => left switch - { - null => right is null, - _ => left.Equals(right) - }; + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; - /// - public static bool operator !=(DateTimeExpression left, DateTimeExpression right) => !(left == right); - } + /// + public override double Complexity => (Date?.Complexity ?? 1) + (Time?.Complexity ?? 1) + (Offset?.Complexity ?? 1); + + /// + public static bool operator ==(DateTimeExpression left, DateTimeExpression right) => left switch + { + null => right is null, + _ => left.Equals(right) + }; + + /// + public static bool operator !=(DateTimeExpression left, DateTimeExpression right) => !(left == right); } diff --git a/src/DataFilters/Grammar/Syntax/DateTimeExpressionKind.cs b/src/DataFilters/Grammar/Syntax/DateTimeExpressionKind.cs index 251d16ec..373a4cb4 100644 --- a/src/DataFilters/Grammar/Syntax/DateTimeExpressionKind.cs +++ b/src/DataFilters/Grammar/Syntax/DateTimeExpressionKind.cs @@ -1,23 +1,22 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Defines a type of instance +/// +public enum DateTimeExpressionKind { /// - /// Defines a type of instance + /// Default value /// - public enum DateTimeExpressionKind - { - /// - /// Default value - /// - Unspecified, + Unspecified, - /// - /// Inidcates that the is a local date/time. - /// - Local, + /// + /// Inidcates that the is a local date/time. + /// + Local, - /// - /// Inidcates that the is a UTC datetime. - /// - Utc - } + /// + /// Inidcates that the is a UTC datetime. + /// + Utc } diff --git a/src/DataFilters/Grammar/Syntax/DurationExpression.cs b/src/DataFilters/Grammar/Syntax/DurationExpression.cs index 60bf2ff6..d71bc50a 100644 --- a/src/DataFilters/Grammar/Syntax/DurationExpression.cs +++ b/src/DataFilters/Grammar/Syntax/DurationExpression.cs @@ -1,145 +1,144 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// A implementation that contains values associated to a duration (see "https://en.wikipedia.org/wiki/ISO_8601#Durations") +/// +public sealed class DurationExpression : FilterExpression, IEquatable { - using System; + /// + /// Years part of the expression + /// + public int Years { get; } /// - /// A implementation that contains values associated to a duration (see "https://en.wikipedia.org/wiki/ISO_8601#Durations") + /// "Months" part of the expression /// - public sealed class DurationExpression : FilterExpression, IEquatable - { - /// - /// Years part of the expression - /// - public int Years { get; } - - /// - /// "Months" part of the expression - /// - public int Months { get; } - - /// - /// "Days" part of the expression - /// - public int Days { get; } - - /// - /// "Hours" part of the expression - /// - public int Hours { get; } - - /// - /// "Minutes" part of the expression - /// - public int Minutes { get; } - - /// - /// Seconds part of the expression - /// - public int Seconds { get; } - - private readonly Lazy lazyEscapedParseableString; - - /// - /// Weeks part of the expression - /// - public int Weeks { get; } - - /// - /// Builds a new instance - /// - /// - /// - /// - /// - /// - /// - /// - public DurationExpression(int years = 0, int months = 0, int weeks = 0, int days = 0, int hours = 0, int minutes = 0, int seconds = 0) - { - if (years < 0) - { - throw new ArgumentOutOfRangeException(nameof(years), years, $"{nameof(years)} cannot be negative"); - } + public int Months { get; } - if (months < 0) - { - throw new ArgumentOutOfRangeException(nameof(months), months, $"{nameof(months)} cannot be negative"); - } + /// + /// "Days" part of the expression + /// + public int Days { get; } - if (weeks < 0) - { - throw new ArgumentOutOfRangeException(nameof(weeks), weeks, $"{nameof(weeks)} cannot be negative"); - } + /// + /// "Hours" part of the expression + /// + public int Hours { get; } - if (days < 0) - { - throw new ArgumentOutOfRangeException(nameof(days), days, $"{nameof(days)} cannot be negative"); - } + /// + /// "Minutes" part of the expression + /// + public int Minutes { get; } - if (hours < 0) - { - throw new ArgumentOutOfRangeException(nameof(hours), hours, $"{nameof(hours)} cannot be negative"); - } + /// + /// Seconds part of the expression + /// + public int Seconds { get; } - if (minutes < 0) - { - throw new ArgumentOutOfRangeException(nameof(minutes), minutes, $"{nameof(minutes)} cannot be negative"); - } + private readonly Lazy lazyEscapedParseableString; - if (seconds < 0) - { - throw new ArgumentOutOfRangeException(nameof(seconds), seconds, $"{nameof(seconds)} cannot be negative"); - } + /// + /// Weeks part of the expression + /// + public int Weeks { get; } + + /// + /// Builds a new instance + /// + /// + /// + /// + /// + /// + /// + /// + public DurationExpression(int years = 0, int months = 0, int weeks = 0, int days = 0, int hours = 0, int minutes = 0, int seconds = 0) + { + if (years < 0) + { + throw new ArgumentOutOfRangeException(nameof(years), years, $"{nameof(years)} cannot be negative"); + } - (Years, Months, Weeks, Days, Hours, Minutes, Seconds) = (years, months, weeks, days, hours, minutes, seconds); + if (months < 0) + { + throw new ArgumentOutOfRangeException(nameof(months), months, $"{nameof(months)} cannot be negative"); + } - lazyEscapedParseableString = new Lazy(() => (Years, Months, Weeks, Days, Hours, Minutes, Seconds) switch - { - (0, 0, 0, 0, 0, 0, 0) => "PT0S", - _ => $"P{(Years > 0 ? $"{Years}Y" : string.Empty)}{(Months > 0 ? $"{Months}M" : string.Empty)}{(Weeks > 0 ? $"{Weeks}W" : string.Empty)}{(Days > 0 ? $"{Days}D" : string.Empty)}T{(Hours > 0 ? $"{Hours}H" : string.Empty)}{(Minutes > 0 ? $"{Minutes}M" : string.Empty)}{(Seconds > 0 ? $"{Seconds}S" : string.Empty)}" - }); + if (weeks < 0) + { + throw new ArgumentOutOfRangeException(nameof(weeks), weeks, $"{nameof(weeks)} cannot be negative"); } - /// - public override bool IsEquivalentTo(FilterExpression other) + if (days < 0) { - bool equivalent = false; + throw new ArgumentOutOfRangeException(nameof(days), days, $"{nameof(days)} cannot be negative"); + } - if (((other as ISimplifiable)?.Simplify() ?? other) is DurationExpression otherDuration) - { - equivalent = Equals(otherDuration); - if (!equivalent) - { - DateTime otherDateTime = ConvertToDateTime(otherDuration); - DateTime current = ConvertToDateTime(this); - - equivalent = (current - otherDateTime) == TimeSpan.Zero; - } - } + if (hours < 0) + { + throw new ArgumentOutOfRangeException(nameof(hours), hours, $"{nameof(hours)} cannot be negative"); + } + + if (minutes < 0) + { + throw new ArgumentOutOfRangeException(nameof(minutes), minutes, $"{nameof(minutes)} cannot be negative"); + } - return equivalent; + if (seconds < 0) + { + throw new ArgumentOutOfRangeException(nameof(seconds), seconds, $"{nameof(seconds)} cannot be negative"); + } + + (Years, Months, Weeks, Days, Hours, Minutes, Seconds) = (years, months, weeks, days, hours, minutes, seconds); + + lazyEscapedParseableString = new Lazy(() => (Years, Months, Weeks, Days, Hours, Minutes, Seconds) switch + { + (0, 0, 0, 0, 0, 0, 0) => "PT0S", + _ => $"P{(Years > 0 ? $"{Years}Y" : string.Empty)}{(Months > 0 ? $"{Months}M" : string.Empty)}{(Weeks > 0 ? $"{Weeks}W" : string.Empty)}{(Days > 0 ? $"{Days}D" : string.Empty)}T{(Hours > 0 ? $"{Hours}H" : string.Empty)}{(Minutes > 0 ? $"{Minutes}M" : string.Empty)}{(Seconds > 0 ? $"{Seconds}S" : string.Empty)}" + }); + } + + /// + public override bool IsEquivalentTo(FilterExpression other) + { + bool equivalent = false; - static DateTime ConvertToDateTime(DurationExpression duration) + if (((other as ISimplifiable)?.Simplify() ?? other) is DurationExpression otherDuration) + { + equivalent = Equals(otherDuration); + if (!equivalent) { - return DateTime.MinValue.AddYears(duration.Years) - .AddMonths(duration.Months) - .AddDays((duration.Weeks * 7) + duration.Days) - .AddHours(duration.Hours) - .AddMinutes(duration.Minutes) - .AddSeconds(duration.Seconds); + DateTime otherDateTime = ConvertToDateTime(otherDuration); + DateTime current = ConvertToDateTime(this); + + equivalent = (current - otherDateTime) == TimeSpan.Zero; } } - /// - public override bool Equals(object obj) => Equals(obj as DurationExpression); + return equivalent; + + static DateTime ConvertToDateTime(DurationExpression duration) + { + return DateTime.MinValue.AddYears(duration.Years) + .AddMonths(duration.Months) + .AddDays((duration.Weeks * 7) + duration.Days) + .AddHours(duration.Hours) + .AddMinutes(duration.Minutes) + .AddSeconds(duration.Seconds); + } + } + + /// + public override bool Equals(object obj) => Equals(obj as DurationExpression); - /// - public bool Equals(DurationExpression other) => (Years, Months, Weeks, Days, Hours, Minutes, Seconds) == (other?.Years, other?.Months, other?.Weeks, other?.Days, other?.Hours, other?.Minutes, other?.Seconds); + /// + public bool Equals(DurationExpression other) => (Years, Months, Weeks, Days, Hours, Minutes, Seconds) == (other?.Years, other?.Months, other?.Weeks, other?.Days, other?.Hours, other?.Minutes, other?.Seconds); - /// - public override int GetHashCode() => (Years, Months, Weeks, Days, Hours, Minutes, Seconds).GetHashCode(); + /// + public override int GetHashCode() => (Years, Months, Weeks, Days, Hours, Minutes, Seconds).GetHashCode(); - /// - public override string EscapedParseableString => lazyEscapedParseableString.Value; - } + /// + public override string EscapedParseableString => lazyEscapedParseableString.Value; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/EndsWithExpression.cs b/src/DataFilters/Grammar/Syntax/EndsWithExpression.cs index c71b3334..d966e52b 100644 --- a/src/DataFilters/Grammar/Syntax/EndsWithExpression.cs +++ b/src/DataFilters/Grammar/Syntax/EndsWithExpression.cs @@ -1,116 +1,119 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; - using static DataFilters.Grammar.Parsing.FilterTokenizer; +using static DataFilters.Grammar.Parsing.FilterTokenizer; #if !NETSTANDARD1_3 - using Ardalis.GuardClauses; +using Ardalis.GuardClauses; #endif +/// +/// A that holds a string value +/// +public sealed class EndsWithExpression : FilterExpression, IEquatable +{ + /// + /// The value of the expression + /// + public string Value { get; } + + private readonly Lazy _lazyEscapedParseableString; + /// - /// A that holds a string value + /// Builds a new that holds the specified . /// - public sealed class EndsWithExpression : FilterExpression, IEquatable + /// + /// is . + /// 's length is 0. + public EndsWithExpression(string value) { - /// - /// The value of the expression - /// - public string Value { get; } - - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Builds a new that holds the specified . - /// - /// - /// is null. - /// 's length is 0. - public EndsWithExpression(string value) +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNullOrWhiteSpace(value); +#else + if (value is null) { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } - if (value.Length == 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } - Value = value; + throw new ArgumentNullException(nameof(value)); + } + if (value.Length == 0) + { + throw new ArgumentException("Value cannot be empty", nameof(value)); + } +#endif + Value = value; - _lazyEscapedParseableString = new(() => - { - // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) - // Also we need an extra position for the final '*' that will be append in all cases - bool requireEscapingCharacters = value.AtLeastOnce(chr => SpecialCharacters.Contains(chr)); - StringBuilder parseableString; + _lazyEscapedParseableString = new(() => + { + // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) + // Also we need an extra position for the final '*' that will be append in all cases + bool requireEscapingCharacters = value.AtLeastOnce(chr => SpecialCharacters.Contains(chr)); + StringBuilder parseableString; - if (requireEscapingCharacters) + if (requireEscapingCharacters) + { + parseableString = new((value.Length * 2) + 1); + foreach (char chr in value) { - parseableString = new((value.Length * 2) + 1); - foreach (char chr in value) + if (SpecialCharacters.Contains(chr)) { - if (SpecialCharacters.Contains(chr)) - { - parseableString = parseableString.Append('\\'); - } - parseableString = parseableString.Append(chr); + parseableString = parseableString.Append('\\'); } + parseableString = parseableString.Append(chr); } - else - { - parseableString = new(value, value.Length + 1); - } + } + else + { + parseableString = new(value, value.Length + 1); + } - return parseableString.Insert(0, '*').ToString(); - }); - } + return parseableString.Insert(0, '*').ToString(); + }); + } - /// - /// Builds a new that holds the specified . - /// - /// - /// is null. - public EndsWithExpression(TextExpression text) + /// + /// Builds a new that holds the specified . + /// + /// + /// is . + public EndsWithExpression(TextExpression text) #if !NETSTANDARD1_3 - : this(Guard.Against.Null(text, nameof(text)).OriginalString) - { - _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}"); - } + : this(Guard.Against.Null(text, nameof(text)).OriginalString) + { + _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}"); + } #else + { + if (text is null) { - if (text is null) - { - throw new ArgumentNullException(nameof(text)); - } - _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}"); + throw new ArgumentNullException(nameof(text)); } + _lazyEscapedParseableString = new(() => $"*{text.EscapedParseableString}"); + } #endif - /// - public bool Equals(EndsWithExpression other) => Value == other?.Value; + /// + public bool Equals(EndsWithExpression other) => Value == other?.Value; - /// - public override bool Equals(object obj) => Equals(obj as EndsWithExpression); + /// + public override bool Equals(object obj) => Equals(obj as EndsWithExpression); - /// - public override int GetHashCode() => Value.GetHashCode(); + /// + public override int GetHashCode() => Value.GetHashCode(); - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; - /// - public override double Complexity => 1.5; + /// + public override double Complexity => 1.5; - /// - /// Constructs a new by adding an to a >. - /// - /// The left operand - /// - /// - public static ContainsExpression operator +(EndsWithExpression left, AsteriskExpression _) => new(left.Value); - } + /// + /// Constructs a new by adding an to a >. + /// + /// The left operand + /// + /// + public static ContainsExpression operator +(EndsWithExpression left, AsteriskExpression _) => new(left.Value); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/FilterExpression.cs b/src/DataFilters/Grammar/Syntax/FilterExpression.cs index 00f89fe3..f7144534 100644 --- a/src/DataFilters/Grammar/Syntax/FilterExpression.cs +++ b/src/DataFilters/Grammar/Syntax/FilterExpression.cs @@ -1,43 +1,42 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Base class for filter expression +/// +/// +/// s can take many forms : from the simplest to more complex s. +/// +public abstract class FilterExpression : IHaveComplexity, IParseableString { /// - /// Base class for filter expression + /// Tests if is equivalent to the current instance. /// - /// - /// s can take many forms : from the simplest to more complex s. - /// - public abstract class FilterExpression : IHaveComplexity, IParseableString - { - /// - /// Tests if is equivalent to the current instance. - /// - /// against which the current instance will test is equivalency. - /// - /// The default implementation defers to implementation. - /// The meaning of the equivalency of two s is left to the implementor. - /// - /// - /// true if is "equivalent" to the current instance. - /// - /// - public virtual bool IsEquivalentTo(FilterExpression other) - => Equals(other) || Equals((other as ISimplifiable)?.Simplify()); + /// against which the current instance will test is equivalency. + /// + /// The default implementation defers to implementation. + /// The meaning of the equivalency of two s is left to the implementor. + /// + /// + /// true if is "equivalent" to the current instance. + /// + /// + public virtual bool IsEquivalentTo(FilterExpression other) + => Equals(other) || Equals((other as ISimplifiable)?.Simplify()); - /// - public virtual double Complexity => 1; + /// + public virtual double Complexity => 1; - /// - public override string ToString() => $"{GetType().Name}: '{OriginalString}'"; + /// + public override string ToString() => $"{GetType().Name}: '{OriginalString}'"; - /// - public abstract string EscapedParseableString { get; } + /// + public abstract string EscapedParseableString { get; } - /// - public virtual string OriginalString => EscapedParseableString; + /// + public virtual string OriginalString => EscapedParseableString; - /// - /// Returns a that is the result of applying the NOT logical operator to the specified . - /// - public static NotExpression operator !(FilterExpression expression) => new(expression); - } + /// + /// Returns a that is the result of applying the NOT logical operator to the specified . + /// + public static NotExpression operator !(FilterExpression expression) => new(expression); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/GroupExpression.cs b/src/DataFilters/Grammar/Syntax/GroupExpression.cs index d7ab58fb..40e73179 100644 --- a/src/DataFilters/Grammar/Syntax/GroupExpression.cs +++ b/src/DataFilters/Grammar/Syntax/GroupExpression.cs @@ -1,67 +1,66 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; +namespace DataFilters.Grammar.Syntax; + +using System; +/// +/// Allows to treat several expressions as a single unit. +/// +/// +/// +/// A group expression can be used whenever there is a need to apply a logical operator to several expressions at once. +/// +/// +/// The value of a is equivalent to the complexity of its inner +/// plus a marginal overhead. +/// +/// +/// +/// Builds a new that holds the specified . +/// +/// the expression to group +/// is . +public sealed class GroupExpression(FilterExpression expression) : FilterExpression, IEquatable, ISimplifiable +{ /// - /// Allows to treat several expressions as a single unit. + /// that the current instance is applied onto. /// - /// - /// - /// A group expression can be used whenever there is a need to apply a logical operator to several expressions at once. - /// - /// - /// The value of a is equivalent to the complexity of its inner - /// plus a marginal overhead. - /// - /// - /// - /// Builds a new that holds the specified . - /// - /// the expression to group - /// is null. - public sealed class GroupExpression(FilterExpression expression) : FilterExpression, IEquatable, ISimplifiable - { - /// - /// that the current instance is applied onto. - /// - public FilterExpression Expression { get; } = expression ?? throw new ArgumentNullException(nameof(expression)); + public FilterExpression Expression { get; } = expression ?? throw new ArgumentNullException(nameof(expression)); - /// - public bool Equals(GroupExpression other) => Expression.Equals(other?.Expression); + /// + public bool Equals(GroupExpression other) => Expression.Equals(other?.Expression); - /// - public override bool Equals(object obj) => Equals(obj as GroupExpression); + /// + public override bool Equals(object obj) => Equals(obj as GroupExpression); - /// - public override int GetHashCode() => Expression.GetHashCode(); + /// + public override int GetHashCode() => Expression.GetHashCode(); - /// - public override string ToString() => $"{{{nameof(GroupExpression)} " + - $"[Expression = {Expression.GetType().Name}, " + - $"{nameof(Expression.EscapedParseableString)} = '{Expression.EscapedParseableString}', " + - $"{nameof(Expression.OriginalString)} = '{Expression.OriginalString}'], " + - $"{nameof(EscapedParseableString)}='{EscapedParseableString}'}}"; + /// + public override string ToString() => $"{{{nameof(GroupExpression)} " + + $"[Expression = {Expression.GetType().Name}, " + + $"{nameof(Expression.EscapedParseableString)} = '{Expression.EscapedParseableString}', " + + $"{nameof(Expression.OriginalString)} = '{Expression.OriginalString}'], " + + $"{nameof(EscapedParseableString)}='{EscapedParseableString}'}}"; - /// - public override string EscapedParseableString => $"({Expression.EscapedParseableString})"; + /// + public override string EscapedParseableString => $"({Expression.EscapedParseableString})"; - /// - public override double Complexity => 0.1 + Expression.Complexity; + /// + public override double Complexity => 0.1 + Expression.Complexity; - /// - public override bool IsEquivalentTo(FilterExpression other) => other switch + /// + public override bool IsEquivalentTo(FilterExpression other) => other switch + { + GroupExpression group => Expression.IsEquivalentTo(group.Simplify()), + _ => Expression.IsEquivalentTo(other) + }; + + /// + public FilterExpression Simplify() + => Expression switch { - GroupExpression group => Expression.IsEquivalentTo(group.Simplify()), - _ => Expression.IsEquivalentTo(other) + ConstantValueExpression constant => constant, + ISimplifiable simplify => simplify.Simplify(), + _ => Expression }; - - /// - public FilterExpression Simplify() - => Expression switch - { - ConstantValueExpression constant => constant, - ISimplifiable simplify => simplify.Simplify(), - _ => Expression - }; - } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs b/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs index 83d29b2b..81f75990 100644 --- a/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/GuidValueExpression.cs @@ -1,16 +1,15 @@  -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Wraps a string that represents a +/// +/// +/// Builds a new instance that can wrap a +/// +/// +public class GuidValueExpression(string value) : ConstantValueExpression(value) { - /// - /// Wraps a string that represents a - /// - /// - /// Builds a new instance that can wrap a - /// - /// - public class GuidValueExpression(string value) : ConstantValueExpression(value) - { - /// - public override string EscapedParseableString => Value; - } + /// + public override string EscapedParseableString => Value; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs b/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs index 00d0ce77..4f94a6ee 100644 --- a/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs +++ b/src/DataFilters/Grammar/Syntax/IBoundaryExpression.cs @@ -1,7 +1,6 @@ -namespace DataFilters.Grammar.Syntax -{ - /// - /// Marker interface that identifies types that can be used as 's boundaries - /// - public interface IBoundaryExpression : IHaveComplexity, IParseableString; -} +namespace DataFilters.Grammar.Syntax; + +/// +/// Marker interface that identifies types that can be used as 's boundaries +/// +public interface IBoundaryExpression : IHaveComplexity, IParseableString; diff --git a/src/DataFilters/Grammar/Syntax/IHaveComplexity.cs b/src/DataFilters/Grammar/Syntax/IHaveComplexity.cs index 4f053f77..0459d013 100644 --- a/src/DataFilters/Grammar/Syntax/IHaveComplexity.cs +++ b/src/DataFilters/Grammar/Syntax/IHaveComplexity.cs @@ -1,25 +1,24 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Marks an expression that have a complexity value. +/// +public interface IHaveComplexity { /// - /// Marks an expression that have a complexity value. + /// Gets the "complexity" of the current instance. /// - public interface IHaveComplexity - { - /// - /// Gets the "complexity" of the current instance. - /// - /// - /// - /// The complexity is a hint, given two s, of which one is the cheapest to compute. - /// For example, *[Mm]an and *Man|*man are semantically equivalent but the latter is less "complex" than the first because it a combination of 3 primary - /// s (starts with, Bracket and constant). - /// - /// + /// + /// + /// The complexity is a hint, given two s, of which one is the cheapest to compute. + /// For example, *[Mm]an and *Man|*man are semantically equivalent but the latter is less "complex" than the first because it a combination of 3 primary + /// s (starts with, Bracket and constant). + /// + /// #if NET5_0_OR_GREATER - public virtual double Complexity => 0; + public virtual double Complexity => 0; #else - double Complexity { get; } + double Complexity { get; } #endif - } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/IParseableString.cs b/src/DataFilters/Grammar/Syntax/IParseableString.cs index 6269c49a..92f3bdd9 100644 --- a/src/DataFilters/Grammar/Syntax/IParseableString.cs +++ b/src/DataFilters/Grammar/Syntax/IParseableString.cs @@ -1,21 +1,20 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Defines property. +/// +public interface IParseableString { /// - /// Defines property. + /// The string that, if parsed, will give the current expression /// - public interface IParseableString - { - /// - /// The string that, if parsed, will give the current expression - /// - string EscapedParseableString { get; } + string EscapedParseableString { get; } - /// - /// Unescaped version of the parseable string. - /// - /// - /// This is the raw version which is computed from. - /// - string OriginalString { get; } - } + /// + /// Unescaped version of the parseable string. + /// + /// + /// This is the raw version which is computed from. + /// + string OriginalString { get; } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/ISimplifiable.cs b/src/DataFilters/Grammar/Syntax/ISimplifiable.cs index 626c1307..5fa6b814 100644 --- a/src/DataFilters/Grammar/Syntax/ISimplifiable.cs +++ b/src/DataFilters/Grammar/Syntax/ISimplifiable.cs @@ -1,15 +1,14 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Defines a method which rewrites an instance to a "simpler" form. +/// +public interface ISimplifiable { /// - /// Defines a method which rewrites an instance to a "simpler" form. + /// Builds a which is equivalent to the current instance but which should be lower. /// - public interface ISimplifiable - { - /// - /// Builds a which is equivalent to the current instance but which should be lower. - /// - /// a rewritten version of the current which - /// should be lower. - FilterExpression Simplify(); - } + /// a rewritten version of the current which + /// should be lower. + FilterExpression Simplify(); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/IntervalExpression.cs b/src/DataFilters/Grammar/Syntax/IntervalExpression.cs index b87985f5..4c668757 100644 --- a/src/DataFilters/Grammar/Syntax/IntervalExpression.cs +++ b/src/DataFilters/Grammar/Syntax/IntervalExpression.cs @@ -1,220 +1,219 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; +using Exceptions; + +/// +/// A that holds an interval between and values. +/// +public sealed class IntervalExpression : FilterExpression, IEquatable, ISimplifiable { - using System; - using Exceptions; + /// + /// Lower bound of the current instance + /// + public BoundaryExpression Min { get; } + + /// + /// Upper bound of the current instance + /// + public BoundaryExpression Max { get; } + + private readonly Lazy _lazyToString; + private readonly Lazy _lazyParseableString; /// - /// A that holds an interval between and values. + /// Builds a new instance /// - public sealed class IntervalExpression : FilterExpression, IEquatable, ISimplifiable + /// Lower bound of the interval + /// Upper bound of the interval + /// if both and are . + /// if and types are not "compatible". + /// if + /// + /// both and types are . + /// both is and . + /// + /// + /// + /// Either or can be null to indicate and unbounded lower (respectivelu upper) bound. + /// + public IntervalExpression(BoundaryExpression min = null, BoundaryExpression max = null) { - /// - /// Lower bound of the current instance - /// - public BoundaryExpression Min { get; } - - /// - /// Upper bound of the current instance - /// - public BoundaryExpression Max { get; } - - private readonly Lazy _lazyToString; - private readonly Lazy _lazyParseableString; - - /// - /// Builds a new instance - /// - /// Lower bound of the interval - /// Upper bound of the interval - /// if both and are null. - /// if and types are not "compatible". - /// if - /// - /// both and types are . - /// both is and null. - /// - /// - /// - /// Either or can be null to indicate and unbounded lower (respectivelu upper) bound. - /// - public IntervalExpression(BoundaryExpression min = null, BoundaryExpression max = null) + if (min?.Expression is AsteriskExpression && max?.Expression is AsteriskExpression expression) { - if (min?.Expression is AsteriskExpression && max?.Expression is AsteriskExpression expression) - { - throw new IncorrectBoundaryException($"{nameof(min)} and {nameof(max)} cannot be both {nameof(AsteriskExpression)}"); - } + throw new IncorrectBoundaryException($"{nameof(min)} and {nameof(max)} cannot be both {nameof(AsteriskExpression)}"); + } - if (min?.Expression is AsteriskExpression && max is null) - { - throw new IncorrectBoundaryException($"{nameof(max)} cannot be null when {nameof(min)} is {nameof(AsteriskExpression)}"); - } + if (min?.Expression is AsteriskExpression && max is null) + { + throw new IncorrectBoundaryException($"{nameof(max)} cannot be null when {nameof(min)} is {nameof(AsteriskExpression)}"); + } - if (min?.Expression is DateExpression && max is not null && !(max.Expression is AsteriskExpression || max.Expression is DateExpression || max.Expression is TimeExpression || max.Expression is DateTimeExpression)) - { - throw new BoundariesTypeMismatchException($"{nameof(min)}[{min?.Expression?.GetType()}] and {nameof(max)}[{max?.Expression?.GetType()}] types are not compatible", nameof(max)); - } + if (min?.Expression is DateExpression && max is not null && !(max.Expression is AsteriskExpression || max.Expression is DateExpression || max.Expression is TimeExpression || max.Expression is DateTimeExpression)) + { + throw new BoundariesTypeMismatchException($"{nameof(min)}[{min?.Expression?.GetType()}] and {nameof(max)}[{max?.Expression?.GetType()}] types are not compatible", nameof(max)); + } - if (min?.Expression is ConstantValueExpression && !(max is null || max.Expression is ConstantValueExpression || max.Expression is AsteriskExpression)) - { - throw new BoundariesTypeMismatchException($"{nameof(min)}[{min?.Expression?.GetType()}] and {nameof(max)}[{max?.Expression?.GetType()}] types are not compatible", nameof(max)); - } + if (min?.Expression is ConstantValueExpression && !(max is null || max.Expression is ConstantValueExpression || max.Expression is AsteriskExpression)) + { + throw new BoundariesTypeMismatchException($"{nameof(min)}[{min?.Expression?.GetType()}] and {nameof(max)}[{max?.Expression?.GetType()}] types are not compatible", nameof(max)); + } - if (min is null && max is null) - { - throw new IncorrectBoundaryException($"{nameof(min)} or {nameof(max)} must not be null."); - } + if (min is null && max is null) + { + throw new IncorrectBoundaryException($"{nameof(min)} or {nameof(max)} must not be null."); + } - if (min?.Expression is TimeExpression && max is not null && max.Expression is not TimeExpression && max.Expression is not AsteriskExpression) - { - throw new BoundariesTypeMismatchException($"{nameof(max)} must be either {nameof(TimeExpression)} or {nameof(AsteriskExpression)} when min is {nameof(TimeExpression)}", nameof(max)); - } + if (min?.Expression is TimeExpression && max is not null && max.Expression is not TimeExpression && max.Expression is not AsteriskExpression) + { + throw new BoundariesTypeMismatchException($"{nameof(max)} must be either {nameof(TimeExpression)} or {nameof(AsteriskExpression)} when min is {nameof(TimeExpression)}", nameof(max)); + } - Min = min?.Expression switch + Min = min?.Expression switch + { + AsteriskExpression or null => null, + DateTimeExpression { Date: var date, Time: var time, Offset: var offset } => (date, time, offset) switch { - AsteriskExpression or null => null, - DateTimeExpression { Date: var date, Time: var time, Offset: var offset } => (date, time, offset) switch - { - (null, { }, _) => new BoundaryExpression(time, included: min.Included), - ({ }, null, _) => new BoundaryExpression(date, included: min.Included), - _ => new BoundaryExpression(new DateTimeExpression(date, time, offset), included: min.Included) - }, - _ => min - }; - - Max = max?.Expression switch + (null, { }, _) => new BoundaryExpression(time, included: min.Included), + ({ }, null, _) => new BoundaryExpression(date, included: min.Included), + _ => new BoundaryExpression(new DateTimeExpression(date, time, offset), included: min.Included) + }, + _ => min + }; + + Max = max?.Expression switch + { + AsteriskExpression or null => null, + DateTimeExpression { Date: var date, Time: var time, Offset: var offset } => (date, time, offset) switch { - AsteriskExpression or null => null, - DateTimeExpression { Date: var date, Time: var time, Offset: var offset } => (date, time, offset) switch - { - (null, { }, _) => new BoundaryExpression(time, included: max.Included), - ({ }, null, _) => new BoundaryExpression(date, included: max.Included), - _ => new BoundaryExpression(new DateTimeExpression(date, time, offset), included: max.Included) - }, - TimeExpression time when Min?.Expression is DateTimeExpression dateTime => new(new DateTimeExpression(dateTime.Date, time, dateTime.Offset), max.Included), - TimeExpression time => new BoundaryExpression(time, included: max.Included), - _ => max - }; - - _lazyParseableString = new(() => $"{GetMinBracket(Min?.Included)}{Min?.Expression?.EscapedParseableString ?? "*"} TO {Max?.Expression?.EscapedParseableString ?? "*"}{GetMaxBracket(Max?.Included)}"); - _lazyToString = new(() => new + (null, { }, _) => new BoundaryExpression(time, included: max.Included), + ({ }, null, _) => new BoundaryExpression(date, included: max.Included), + _ => new BoundaryExpression(new DateTimeExpression(date, time, offset), included: max.Included) + }, + TimeExpression time when Min?.Expression is DateTimeExpression dateTime => new(new DateTimeExpression(dateTime.Date, time, dateTime.Offset), max.Included), + TimeExpression time => new BoundaryExpression(time, included: max.Included), + _ => max + }; + + _lazyParseableString = new(() => $"{GetMinBracket(Min?.Included)}{Min?.Expression?.EscapedParseableString ?? "*"} TO {Max?.Expression?.EscapedParseableString ?? "*"}{GetMaxBracket(Max?.Included)}"); + _lazyToString = new(() => new + { + Min = new { - Min = new - { - Min?.Included, - Min?.Expression?.EscapedParseableString, - Type = Min?.Expression?.GetType().Name, - DebugView = Min?.Expression?.ToString() - }, - Max = new - { - Max?.Included, - Max?.Expression?.EscapedParseableString, - Type = Max?.Expression?.GetType().Name, - DebugView = Max?.Expression?.ToString() - }, - EscapedParseableString - } + Min?.Included, + Min?.Expression?.EscapedParseableString, + Type = Min?.Expression?.GetType().Name, + DebugView = Min?.Expression?.ToString() + }, + Max = new + { + Max?.Included, + Max?.Expression?.EscapedParseableString, + Type = Max?.Expression?.GetType().Name, + DebugView = Max?.Expression?.ToString() + }, + EscapedParseableString + } #if NETSTANDARD1_3 - .Jsonify(new Newtonsoft.Json.JsonSerializerSettings() { Formatting = Newtonsoft.Json.Formatting.Indented }) + .Jsonify(new Newtonsoft.Json.JsonSerializerSettings() { Formatting = Newtonsoft.Json.Formatting.Indented }) #elif NETSTANDARD2_0_OR_GREATER || NET6_0_OR_GREATER - .Jsonify(new() { WriteIndented = true, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }) + .Jsonify(new() { WriteIndented = true, DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.WhenWritingNull }) #endif - ) - ; + ) + ; - static string GetMinBracket(bool? included) => true.Equals(included) ? "[" : "]"; - static string GetMaxBracket(bool? included) => true.Equals(included) ? "]" : "["; - } + static string GetMinBracket(bool? included) => true.Equals(included) ? "[" : "]"; + static string GetMaxBracket(bool? included) => true.Equals(included) ? "]" : "["; + } - /// - public override bool Equals(object obj) => Equals(obj as IntervalExpression); + /// + public override bool Equals(object obj) => Equals(obj as IntervalExpression); - /// - public bool Equals(IntervalExpression other) - { - bool equals = false; + /// + public bool Equals(IntervalExpression other) + { + bool equals = false; - if (other is not null) + if (other is not null) + { + if (Min is null) { - if (Min is null) - { - if (other.Min is null) - { - equals = Max is null - ? other.Max is null - : Max.Equals(other.Max); - } - } - else if (Min.Equals(other.Min)) + if (other.Min is null) { equals = Max is null ? other.Max is null : Max.Equals(other.Max); } } - - return equals; + else if (Min.Equals(other.Min)) + { + equals = Max is null + ? other.Max is null + : Max.Equals(other.Max); + } } - /// - public override int GetHashCode() => (Min, Max).GetHashCode(); + return equals; + } - /// - public override string ToString() => _lazyToString.Value; + /// + public override int GetHashCode() => (Min, Max).GetHashCode(); - /// - public override string EscapedParseableString => _lazyParseableString.Value; + /// + public override string ToString() => _lazyToString.Value; - /// - public override bool IsEquivalentTo(FilterExpression other) + /// + public override string EscapedParseableString => _lazyParseableString.Value; + + /// + public override bool IsEquivalentTo(FilterExpression other) + { + bool equivalent = false; + if (other is not null) { - bool equivalent = false; - if (other is not null) + if (ReferenceEquals(this, other)) { - if (ReferenceEquals(this, other)) - { - equivalent = true; - } - else if (((other as ISimplifiable)?.Simplify() ?? other) is IntervalExpression range) - { - equivalent = Equals(range); - } - else if (Min?.Included == true && Max?.Included == true && Min.Equals(Max)) - { - equivalent = other.Equals(Min.Expression.As(other.GetType())); - } + equivalent = true; + } + else if (((other as ISimplifiable)?.Simplify() ?? other) is IntervalExpression range) + { + equivalent = Equals(range); + } + else if (Min?.Included == true && Max?.Included == true && Min.Equals(Max)) + { + equivalent = other.Equals(Min.Expression.As(other.GetType())); } - - return equivalent; } - /// - /// Deconstruction method - /// - /// lower bound of the interval - /// Upper bound of the interval - public void Deconstruct(out BoundaryExpression min, out BoundaryExpression max) - { - min = Min; - max = Max; - } + return equivalent; + } - /// - public override double Complexity => (Min?.Expression?.Complexity ?? 0) + (Max?.Expression?.Complexity ?? 0); + /// + /// Deconstruction method + /// + /// lower bound of the interval + /// Upper bound of the interval + public void Deconstruct(out BoundaryExpression min, out BoundaryExpression max) + { + min = Min; + max = Max; + } - /// - public FilterExpression Simplify() - { - FilterExpression simplified = this; + /// + public override double Complexity => (Min?.Expression?.Complexity ?? 0) + (Max?.Expression?.Complexity ?? 0); - if (Min is not null && Max is not null && Min.Included && Max.Included && Min.Equals(Max)) - { - simplified = ((FilterExpression)Min.Expression).Complexity < ((FilterExpression)Max.Expression).Complexity - ? (FilterExpression)Min.Expression - : (FilterExpression)Max.Expression; - } + /// + public FilterExpression Simplify() + { + FilterExpression simplified = this; - return simplified; + if (Min is not null && Max is not null && Min.Included && Max.Included && Min.Equals(Max)) + { + simplified = ((FilterExpression)Min.Expression).Complexity < ((FilterExpression)Max.Expression).Complexity + ? (FilterExpression)Min.Expression + : (FilterExpression)Max.Expression; } + + return simplified; } } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/NotExpression.cs b/src/DataFilters/Grammar/Syntax/NotExpression.cs index 00ed5c93..942c37ed 100644 --- a/src/DataFilters/Grammar/Syntax/NotExpression.cs +++ b/src/DataFilters/Grammar/Syntax/NotExpression.cs @@ -1,69 +1,68 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; +namespace DataFilters.Grammar.Syntax; + +using System; +/// +/// An expression that negate wrapped inside +/// +public sealed class NotExpression : FilterExpression, IEquatable +{ /// - /// An expression that negate wrapped inside + /// Expression that the NOT logical is applied to /// - public sealed class NotExpression : FilterExpression, IEquatable - { - /// - /// Expression that the NOT logical is applied to - /// - public FilterExpression Expression { get; } + public FilterExpression Expression { get; } - private readonly Lazy lazyEscapedParseableString; + private readonly Lazy lazyEscapedParseableString; - /// - /// Builds a new that holds the specified . - /// - /// - /// is . - /// - /// will be automatically wrapped inside a when it's a . - /// - public NotExpression(FilterExpression expression) + /// + /// Builds a new that holds the specified . + /// + /// + /// is . + /// + /// will be automatically wrapped inside a when it's a . + /// + public NotExpression(FilterExpression expression) + { + Expression = expression switch { - Expression = expression switch - { - null => throw new ArgumentNullException(nameof(expression)), - BinaryFilterExpression expr => new GroupExpression(expr), - FilterExpression expr => expr - }; + null => throw new ArgumentNullException(nameof(expression)), + BinaryFilterExpression expr => new GroupExpression(expr), + FilterExpression expr => expr + }; - lazyEscapedParseableString = new Lazy(() => $"!{Expression.EscapedParseableString}"); - } + lazyEscapedParseableString = new Lazy(() => $"!{Expression.EscapedParseableString}"); + } - /// - public bool Equals(NotExpression other) => Expression.Equals(other?.Expression); + /// + public bool Equals(NotExpression other) => Expression.Equals(other?.Expression); - /// - public override bool Equals(object obj) => Equals(obj as NotExpression); + /// + public override bool Equals(object obj) => Equals(obj as NotExpression); - /// - public override int GetHashCode() => Expression.GetHashCode(); + /// + public override int GetHashCode() => Expression.GetHashCode(); - /// - public override string ToString() => "{NotExpression [" + - $"Expression = {Expression.GetType().Name}, " + - $"{nameof(Expression.EscapedParseableString)} = '{Expression.EscapedParseableString}', " + - $"{nameof(Expression.OriginalString)} = '{Expression.OriginalString}']," + - $"{nameof(EscapedParseableString)} = {EscapedParseableString}}}"; + /// + public override string ToString() => "{NotExpression [" + + $"Expression = {Expression.GetType().Name}, " + + $"{nameof(Expression.EscapedParseableString)} = '{Expression.EscapedParseableString}', " + + $"{nameof(Expression.OriginalString)} = '{Expression.OriginalString}']," + + $"{nameof(EscapedParseableString)} = {EscapedParseableString}}}"; - /// - public override string EscapedParseableString => lazyEscapedParseableString.Value; + /// + public override string EscapedParseableString => lazyEscapedParseableString.Value; - /// - public override double Complexity => Expression.Complexity; + /// + public override double Complexity => Expression.Complexity; - /// - public static bool operator ==(NotExpression left, NotExpression right) => left switch - { - null => right is null, - _ => left.Equals(right) - }; + /// + public static bool operator ==(NotExpression left, NotExpression right) => left switch + { + null => right is null, + _ => left.Equals(right) + }; - /// - public static bool operator !=(NotExpression left, NotExpression right) => !(left == right); - } + /// + public static bool operator !=(NotExpression left, NotExpression right) => !(left == right); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/NumericSign.cs b/src/DataFilters/Grammar/Syntax/NumericSign.cs index 29a1c57a..fd1080b5 100644 --- a/src/DataFilters/Grammar/Syntax/NumericSign.cs +++ b/src/DataFilters/Grammar/Syntax/NumericSign.cs @@ -1,18 +1,17 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Sign of the offset +/// +public enum NumericSign { /// - /// Sign of the offset + /// + sign /// - public enum NumericSign - { - /// - /// + sign - /// - Plus = 0, + Plus = 0, - /// - /// - sign - /// - Minus = 1 - } + /// + /// - sign + /// + Minus = 1 } diff --git a/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs b/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs index 9ca2ba03..ee18660d 100644 --- a/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/NumericValueExpression.cs @@ -1,48 +1,47 @@  using System; -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +/// +/// Wraps a string that represents a numeric value of some sort +/// +/// +/// Builds a new instance that can wrap a numeric value of some sort +/// +/// +/// is +/// is +public class NumericValueExpression(string value) : ConstantValueExpression(value), IEquatable, IBoundaryExpression { - /// - /// Wraps a string that represents a numeric value of some sort - /// - /// - /// Builds a new instance that can wrap a numeric value of some sort - /// - /// - /// is null - /// is - public class NumericValueExpression(string value) : ConstantValueExpression(value), IEquatable, IBoundaryExpression - { - /// - public override string EscapedParseableString => Value; + /// + public override string EscapedParseableString => Value; - ///// - //public override bool Equals(object obj) => Equals(obj as NumericValueExpression); + ///// + //public override bool Equals(object obj) => Equals(obj as NumericValueExpression); - /// - public virtual bool Equals(NumericValueExpression other) => Value.Equals(other?.Value); + /// + public virtual bool Equals(NumericValueExpression other) => Value.Equals(other?.Value); - /// - public override int GetHashCode() => Value.GetHashCode(); + /// + public override int GetHashCode() => Value.GetHashCode(); - /// - public override bool IsEquivalentTo(FilterExpression other) => other switch - { - NumericValueExpression numeric => Equals(numeric), - ConstantValueExpression constant => Equals(constant), - ISimplifiable simplifiable => Equals(simplifiable.Simplify()), - _ => false - }; + /// + public override bool IsEquivalentTo(FilterExpression other) => other switch + { + NumericValueExpression numeric => Equals(numeric), + ConstantValueExpression constant => Equals(constant), + ISimplifiable simplifiable => Equals(simplifiable.Simplify()), + _ => false + }; - /// - public static bool operator ==(NumericValueExpression left, NumericValueExpression right) => Equals(left, right); + /// + public static bool operator ==(NumericValueExpression left, NumericValueExpression right) => Equals(left, right); - /// - public static bool operator !=(NumericValueExpression left, NumericValueExpression right) => !(left == right); + /// + public static bool operator !=(NumericValueExpression left, NumericValueExpression right) => !(left == right); - /// - public override bool Equals(object obj) - => ReferenceEquals(this, obj) || Equals(obj.As()?.Value, Value); - } + /// + public override bool Equals(object obj) + => ReferenceEquals(this, obj) || Equals(obj.As()?.Value, Value); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/OffsetExpression.cs b/src/DataFilters/Grammar/Syntax/OffsetExpression.cs index d711301f..a2dec526 100644 --- a/src/DataFilters/Grammar/Syntax/OffsetExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OffsetExpression.cs @@ -1,109 +1,108 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// records the offset between a time and the UTC time +/// +public sealed class OffsetExpression : FilterExpression, IEquatable { - using System; + /// + /// Gets the number of hours of offset with the UTC time + /// + public int Hours { get; } + + /// + /// Get the number of minutes of offset with the UTC time + /// + public int Minutes { get; } + + /// + /// Sign associated with offset + /// + public NumericSign Sign { get; } + + /// + /// The Zero time offset + /// + public static OffsetExpression Zero => new(); + + private readonly Lazy _lazyParseableString; /// - /// records the offset between a time and the UTC time + /// Builds a new instance /// - public sealed class OffsetExpression : FilterExpression, IEquatable + /// + /// + /// The sign of + public OffsetExpression(NumericSign sign = NumericSign.Plus, uint hours = 0, uint minutes = 0) { - /// - /// Gets the number of hours of offset with the UTC time - /// - public int Hours { get; } - - /// - /// Get the number of minutes of offset with the UTC time - /// - public int Minutes { get; } - - /// - /// Sign associated with offset - /// - public NumericSign Sign { get; } - - /// - /// The Zero time offset - /// - public static OffsetExpression Zero => new(); - - private readonly Lazy _lazyParseableString; - - /// - /// Builds a new instance - /// - /// - /// - /// The sign of - public OffsetExpression(NumericSign sign = NumericSign.Plus, uint hours = 0, uint minutes = 0) + if (hours > 23) { - if (hours > 23) - { - throw new ArgumentOutOfRangeException(nameof(hours), $"{nameof(hours)} must be between 0 and 23 inclusive"); - } - - if (minutes > 59) - { - throw new ArgumentOutOfRangeException(nameof(minutes), $"{nameof(minutes)} must be between 0 and 59 inclusive"); - } - - Hours = (int)hours; - Minutes = (int)minutes; - Sign = sign; - - _lazyParseableString = new(() => (Hours, Minutes, Sign) switch - { - (0, 0, _) => "Z", - (_, _, NumericSign.Minus) => $"-{Hours:D2}:{Minutes:D2}", - _ => $"+{Hours:D2}:{Minutes:D2}", - }); + throw new ArgumentOutOfRangeException(nameof(hours), $"{nameof(hours)} must be between 0 and 23 inclusive"); } - /// - public override string EscapedParseableString => _lazyParseableString.Value; + if (minutes > 59) + { + throw new ArgumentOutOfRangeException(nameof(minutes), $"{nameof(minutes)} must be between 0 and 59 inclusive"); + } - /// - public override bool Equals(object obj) => Equals(obj as OffsetExpression); + Hours = (int)hours; + Minutes = (int)minutes; + Sign = sign; - /// - public bool Equals(OffsetExpression other) => (Hours, Minutes, Sign) switch + _lazyParseableString = new(() => (Hours, Minutes, Sign) switch { - (0, 0, _) => other?.Hours == 0 && other?.Minutes == 0, - _ => (Hours, Minutes, Sign) == (other?.Hours, other?.Minutes, other?.Sign) - }; + (0, 0, _) => "Z", + (_, _, NumericSign.Minus) => $"-{Hours:D2}:{Minutes:D2}", + _ => $"+{Hours:D2}:{Minutes:D2}", + }); + } + + /// + public override string EscapedParseableString => _lazyParseableString.Value; + + /// + public override bool Equals(object obj) => Equals(obj as OffsetExpression); + + /// + public bool Equals(OffsetExpression other) => (Hours, Minutes, Sign) switch + { + (0, 0, _) => other?.Hours == 0 && other?.Minutes == 0, + _ => (Hours, Minutes, Sign) == (other?.Hours, other?.Minutes, other?.Sign) + }; - /// + /// #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - public override int GetHashCode() => HashCode.Combine(Hours, Minutes, Sign); + public override int GetHashCode() => HashCode.Combine(Hours, Minutes, Sign); #else - public override int GetHashCode() - { - int hashCode = -793696894; - hashCode = (hashCode * -1521134295) + Hours.GetHashCode(); - hashCode = (hashCode * -1521134295) + Minutes.GetHashCode(); - hashCode = (hashCode * -1521134295) + Sign.GetHashCode(); - return hashCode; - } + public override int GetHashCode() + { + int hashCode = -793696894; + hashCode = (hashCode * -1521134295) + Hours.GetHashCode(); + hashCode = (hashCode * -1521134295) + Minutes.GetHashCode(); + hashCode = (hashCode * -1521134295) + Sign.GetHashCode(); + return hashCode; + } #endif - /// - public void Deconstruct(out NumericSign sign, out int hours, out int minutes, out string escapedParseableString, out string originalString) - { - sign = Sign; - hours = Hours; - minutes = Minutes; - escapedParseableString = EscapedParseableString; - originalString = OriginalString; - } + /// + public void Deconstruct(out NumericSign sign, out int hours, out int minutes, out string escapedParseableString, out string originalString) + { + sign = Sign; + hours = Hours; + minutes = Minutes; + escapedParseableString = EscapedParseableString; + originalString = OriginalString; + } - /// - public static bool operator ==(OffsetExpression left, OffsetExpression right) => left switch - { - null => right is null, - _ => left.Equals(right) - }; + /// + public static bool operator ==(OffsetExpression left, OffsetExpression right) => left switch + { + null => right is null, + _ => left.Equals(right) + }; - /// - public static bool operator !=(OffsetExpression left, OffsetExpression right) => !(left == right); - } + /// + public static bool operator !=(OffsetExpression left, OffsetExpression right) => !(left == right); } diff --git a/src/DataFilters/Grammar/Syntax/OneOfExpression.cs b/src/DataFilters/Grammar/Syntax/OneOfExpression.cs index 378154a1..bb425abd 100644 --- a/src/DataFilters/Grammar/Syntax/OneOfExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OneOfExpression.cs @@ -1,147 +1,149 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; - using System.Linq; +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; + +using Utilities; - using Utilities; +/// +/// a that contains multiple s as . +/// +public sealed class OneOfExpression : FilterExpression, IEquatable, ISimplifiable +{ + private static readonly ArrayEqualityComparer EqualityComparer = new(); /// - /// a that contains multiple s as . + /// Collection of that the current instance holds. /// - public sealed class OneOfExpression : FilterExpression, IEquatable, ISimplifiable - { - private static readonly ArrayEqualityComparer EqualityComparer = new(); + public IReadOnlyCollection Values => [.. _values]; - /// - /// Collection of that the current instance holds. - /// - public IReadOnlyCollection Values => _values.ToList().AsReadOnly(); + private readonly FilterExpression[] _values; - private readonly FilterExpression[] _values; + private readonly Lazy _lazyParseableString; - private readonly Lazy _lazyParseableString; + /// + /// Builds a new instance. + /// + /// + /// is . + /// is empty. + public OneOfExpression(params FilterExpression[] values) + { +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(values); +#else + if (values is null) + { + throw new ArgumentNullException(nameof(values)); + } +#endif - /// - /// Builds a new instance. - /// - /// - /// is null. - /// is empty. - public OneOfExpression(params FilterExpression[] values) + if (values.Length == 0) { - if (values is null) - { - throw new ArgumentNullException(nameof(values)); - } + throw new ArgumentException($"{nameof(OneOfExpression)} cannot be empty"); + } - if (values.Length == 0) - { - throw new InvalidOperationException($"{nameof(OneOfExpression)} cannot be empty"); - } + _values = [.. values.Where(x => x is not null)]; - _values = values.Where(x => x is not null) - .ToArray(); + _lazyParseableString = new(() => $"{{{string.Join(",", Values.Select(v => v.EscapedParseableString))}}}"); + } - _lazyParseableString = new(() => $"{{{string.Join(",", Values.Select(v => v.EscapedParseableString))}}}"); + /// + public override bool IsEquivalentTo(FilterExpression other) + { + bool equivalent; + if (ReferenceEquals(this, other)) + { + equivalent = true; } - - /// - public override bool IsEquivalentTo(FilterExpression other) + else { - bool equivalent; - if (ReferenceEquals(this, other)) - { - equivalent = true; - } - else - { - equivalent = other is OneOfExpression oneOfExpression - ? EqualityComparer.Equals([.. oneOfExpression._values], [.. _values]) - || !(_values.Except(oneOfExpression._values).Any() || oneOfExpression._values.Except(_values).Any()) - : other switch - { - AsteriskExpression asterisk => Simplify().IsEquivalentTo(asterisk), - ConstantValueExpression constant => Simplify().IsEquivalentTo(constant), - DateExpression date => Simplify().IsEquivalentTo(date), - DateTimeExpression dateTime => Simplify().IsEquivalentTo(dateTime), - ISimplifiable simplifiable => Simplify().IsEquivalentTo(simplifiable.Simplify()), - _ => false - }; - } - - return equivalent; + equivalent = other is OneOfExpression oneOfExpression + ? EqualityComparer.Equals([.. oneOfExpression._values], [.. _values]) + || !(_values.Except(oneOfExpression._values).Any() || oneOfExpression._values.Except(_values).Any()) + : other switch + { + AsteriskExpression asterisk => Simplify().IsEquivalentTo(asterisk), + ConstantValueExpression constant => Simplify().IsEquivalentTo(constant), + DateExpression date => Simplify().IsEquivalentTo(date), + DateTimeExpression dateTime => Simplify().IsEquivalentTo(dateTime), + ISimplifiable simplifiable => Simplify().IsEquivalentTo(simplifiable.Simplify()), + _ => false + }; } - /// - public bool Equals(OneOfExpression other) => other is not null && EqualityComparer.Equals(_values, other._values); + return equivalent; + } - /// - public override bool Equals(object obj) => Equals(obj as OneOfExpression); + /// + public bool Equals(OneOfExpression other) => other is not null && EqualityComparer.Equals(_values, other._values); - /// - public override int GetHashCode() => EqualityComparer.GetHashCode(_values); + /// + public override bool Equals(object obj) => Equals(obj as OneOfExpression); - /// - public static bool operator ==(OneOfExpression left, OneOfExpression right) => left?.Equals(right) ?? false; + /// + public override int GetHashCode() => EqualityComparer.GetHashCode(_values); - /// - public static bool operator !=(OneOfExpression left, OneOfExpression right) => !(left == right); + /// + public static bool operator ==(OneOfExpression left, OneOfExpression right) => left?.Equals(right) ?? false; - /// - public override double Complexity => Values.Sum(expression => expression.Complexity); + /// + public static bool operator !=(OneOfExpression left, OneOfExpression right) => !(left == right); - /// - public FilterExpression Simplify() - { - HashSet curatedExpressions = []; + /// + public override double Complexity => Values.Sum(expression => expression.Complexity); + + /// + public FilterExpression Simplify() + { + HashSet curatedExpressions = []; - foreach (IGrouping expr in _values.ToLookup(x => x is OneOfExpression)) + foreach (IGrouping expr in _values.ToLookup(x => x is OneOfExpression)) + { + if (expr.Key) { - if (expr.Key) - { - OneOfExpression oneOfExpression = new(expr.Select(item => ((OneOfExpression)item).Values) - .SelectMany(x => x) - .ToArray()); + OneOfExpression oneOfExpression = new(expr.Select(item => ((OneOfExpression)item).Values) + .SelectMany(x => x) + .ToArray()); - curatedExpressions.Add(oneOfExpression.Simplify()); - } - else + curatedExpressions.Add(oneOfExpression.Simplify()); + } + else + { + foreach (FilterExpression expression in expr) { - foreach (FilterExpression expression in expr) + FilterExpression simplifiedExpression = (expression as ISimplifiable)?.Simplify() ?? expression; + if (!curatedExpressions.Contains(simplifiedExpression) && !curatedExpressions.Any(existing => existing.IsEquivalentTo(simplifiedExpression))) { - FilterExpression simplifiedExpression = (expression as ISimplifiable)?.Simplify() ?? expression; - if (!curatedExpressions.Contains(simplifiedExpression) && !curatedExpressions.Any(existing => existing.IsEquivalentTo(simplifiedExpression))) - { - curatedExpressions.Add(simplifiedExpression); - } + curatedExpressions.Add(simplifiedExpression); } } } + } - FilterExpression simplifiedResult; - - switch (curatedExpressions.Count) - { - case 1: - simplifiedResult = curatedExpressions.Single(); - break; - case 2: - FilterExpression first = curatedExpressions.First(); - FilterExpression other = curatedExpressions.Last(); - simplifiedResult = first is OneOfExpression oneOfFirst && other is OneOfExpression oneOfSecond - ? new OneOfExpression([.. oneOfFirst.Values, .. oneOfSecond.Values]) - : new OrExpression(first, other); - break; - default: - simplifiedResult = new OneOfExpression([.. curatedExpressions]); - break; - } + FilterExpression simplifiedResult; - return simplifiedResult; + switch (curatedExpressions.Count) + { + case 1: + simplifiedResult = curatedExpressions.Single(); + break; + case 2: + FilterExpression first = curatedExpressions.First(); + FilterExpression other = curatedExpressions.Last(); + simplifiedResult = first is OneOfExpression oneOfFirst && other is OneOfExpression oneOfSecond + ? new OneOfExpression([.. oneOfFirst.Values, .. oneOfSecond.Values]) + : new OrExpression(first, other); + break; + default: + simplifiedResult = new OneOfExpression([.. curatedExpressions]); + break; } - /// - public override string EscapedParseableString => _lazyParseableString.Value; + return simplifiedResult; } + + /// + public override string EscapedParseableString => _lazyParseableString.Value; } diff --git a/src/DataFilters/Grammar/Syntax/OrExpression.cs b/src/DataFilters/Grammar/Syntax/OrExpression.cs index bd7357ff..a7668708 100644 --- a/src/DataFilters/Grammar/Syntax/OrExpression.cs +++ b/src/DataFilters/Grammar/Syntax/OrExpression.cs @@ -1,64 +1,63 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// Combines two s using the logical OR operator +/// +public sealed class OrExpression : BinaryFilterExpression, IEquatable { - using System; + private readonly Lazy _lazyToString; + private readonly Lazy _lazyEscapedParseableString; /// - /// Combines two s using the logical OR operator + /// Builds a new that combines and using the logical + /// OR operator /// - public sealed class OrExpression : BinaryFilterExpression, IEquatable + /// + /// The constructor will wrap (respectively ) inside a when is either + /// a or a . + /// + /// Left member of the expression + /// Right member of the expression + /// if either or is . + public OrExpression(FilterExpression left, FilterExpression right) : base(left, right) { - private readonly Lazy _lazyToString; - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Builds a new that combines and using the logical - /// OR operator - /// - /// - /// The constructor will wrap (respectively ) inside a when is either - /// a or a . - /// - /// Left member of the expression - /// Right member of the expression - /// if either or is . - public OrExpression(FilterExpression left, FilterExpression right) : base(left, right) - { - _lazyToString = new(() => $@"[""{nameof(Left)} ({Left.GetType().Name})"": {Left.EscapedParseableString}; ""{nameof(Right)} ({Right.GetType().Name})"": {Right.EscapedParseableString}]"); - _lazyEscapedParseableString = new(() => $"{Left.EscapedParseableString}|{Right.EscapedParseableString}"); - } + _lazyToString = new(() => $@"[""{nameof(Left)} ({Left.GetType().Name})"": {Left.EscapedParseableString}; ""{nameof(Right)} ({Right.GetType().Name})"": {Right.EscapedParseableString}]"); + _lazyEscapedParseableString = new(() => $"{Left.EscapedParseableString}|{Right.EscapedParseableString}"); + } - /// - public bool Equals(OrExpression other) => Left.Equals(other?.Left) && Right.Equals(other?.Right); + /// + public bool Equals(OrExpression other) => Left.Equals(other?.Left) && Right.Equals(other?.Right); - /// - public override bool Equals(object obj) => Equals(obj as OrExpression); + /// + public override bool Equals(object obj) => Equals(obj as OrExpression); - /// + /// #if NETSTANDARD1_3 || NETSTANDARD2_0 - public override int GetHashCode() => (Left, Right).GetHashCode(); + public override int GetHashCode() => (Left, Right).GetHashCode(); #else - public override int GetHashCode() => HashCode.Combine(Left, Right); + public override int GetHashCode() => HashCode.Combine(Left, Right); #endif - /// - public override string ToString() => _lazyToString.Value; + /// + public override string ToString() => _lazyToString.Value; - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; - /// - public override bool IsEquivalentTo(FilterExpression other) + /// + public override bool IsEquivalentTo(FilterExpression other) + { + return other switch { - return other switch - { - OrExpression or => (or.Right.IsEquivalentTo(Right) && or.Left.IsEquivalentTo(Left)) - || (or.Left.IsEquivalentTo(Right) && or.Right.IsEquivalentTo(Left)), - OneOfExpression oneOf => IsEquivalentTo(oneOf.Simplify()), - _ => Left.IsEquivalentTo(Right) && Left.IsEquivalentTo(other), - }; - } - - /// - public override double Complexity => Left.Complexity + Right.Complexity; + OrExpression or => (or.Right.IsEquivalentTo(Right) && or.Left.IsEquivalentTo(Left)) + || (or.Left.IsEquivalentTo(Right) && or.Right.IsEquivalentTo(Left)), + OneOfExpression oneOf => IsEquivalentTo(oneOf.Simplify()), + _ => Left.IsEquivalentTo(Right) && Left.IsEquivalentTo(other), + }; } + + /// + public override double Complexity => Left.Complexity + Right.Complexity; } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/PropertyName.cs b/src/DataFilters/Grammar/Syntax/PropertyName.cs index dbdc3dfc..5aa066d8 100644 --- a/src/DataFilters/Grammar/Syntax/PropertyName.cs +++ b/src/DataFilters/Grammar/Syntax/PropertyName.cs @@ -1,54 +1,56 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; +namespace DataFilters.Grammar.Syntax; - /// - /// a holds the name of a property a filter is build against. - /// +using System; + +/// +/// a holds the name of a property a filter is build against. +/// #if NETSTANDARD1_3 - public class PropertyName : IEquatable +public class PropertyName : IEquatable #else - public record PropertyName +public record PropertyName #endif +{ + /// + /// Name of the property a filter is applied to + /// + public string Name { get; } + + /// + /// Builds a new with the specified . + /// + /// + /// is + /// is string.Empty or contains only whitespaces. + public PropertyName(string name) { - /// - /// Name of the property a filter is applied to - /// - public string Name { get; } - - /// - /// Builds a new with the specified . - /// - /// - /// is null - /// is string.Empty or contains only whitespaces. - public PropertyName(string name) +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNullOrWhiteSpace(name); +#else + if (name is null) { - if (name is null) - { - throw new ArgumentNullException(nameof(name)); - } - - if (string.IsNullOrWhiteSpace(name)) - { - throw new ArgumentOutOfRangeException(nameof(name)); - } + throw new ArgumentNullException(nameof(name)); + } - Name = name; + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentException("Name cannot be null or whitespace", nameof(name)); } +#endif + Name = name; + } #if NETSTANDARD1_3 - /// - public bool Equals(PropertyName other) => Name == other?.Name; + /// + public bool Equals(PropertyName other) => Name == other?.Name; - /// - public override bool Equals(object obj) => Equals(obj as PropertyName); + /// + public override bool Equals(object obj) => Equals(obj as PropertyName); - /// - public override int GetHashCode() => Name.GetHashCode(); + /// + public override int GetHashCode() => Name.GetHashCode(); - /// - public override string ToString() => $"{nameof(PropertyName)}[{Name}]"; + /// + public override string ToString() => $"{nameof(PropertyName)}[{Name}]"; #endif - } } diff --git a/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs b/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs index 41cd07cf..74c9c022 100644 --- a/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs +++ b/src/DataFilters/Grammar/Syntax/RangeBracketValue.cs @@ -1,96 +1,95 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; + +/// +/// stores a range value used in a bracket expression +/// +/// +/// Builds a new instance. +/// +/// +/// +public sealed class RangeBracketValue(char start, char end) : BracketValue, IEquatable, IComparable { - using System; - using System.Collections.Generic; - /// - /// stores a range value used in a bracket expression + /// Start of the regex range /// - /// - /// Builds a new instance. - /// - /// - /// - public sealed class RangeBracketValue(char start, char end) : BracketValue, IEquatable, IComparable - { - /// - /// Start of the regex range - /// - public char Start { get; } = start; + public char Start { get; } = start; - /// - /// Ends of the regex range - /// - public char End { get; } = end; + /// + /// Ends of the regex range + /// + public char End { get; } = end; - /// - public bool Equals(RangeBracketValue other) => (Start, End) == (other?.Start, other?.End); + /// + public bool Equals(RangeBracketValue other) => (Start, End) == (other?.Start, other?.End); - /// - public override bool Equals(object obj) + /// + public override bool Equals(object obj) + { + bool equals; + switch (obj) { - bool equals; - switch (obj) - { - case ConstantBracketValue constantBracketValue: - char[] chrs = constantBracketValue.Value.ToCharArray(); - char head = chrs[0]; + case ConstantBracketValue constantBracketValue: + char[] chrs = constantBracketValue.Value.ToCharArray(); + char head = chrs[0]; #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - char tail = chrs[^1]; + char tail = chrs[^1]; #else - char tail = chrs[chrs.Length - 1]; + char tail = chrs[chrs.Length - 1]; #endif - equals = (Start, End).Equals((head, tail)); - break; - default: - equals = Equals(obj as RangeBracketValue); - break; - } - return equals; + equals = (Start, End).Equals((head, tail)); + break; + default: + equals = Equals(obj as RangeBracketValue); + break; } + return equals; + } - /// - public static bool operator ==(RangeBracketValue left, RangeBracketValue right) => EqualityComparer.Default.Equals(left, right); + /// + public static bool operator ==(RangeBracketValue left, RangeBracketValue right) => EqualityComparer.Default.Equals(left, right); - /// - public static bool operator !=(RangeBracketValue left, RangeBracketValue right) => !(left == right); + /// + public static bool operator !=(RangeBracketValue left, RangeBracketValue right) => !(left == right); - /// - public static bool operator <(RangeBracketValue left, RangeBracketValue right) => left.Start < right.Start; + /// + public static bool operator <(RangeBracketValue left, RangeBracketValue right) => left.Start < right.Start; - /// - public static bool operator >(RangeBracketValue left, RangeBracketValue right) => left.Start > right.Start; + /// + public static bool operator >(RangeBracketValue left, RangeBracketValue right) => left.Start > right.Start; - /// - public static bool operator <=(RangeBracketValue left, RangeBracketValue right) => left < right || left == right; + /// + public static bool operator <=(RangeBracketValue left, RangeBracketValue right) => left < right || left == right; - /// - public static bool operator >=(RangeBracketValue left, RangeBracketValue right) => left > right || left == right; + /// + public static bool operator >=(RangeBracketValue left, RangeBracketValue right) => left > right || left == right; - /// + /// #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER - public override int GetHashCode() => HashCode.Combine(Start, End); + public override int GetHashCode() => HashCode.Combine(Start, End); #else - public override int GetHashCode() - { - int hashCode = -1676728671; - hashCode = (hashCode * -1521134295) + Start.GetHashCode(); - hashCode = (hashCode * -1521134295) + End.GetHashCode(); - return hashCode; - } + public override int GetHashCode() + { + int hashCode = -1676728671; + hashCode = (hashCode * -1521134295) + Start.GetHashCode(); + hashCode = (hashCode * -1521134295) + End.GetHashCode(); + return hashCode; + } #endif - /// - public override string EscapedParseableString => $"[{Start}-{End}]"; + /// + public override string EscapedParseableString => $"[{Start}-{End}]"; - /// - public override string OriginalString => $"[{Start}-{End}]"; + /// + public override string OriginalString => $"[{Start}-{End}]"; - /// - public override double Complexity => 1 + Math.Pow(2, End - Start); + /// + public override double Complexity => 1 + Math.Pow(2, End - Start); - /// - public int CompareTo(RangeBracketValue other) => (Start, End).CompareTo((other.Start, other.End)); - } + /// + public int CompareTo(RangeBracketValue other) => (Start, End).CompareTo((other.Start, other.End)); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs b/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs index a8078a1a..c1c54722 100644 --- a/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs +++ b/src/DataFilters/Grammar/Syntax/StartsWithExpression.cs @@ -1,159 +1,161 @@ -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using DataFilters.Grammar.Parsing; +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using DataFilters.Grammar.Parsing; #if !NETSTANDARD1_3 - using Ardalis.GuardClauses; +using Ardalis.GuardClauses; #endif +/// +/// A that defines a string that starts with a specified . +/// +public sealed class StartsWithExpression : FilterExpression, IEquatable +{ + /// + /// Value associated with the expression + /// + public string Value { get; } + + private readonly Lazy _lazyEscapedParseableString; + /// - /// A that defines a string that starts with a specified . + /// Builds a new that holds the specified . /// - public sealed class StartsWithExpression : FilterExpression, IEquatable + /// + /// if is + /// if is . + public StartsWithExpression(string value) { - /// - /// Value associated with the expression - /// - public string Value { get; } - - private readonly Lazy _lazyEscapedParseableString; - - /// - /// Builds a new that holds the specified . - /// - /// - /// if is null - /// if is . - public StartsWithExpression(string value) +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNullOrWhiteSpace(value); +#else + if (value is null) { - if (value is null) - { - throw new ArgumentNullException(nameof(value)); - } + throw new ArgumentNullException(nameof(value)); + } - if (value.Length == 0) - { - throw new ArgumentOutOfRangeException(nameof(value)); - } + if (value.Length == 0) + { + throw new ArgumentException("Value cannot be empty", nameof(value)); + } +#endif + Value = value; - Value = value; + _lazyEscapedParseableString = new(() => + { + // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) + // Also we need an extra position for the final '*' that will be append in all cases + bool requireEscapingCharacters = Value.AtLeastOnce(chr => FilterTokenizer.SpecialCharacters.Contains(chr)); + StringBuilder parseableString; - _lazyEscapedParseableString = new(() => + if (requireEscapingCharacters) { - // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) - // Also we need an extra position for the final '*' that will be append in all cases - bool requireEscapingCharacters = Value.AtLeastOnce(chr => FilterTokenizer.SpecialCharacters.Contains(chr)); - StringBuilder parseableString; - - if (requireEscapingCharacters) + parseableString = new((Value.Length * 2) + 1); + foreach (char chr in Value) { - parseableString = new((Value.Length * 2) + 1); - foreach (char chr in Value) + if (FilterTokenizer.SpecialCharacters.Contains(chr)) { - if (FilterTokenizer.SpecialCharacters.Contains(chr)) - { - parseableString = parseableString.Append('\\'); - } - parseableString = parseableString.Append(chr); + parseableString = parseableString.Append('\\'); } + parseableString = parseableString.Append(chr); } - else - { - parseableString = new(Value, Value.Length + 1); - } + } + else + { + parseableString = new(Value, Value.Length + 1); + } - return parseableString.Append('*').ToString(); - }); - } + return parseableString.Append('*').ToString(); + }); + } - /// - /// Builds a new that holds the specified . - /// - /// - /// is null. - public StartsWithExpression(TextExpression text) + /// + /// Builds a new that holds the specified . + /// + /// + /// is . + public StartsWithExpression(TextExpression text) #if !NETSTANDARD1_3 - : this (Guard.Against.Null(text, nameof(text)).OriginalString) - { - _lazyEscapedParseableString = new(() => $"{text.EscapedParseableString}*"); - } + : this(Guard.Against.Null(text, nameof(text)).OriginalString) + { + _lazyEscapedParseableString = new(() => $"{text.EscapedParseableString}*"); + } #else + { + if (text is null) { - if (text is null) - { - throw new ArgumentNullException(nameof(text)); - } - _lazyEscapedParseableString = new(() => $"{text.EscapedParseableString}*"); + throw new ArgumentNullException(nameof(text)); } + _lazyEscapedParseableString = new(() => $"{text.EscapedParseableString}*"); + } #endif - /// - public bool Equals(StartsWithExpression other) => Value == other?.Value; - - /// - public override bool Equals(object obj) => Equals(obj as StartsWithExpression); - - /// - public override int GetHashCode() => Value.GetHashCode(); - - /// - public override string ToString() => OriginalString; - - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; - - /// - /// Combines the specified and s into a new . - /// - /// The left operand - /// The right operand - /// a whose is and is - /// - public static AndExpression operator +(StartsWithExpression left, EndsWithExpression right) => new(left, right); - - /// - /// Combines the specified and - /// - /// the left operand - /// the right operand - /// - /// A that can either : - /// - /// exactly matches the concatenation of 's value and 's value. - /// exactly starts with the concatenation of 's value and 's value. - /// exactly starts with 's value and contains 's value. - /// - /// - public static OneOfExpression operator +(StartsWithExpression left, StartsWithExpression right) => new(new StringValueExpression(left.Value + right.Value), - new StartsWithExpression(left.Value + right.Value), - new AndExpression(left, new ContainsExpression(right.Value))); - - /// - /// Combines the specified and - /// - /// the left operand - /// the right operand - /// - /// A that can either : - /// - /// exactly matches the concatenation of 's value and 's value. - /// exactly starts with the concatenation of 's value and 's value. - /// exactly starts with 's value and contains 's value. - /// - /// - public static OneOfExpression operator +(StartsWithExpression left, ContainsExpression right) => left + new StartsWithExpression(right.Value); - - /// - /// Combines the specified and - /// - /// the left operand - /// the right operand - /// - /// A that can match any that starts with and end - /// - public static AndExpression operator +(StartsWithExpression left, StringValueExpression right) => left + new EndsWithExpression(right.Value); - } + /// + public bool Equals(StartsWithExpression other) => Value == other?.Value; + + /// + public override bool Equals(object obj) => Equals(obj as StartsWithExpression); + + /// + public override int GetHashCode() => Value.GetHashCode(); + + /// + public override string ToString() => OriginalString; + + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; + + /// + /// Combines the specified and s into a new . + /// + /// The left operand + /// The right operand + /// a whose is and is + /// + public static AndExpression operator +(StartsWithExpression left, EndsWithExpression right) => new(left, right); + + /// + /// Combines the specified and + /// + /// the left operand + /// the right operand + /// + /// A that can either : + /// + /// exactly matches the concatenation of 's value and 's value. + /// exactly starts with the concatenation of 's value and 's value. + /// exactly starts with 's value and contains 's value. + /// + /// + public static OneOfExpression operator +(StartsWithExpression left, StartsWithExpression right) => new(new StringValueExpression(left.Value + right.Value), + new StartsWithExpression(left.Value + right.Value), + new AndExpression(left, new ContainsExpression(right.Value))); + + /// + /// Combines the specified and + /// + /// the left operand + /// the right operand + /// + /// A that can either : + /// + /// exactly matches the concatenation of 's value and 's value. + /// exactly starts with the concatenation of 's value and 's value. + /// exactly starts with 's value and contains 's value. + /// + /// + public static OneOfExpression operator +(StartsWithExpression left, ContainsExpression right) => left + new StartsWithExpression(right.Value); + + /// + /// Combines the specified and + /// + /// the left operand + /// the right operand + /// + /// A that can match any that starts with and end + /// + public static AndExpression operator +(StartsWithExpression left, StringValueExpression right) => left + new EndsWithExpression(right.Value); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/StringValueExpression.cs b/src/DataFilters/Grammar/Syntax/StringValueExpression.cs index 47814398..70d367ae 100644 --- a/src/DataFilters/Grammar/Syntax/StringValueExpression.cs +++ b/src/DataFilters/Grammar/Syntax/StringValueExpression.cs @@ -1,85 +1,84 @@  -namespace DataFilters.Grammar.Syntax -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; +namespace DataFilters.Grammar.Syntax; - using static DataFilters.Grammar.Parsing.FilterTokenizer; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; - /// - /// Wraps a string that represents a constant string value - /// - /// - /// Builds a new instance that can wrap a string value - /// - /// value of the expression. - /// - /// The property automatically escapes from . - /// - public class StringValueExpression(string value) : ConstantValueExpression(value), IEquatable - { - private readonly Lazy _lazyParseableString = new(() => - { - // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) - // Also we need an extra position for the final '*' that will be append in all cases - bool requireEscapingCharacters = value.AtLeastOnce(chr => SpecialCharacters.Contains(chr)); - StringBuilder parseableString; +using static DataFilters.Grammar.Parsing.FilterTokenizer; - if (requireEscapingCharacters) +/// +/// Wraps a string that represents a constant string value +/// +/// +/// Builds a new instance that can wrap a string value +/// +/// value of the expression. +/// +/// The property automatically escapes from . +/// +public class StringValueExpression(string value) : ConstantValueExpression(value), IEquatable +{ + private readonly Lazy _lazyParseableString = new(() => + { + // The length of the final parseable string in worst cases scenario will double (1 backlash + the escaped character) + // Also we need an extra position for the final '*' that will be append in all cases + bool requireEscapingCharacters = value.AtLeastOnce(chr => SpecialCharacters.Contains(chr)); + StringBuilder parseableString; + + if (requireEscapingCharacters) + { + parseableString = new((value.Length * 2) + 1); + foreach (char chr in value) { - parseableString = new((value.Length * 2) + 1); - foreach (char chr in value) + if (SpecialCharacters.Contains(chr)) { - if (SpecialCharacters.Contains(chr)) - { - parseableString = parseableString.Append('\\'); - } - parseableString = parseableString.Append(chr); + parseableString = parseableString.Append('\\'); } + parseableString = parseableString.Append(chr); } - else - { - parseableString = new(value); - } + } + else + { + parseableString = new(value); + } - return parseableString.ToString(); - }); + return parseableString.ToString(); + }); - /// - public override string EscapedParseableString => _lazyParseableString.Value; + /// + public override string EscapedParseableString => _lazyParseableString.Value; - /// - public override string OriginalString => Value; + /// + public override string OriginalString => Value; - /// - public virtual bool Equals(StringValueExpression other) => Equals(Value, other?.Value); + /// + public virtual bool Equals(StringValueExpression other) => Equals(Value, other?.Value); - /// - public override bool Equals(object obj) => ReferenceEquals(this, obj) || Equals(obj as StringValueExpression); + /// + public override bool Equals(object obj) => ReferenceEquals(this, obj) || Equals(obj as StringValueExpression); - /// - public override bool IsEquivalentTo(FilterExpression other) - => other switch - { - StringValueExpression stringValue => Equals(stringValue), - ISimplifiable simplifiable => Equals(simplifiable.Simplify() as StringValueExpression), - _ => false - }; + /// + public override bool IsEquivalentTo(FilterExpression other) + => other switch + { + StringValueExpression stringValue => Equals(stringValue), + ISimplifiable simplifiable => Equals(simplifiable.Simplify() as StringValueExpression), + _ => false + }; - /// - public override int GetHashCode() => Value.GetHashCode(); + /// + public override int GetHashCode() => Value.GetHashCode(); - /// - public override double Complexity => 1; + /// + public override double Complexity => 1; - /// - public static bool operator ==(StringValueExpression left, StringValueExpression right) - => (left is null && right is null) || (left?.Equals(right) ?? false); + /// + public static bool operator ==(StringValueExpression left, StringValueExpression right) + => (left is null && right is null) || (left?.Equals(right) ?? false); - /// - public static bool operator !=(StringValueExpression left, StringValueExpression right) - => !(left == right); - } + /// + public static bool operator !=(StringValueExpression left, StringValueExpression right) + => !(left == right); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/TextExpression.cs b/src/DataFilters/Grammar/Syntax/TextExpression.cs index d4355d0e..54a66666 100644 --- a/src/DataFilters/Grammar/Syntax/TextExpression.cs +++ b/src/DataFilters/Grammar/Syntax/TextExpression.cs @@ -1,55 +1,54 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Text; + +/// +/// An expression that holds a string value "as is". +/// +/// +/// Builds a new instance. +/// +/// Value of the expression +public class TextExpression(string value) : StringValueExpression(value), IEquatable { - using System; - using System.Collections.Generic; - using System.Text; - - /// - /// An expression that holds a string value "as is". - /// - /// - /// Builds a new instance. - /// - /// Value of the expression - public class TextExpression(string value) : StringValueExpression(value), IEquatable - { - private readonly Lazy _lazyEscapedParseableString = new(() => + private readonly Lazy _lazyEscapedParseableString = new(() => + { + StringBuilder escapedParseableString; + if (value.AtLeastOnce(chr => chr == '"' || chr == '\\')) { - StringBuilder escapedParseableString; - if (value.AtLeastOnce(chr => chr == '"' || chr == '\\')) - { - escapedParseableString = new StringBuilder((value.Length * 2) + 2); + escapedParseableString = new StringBuilder((value.Length * 2) + 2); - foreach (char chr in value) + foreach (char chr in value) + { + if (chr == '"' || chr == '\\') { - if (chr == '"' || chr == '\\') - { - escapedParseableString.Append('\\'); - } - escapedParseableString.Append(chr); + escapedParseableString.Append('\\'); } + escapedParseableString.Append(chr); } - else - { - escapedParseableString = new(value); - } + } + else + { + escapedParseableString = new(value); + } - return escapedParseableString.Insert(0, '"').Append('"').ToString(); - }); + return escapedParseableString.Insert(0, '"').Append('"').ToString(); + }); - /// - public override string EscapedParseableString => _lazyEscapedParseableString.Value; + /// + public override string EscapedParseableString => _lazyEscapedParseableString.Value; - /// - public override string OriginalString => Value; + /// + public override string OriginalString => Value; - /// - public virtual bool Equals(TextExpression other) => Value.Equals(other?.Value); + /// + public virtual bool Equals(TextExpression other) => Value.Equals(other?.Value); - /// - public override bool Equals(object obj) => Equals(obj as TextExpression); + /// + public override bool Equals(object obj) => Equals(obj as TextExpression); - /// - public override int GetHashCode() => Value.GetHashCode(); - } + /// + public override int GetHashCode() => Value.GetHashCode(); } \ No newline at end of file diff --git a/src/DataFilters/Grammar/Syntax/TimeExpression.cs b/src/DataFilters/Grammar/Syntax/TimeExpression.cs index da4f2c97..265122d4 100644 --- a/src/DataFilters/Grammar/Syntax/TimeExpression.cs +++ b/src/DataFilters/Grammar/Syntax/TimeExpression.cs @@ -1,109 +1,108 @@ -namespace DataFilters.Grammar.Syntax +namespace DataFilters.Grammar.Syntax; + +using System; + +/// +/// A that only consists of time part. +/// +public sealed class TimeExpression : FilterExpression, IEquatable, IBoundaryExpression { - using System; + /// + /// Hours part of the expression + /// + public int Hours { get; } /// - /// A that only consists of time part. + /// Minutes of the expression /// - public sealed class TimeExpression : FilterExpression, IEquatable, IBoundaryExpression + public int Minutes { get; } + + /// + /// Seconds of the expression + /// + public int Seconds { get; } + + /// + /// Milliseconds of the expression + /// + public int Milliseconds { get; } + + /// + /// Builds a new instance. + /// + /// + /// + /// + /// + /// + /// either , , , is < 0. + /// + public TimeExpression(int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0) { - /// - /// Hours part of the expression - /// - public int Hours { get; } - - /// - /// Minutes of the expression - /// - public int Minutes { get; } - - /// - /// Seconds of the expression - /// - public int Seconds { get; } - - /// - /// Milliseconds of the expression - /// - public int Milliseconds { get; } - - /// - /// Builds a new instance. - /// - /// - /// - /// - /// - /// - /// either , , , is < 0. - /// - public TimeExpression(int hours = 0, int minutes = 0, int seconds = 0, int milliseconds = 0) + if (hours < 0) { - if (hours < 0) - { - throw new ArgumentOutOfRangeException(nameof(hours), hours, $"{nameof(hours)} must be zero or positive"); - } - - if (minutes < 0) - { - throw new ArgumentOutOfRangeException(nameof(minutes), minutes, $"{nameof(minutes)} must be zero or positive"); - } - - if (seconds < 0) - { - throw new ArgumentOutOfRangeException(nameof(seconds), seconds, $"{nameof(seconds)} must be zero or positive"); - } - - if (milliseconds < 0) - { - throw new ArgumentOutOfRangeException(nameof(milliseconds), milliseconds, $"{nameof(milliseconds)} must be zero or positive"); - } - - Hours = hours; - Minutes = minutes; - Seconds = seconds; - Milliseconds = milliseconds; + throw new ArgumentOutOfRangeException(nameof(hours), hours, $"{nameof(hours)} must be zero or positive"); } - /// - public bool Equals(TimeExpression other) => other is not null - && ((Hours, Minutes, Seconds, Milliseconds) == (other.Hours, other.Minutes, other.Seconds, other.Milliseconds) - || new TimeSpan(Hours, Minutes, Seconds).Add(TimeSpan.FromMilliseconds(Milliseconds)) == new TimeSpan(other.Hours, other.Minutes, other.Seconds).Add(TimeSpan.FromMilliseconds(other.Milliseconds))); + if (minutes < 0) + { + throw new ArgumentOutOfRangeException(nameof(minutes), minutes, $"{nameof(minutes)} must be zero or positive"); + } - /// - public override bool Equals(object obj) => Equals(obj as TimeExpression); + if (seconds < 0) + { + throw new ArgumentOutOfRangeException(nameof(seconds), seconds, $"{nameof(seconds)} must be zero or positive"); + } - /// - public override bool IsEquivalentTo(FilterExpression other) => other switch + if (milliseconds < 0) { - TimeExpression time => Equals(time), - DateTimeExpression { Date: null, Time: var time, Offset: null } => Equals(time), - _ => Equals((other as ISimplifiable)?.Simplify() ?? other) - }; + throw new ArgumentOutOfRangeException(nameof(milliseconds), milliseconds, $"{nameof(milliseconds)} must be zero or positive"); + } - /// - public override int GetHashCode() => (Hours, Minutes, Seconds, Milliseconds).GetHashCode(); + Hours = hours; + Minutes = minutes; + Seconds = seconds; + Milliseconds = milliseconds; + } - /// - public override string EscapedParseableString => $"{Hours:D2}:{Minutes:D2}:{Seconds:D2}{(Milliseconds > 0 ? $".{Milliseconds}" : string.Empty)}"; + /// + public bool Equals(TimeExpression other) => other is not null + && ((Hours, Minutes, Seconds, Milliseconds) == (other.Hours, other.Minutes, other.Seconds, other.Milliseconds) + || new TimeSpan(Hours, Minutes, Seconds).Add(TimeSpan.FromMilliseconds(Milliseconds)) == new TimeSpan(other.Hours, other.Minutes, other.Seconds).Add(TimeSpan.FromMilliseconds(other.Milliseconds))); - /// - public void Deconstruct(out int hours, out int minutes, out int seconds, out int milliseconds) - { - hours = Hours; - minutes = Minutes; - seconds = Seconds; - milliseconds = Milliseconds; - } + /// + public override bool Equals(object obj) => Equals(obj as TimeExpression); - /// - public static bool operator ==(TimeExpression left, TimeExpression right) => left switch - { - null => right is null, - _ => left.Equals(right) - }; + /// + public override bool IsEquivalentTo(FilterExpression other) => other switch + { + TimeExpression time => Equals(time), + DateTimeExpression { Date: null, Time: var time, Offset: null } => Equals(time), + _ => Equals((other as ISimplifiable)?.Simplify() ?? other) + }; + + /// + public override int GetHashCode() => (Hours, Minutes, Seconds, Milliseconds).GetHashCode(); - /// - public static bool operator !=(TimeExpression left, TimeExpression right) => !(left == right); + /// + public override string EscapedParseableString => $"{Hours:D2}:{Minutes:D2}:{Seconds:D2}{(Milliseconds > 0 ? $".{Milliseconds}" : string.Empty)}"; + + /// + public void Deconstruct(out int hours, out int minutes, out int seconds, out int milliseconds) + { + hours = Hours; + minutes = Minutes; + seconds = Seconds; + milliseconds = Milliseconds; } + + /// + public static bool operator ==(TimeExpression left, TimeExpression right) => left switch + { + null => right is null, + _ => left.Equals(right) + }; + + /// + public static bool operator !=(TimeExpression left, TimeExpression right) => !(left == right); } diff --git a/src/DataFilters/IFIlter.cs b/src/DataFilters/IFIlter.cs index 77685e2d..d34647f6 100644 --- a/src/DataFilters/IFIlter.cs +++ b/src/DataFilters/IFIlter.cs @@ -1,27 +1,26 @@ -namespace DataFilters -{ - using System; +namespace DataFilters; + +using System; +/// +/// Defines the basic shape of a filter +/// +public interface IFilter : IEquatable +{ /// - /// Defines the basic shape of a filter + /// Gets the JSON representation of the filter /// - public interface IFilter : IEquatable - { - /// - /// Gets the JSON representation of the filter - /// - /// A json representation of the current instance. - string ToJson(); + /// A json representation of the current instance. + string ToJson(); - /// - /// Computes a new instance which is the exact opposite of the current instance. - /// - /// The exact opposite of the current instance. - IFilter Negate(); + /// + /// Computes a new instance which is the exact opposite of the current instance. + /// + /// The exact opposite of the current instance. + IFilter Negate(); #if NETSTANDARD2_1 || NET5_0_OR_GREATER - /// - public virtual void ToString() => ToJson(); + /// + public virtual void ToString() => ToJson(); #endif - } } diff --git a/src/DataFilters/IFilterService.cs b/src/DataFilters/IFilterService.cs deleted file mode 100644 index 38243789..00000000 --- a/src/DataFilters/IFilterService.cs +++ /dev/null @@ -1,22 +0,0 @@ -#if NETSTANDARD2_0_OR_GREATER || NET5_0_OR_GREATER -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace DataFilters; - -/// -/// A service that can build instances -/// -public interface IFilterService -{ - /// - /// Computes a instance. - /// - /// Type onto which the resulting filter should be applied - /// Query string that describe the filter to apply - /// an instance - IFilter Compute(string input); -} - -#endif \ No newline at end of file diff --git a/src/DataFilters/IOrder.cs b/src/DataFilters/IOrder.cs index 9f1bc98f..e6872c9f 100644 --- a/src/DataFilters/IOrder.cs +++ b/src/DataFilters/IOrder.cs @@ -1,25 +1,24 @@ -namespace DataFilters -{ - using System; +namespace DataFilters; + +using System; +/// +/// Marker interface +/// +/// Type of the element the sort will be applied onto +public interface IOrder : IEquatable> +{ /// - /// Marker interface + /// Tests if the current instance is equivalent to . /// - /// Type of the element the sort will be applied onto - public interface IOrder : IEquatable> - { - /// - /// Tests if the current instance is equivalent to . - /// - /// - /// - /// Two instances are equivalent when, given an expression, one can be swapped for the other without altering the meaning of the whole expression. - /// - /// true when current instance and other are equivalent and false otherwise. + /// + /// + /// Two instances are equivalent when, given an expression, one can be swapped for the other without altering the meaning of the whole expression. + /// + /// true when current instance and other are equivalent and false otherwise. #if !(NETSTANDARD1_3 || NETSTANDARD2_0) - public virtual bool IsEquivalentTo(IOrder other) => Equals(other); + public virtual bool IsEquivalentTo(IOrder other) => Equals(other); #else - bool IsEquivalentTo(IOrder other); + bool IsEquivalentTo(IOrder other); #endif - } } diff --git a/src/DataFilters/InvalidOrderExpressionException.cs b/src/DataFilters/InvalidOrderExpressionException.cs index 8972ff94..dcbdfb7c 100644 --- a/src/DataFilters/InvalidOrderExpressionException.cs +++ b/src/DataFilters/InvalidOrderExpressionException.cs @@ -1,26 +1,25 @@ -namespace DataFilters -{ - using System; +namespace DataFilters; + +using System; - /// - /// Exception thrown for an invalid sort expression. - /// - [Serializable] - public class InvalidOrderExpressionException : Exception +/// +/// Exception thrown for an invalid sort expression. +/// +[Serializable] +public class InvalidOrderExpressionException : Exception +{ + /// + public InvalidOrderExpressionException(string expression) : base($"'{expression}' must matches '{OrderValidator.Pattern}' pattern") { - /// - public InvalidOrderExpressionException(string expression) : base($"'{expression}' must matches '{OrderValidator.Pattern}' pattern") - { - } + } - /// - public InvalidOrderExpressionException() : base() - { - } + /// + public InvalidOrderExpressionException() : base() + { + } - /// - public InvalidOrderExpressionException(string message, Exception innerException) : base(message, innerException) - { - } + /// + public InvalidOrderExpressionException(string message, Exception innerException) : base(message, innerException) + { } } diff --git a/src/DataFilters/MultiFilter.cs b/src/DataFilters/MultiFilter.cs index 6320710a..a11ff2d4 100644 --- a/src/DataFilters/MultiFilter.cs +++ b/src/DataFilters/MultiFilter.cs @@ -1,129 +1,128 @@ -namespace DataFilters -{ - using DataFilters.Converters; - using Newtonsoft.Json; - using Newtonsoft.Json.Schema; - using System; - using System.Collections.Generic; - using System.Linq; +namespace DataFilters; + +using DataFilters.Converters; +using Newtonsoft.Json; +using Newtonsoft.Json.Schema; +using System; +using System.Collections.Generic; +using System.Linq; #if !NETSTANDARD1_3 - using System.Text.Json.Serialization; +using System.Text.Json.Serialization; #else - using static Newtonsoft.Json.DefaultValueHandling; - using static Newtonsoft.Json.Required; +using static Newtonsoft.Json.DefaultValueHandling; +using static Newtonsoft.Json.Required; #endif - /// - /// An instance of this class holds combination of - /// - [JsonObject] +/// +/// An instance of this class holds combination of +/// +[JsonObject] #if NETSTANDARD1_3 - [JsonConverter(typeof(MultiFilterConverter))] +[JsonConverter(typeof(MultiFilterConverter))] #else - [System.Text.Json.Serialization.JsonConverter(typeof(MultiFilterConverter))] +[System.Text.Json.Serialization.JsonConverter(typeof(MultiFilterConverter))] #endif - public sealed class MultiFilter : IFilter, IEquatable - { - /// - /// Name of the json property that holds filter's filters collection. - /// - public const string FiltersJsonPropertyName = "filters"; +public sealed class MultiFilter : IFilter, IEquatable +{ + /// + /// Name of the json property that holds filter's filters collection. + /// + public const string FiltersJsonPropertyName = "filters"; - /// - /// Name of the json property that holds the composite filter's logic - /// - public const string LogicJsonPropertyName = "logic"; + /// + /// Name of the json property that holds the composite filter's logic + /// + public const string LogicJsonPropertyName = "logic"; - /// - /// JSON schema - /// - public static JSchema Schema => new() + /// + /// JSON schema + /// + public static JSchema Schema => new() + { + Type = JSchemaType.Object, + Properties = { - Type = JSchemaType.Object, - Properties = - { - [FiltersJsonPropertyName] = new JSchema { Type = JSchemaType.Array, MinimumItems = 2 }, - [LogicJsonPropertyName] = new JSchema { Type = JSchemaType.String, Default = "and"} - }, - Required = { FiltersJsonPropertyName }, - AllowAdditionalProperties = false - }; + [FiltersJsonPropertyName] = new JSchema { Type = JSchemaType.Array, MinimumItems = 2 }, + [LogicJsonPropertyName] = new JSchema { Type = JSchemaType.String, Default = "and"} + }, + Required = { FiltersJsonPropertyName }, + AllowAdditionalProperties = false + }; - /// - /// Collections of filters - /// + /// + /// Collections of filters + /// #if NETSTANDARD1_3 - [JsonProperty(PropertyName = FiltersJsonPropertyName, Required = Always)] + [JsonProperty(PropertyName = FiltersJsonPropertyName, Required = Always)] #else - [JsonPropertyName(FiltersJsonPropertyName)] + [JsonPropertyName(FiltersJsonPropertyName)] #endif - public IEnumerable Filters { get; set; } = Enumerable.Empty(); + public IEnumerable Filters { get; set; } = Enumerable.Empty(); - /// - /// Operator to apply between - /// + /// + /// Operator to apply between + /// #if NETSTANDARD1_3 - [JsonProperty(PropertyName = LogicJsonPropertyName, DefaultValueHandling = IgnoreAndPopulate)] - [JsonConverter(typeof(CamelCaseEnumTypeConverter))] + [JsonProperty(PropertyName = LogicJsonPropertyName, DefaultValueHandling = IgnoreAndPopulate)] + [JsonConverter(typeof(CamelCaseEnumTypeConverter))] #else - [JsonPropertyName(LogicJsonPropertyName)] + [JsonPropertyName(LogicJsonPropertyName)] #endif - public FilterLogic Logic { get; set; } + public FilterLogic Logic { get; set; } - /// - public string ToJson() => this.Jsonify(); + /// + public string ToJson() => this.Jsonify(); - /// - public IFilter Negate() + /// + public IFilter Negate() + { + MultiFilter filter = new() { - MultiFilter filter = new() + Logic = Logic switch { - Logic = Logic switch - { - FilterLogic.And => FilterLogic.Or, - FilterLogic.Or => FilterLogic.And, - _ => throw new ArgumentOutOfRangeException($"Unsupported {Logic}") - }, - Filters = Filters.Select(f => f.Negate()) + FilterLogic.And => FilterLogic.Or, + FilterLogic.Or => FilterLogic.And, + _ => throw new ArgumentOutOfRangeException($"Unsupported {Logic}") + }, + Filters = Filters.Select(f => f.Negate()) #if DEBUG - .ToArray() + .ToArray() #endif - }; + }; - return filter; - } + return filter; + } - /// + /// #if NETSTANDARD1_3 || NETSTANDARD2_0 - public override int GetHashCode() => (Logic, Filters).GetHashCode(); + public override int GetHashCode() => (Logic, Filters).GetHashCode(); #else - public override int GetHashCode() + public override int GetHashCode() + { + HashCode hash = new(); + hash.Add(Logic); + foreach (IFilter filter in Filters) { - HashCode hash = new(); - hash.Add(Logic); - foreach (IFilter filter in Filters) - { - hash.Add(filter); - } - return hash.ToHashCode(); + hash.Add(filter); } + return hash.ToHashCode(); + } #endif - /// - public bool Equals(IFilter other) => Equals(other as MultiFilter); + /// + public bool Equals(IFilter other) => Equals(other as MultiFilter); - /// - public override bool Equals(object obj) => Equals(obj as MultiFilter); + /// + public override bool Equals(object obj) => Equals(obj as MultiFilter); - /// - public bool Equals(MultiFilter other) - => Logic == other?.Logic - && Filters.Count() == other?.Filters?.Count() - && Filters.All(filter => other?.Filters?.Contains(filter) ?? false) - && (other?.Filters.All(filter => Filters.Contains(filter)) ?? false); + /// + public bool Equals(MultiFilter other) + => Logic == other?.Logic + && Filters.Count() == other?.Filters?.Count() + && Filters.All(filter => other?.Filters?.Contains(filter) ?? false) + && (other?.Filters.All(filter => Filters.Contains(filter)) ?? false); - /// - public override string ToString() => this.Jsonify(); - } + /// + public override string ToString() => this.Jsonify(); } diff --git a/src/DataFilters/MultiOrder.cs b/src/DataFilters/MultiOrder.cs index fc9b9c63..e4d1a67b 100644 --- a/src/DataFilters/MultiOrder.cs +++ b/src/DataFilters/MultiOrder.cs @@ -1,55 +1,54 @@ -namespace DataFilters +namespace DataFilters; + +using System; +using System.Collections.Generic; +using System.Linq; + +using Utilities; + +/// +/// Allow to sort by multiple expression +/// +/// Type onto which the sort will be applied +/// +/// Builds a new instance. +/// +/// +public class MultiOrder(params Order[] orders) : IOrder, IEquatable> { - using System; - using System.Collections.Generic; - using System.Linq; - - using Utilities; - /// - /// Allow to sort by multiple expression + /// items that the current instance carries. /// - /// Type onto which the sort will be applied - /// - /// Builds a new instance. - /// - /// - public class MultiOrder(params Order[] orders) : IOrder, IEquatable> - { - /// - /// items that the current instance carries. - /// - public IEnumerable> Orders => _orders; + public IEnumerable> Orders => _orders; - private readonly Order[] _orders = orders.Where(s => s is not null) - .ToArray(); + private readonly Order[] _orders = orders.Where(s => s is not null) + .ToArray(); - private static readonly ArrayEqualityComparer> equalityComparer = new(); + private static readonly ArrayEqualityComparer> equalityComparer = new(); - /// - public bool Equals(IOrder other) => Equals(other as MultiOrder); + /// + public bool Equals(IOrder other) => Equals(other as MultiOrder); - /// - public bool Equals(MultiOrder other) => other is not null - && equalityComparer.Equals(_orders, other._orders); + /// + public bool Equals(MultiOrder other) => other is not null + && equalityComparer.Equals(_orders, other._orders); - /// - public override bool Equals(object obj) => Equals(obj as MultiOrder); + /// + public override bool Equals(object obj) => Equals(obj as MultiOrder); - /// - public bool IsEquivalentTo(IOrder other) => other is MultiOrder otherMultisort - && Orders.SequenceEqual(otherMultisort.Orders); + /// + public bool IsEquivalentTo(IOrder other) => other is MultiOrder otherMultisort + && Orders.SequenceEqual(otherMultisort.Orders); - /// - public override int GetHashCode() => equalityComparer.GetHashCode(_orders); + /// + public override int GetHashCode() => equalityComparer.GetHashCode(_orders); - /// - public override string ToString() => $"{nameof(Orders)}:[{string.Join(",", Orders.Select(x => x.ToString()))}]"; + /// + public override string ToString() => $"{nameof(Orders)}:[{string.Join(",", Orders.Select(x => x.ToString()))}]"; - /// - public static bool operator ==(MultiOrder left, MultiOrder right) => EqualityComparer>.Default.Equals(left, right); + /// + public static bool operator ==(MultiOrder left, MultiOrder right) => EqualityComparer>.Default.Equals(left, right); - /// - public static bool operator !=(MultiOrder left, MultiOrder right) => !(left == right); - } + /// + public static bool operator !=(MultiOrder left, MultiOrder right) => !(left == right); } diff --git a/src/DataFilters/Order.cs b/src/DataFilters/Order.cs index b3df6677..7e5b1025 100644 --- a/src/DataFilters/Order.cs +++ b/src/DataFilters/Order.cs @@ -1,62 +1,61 @@ -namespace DataFilters -{ +namespace DataFilters; + #if STRING_SEGMENT - using Microsoft.Extensions.Primitives; +using Microsoft.Extensions.Primitives; #endif - using System; +using System; + +using static DataFilters.OrderDirection; - using static DataFilters.OrderDirection; +/// +/// An expression to order elements. +/// +/// Type of elements onto which the sort expression will applies +public class Order : IOrder +{ + /// + /// The original expression used to create the current . + /// + public string Expression { get; } /// - /// An expression to order elements. + /// The "direction" of the sort./> /// - /// Type of elements onto which the sort expression will applies - public class Order : IOrder + public OrderDirection Direction { get; } + + /// + /// Creates a new using the specified and . + /// + /// The expression + /// The direction (optional). + /// is , empty or contains only whitespaces. + public Order(string expression, OrderDirection direction = Ascending) { - /// - /// The original expression used to create the current . - /// - public string Expression { get; } - - /// - /// The "direction" of the sort./> - /// - public OrderDirection Direction { get; } - - /// - /// Creates a new using the specified and . - /// - /// The expression - /// The direction (optional). - /// is null, empty or contains only whitespaces. - public Order(string expression, OrderDirection direction = Ascending) + if (string.IsNullOrWhiteSpace(expression)) { - if (string.IsNullOrWhiteSpace(expression)) - { - throw new ArgumentOutOfRangeException(nameof(expression), "cannot be null or whitespace"); - } - Expression = expression; - Direction = direction; + throw new ArgumentOutOfRangeException(nameof(expression), "cannot be null or whitespace"); } + Expression = expression; + Direction = direction; + } - /// - public bool Equals(IOrder other) - => other is Order sort && (Expression, Direction) == (sort.Expression, sort.Direction); + /// + public bool Equals(IOrder other) + => other is Order sort && (Expression, Direction) == (sort.Expression, sort.Direction); - /// - public override bool Equals(object obj) => Equals(obj as Order); + /// + public override bool Equals(object obj) => Equals(obj as Order); - /// + /// #if !(NETSTANDARD1_0 || NETSTANDARD1_3 || NETSTANDARD2_0) - public override int GetHashCode() => HashCode.Combine(Expression, Direction); + public override int GetHashCode() => HashCode.Combine(Expression, Direction); #else - public override int GetHashCode() => (Expression, Direction).GetHashCode(); + public override int GetHashCode() => (Expression, Direction).GetHashCode(); - /// - public bool IsEquivalentTo(IOrder other) => Equals(other); + /// + public bool IsEquivalentTo(IOrder other) => Equals(other); #endif - /// - public override string ToString() => this.Jsonify(); - } + /// + public override string ToString() => this.Jsonify(); } diff --git a/src/DataFilters/OrderDirection.cs b/src/DataFilters/OrderDirection.cs index b9a129a0..4163a7c4 100644 --- a/src/DataFilters/OrderDirection.cs +++ b/src/DataFilters/OrderDirection.cs @@ -1,17 +1,16 @@ -namespace DataFilters +namespace DataFilters; + +/// +/// Enumeration of direction that can be associated to a instance +/// +public enum OrderDirection : short { /// - /// Enumeration of direction that can be associated to a instance + /// Order elements from the lowest to the highest. /// - public enum OrderDirection : short - { - /// - /// Order elements from the lowest to the highest. - /// - Ascending, - /// - /// Order eleemnts from the highest to the lowest. - /// - Descending - } + Ascending, + /// + /// Order eleemnts from the highest to the lowest. + /// + Descending } diff --git a/src/DataFilters/OrderValidator.cs b/src/DataFilters/OrderValidator.cs index 64f4e7a6..bcbaa0cc 100644 --- a/src/DataFilters/OrderValidator.cs +++ b/src/DataFilters/OrderValidator.cs @@ -1,37 +1,36 @@ -namespace DataFilters -{ - using System; - using System.Linq; - using System.Text.RegularExpressions; - using FluentValidation; +namespace DataFilters; + +using System; +using System.Linq; +using System.Text.RegularExpressions; +using FluentValidation; +/// +/// Validates sort expression +/// +public class OrderValidator : AbstractValidator +{ + private const string FieldPattern = Filter.ValidFieldNamePattern; /// - /// Validates sort expression + /// Order expression pattern. /// - public class OrderValidator : AbstractValidator - { - private const string FieldPattern = Filter.ValidFieldNamePattern; - /// - /// Order expression pattern. - /// - public readonly static string Pattern = @$"^\s*(-|\+)?(({FieldPattern})\w*)+(\s*,\s*((-|\+)?(({FieldPattern})\w*)+)\s*)*$"; - private readonly Regex _orderRegex = new(Pattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); - private const char _separator = ','; + public readonly static string Pattern = @$"^\s*(-|\+)?(({FieldPattern})\w*)+(\s*,\s*((-|\+)?(({FieldPattern})\w*)+)\s*)*$"; + private readonly Regex _orderRegex = new(Pattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + private const char _separator = ','; - /// - /// Builds a new instance. - /// - public OrderValidator() => RuleFor(x => x) - .Matches(Pattern) - .WithMessage(search => - { - string[] incorrectExpresions = search.Split([_separator]) - .Where(x => !_orderRegex.IsMatch(x)) - .Select(x => $@"""{x}""") - .ToArray(); + /// + /// Builds a new instance. + /// + public OrderValidator() => RuleFor(x => x) + .Matches(Pattern) + .WithMessage(search => + { + string[] incorrectExpresions = search.Split([_separator]) + .Where(x => !_orderRegex.IsMatch(x)) + .Select(x => $@"""{x}""") + .ToArray(); - return $"Sort expression{(incorrectExpresions.Length == 1 ? string.Empty : "s")} {string.Join(", ", incorrectExpresions)} " + - $@"do{(incorrectExpresions.Length == 1 ? "es" : string.Empty)} not match ""{Pattern}""."; - }); - } + return $"Sort expression{(incorrectExpresions.Length == 1 ? string.Empty : "s")} {string.Join(", ", incorrectExpresions)} " + + $@"do{(incorrectExpresions.Length == 1 ? "es" : string.Empty)} not match ""{Pattern}""."; + }); } diff --git a/src/DataFilters/Serialization/CamelCaseEnumTypeConverter.cs b/src/DataFilters/Serialization/CamelCaseEnumTypeConverter.cs index e75e4e39..ad6b0718 100644 --- a/src/DataFilters/Serialization/CamelCaseEnumTypeConverter.cs +++ b/src/DataFilters/Serialization/CamelCaseEnumTypeConverter.cs @@ -1,16 +1,15 @@ -namespace DataFilters.Converters -{ - using Newtonsoft.Json.Converters; - using Newtonsoft.Json.Serialization; +namespace DataFilters.Converters; + +using Newtonsoft.Json.Converters; +using Newtonsoft.Json.Serialization; +/// +/// Converter that specifies that enum members should be converted using CamelCaseNamingStrategy +/// +public class CamelCaseEnumTypeConverter : StringEnumConverter +{ /// - /// Converter that specifies that enum members should be converted using CamelCaseNamingStrategy + /// Builds a new instance /// - public class CamelCaseEnumTypeConverter : StringEnumConverter - { - /// - /// Builds a new instance - /// - public CamelCaseEnumTypeConverter() => NamingStrategy = new CamelCaseNamingStrategy(); - } + public CamelCaseEnumTypeConverter() => NamingStrategy = new CamelCaseNamingStrategy(); } diff --git a/src/DataFilters/Serialization/FilterConverter.cs b/src/DataFilters/Serialization/FilterConverter.cs index 268176cf..5760633f 100644 --- a/src/DataFilters/Serialization/FilterConverter.cs +++ b/src/DataFilters/Serialization/FilterConverter.cs @@ -1,192 +1,191 @@ -namespace DataFilters.Converters -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; +namespace DataFilters.Converters; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; #if NETSTANDARD1_3 - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; #else - using System.Text.Json; - using System.Text.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; #endif - /// - /// implementation that can convert from/to - /// +/// +/// implementation that can convert from/to +/// #if NETSTANDARD1_3 - public class FilterConverter : JsonConverter +public class FilterConverter : JsonConverter #else - public class FilterConverter : JsonConverter +public class FilterConverter : JsonConverter #endif +{ + private readonly static IImmutableDictionary _operators = new Dictionary { - private readonly static IImmutableDictionary _operators = new Dictionary - { - ["contains"] = FilterOperator.Contains, - ["ncontains"] = FilterOperator.NotContains, - ["endswith"] = FilterOperator.EndsWith, - ["nendswith"] = FilterOperator.NotEndsWith, - ["eq"] = FilterOperator.EqualTo, - ["neq"] = FilterOperator.NotEqualTo, - ["gt"] = FilterOperator.GreaterThan, - ["gte"] = FilterOperator.GreaterThanOrEqual, - ["isempty"] = FilterOperator.IsEmpty, - ["isnotempty"] = FilterOperator.IsNotEmpty, - ["isnull"] = FilterOperator.IsNull, - ["isnotnull"] = FilterOperator.IsNotNull, - ["lt"] = FilterOperator.LessThan, - ["lte"] = FilterOperator.LessThanOrEqualTo, - ["startswith"] = FilterOperator.StartsWith, - ["nstartswith"] = FilterOperator.NotStartsWith - }.ToImmutableDictionary(); + ["contains"] = FilterOperator.Contains, + ["ncontains"] = FilterOperator.NotContains, + ["endswith"] = FilterOperator.EndsWith, + ["nendswith"] = FilterOperator.NotEndsWith, + ["eq"] = FilterOperator.EqualTo, + ["neq"] = FilterOperator.NotEqualTo, + ["gt"] = FilterOperator.GreaterThan, + ["gte"] = FilterOperator.GreaterThanOrEqual, + ["isempty"] = FilterOperator.IsEmpty, + ["isnotempty"] = FilterOperator.IsNotEmpty, + ["isnull"] = FilterOperator.IsNull, + ["isnotnull"] = FilterOperator.IsNotNull, + ["lt"] = FilterOperator.LessThan, + ["lte"] = FilterOperator.LessThanOrEqualTo, + ["startswith"] = FilterOperator.StartsWith, + ["nstartswith"] = FilterOperator.NotStartsWith + }.ToImmutableDictionary(); #if NETSTANDARD1_3 - /// - public override bool CanConvert(Type objectType) => objectType == typeof(Filter); + /// + public override bool CanConvert(Type objectType) => objectType == typeof(Filter); - /// - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + Filter filter = null; + + JToken token = JToken.ReadFrom(reader); + if (objectType == typeof(Filter) && token.Type == JTokenType.Object) { - Filter filter = null; + IEnumerable properties = ((JObject)token).Properties(); - JToken token = JToken.ReadFrom(reader); - if (objectType == typeof(Filter) && token.Type == JTokenType.Object) + if (properties.Any(prop => prop.Name == Filter.FieldJsonPropertyName) + && properties.Any(prop => prop.Name == Filter.OperatorJsonPropertyName)) { - IEnumerable properties = ((JObject)token).Properties(); - - if (properties.Any(prop => prop.Name == Filter.FieldJsonPropertyName) - && properties.Any(prop => prop.Name == Filter.OperatorJsonPropertyName)) + string field = token[Filter.FieldJsonPropertyName].Value(); + FilterOperator @operator = _operators[token[Filter.OperatorJsonPropertyName].Value()]; + object value = null; + if (!Filter.UnaryOperators.Contains(@operator)) { - string field = token[Filter.FieldJsonPropertyName].Value(); - FilterOperator @operator = _operators[token[Filter.OperatorJsonPropertyName].Value()]; - object value = null; - if (!Filter.UnaryOperators.Contains(@operator)) + JToken valueToken = token[Filter.ValueJsonPropertyName]; + value = valueToken?.Type switch { - JToken valueToken = token[Filter.ValueJsonPropertyName]; - value = valueToken?.Type switch - { - JTokenType.String => valueToken.Value(), - JTokenType.Boolean => valueToken.Value(), - JTokenType.Integer => valueToken.Value(), - JTokenType.Float => valueToken.Value(), - JTokenType.Null => null, - null => null, - _ => throw new NotSupportedException($"Unexpected valueTokenType {valueToken.Type} when dealing with operator {@operator}") - - }; - } - filter = new Filter(field, @operator, value); + JTokenType.String => valueToken.Value(), + JTokenType.Boolean => valueToken.Value(), + JTokenType.Integer => valueToken.Value(), + JTokenType.Float => valueToken.Value(), + JTokenType.Null => null, + null => null, + _ => throw new NotSupportedException($"Unexpected valueTokenType {valueToken.Type} when dealing with operator {@operator}") + + }; } + filter = new Filter(field, @operator, value); } - - return filter?.As(objectType); } + return filter?.As(objectType); + } + #else - /// - public override Filter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + /// + public override Filter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + object value = null; + + if (reader.TokenType != JsonTokenType.StartObject) { - object value = null; + throw new JsonException($"Expected '{{' but found {reader.TokenType}"); + } - if (reader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException($"Expected '{{' but found {reader.TokenType}"); - } + if (!reader.Read() || reader.TokenType != JsonTokenType.PropertyName || Filter.FieldJsonPropertyName != reader.GetString()) + { + throw new JsonException($"Missing {Filter.FieldJsonPropertyName} property."); + } - if (!reader.Read() || reader.TokenType != JsonTokenType.PropertyName || Filter.FieldJsonPropertyName != reader.GetString()) - { - throw new JsonException($"Missing {Filter.FieldJsonPropertyName} property."); - } + reader.Read(); + string field = reader.GetString(); - reader.Read(); - string field = reader.GetString(); + if (!reader.Read() || reader.TokenType != JsonTokenType.PropertyName || Filter.OperatorJsonPropertyName != reader.GetString()) + { + throw new JsonException($@"Missing ""{Filter.OperatorJsonPropertyName}"" property."); + } - if (!reader.Read() || reader.TokenType != JsonTokenType.PropertyName || Filter.OperatorJsonPropertyName != reader.GetString()) + reader.Read(); + FilterOperator op = _operators[reader.GetString()]; + if (!Filter.UnaryOperators.Contains(op)) + { + if (reader.Read() && reader.TokenType == JsonTokenType.PropertyName && Filter.ValueJsonPropertyName == reader.GetString()) { - throw new JsonException($@"Missing ""{Filter.OperatorJsonPropertyName}"" property."); + reader.Read(); + value = reader.TokenType switch + { + JsonTokenType.Number => reader.GetInt64(), + JsonTokenType.String => reader.GetString(), + JsonTokenType.False => reader.GetBoolean(), + JsonTokenType.True => reader.GetBoolean(), + _ => null + }; } - reader.Read(); - FilterOperator op = _operators[reader.GetString()]; - if (!Filter.UnaryOperators.Contains(op)) + if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject) { - if (reader.Read() && reader.TokenType == JsonTokenType.PropertyName && Filter.ValueJsonPropertyName == reader.GetString()) - { - reader.Read(); - value = reader.TokenType switch - { - JsonTokenType.Number => reader.GetInt64(), - JsonTokenType.String => reader.GetString(), - JsonTokenType.False => reader.GetBoolean(), - JsonTokenType.True => reader.GetBoolean(), - _ => null - }; - } - - if (!reader.Read() || reader.TokenType != JsonTokenType.EndObject) - { - throw new JsonException("Filter json must end with '}'."); - } - reader.Read(); + throw new JsonException("Filter json must end with '}'."); } - else + reader.Read(); + } + else + { + while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) { - while (reader.Read() && reader.TokenType != JsonTokenType.EndObject) - { - // empty loop to get to the end of the current JSON object - } + // empty loop to get to the end of the current JSON object } - - return new Filter(field, op, value); } + return new Filter(field, op, value); + } + #endif - /// + /// #if NETSTANDARD1_3 - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - Filter filter = (Filter)value; + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + Filter filter = (Filter)value; #else - public override void Write(Utf8JsonWriter writer, Filter value, JsonSerializerOptions options) - { - Filter filter = value; + public override void Write(Utf8JsonWriter writer, Filter value, JsonSerializerOptions options) + { + Filter filter = value; #endif - writer.WriteStartObject(); + writer.WriteStartObject(); - // Field - writer.WritePropertyName(Filter.FieldJsonPropertyName); + // Field + writer.WritePropertyName(Filter.FieldJsonPropertyName); #if NETSTANDARD1_3 - writer.WriteValue(filter.Field); + writer.WriteValue(filter.Field); #else - writer.WriteStringValue(filter.Field); + writer.WriteStringValue(filter.Field); #endif - // operator - writer.WritePropertyName(Filter.OperatorJsonPropertyName); - KeyValuePair kv = _operators.Single(item => item.Value == filter.Operator); + // operator + writer.WritePropertyName(Filter.OperatorJsonPropertyName); + KeyValuePair kv = _operators.Single(item => item.Value == filter.Operator); #if NETSTANDARD1_3 - writer.WriteValue(kv.Key); + writer.WriteValue(kv.Key); #else - writer.WriteStringValue(kv.Key); + writer.WriteStringValue(kv.Key); #endif - // value (only if the operator is not an unary operator) - if (!Filter.UnaryOperators.Contains(filter.Operator)) - { - writer.WritePropertyName(Filter.ValueJsonPropertyName); + // value (only if the operator is not an unary operator) + if (!Filter.UnaryOperators.Contains(filter.Operator)) + { + writer.WritePropertyName(Filter.ValueJsonPropertyName); #if NETSTANDARD1_3 - writer.WriteValue(filter.Value); + writer.WriteValue(filter.Value); #else - writer.WriteStringValue(filter.Value.ToString()); + writer.WriteStringValue(filter.Value.ToString()); #endif - } - - writer.WriteEndObject(); } + + writer.WriteEndObject(); } } diff --git a/src/DataFilters/Serialization/FilterOperatorConverter.cs b/src/DataFilters/Serialization/FilterOperatorConverter.cs index 29224cbd..c8d45735 100644 --- a/src/DataFilters/Serialization/FilterOperatorConverter.cs +++ b/src/DataFilters/Serialization/FilterOperatorConverter.cs @@ -1,96 +1,95 @@ -namespace DataFilters.Converters -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; +namespace DataFilters.Converters; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; #if NETSTANDARD1_3 - using Newtonsoft.Json; +using Newtonsoft.Json; #else - using System.Text.Json; - using System.Text.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; #endif - /// - /// implementation that can handle (de)serialization of - /// from/to JSon . - /// +/// +/// implementation that can handle (de)serialization of +/// from/to JSon . +/// #if NETSTANDARD1_3 - public class FilterOperatorConverter : JsonConverter - { +public class FilterOperatorConverter : JsonConverter +{ #else - public class FilterOperatorConverter : JsonConverter - { +public class FilterOperatorConverter : JsonConverter +{ #endif - private readonly static IImmutableDictionary _operators = new Dictionary - { - ["contains"] = FilterOperator.Contains, - ["ncontains"] = FilterOperator.NotContains, - ["endswith"] = FilterOperator.EndsWith, - ["nendswith"] = FilterOperator.NotEndsWith, - ["eq"] = FilterOperator.EqualTo, - ["neq"] = FilterOperator.NotEqualTo, - ["gt"] = FilterOperator.GreaterThan, - ["gte"] = FilterOperator.GreaterThanOrEqual, - ["isempty"] = FilterOperator.IsEmpty, - ["isnotempty"] = FilterOperator.IsNotEmpty, - ["isnull"] = FilterOperator.IsNull, - ["isnotnull"] = FilterOperator.IsNotNull, - ["lt"] = FilterOperator.LessThan, - ["lte"] = FilterOperator.LessThanOrEqualTo, - ["startswith"] = FilterOperator.StartsWith, - ["nstartswith"] = FilterOperator.NotStartsWith - }.ToImmutableDictionary(); + private readonly static IImmutableDictionary _operators = new Dictionary + { + ["contains"] = FilterOperator.Contains, + ["ncontains"] = FilterOperator.NotContains, + ["endswith"] = FilterOperator.EndsWith, + ["nendswith"] = FilterOperator.NotEndsWith, + ["eq"] = FilterOperator.EqualTo, + ["neq"] = FilterOperator.NotEqualTo, + ["gt"] = FilterOperator.GreaterThan, + ["gte"] = FilterOperator.GreaterThanOrEqual, + ["isempty"] = FilterOperator.IsEmpty, + ["isnotempty"] = FilterOperator.IsNotEmpty, + ["isnull"] = FilterOperator.IsNull, + ["isnotnull"] = FilterOperator.IsNotNull, + ["lt"] = FilterOperator.LessThan, + ["lte"] = FilterOperator.LessThanOrEqualTo, + ["startswith"] = FilterOperator.StartsWith, + ["nstartswith"] = FilterOperator.NotStartsWith + }.ToImmutableDictionary(); #if NETSTANDARD1_3 - /// - public override bool CanConvert(Type objectType) => typeof(FilterOperator) == objectType; + /// + public override bool CanConvert(Type objectType) => typeof(FilterOperator) == objectType; - /// - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + if (reader.TokenType != JsonToken.String) { - if (reader.TokenType != JsonToken.String) - { - throw new JsonReaderException(); - } + throw new JsonReaderException(); + } - string op = reader.ReadAsString(); + string op = reader.ReadAsString(); - return _operators[op.ToLower()]; - } + return _operators[op.ToLower()]; + } #else - /// - public override FilterOperator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + /// + public override FilterOperator Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.String) { - if (reader.TokenType != JsonTokenType.String) - { - throw new JsonException($"Expected {nameof(FilterOperator)} value"); - } - - string op = reader.GetString(); - return _operators[op.ToLower()]; + throw new JsonException($"Expected {nameof(FilterOperator)} value"); } + + string op = reader.GetString(); + return _operators[op.ToLower()]; + } #endif #if NETSTANDARD1_3 - /// - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - KeyValuePair result = _operators.Single(op => op.Value == (FilterOperator)value); - writer.WriteValue(result.Key); - } + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + KeyValuePair result = _operators.Single(op => op.Value == (FilterOperator)value); + writer.WriteValue(result.Key); + } #else - /// - public override void Write(Utf8JsonWriter writer, FilterOperator value, JsonSerializerOptions options) - { + /// + public override void Write(Utf8JsonWriter writer, FilterOperator value, JsonSerializerOptions options) + { #if NETSTANDARD2_0 - string key = _operators.Single(op => op.Value == value).Key; + string key = _operators.Single(op => op.Value == value).Key; #else - (string key, _) = _operators.Single(op => op.Value == value); -#endif - writer.WriteStringValue(key); - } + (string key, _) = _operators.Single(op => op.Value == value); #endif + writer.WriteStringValue(key); } +#endif } diff --git a/src/DataFilters/Serialization/MultiFilterConverter.cs b/src/DataFilters/Serialization/MultiFilterConverter.cs index 7e7b7076..e763b04e 100644 --- a/src/DataFilters/Serialization/MultiFilterConverter.cs +++ b/src/DataFilters/Serialization/MultiFilterConverter.cs @@ -1,208 +1,207 @@ -namespace DataFilters.Converters -{ - using System; - using System.Collections.Generic; +namespace DataFilters.Converters; + +using System; +using System.Collections.Generic; #if NETSTANDARD1_3 - using System.Collections.Immutable; - using System.Linq; +using System.Collections.Immutable; +using System.Linq; - using Newtonsoft.Json; - using Newtonsoft.Json.Linq; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; #else - using System.Text.Json; - using System.Text.Json.Serialization; +using System.Text.Json; +using System.Text.Json.Serialization; #endif - /// - /// implementation that allow to convert json string from/to - /// +/// +/// implementation that allow to convert json string from/to +/// #if NETSTANDARD1_3 - public class MultiFilterConverter : JsonConverter +public class MultiFilterConverter : JsonConverter +{ + private static readonly IImmutableDictionary _logics = new Dictionary { - private static readonly IImmutableDictionary _logics = new Dictionary - { - [nameof(FilterLogic.And).ToLower()] = FilterLogic.And, - [nameof(FilterLogic.Or).ToLower()] = FilterLogic.Or - }.ToImmutableDictionary(); + [nameof(FilterLogic.And).ToLower()] = FilterLogic.And, + [nameof(FilterLogic.Or).ToLower()] = FilterLogic.Or + }.ToImmutableDictionary(); - /// - public override bool CanConvert(Type objectType) => objectType == typeof(MultiFilter); + /// + public override bool CanConvert(Type objectType) => objectType == typeof(MultiFilter); - /// - public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) - { - MultiFilter multiFilter = null; + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) + { + MultiFilter multiFilter = null; - JToken token = JToken.ReadFrom(reader); - if (objectType == typeof(MultiFilter) && token.Type == JTokenType.Object) - { - IEnumerable properties = ((JObject)token).Properties(); + JToken token = JToken.ReadFrom(reader); + if (objectType == typeof(MultiFilter) && token.Type == JTokenType.Object) + { + IEnumerable properties = ((JObject)token).Properties(); - JProperty logicProperty = properties.SingleOrDefault(prop => prop.Name == MultiFilter.LogicJsonPropertyName); + JProperty logicProperty = properties.SingleOrDefault(prop => prop.Name == MultiFilter.LogicJsonPropertyName); - if (logicProperty != null) + if (logicProperty != null) + { + JProperty filtersProperty = properties.SingleOrDefault(prop => prop.Name == MultiFilter.FiltersJsonPropertyName); + if (filtersProperty is JProperty prop && filtersProperty.Value.Type == JTokenType.Array) { - JProperty filtersProperty = properties.SingleOrDefault(prop => prop.Name == MultiFilter.FiltersJsonPropertyName); - if (filtersProperty is JProperty prop && filtersProperty.Value.Type == JTokenType.Array) + JArray filtersArray = token[MultiFilter.FiltersJsonPropertyName].Value(); + int nbFilters = filtersArray.Count; + if (nbFilters >= 2) { - JArray filtersArray = token[MultiFilter.FiltersJsonPropertyName].Value(); - int nbFilters = filtersArray.Count; - if (nbFilters >= 2) + IList filters = new List(nbFilters); + foreach (JToken item in filtersArray) { - IList filters = new List(nbFilters); - foreach (JToken item in filtersArray) - { - IFilter kf = (IFilter)item.ToObject() ?? item.ToObject(); - filters.Add(kf); - } - - multiFilter = new MultiFilter - { - Logic = _logics[token[MultiFilter.LogicJsonPropertyName].Value()], - Filters = filters - }; + IFilter kf = (IFilter)item.ToObject() ?? item.ToObject(); + filters.Add(kf); } + + multiFilter = new MultiFilter + { + Logic = _logics[token[MultiFilter.LogicJsonPropertyName].Value()], + Filters = filters + }; } } } - - return multiFilter?.As(objectType); } + + return multiFilter?.As(objectType); + } #else - public class MultiFilterConverter : JsonConverter +public class MultiFilterConverter : JsonConverter +{ + private readonly FilterConverter _filterConverter; + + /// + /// Builds a new isntance. + /// + public MultiFilterConverter() { - private readonly FilterConverter _filterConverter; + _filterConverter = new FilterConverter(); + } - /// - /// Builds a new isntance. - /// - public MultiFilterConverter() + /// + public override MultiFilter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType != JsonTokenType.StartObject) { - _filterConverter = new FilterConverter(); + throw new JsonException("Multifilter json must start with '{'."); } - /// - public override MultiFilter Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + reader.Read(); + + if (reader.TokenType != JsonTokenType.PropertyName || MultiFilter.LogicJsonPropertyName != reader.GetString()) { - if (reader.TokenType != JsonTokenType.StartObject) - { - throw new JsonException("Multifilter json must start with '{'."); - } + throw new JsonException($@"Expected ""{MultiFilter.LogicJsonPropertyName}"" property."); + } - reader.Read(); + reader.Read(); + FilterLogic logic = reader.GetString()?.ToLowerInvariant() switch + { + "and" => FilterLogic.And, + "or" => FilterLogic.Or, + object value => throw new JsonException(@$"Unexpected ""{value}"" value for ""{MultiFilter.LogicJsonPropertyName}"" property."), + null => throw new JsonException(@$"Unexpected ""null"" value for ""{MultiFilter.LogicJsonPropertyName}"" property.") - if (reader.TokenType != JsonTokenType.PropertyName || MultiFilter.LogicJsonPropertyName != reader.GetString()) - { - throw new JsonException($@"Expected ""{MultiFilter.LogicJsonPropertyName}"" property."); - } + }; - reader.Read(); - FilterLogic logic = reader.GetString()?.ToLowerInvariant() switch - { - "and" => FilterLogic.And, - "or" => FilterLogic.Or, - object value => throw new JsonException(@$"Unexpected ""{value}"" value for ""{MultiFilter.LogicJsonPropertyName}"" property."), - null => throw new JsonException(@$"Unexpected ""null"" value for ""{MultiFilter.LogicJsonPropertyName}"" property.") + List filters = []; + if (reader.Read() && (reader.TokenType != JsonTokenType.PropertyName || MultiFilter.FiltersJsonPropertyName != reader.GetString())) + { + throw new JsonException($@"Expected ""{MultiFilter.FiltersJsonPropertyName}"" property."); + } - }; + reader.Read(); + if (reader.TokenType != JsonTokenType.StartArray) + { + throw new JsonException(@"Expected ""[""."); + } - List filters = []; - if (reader.Read() && (reader.TokenType != JsonTokenType.PropertyName || MultiFilter.FiltersJsonPropertyName != reader.GetString())) - { - throw new JsonException($@"Expected ""{MultiFilter.FiltersJsonPropertyName}"" property."); - } + reader.Read(); - reader.Read(); - if (reader.TokenType != JsonTokenType.StartArray) + // We are about to try parsing the JSON to get either a Filter or a MultiFilter. + // We store a copy of the original reader in case the parsing process fails. + Utf8JsonReader readerCopy = reader; + while (reader.TokenType != JsonTokenType.EndArray) + { + long position = reader.TokenStartIndex; + try { - throw new JsonException(@"Expected ""[""."); + filters.Add(_filterConverter.Read(ref reader, typeof(Filter), options)); } - - reader.Read(); - - // We are about to try parsing the JSON to get either a Filter or a MultiFilter. - // We store a copy of the original reader in case the parsing process fails. - Utf8JsonReader readerCopy = reader; - while (reader.TokenType != JsonTokenType.EndArray) + catch { - long position = reader.TokenStartIndex; - try + // The json is not a Filter so we need to go back to where the parsing was performed + // and try to get a MultFilter instead + + // 1. The copyReader position is moved to where the original parser were before failing + while (readerCopy.TokenStartIndex < position) { - filters.Add(_filterConverter.Read(ref reader, typeof(Filter), options)); + readerCopy.Read(); } - catch - { - // The json is not a Filter so we need to go back to where the parsing was performed - // and try to get a MultFilter instead - - // 1. The copyReader position is moved to where the original parser were before failing - while (readerCopy.TokenStartIndex < position) - { - readerCopy.Read(); - } - // 2. Try to parse the Json and get a MultiFilter. - filters.Add(Read(ref readerCopy, typeof(MultiFilter), options)); - // The parsing was a success -> we move the reader to the continue the parsing process + // 2. Try to parse the Json and get a MultiFilter. + filters.Add(Read(ref readerCopy, typeof(MultiFilter), options)); + // The parsing was a success -> we move the reader to the continue the parsing process - while (reader.TokenStartIndex < readerCopy.TokenStartIndex) - { - reader.Read(); - } - reader.Read(); // Advances the reader until the next token + while (reader.TokenStartIndex < readerCopy.TokenStartIndex) + { + reader.Read(); } + reader.Read(); // Advances the reader until the next token } - - reader.Read(); - return reader.TokenType != JsonTokenType.EndObject - ? throw new JsonException(@"Expected ""}"".") - : new MultiFilter - { - Logic = logic, - Filters = filters - }; } + + reader.Read(); + return reader.TokenType != JsonTokenType.EndObject + ? throw new JsonException(@"Expected ""}"".") + : new MultiFilter + { + Logic = logic, + Filters = filters + }; + } #endif - /// + /// #if NETSTANDARD1_3 - public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) - { - MultiFilter mf = (MultiFilter)value; + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + MultiFilter mf = (MultiFilter)value; #else - public override void Write(Utf8JsonWriter writer, MultiFilter value, JsonSerializerOptions options) - { - MultiFilter mf = value; + public override void Write(Utf8JsonWriter writer, MultiFilter value, JsonSerializerOptions options) + { + MultiFilter mf = value; #endif - writer.WriteStartObject(); + writer.WriteStartObject(); - writer.WritePropertyName(MultiFilter.LogicJsonPropertyName); + writer.WritePropertyName(MultiFilter.LogicJsonPropertyName); #if NETSTANDARD1_3 - writer.WriteValue(mf.Logic.ToString().ToLower()); + writer.WriteValue(mf.Logic.ToString().ToLower()); #else - writer.WriteStringValue(mf.Logic.ToString().ToLower()); + writer.WriteStringValue(mf.Logic.ToString().ToLower()); #endif - writer.WritePropertyName(MultiFilter.FiltersJsonPropertyName); - writer.WriteStartArray(); - foreach (IFilter filter in mf.Filters) - { + writer.WritePropertyName(MultiFilter.FiltersJsonPropertyName); + writer.WriteStartArray(); + foreach (IFilter filter in mf.Filters) + { #if NETSTANDARD1_3 - serializer.Serialize(writer, filter); + serializer.Serialize(writer, filter); #else - if (filter is Filter f) - { - _filterConverter.Write(writer, f, options); - } - else if (filter is MultiFilter multiFilter) - { - Write(writer, multiFilter, options); - } -#endif + if (filter is Filter f) + { + _filterConverter.Write(writer, f, options); } - writer.WriteEndArray(); - - writer.WriteEndObject(); + else if (filter is MultiFilter multiFilter) + { + Write(writer, multiFilter, options); + } +#endif } + writer.WriteEndArray(); + + writer.WriteEndObject(); } } diff --git a/src/DataFilters/StringExtensions.cs b/src/DataFilters/StringExtensions.cs index 7ee85c9a..69272268 100644 --- a/src/DataFilters/StringExtensions.cs +++ b/src/DataFilters/StringExtensions.cs @@ -1,389 +1,400 @@ -namespace System -{ - using DataFilters; +namespace System; + +using DataFilters; - using FluentValidation.Results; +using FluentValidation.Results; - using System.Linq; +using System.Linq; - using DataFilters.Grammar.Parsing; - using DataFilters.Grammar.Syntax; +using DataFilters.Grammar.Parsing; +using DataFilters.Grammar.Syntax; - using Superpower; - using Superpower.Model; +using Superpower; +using Superpower.Model; - using System.Collections.Generic; - using System.ComponentModel; - using System.Reflection; +using System.Collections.Generic; +using System.ComponentModel; +using System.Reflection; - using static DataFilters.FilterOperator; +using static DataFilters.FilterOperator; - using DataFilters.Casing; +using DataFilters.Casing; #if STRING_SEGMENT - using Microsoft.Extensions.Primitives; +using Microsoft.Extensions.Primitives; #endif - using static DataFilters.OrderDirection; +using static DataFilters.OrderDirection; #if NETSTANDARD2_0 || NETSTANDARD2_1 || NET5_0_OR_GREATER - using System.Runtime.InteropServices; +using System.Runtime.InteropServices; #if NET6_0 - using DateOnlyTimeOnly.AspNet.Converters; +using DateOnlyTimeOnly.AspNet.Converters; +using System.Collections.Concurrent; +#elif NET7_0_OR_GREATER +using System.Diagnostics; #endif - using System.Collections.Concurrent; - -#if NET7_0_OR_GREATER - using System.Diagnostics; #endif +/// +/// String extensions methods +/// +public static class StringExtensions +{ + private static char Separator => ','; +#if NET6_0 + private readonly static ConcurrentDictionary HackZone = new (); #endif + /// - /// String extensions methods + /// Converts to a instance. /// - public static class StringExtensions - { - private static char Separator => ','; -#if NET6_0_OR_GREATER - private readonly static ConcurrentDictionary HackZone = new (); -#endif + /// Type of the element to which the will be generated from + /// + /// when is or whitespace + /// when is not a valid sort expression. + public static IOrder ToSort(this string sortString) => sortString.ToOrder(PropertyNameResolutionStrategy.Default); - /// - /// Converts to a instance. - /// - /// Type of the element to which the will be generated from - /// - /// when is null or whitespace - /// when is not a valid sort expression. - public static IOrder ToSort(this string sortString) => sortString.ToOrder(PropertyNameResolutionStrategy.Default); - - /// - /// Converts to a instance. - /// - /// Type of the element to which the will be generated from - /// - /// The transformation to apply to each property name. - /// when is null or whitespace - /// when is not a valid sort expression. - public static IOrder ToOrder(this string sortString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) + /// + /// Converts to a instance. + /// + /// Type of the element to which the will be generated from + /// + /// The transformation to apply to each property name. + /// when is or whitespace + /// when is not a valid sort expression. + public static IOrder ToOrder(this string sortString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) + { + if (string.IsNullOrWhiteSpace(sortString) || sortString?.Length == 0) { - if (string.IsNullOrWhiteSpace(sortString) || sortString?.Length == 0) - { - throw new ArgumentOutOfRangeException(nameof(sortString), "cannot be be null or whitespace only"); - } + throw new ArgumentOutOfRangeException(nameof(sortString), "cannot be be null or whitespace only"); + } - OrderValidator validator = new(); - ValidationResult validationResult = validator.Validate(sortString); + OrderValidator validator = new(); + ValidationResult validationResult = validator.Validate(sortString); - if (!validationResult.IsValid) - { - throw new InvalidOrderExpressionException(sortString); - } + if (!validationResult.IsValid) + { + throw new InvalidOrderExpressionException(sortString); + } #if NETSTANDARD2_0 || NETSTANDARD2_1 || NET5_0_OR_GREATER - ReadOnlyMemory sorts = sortString.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries) - .AsMemory(); + ReadOnlyMemory sorts = sortString.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries) + .AsMemory(); #else - string[] sorts = sortString.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries); + string[] sorts = sortString.Split(new[] { Separator }, StringSplitOptions.RemoveEmptyEntries); #endif - IOrder sort = null; + IOrder sort = null; - if (sorts.Length > 1) - { + if (sorts.Length > 1) + { #if NETSTANDARD2_0 || NETSTANDARD2_1 || NET5_0_OR_GREATER - sort = new MultiOrder(MemoryMarshal.ToEnumerable(sorts).Select(s => s.ToOrder(propertyNameResolutionStrategy) as Order).ToArray()); + sort = new MultiOrder(MemoryMarshal.ToEnumerable(sorts).Select(s => s.ToOrder(propertyNameResolutionStrategy) as Order).ToArray()); #else - sort = new MultiOrder(sorts.Select(s => s.ToOrder(propertyNameResolutionStrategy) as Order).ToArray()); + sort = new MultiOrder(sorts.Select(s => s.ToOrder(propertyNameResolutionStrategy) as Order).ToArray()); #endif - } - else if (sortString.StartsWith("+")) - { + } +#if NET8_0_OR_GREATER + else if (sortString.StartsWith('+')) +#else + else if (sortString.StartsWith("+")) +#endif + { #if NETSTANDARD1_3 || NETSTANDARD2_0 - sort = new Order(propertyNameResolutionStrategy.Handle(sortString.Substring(1))); + sort = new Order(propertyNameResolutionStrategy.Handle(sortString.Substring(1))); #else - sort = new Order(propertyNameResolutionStrategy.Handle(sortString[1..])); + sort = new Order(propertyNameResolutionStrategy.Handle(sortString[1..])); #endif - } - else if (sortString.StartsWith("-")) - { + } +#if NET8_0_OR_GREATER + else if (sortString.StartsWith('-')) +#else + else if (sortString.StartsWith("-")) +#endif + { #if NETSTANDARD1_3 || NETSTANDARD2_0 - sort = new Order(propertyNameResolutionStrategy.Handle(sortString.Substring(1)), - direction: Descending); + sort = new Order(propertyNameResolutionStrategy.Handle(sortString.Substring(1)), + direction: Descending); #else - sort = new Order(propertyNameResolutionStrategy.Handle(sortString[1..]), - direction: Descending); + sort = new Order(propertyNameResolutionStrategy.Handle(sortString[1..]), + direction: Descending); #endif - } - else - { - sort = new Order(propertyNameResolutionStrategy.Handle(sortString)); - } - - return sort; + } + else + { + sort = new Order(propertyNameResolutionStrategy.Handle(sortString)); } + return sort; + } + #if STRING_SEGMENT - /// - /// Builds a from - /// - /// Type of element to filter - /// A query string (without any leading ? character) - /// - /// - /// is null. - /// is not a valid query string. - public static IFilter ToFilter(this string queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) - => new StringSegment(queryString).ToFilter(propertyNameResolutionStrategy); - - /// - /// Builds a from using - /// - /// Type of element to filter - /// A query string (without any leading ? character) - /// a that correspond to the . - /// is null. - /// is not a valid query string. - public static IFilter ToFilter(this string queryString) => ToFilter(queryString, PropertyNameResolutionStrategy.Default); + /// + /// Builds a from + /// + /// Type of element to filter + /// A query string (without any leading ? character) + /// + /// + /// is . + /// is not a valid query string. + public static IFilter ToFilter(this string queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) + => new StringSegment(queryString).ToFilter(propertyNameResolutionStrategy); + + /// + /// Builds a from using + /// + /// Type of element to filter + /// A query string (without any leading ? character) + /// a that correspond to the . + /// is . + /// is not a valid query string. + public static IFilter ToFilter(this string queryString) => ToFilter(queryString, PropertyNameResolutionStrategy.Default); #endif - /// - /// Builds a from with the specified . - /// - /// Type of element to filter - /// A query string (without any leading ? character) - /// - /// The corresponding - /// either or is null. - /// is not a valid query string. + /// + /// Builds a from with the specified . + /// + /// Type of element to filter + /// A query string (without any leading ? character) + /// + /// The corresponding + /// either or is . + /// is not a valid query string. #if STRING_SEGMENT - public static IFilter ToFilter(this StringSegment queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) - => ToFilter(queryString.Value, new FilterOptions() { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy}); + public static IFilter ToFilter(this StringSegment queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) + => ToFilter(queryString.Value, new FilterOptions() { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy }); #else - public static IFilter ToFilter(this string queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) - => ToFilter(queryString, new FilterOptions() { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy }); + public static IFilter ToFilter(this string queryString, PropertyNameResolutionStrategy propertyNameResolutionStrategy) + => ToFilter(queryString, new FilterOptions() { DefaultPropertyNameResolutionStrategy = propertyNameResolutionStrategy }); #endif - /// - /// Builds a from with the specified . - /// - /// Type of element to filter - /// A query string (without any leading ? character) - /// - /// The corresponding - /// either or is null. - /// is not a valid query string. - public static IFilter ToFilter(this string queryString, FilterOptions options) - { - string localQueryString = queryString; + /// + /// Builds a from with the specified . + /// + /// Type of element to filter + /// A query string (without any leading ? character) + /// + /// The corresponding + /// either or is . + /// is not a valid query string. + public static IFilter ToFilter(this string queryString, FilterOptions options) + { + string localQueryString = queryString; - static IFilter ConvertExpressionToFilter(PropertyInfo propInfo, FilterExpression expression, TypeConverter tc) + static IFilter ConvertExpressionToFilter(PropertyInfo propInfo, FilterExpression expression, TypeConverter tc) + { + IFilter filter = Filter.True; + switch (expression) { - IFilter filter = Filter.True; - switch (expression) - { - case ConstantValueExpression constant: - string constantValue = constant.Value; - - filter = new Filter(propInfo.Name, - EqualTo, - tc.ConvertFromInvariantString(constantValue)); - break; - case StartsWithExpression startsWith: - filter = new Filter(propInfo.Name, StartsWith, startsWith.Value); - break; - case EndsWithExpression endsWith: - filter = new Filter(propInfo.Name, EndsWith, endsWith.Value); - break; - case ContainsExpression endsWith: - filter = new Filter(propInfo.Name, Contains, endsWith.Value); - break; - case NotExpression not: - filter = ConvertExpressionToFilter(propInfo, not.Expression, tc).Negate(); - break; - case OrExpression orExpression: - filter = new MultiFilter + case ConstantValueExpression constant: + string constantValue = constant.Value; + + filter = new Filter(propInfo.Name, + EqualTo, + tc.ConvertFromInvariantString(constantValue)); + break; + case StartsWithExpression startsWith: + filter = new Filter(propInfo.Name, StartsWith, startsWith.Value); + break; + case EndsWithExpression endsWith: + filter = new Filter(propInfo.Name, EndsWith, endsWith.Value); + break; + case ContainsExpression endsWith: + filter = new Filter(propInfo.Name, Contains, endsWith.Value); + break; + case NotExpression not: + filter = ConvertExpressionToFilter(propInfo, not.Expression, tc).Negate(); + break; + case OrExpression orExpression: + filter = new MultiFilter + { + Logic = FilterLogic.Or, + Filters = new IFilter[] { - Logic = FilterLogic.Or, - Filters = new IFilter[] - { - ConvertExpressionToFilter(propInfo, orExpression.Left, tc), - ConvertExpressionToFilter(propInfo, orExpression.Right, tc) - } + ConvertExpressionToFilter(propInfo, orExpression.Left, tc), + ConvertExpressionToFilter(propInfo, orExpression.Right, tc) + } + }; + break; + case AndExpression andExpression: + filter = new MultiFilter + { + Logic = FilterLogic.And, + Filters = new IFilter[] + { + ConvertExpressionToFilter(propInfo, andExpression.Left, tc), + ConvertExpressionToFilter(propInfo, andExpression.Right, tc) + } + }; + break; + case BracketExpression regex: + filter = new MultiFilter + { + Logic = FilterLogic.Or, + //Filters = regex.Value.Select(c => ConvertExpressionToFilter(new ConstantExpression($"{regex.Before?.Value ?? string.Empty}{c}{regex.After?.Value ?? string.Empty}"), propertyName, tc)) + }; + break; + case GroupExpression group: + filter = ConvertExpressionToFilter(propInfo, group.Expression, tc); + break; + case OneOfExpression oneOf: + FilterExpression[] possibleValues = [.. oneOf.Values]; + if (oneOf.Values.Exactly(1)) + { + filter = ConvertExpressionToFilter(propInfo, possibleValues[0], tc); + } + else + { + List filters = new(possibleValues.Length); + + foreach (FilterExpression item in possibleValues) + { + filters.Add(ConvertExpressionToFilter(propInfo, item, tc)); + } + filter = new MultiFilter { Logic = FilterLogic.Or, Filters = filters }; + } + break; + case IntervalExpression range: + + static (ConstantValueExpression ConstantExpression, bool Included) ConvertBoundaryExpressionToConstantExpression(BoundaryExpression input) + => input?.Expression switch + { + StringValueExpression ce => (ce, input.Included), + NumericValueExpression numeric => (new(numeric.Value), input.Included), + DateTimeExpression { Date: not null, Time: null } dateTime => (new StringValueExpression($"{dateTime.Date.Year:D4}-{dateTime.Date.Month:D2}-{dateTime.Date.Day:D2}"), input.Included), + DateExpression date => (new StringValueExpression($"{date.Year:D4}-{date.Month:D2}-{date.Day:D2}"), input.Included), + DateTimeExpression { Date: null, Time: not null, Offset: null } dateTime => (new StringValueExpression($"0001-01-01T{dateTime.Time.Hours}:{dateTime.Time.Minutes}:{dateTime.Time.Seconds}"), input.Included), + TimeExpression time => (new StringValueExpression($"0001-01-01T{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}"), input.Included), + DateTimeExpression { Date: not null, Time: not null, Offset: null } dateTime => (new StringValueExpression($"{dateTime.Date.Year:D4}-{dateTime.Date.Month:D2}-{dateTime.Date.Day:D2}T{dateTime.Time.Hours:D2}:{dateTime.Time.Minutes:D2}:{dateTime.Time.Seconds:D2}.{dateTime.Time.Milliseconds}"), input.Included), + DateTimeExpression { Date: not null, Time: not null, Offset: not null } dateTime => (new StringValueExpression(dateTime.EscapedParseableString), input.Included), + AsteriskExpression or null => default, // because this is equivalent to an unbounded range +#if NET7_0_OR_GREATER + _ => throw new UnreachableException($"Unsupported boundary type {input.Expression.GetType()}") +#else + _ => throw new NotSupportedException($"Unsupported boundary type {input.Expression.GetType()}") +#endif }; - break; - case AndExpression andExpression: + +#pragma warning disable IDE0042 // Déconstruire la déclaration de variable + (ConstantValueExpression ConstantExpression, bool Included) min = ConvertBoundaryExpressionToConstantExpression(range.Min); + (ConstantValueExpression ConstantExpression, bool Included) max = ConvertBoundaryExpressionToConstantExpression(range.Max); +#pragma warning restore IDE0042 // Déconstruire la déclaration de variable + + FilterOperator minOperator = min.Included ? GreaterThanOrEqual : GreaterThan; + FilterOperator maxOperator = max.Included ? LessThanOrEqualTo : LessThan; + + if (min.ConstantExpression?.Value != default && max.ConstantExpression?.Value != default) + { + object minValue = min.ConstantExpression.Value; + object maxValue = max.ConstantExpression.Value; filter = new MultiFilter { Logic = FilterLogic.And, Filters = new IFilter[] { - ConvertExpressionToFilter(propInfo, andExpression.Left, tc), - ConvertExpressionToFilter(propInfo, andExpression.Right, tc) + new Filter(propInfo.Name, + minOperator, + tc.ConvertFrom(minValue)), + new Filter(propInfo.Name, + maxOperator, + tc.ConvertFrom(maxValue)) } }; - break; - case BracketExpression regex: - filter = new MultiFilter - { - Logic = FilterLogic.Or, - //Filters = regex.Value.Select(c => ConvertExpressionToFilter(new ConstantExpression($"{regex.Before?.Value ?? string.Empty}{c}{regex.After?.Value ?? string.Empty}"), propertyName, tc)) - }; - break; - case GroupExpression group: - filter = ConvertExpressionToFilter(propInfo, group.Expression, tc); - break; - case OneOfExpression oneOf: - FilterExpression[] possibleValues = [.. oneOf.Values]; - if (oneOf.Values.Exactly(1)) - { - filter = ConvertExpressionToFilter(propInfo, possibleValues[0], tc); - } - else - { - IList filters = new List(possibleValues.Length); - - foreach (FilterExpression item in possibleValues) - { - filters.Add(ConvertExpressionToFilter(propInfo, item, tc)); - } - filter = new MultiFilter { Logic = FilterLogic.Or, Filters = filters }; - } - break; - case IntervalExpression range: - - static (ConstantValueExpression constantExpression, bool included) ConvertBounderyExpressionToConstantExpression(BoundaryExpression input) - => input?.Expression switch - { - StringValueExpression ce => (ce, input.Included), - NumericValueExpression numeric => (new(numeric.Value), input.Included), - DateTimeExpression { Date: not null, Time: null } dateTime => (new StringValueExpression($"{dateTime.Date.Year:D4}-{dateTime.Date.Month:D2}-{dateTime.Date.Day:D2}"), input.Included), - DateExpression date => (new StringValueExpression($"{date.Year:D4}-{date.Month:D2}-{date.Day:D2}"), input.Included), - DateTimeExpression { Date: null, Time: not null, Offset: null } dateTime => (new StringValueExpression($"0001-01-01T{dateTime.Time.Hours}:{dateTime.Time.Minutes}:{dateTime.Time.Seconds}"), input.Included), - TimeExpression time => (new StringValueExpression($"0001-01-01T{time.Hours:D2}:{time.Minutes:D2}:{time.Seconds:D2}"), input.Included), - DateTimeExpression { Date: not null, Time: not null, Offset: null } dateTime => (new StringValueExpression($"{dateTime.Date.Year:D4}-{dateTime.Date.Month:D2}-{dateTime.Date.Day:D2}T{dateTime.Time.Hours:D2}:{dateTime.Time.Minutes:D2}:{dateTime.Time.Seconds:D2}.{dateTime.Time.Milliseconds}"), input.Included), - DateTimeExpression { Date: not null, Time: not null, Offset: not null } dateTime => (new StringValueExpression(dateTime.EscapedParseableString), input.Included), - AsteriskExpression or null => default, // because this is equivalent to an unbounded range + } + else if (min.ConstantExpression?.Value != default) + { + object minValue = min.ConstantExpression.Value; + filter = new Filter(propInfo.Name, minOperator, tc.ConvertFrom(minValue)); + } + else + { + object maxValue = max.ConstantExpression.Value; + filter = new Filter(propInfo.Name, maxOperator, tc.ConvertFrom(maxValue)); + } + break; + default: #if NET7_0_OR_GREATER - _ => throw new UnreachableException($"Unsupported boundary type {input.Expression.GetType()}") + throw new UnreachableException($"Unsupported '{expression.GetType()}'s expression type."); #else - _ => throw new NotSupportedException($"Unsupported boundary type {input.Expression.GetType()}") + throw new NotSupportedException($"Unsupported '{expression.GetType()}'s expression type."); #endif - }; + } - (ConstantValueExpression constantExpression, bool included) min = ConvertBounderyExpressionToConstantExpression(range.Min); - (ConstantValueExpression constantExpression, bool included) max = ConvertBounderyExpressionToConstantExpression(range.Max); + return filter; + } - FilterOperator minOperator = min.included ? GreaterThanOrEqual : GreaterThan; - FilterOperator maxOperator = max.included ? LessThanOrEqualTo : LessThan; + if (localQueryString == default) + { + throw new ArgumentNullException(nameof(queryString)); + } - if (min.constantExpression?.Value != default && max.constantExpression?.Value != default) - { - object minValue = min.constantExpression.Value; - object maxValue = max.constantExpression.Value; - filter = new MultiFilter - { - Logic = FilterLogic.And, - Filters = new IFilter[] - { - new Filter(propInfo.Name, - minOperator, - tc.ConvertFrom(minValue)), - new Filter(propInfo.Name, - maxOperator, - tc.ConvertFrom(maxValue)) - } - }; - } - else if (min.constantExpression?.Value != default) - { - object minValue = min.constantExpression.Value; - filter = new Filter(propInfo.Name, minOperator, tc.ConvertFrom(minValue)); - } - else - { - object maxValue = max.constantExpression.Value; - filter = new Filter(propInfo.Name, maxOperator, tc.ConvertFrom(maxValue)); - } - break; - default: - throw new NotSupportedException($"Unsupported '{expression.GetType()}'s expression type."); - } + IFilter filter = Filter.True; + bool isEmptyQueryString = string.IsNullOrWhiteSpace(localQueryString); - return filter; - } + if (!isEmptyQueryString) + { + FilterTokenizer tokenizer = new(); + TokenList tokens = tokenizer.Tokenize(localQueryString); - if (localQueryString == default) + (PropertyName Property, FilterExpression Expression)[] expressions = FilterTokenParser.Criteria.Parse(tokens); +#if NET6_0 + if (HackZone.TryAdd(true, 1) && expressions.AtLeastOnce()) { - throw new ArgumentNullException(nameof(queryString)); + TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter))); } - - IFilter filter = Filter.True; - bool isEmptyQueryString = string.IsNullOrWhiteSpace(localQueryString); - - if (!isEmptyQueryString) +#endif + if (expressions.Once()) { - FilterTokenizer tokenizer = new(); - TokenList tokens = tokenizer.Tokenize(localQueryString); + (PropertyName property, FilterExpression expression) = expressions[0]; - (PropertyName Property, FilterExpression Expression)[] expressions = FilterTokenParser.Criteria.Parse(tokens); -#if NET6_0 - if (HackZone.TryAdd(true, 1) && expressions.AtLeastOnce()) + PropertyInfo pi = typeof(T).GetRuntimeProperties() + .SingleOrDefault(x => x.CanRead && x.Name == options.DefaultPropertyNameResolutionStrategy.Handle(property.Name)); + + if (pi is not null) { - TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter))); + TypeConverter tc = TypeDescriptor.GetConverter(pi.PropertyType); + filter = ConvertExpressionToFilter(pi, expression, tc); } -#endif - if (expressions.Once()) - { - (PropertyName property, FilterExpression expression) = expressions[0]; + } + else + { + List filters = []; + foreach ((PropertyName property, FilterExpression expression) in expressions) + { PropertyInfo pi = typeof(T).GetRuntimeProperties() .SingleOrDefault(x => x.CanRead && x.Name == options.DefaultPropertyNameResolutionStrategy.Handle(property.Name)); if (pi is not null) { TypeConverter tc = TypeDescriptor.GetConverter(pi.PropertyType); - filter = ConvertExpressionToFilter(pi, expression, tc); + filters.Add(ConvertExpressionToFilter(pi, expression, tc)); } } - else - { - IList filters = new List(); - - foreach ((PropertyName property, FilterExpression expression) in expressions) - { - PropertyInfo pi = typeof(T).GetRuntimeProperties() - .SingleOrDefault(x => x.CanRead && x.Name == options.DefaultPropertyNameResolutionStrategy.Handle(property.Name)); - - if (pi is not null) - { - TypeConverter tc = TypeDescriptor.GetConverter(pi.PropertyType); - filters.Add(ConvertExpressionToFilter(pi, expression, tc)); - } - } - filter = new MultiFilter { Logic = options.Logic, Filters = filters }; - } + filter = new MultiFilter { Logic = options.Logic, Filters = filters }; } - - return filter; } - /// - /// Builds a from using . - /// - /// Type of element to filter - /// A query string (without any leading ? character) - /// The corresponding - /// is null. - /// is not a valid query string. + return filter; + } + + /// + /// Builds a from using . + /// + /// Type of element to filter + /// A query string (without any leading ? character) + /// The corresponding + /// is . + /// is not a valid query string. #if STRING_SEGMENT - public static IFilter ToFilter(this StringSegment queryString) => ToFilter(queryString.Value, PropertyNameResolutionStrategy.Default); + public static IFilter ToFilter(this StringSegment queryString) => ToFilter(queryString.Value, PropertyNameResolutionStrategy.Default); #else - public static IFilter ToFilter(this string queryString) => ToFilter(queryString, PropertyNameResolutionStrategy.Default); + public static IFilter ToFilter(this string queryString) => ToFilter(queryString, PropertyNameResolutionStrategy.Default); #endif - } } diff --git a/src/Datafilters.Expressions/FilterExtensions.cs b/src/Datafilters.Expressions/FilterExtensions.cs index 39759b18..53b73184 100644 --- a/src/Datafilters.Expressions/FilterExtensions.cs +++ b/src/Datafilters.Expressions/FilterExtensions.cs @@ -1,629 +1,628 @@ -namespace DataFilters +namespace DataFilters; + +#if NET6_0 +using DateOnlyTimeOnly.AspNet.Converters; +using System.ComponentModel; +#endif + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using DataFilters.Expressions; +using static System.Linq.Expressions.Expression; +using static DataFilters.FilterOperator; +using static Expressions.NullableValueBehavior; + +/// +/// The `FilterExtensions` class provides extension methods for building expression trees from `IFilter` instances. +/// It allows for filtering data based on various conditions. +/// +/// +/// Example Usage: +/// +/// // Create a filter +/// IFilter filter = new Filter +/// { +/// Field = "Name", +/// Operator = FilterOperator.EqualTo, +/// Value = "John" +/// }; +/// +/// // Build an expression tree +/// Expression<Func<Person, bool>> expression = filter.ToExpression<Person>(); +/// +/// // Use the expression to filter data +/// var filteredData = data.Where(expression.Compile()); +/// +/// +public static class FilterExtensions { #if NET6_0 - using DateOnlyTimeOnly.AspNet.Converters; - using System.ComponentModel; + private readonly static ISet HackZone = new HashSet(); #endif - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using System.Reflection; - using DataFilters.Expressions; - using static System.Linq.Expressions.Expression; - using static DataFilters.FilterOperator; - using static Expressions.NullableValueBehavior; + /// + /// List of all primitives types + /// + /// + private static readonly Type[] PrimitiveTypes = [ + typeof(string), + typeof(Guid), + typeof(Guid?), + typeof(int), + typeof(int?), + typeof(long?), + typeof(long?), + typeof(short?), + typeof(short?), + typeof(decimal?), + typeof(decimal?), + typeof(double?), + typeof(double?), + typeof(ushort?), + typeof(ushort?), + typeof(uint?), + typeof(uint?), + typeof(ulong?), + typeof(ulong?), + typeof(DateTime), + typeof(DateTime?), + typeof(DateTimeOffset), + typeof(DateTimeOffset?), +#if NET6_0_OR_GREATER + typeof(DateOnly), typeof(DateOnly?), + typeof(TimeOnly), typeof(TimeOnly?), +#endif + typeof(bool), + typeof(bool?), + typeof(char), + typeof(char?) + ]; /// - /// The `FilterExtensions` class provides extension methods for building expression trees from `IFilter` instances. - /// It allows for filtering data based on various conditions. + /// Builds an tree from a instance. /// + /// Type of the + /// instance to build an tree from. + /// Indicates if a "null check" should be added to avoid any potential when accessing a property + /// + /// if is . /// - /// Example Usage: - /// - /// // Create a filter - /// IFilter filter = new Filter - /// { - /// Field = "Name", - /// Operator = FilterOperator.EqualTo, - /// Value = "John" - /// }; - /// - /// // Build an expression tree - /// Expression<Func<Person, bool>> expression = filter.ToExpression<Person>(); - /// - /// // Use the expression to filter data - /// var filteredData = data.Where(expression.Compile()); - /// + /// Setting to can result in a little overhead /// - public static class FilterExtensions + public static Expression> ToExpression(this IFilter filter, NullableValueBehavior nullableValueBehavior = NoAction) { -#if NET6_0 - private readonly static ISet HackZone = new HashSet(); -#endif - - /// - /// List of all primitives types - /// - /// - private static readonly Type[] PrimitiveTypes = [ - typeof(string), - typeof(Guid), - typeof(Guid?), - typeof(int), - typeof(int?), - typeof(long?), - typeof(long?), - typeof(short?), - typeof(short?), - typeof(decimal?), - typeof(decimal?), - typeof(double?), - typeof(double?), - typeof(ushort?), - typeof(ushort?), - typeof(uint?), - typeof(uint?), - typeof(ulong?), - typeof(ulong?), - typeof(DateTime), - typeof(DateTime?), - typeof(DateTimeOffset), - typeof(DateTimeOffset?), -#if NET6_0_OR_GREATER - typeof(DateOnly), typeof(DateOnly?), - typeof(TimeOnly), typeof(TimeOnly?), -#endif - typeof(bool), - typeof(bool?), - typeof(char), - typeof(char?) - ]; - - /// - /// Builds an tree from a instance. - /// - /// Type of the - /// instance to build an tree from. - /// Indicates if a "null check" should be added to avoid any potential when accessing a property - /// - /// if is null. - /// - /// Setting to can result in a little overhead - /// - public static Expression> ToExpression(this IFilter filter, NullableValueBehavior nullableValueBehavior = NoAction) + if (filter == null) { - if (filter == null) - { - throw new ArgumentNullException(nameof(filter), $"{nameof(filter)} cannot be null"); - } + throw new ArgumentNullException(nameof(filter), $"{nameof(filter)} cannot be null"); + } #if NET6_0 - // HACK require to handle DateOnly and TimeOnly types. - if (HackZone.Add(true)) - { - TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter))); - TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter))); - } + // HACK require to handle DateOnly and TimeOnly types. + if (HackZone.Add(true)) + { + TypeDescriptor.AddAttributes(typeof(DateOnly), new TypeConverterAttribute(typeof(DateOnlyTypeConverter))); + TypeDescriptor.AddAttributes(typeof(TimeOnly), new TypeConverterAttribute(typeof(TimeOnlyTypeConverter))); + } #endif - Expression> filterExpression = null; + Expression> filterExpression = null; - switch (filter) - { - case Filter df: + switch (filter) + { + case Filter df: + { + if (df.Field == null) { - if (df.Field == null) - { - filterExpression = _ => true; - } - else - { - Type type = typeof(T); - ParameterExpression pe = Parameter(type, "item"); - - string[] fields = - [ - .. df.Field.Replace(@"[""", ".") - .Replace(@"""]", string.Empty) - .Split(['.']) + filterExpression = _ => true; + } + else + { + Type type = typeof(T); + ParameterExpression pe = Parameter(type, "item"); + + string[] fields = + [ + .. df.Field.Replace(@"[""", ".") + .Replace(@"""]", string.Empty) + .Split(['.']) , - ]; + ]; - Expression body = nullableValueBehavior switch - { - NoAction => ComputeExpression(pe, fields, type, df.Operator, df.Value), - AddNullCheck => ComputeNullSafeExpression(pe, fields, type, df.Operator, df.Value), - _ => throw new NotSupportedException($"Unsupported '{nullableValueBehavior}' behavior") - }; - - filterExpression = Lambda>(body, pe); - } + Expression body = nullableValueBehavior switch + { + NoAction => ComputeExpression(pe, fields, type, df.Operator, df.Value), + AddNullCheck => ComputeNullSafeExpression(pe, fields, type, df.Operator, df.Value), + _ => throw new NotSupportedException($"Unsupported '{nullableValueBehavior}' behavior") + }; - break; + filterExpression = Lambda>(body, pe); } - case MultiFilter dcf: - { - Expression> expression = null; - foreach (IFilter item in dcf.Filters) - { - expression = expression == null - ? item.ToExpression() - : MergeExpressions(expression, dcf.Logic, item.ToExpression()); - } + break; + } - filterExpression = expression; - break; + case MultiFilter dcf: + { + Expression> expression = null; + foreach (IFilter item in dcf.Filters) + { + expression = expression == null + ? item.ToExpression() + : MergeExpressions(expression, dcf.Logic, item.ToExpression()); } - } - return filterExpression; + filterExpression = expression; + break; + } } - private static object ConvertObjectToDateTime(object source, Type targetType) - { - object dateTime = null; + return filterExpression; + } - if (targetType == typeof(DateTime) || targetType == typeof(DateTime?)) + private static object ConvertObjectToDateTime(object source, Type targetType) + { + object dateTime = null; + + if (targetType == typeof(DateTime) || targetType == typeof(DateTime?)) + { + if (DateTime.TryParse(source?.ToString(), out DateTime result)) { - if (DateTime.TryParse(source?.ToString(), out DateTime result)) - { - dateTime = result; - } - else if (targetType == typeof(DateTime?)) - { - dateTime = null; - } + dateTime = result; } - else if (targetType == typeof(DateTimeOffset) || targetType == typeof(DateTimeOffset?)) + else if (targetType == typeof(DateTime?)) { - if (DateTimeOffset.TryParse(source?.ToString(), out DateTimeOffset result)) - { - dateTime = result; - } - else if (targetType == typeof(DateTimeOffset?)) - { - dateTime = null; - } + dateTime = null; + } + } + else if (targetType == typeof(DateTimeOffset) || targetType == typeof(DateTimeOffset?)) + { + if (DateTimeOffset.TryParse(source?.ToString(), out DateTimeOffset result)) + { + dateTime = result; + } + else if (targetType == typeof(DateTimeOffset?)) + { + dateTime = null; } + } #if NET6_0_OR_GREATER - else if (targetType == typeof(DateOnly) || targetType == typeof(DateOnly)) + else if (targetType == typeof(DateOnly) || targetType == typeof(DateOnly)) + { + if (DateOnly.TryParse(source?.ToString(), out DateOnly result)) { - if (DateOnly.TryParse(source?.ToString(), out DateOnly result)) - { - dateTime = result; - } + dateTime = result; } + } #endif - return dateTime; - } + return dateTime; + } - private static Expression ComputeBodyExpression(MemberExpression property, FilterOperator @operator, object value) + private static Expression ComputeBodyExpression(MemberExpression property, FilterOperator @operator, object value) + { + ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(((PropertyInfo)property.Member).PropertyType, value); + + return @operator switch { - ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(((PropertyInfo)property.Member).PropertyType, value); + NotEqualTo => NotEqual(property, constantExpression), + IsNull => Equal(property, Constant(null)), + IsNotNull => NotEqual(property, Constant(null)), + FilterOperator.LessThan => LessThan(property, constantExpression), + FilterOperator.GreaterThan => GreaterThan(property, constantExpression), + FilterOperator.GreaterThanOrEqual => GreaterThanOrEqual(property, constantExpression), + LessThanOrEqualTo => LessThanOrEqual(property, constantExpression), + StartsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression), + NotStartsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression)), + EndsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression), + NotEndsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression)), + Contains => ComputeContains(property, value), + NotContains => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), constantExpression)), + IsEmpty => ComputeIsEmpty(property), + IsNotEmpty => ComputeIsNotEmpty(property), + EqualTo => ComputeEquals(property, value), + _ => throw new NotSupportedException($"Unsupported {@operator} operator when computing a body expression") + }; + } - return @operator switch - { - NotEqualTo => NotEqual(property, constantExpression), - IsNull => Equal(property, Constant(null)), - IsNotNull => NotEqual(property, Constant(null)), - FilterOperator.LessThan => LessThan(property, constantExpression), - FilterOperator.GreaterThan => GreaterThan(property, constantExpression), - FilterOperator.GreaterThanOrEqual => GreaterThanOrEqual(property, constantExpression), - LessThanOrEqualTo => LessThanOrEqual(property, constantExpression), - StartsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression), - NotStartsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression)), - EndsWith => Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression), - NotEndsWith => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression)), - Contains => ComputeContains(property, value), - NotContains => Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), constantExpression)), - IsEmpty => ComputeIsEmpty(property), - IsNotEmpty => ComputeIsNotEmpty(property), - EqualTo => ComputeEquals(property, value), - _ => throw new NotSupportedException($"Unsupported {@operator} operator when computing a body expression") - }; - } + private static Expression ComputeNullSafeBodyExpression(MemberExpression property, FilterOperator @operator, object value) + { + ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(((PropertyInfo)property.Member).PropertyType, value); - private static Expression ComputeNullSafeBodyExpression(MemberExpression property, FilterOperator @operator, object value) + return @operator switch { - ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(((PropertyInfo)property.Member).PropertyType, value); + NotEqualTo => NotEqual(property, constantExpression), + IsNull => Equal(property, Constant(null)), + IsNotNull => NotEqual(property, Constant(null)), + FilterOperator.LessThan => LessThan(property, constantExpression), + FilterOperator.GreaterThan => GreaterThan(property, constantExpression), + FilterOperator.GreaterThanOrEqual => GreaterThanOrEqual(property, constantExpression), + LessThanOrEqualTo => LessThanOrEqual(property, constantExpression), + StartsWith => AndAlso(NotEqual(property, Constant(null)), + Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression)), + NotStartsWith => AndAlso(NotEqual(property, Constant(null)), + Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression))), + EndsWith => AndAlso(NotEqual(property, Constant(null)), + Call(property, + typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), + constantExpression)), + NotEndsWith => AndAlso(NotEqual(property, Constant(null)), + Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression))), + Contains => ComputeNullSafeContains(property, value), + NotContains => Not(ComputeNullSafeContains(property, value)), + IsEmpty => ComputeNullSafeIsEmpty(property), + IsNotEmpty => Not(ComputeNullSafeIsEmpty(property)), + EqualTo => ComputeNullSafeEquals(property, value), + _ => throw new NotSupportedException($"Unsupported {@operator} operator when computing a body expression") + }; + } - return @operator switch - { - NotEqualTo => NotEqual(property, constantExpression), - IsNull => Equal(property, Constant(null)), - IsNotNull => NotEqual(property, Constant(null)), - FilterOperator.LessThan => LessThan(property, constantExpression), - FilterOperator.GreaterThan => GreaterThan(property, constantExpression), - FilterOperator.GreaterThanOrEqual => GreaterThanOrEqual(property, constantExpression), - LessThanOrEqualTo => LessThanOrEqual(property, constantExpression), - StartsWith => AndAlso(NotEqual(property, Constant(null)), - Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression)), - NotStartsWith => AndAlso(NotEqual(property, Constant(null)), - Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.StartsWith), [typeof(string)]), constantExpression))), - EndsWith => AndAlso(NotEqual(property, Constant(null)), - Call(property, - typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), - constantExpression)), - NotEndsWith => AndAlso(NotEqual(property, Constant(null)), - Not(Call(property, typeof(string).GetRuntimeMethod(nameof(string.EndsWith), [typeof(string)]), constantExpression))), - Contains => ComputeNullSafeContains(property, value), - NotContains => Not(ComputeNullSafeContains(property, value)), - IsEmpty => ComputeNullSafeIsEmpty(property), - IsNotEmpty => Not(ComputeNullSafeIsEmpty(property)), - EqualTo => ComputeNullSafeEquals(property, value), - _ => throw new NotSupportedException($"Unsupported {@operator} operator when computing a body expression") - }; - } + private static ConstantExpression ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(Type memberType, object value) + { + ConstantExpression ce; - private static ConstantExpression ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(Type memberType, object value) + if (IsDatetimeMember(memberType)) { - ConstantExpression ce; - - if (IsDatetimeMember(memberType)) - { - ce = Constant(ConvertObjectToDateTime(value, memberType), memberType); - } + ce = Constant(ConvertObjectToDateTime(value, memberType), memberType); + } #if NET6_0_OR_GREATER - else if (IsDateOnly(memberType)) - { - ce = Constant(ConvertObjectToDateTime(value, memberType), memberType); - } + else if (IsDateOnly(memberType)) + { + ce = Constant(ConvertObjectToDateTime(value, memberType), memberType); + } #endif - else if (IsNotAStringAndIsEnumerable(memberType)) - { - Type parameterType = memberType.GenericTypeArguments?.SingleOrDefault() ?? typeof(object); - ce = Constant(value, parameterType); - } - else + else if (IsNotAStringAndIsEnumerable(memberType)) + { + Type parameterType = memberType.GenericTypeArguments?.SingleOrDefault() ?? typeof(object); + ce = Constant(value, parameterType); + } + else + { + ce = Constant(value, memberType); + } + + return ce; + } + + private static Expression ComputeIsEmpty(MemberExpression property) => IsNotAStringAndIsEnumerable(property.Type) + ? Not(Call(typeof(Enumerable), + nameof(Enumerable.Any), + new Type[] { property.Type.GenericTypeArguments[0] }, + property)) + : Equal(property, Constant(string.Empty)); + + private static Expression ComputeNullSafeIsEmpty(MemberExpression property) + { + Expression isEmpty = null; + if (IsNotAStringAndIsEnumerable(property.Type)) + { + Expression left = NotEqual(property, Constant(null)); + Expression right = null; + Type genericType = null; +#if !NETSTANDARD1_3 + if (property.Type.IsGenericType) { - ce = Constant(value, memberType); + genericType = property.Type.GenericTypeArguments[0]; } +#endif + + right = Not(Call(typeof(Enumerable), + nameof(Enumerable.Any), + genericType is not null + ? [genericType] + : null, + property)); - return ce; + isEmpty = AndAlso(left, right); + } + else + { + isEmpty = Not(Equal(property, Constant(string.Empty))); } - private static Expression ComputeIsEmpty(MemberExpression property) => IsNotAStringAndIsEnumerable(property.Type) - ? Not(Call(typeof(Enumerable), - nameof(Enumerable.Any), - new Type[] { property.Type.GenericTypeArguments[0] }, - property)) - : Equal(property, Constant(string.Empty)); + return isEmpty; + } + + private static Expression ComputeContains(MemberExpression property, object value) + { + Expression contains = null; + ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); - private static Expression ComputeNullSafeIsEmpty(MemberExpression property) + if (IsNotAStringAndIsEnumerable(property.Type)) { - Expression isEmpty = null; - if (IsNotAStringAndIsEnumerable(property.Type)) - { - Expression left = NotEqual(property, Constant(null)); - Expression right = null; - Type genericType = null; -#if !NETSTANDARD1_3 - if (property.Type.IsGenericType) - { - genericType = property.Type.GenericTypeArguments[0]; - } -#endif + Type genericArgType = property.Type.GenericTypeArguments[0]; + ParameterExpression pe = Parameter(genericArgType); - right = Not(Call(typeof(Enumerable), + contains = typeof(string).Equals(genericArgType) + ? Call(typeof(Enumerable), + nameof(Enumerable.Any), + [typeof(string)], + property, + Lambda( + Call(pe, + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), + constantExpression), [pe])) + : Call(typeof(Enumerable), nameof(Enumerable.Any), - genericType is not null - ? [genericType] - : null, - property)); + [property.Type.GenericTypeArguments[0]], + property, + Lambda(Equal(pe, constantExpression), [pe])); + } + else + { + contains = Call(property, + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), + constantExpression); + } - isEmpty = AndAlso(left, right); - } - else - { - isEmpty = Not(Equal(property, Constant(string.Empty))); - } + return contains; + } - return isEmpty; - } + private static Expression ComputeNullSafeContains(MemberExpression property, object value) + { + Expression contains = null; + ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); - private static Expression ComputeContains(MemberExpression property, object value) + if (IsNotAStringAndIsEnumerable(property.Type)) { - Expression contains = null; - ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); - - if (IsNotAStringAndIsEnumerable(property.Type)) - { - Type genericArgType = property.Type.GenericTypeArguments[0]; - ParameterExpression pe = Parameter(genericArgType); - - contains = typeof(string).Equals(genericArgType) - ? Call(typeof(Enumerable), - nameof(Enumerable.Any), - [typeof(string)], - property, - Lambda( - Call(pe, - typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), - constantExpression), [pe])) - : Call(typeof(Enumerable), - nameof(Enumerable.Any), - [property.Type.GenericTypeArguments[0]], - property, - Lambda(Equal(pe, constantExpression), [pe])); - } - else - { - contains = Call(property, - typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), - constantExpression); - } + Type genericArgType = property.Type.GenericTypeArguments[0]; + ParameterExpression pe = Parameter(genericArgType); - return contains; + contains = typeof(string).Equals(genericArgType) + ? Call(typeof(Enumerable), + nameof(Enumerable.Any), + [typeof(string)], + property, + Lambda( + AndAlso(NotEqual(pe, Constant(null)), + Call(pe, + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), + constantExpression)), [pe])) + : Call(typeof(Enumerable), + nameof(Enumerable.Any), + [property.Type.GenericTypeArguments[0]], + property, + Lambda(Equal(pe, constantExpression), [pe])); } - - private static Expression ComputeNullSafeContains(MemberExpression property, object value) + else { - Expression contains = null; - ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); + contains = Call(property, + typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), + constantExpression); + } - if (IsNotAStringAndIsEnumerable(property.Type)) - { - Type genericArgType = property.Type.GenericTypeArguments[0]; - ParameterExpression pe = Parameter(genericArgType); - - contains = typeof(string).Equals(genericArgType) - ? Call(typeof(Enumerable), - nameof(Enumerable.Any), - [typeof(string)], - property, - Lambda( - AndAlso(NotEqual(pe, Constant(null)), - Call(pe, - typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), - constantExpression)), [pe])) - : Call(typeof(Enumerable), - nameof(Enumerable.Any), - [property.Type.GenericTypeArguments[0]], - property, - Lambda(Equal(pe, constantExpression), [pe])); - } - else - { - contains = Call(property, - typeof(string).GetRuntimeMethod(nameof(string.Contains), [typeof(string)]), - constantExpression); - } + return contains; + } - return contains; - } + private static Expression ComputeEquals(MemberExpression property, object value) + { + Expression equals = null; + ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); - private static Expression ComputeEquals(MemberExpression property, object value) + if (IsNotAStringAndIsEnumerable(property.Type)) + { + ParameterExpression pe = Parameter(property.Type.GenericTypeArguments[0]); + equals = Call(typeof(Enumerable), + nameof(Enumerable.Any), + [property.Type.GenericTypeArguments[0]], + property, + Lambda(Equal(pe, constantExpression), [pe])); + } + else { - Expression equals = null; - ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); + equals = Equal(property, constantExpression); + } - if (IsNotAStringAndIsEnumerable(property.Type)) - { - ParameterExpression pe = Parameter(property.Type.GenericTypeArguments[0]); - equals = Call(typeof(Enumerable), - nameof(Enumerable.Any), - [property.Type.GenericTypeArguments[0]], - property, - Lambda(Equal(pe, constantExpression), [pe])); - } - else - { - equals = Equal(property, constantExpression); - } + return equals; + } - return equals; - } + private static Expression ComputeNullSafeEquals(MemberExpression property, object value) + { + Expression equals = null; + ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); - private static Expression ComputeNullSafeEquals(MemberExpression property, object value) + if (IsNotAStringAndIsEnumerable(property.Type)) { - Expression equals = null; - ConstantExpression constantExpression = ComputeConstantExpressionBasedOnPropertyExpressionTargetTypeAndValue(property.Type, value); + ParameterExpression pe = Parameter(property.Type.GenericTypeArguments[0]); + equals = Call(typeof(Enumerable), + nameof(Enumerable.Any), + [property.Type.GenericTypeArguments[0]], + property, + Lambda(Equal(pe, constantExpression), [pe])); + } + else + { + equals = Equal(property, constantExpression); + } - if (IsNotAStringAndIsEnumerable(property.Type)) + return equals; + } + + private static Expression ComputeIsNotEmpty(MemberExpression property) => IsNotAStringAndIsEnumerable(property.Type) + ? Not(ComputeIsEmpty(property)) + : NotEqual(property, Constant(string.Empty)); + + private static bool IsNotAStringAndIsEnumerable(Type propertyType) => propertyType != typeof(string) + && propertyType.IsAssignableToGenericType(typeof(IEnumerable<>)); + + private static Expression ComputeExpression(ParameterExpression pe, IEnumerable fields, Type targetType, FilterOperator @operator, object value, MemberExpression property = null) + { + Expression body = null; + int i = 0; + + if (fields.Once()) + { + if (IsNotAStringAndIsEnumerable(targetType)) { - ParameterExpression pe = Parameter(property.Type.GenericTypeArguments[0]); - equals = Call(typeof(Enumerable), - nameof(Enumerable.Any), - [property.Type.GenericTypeArguments[0]], - property, - Lambda(Equal(pe, constantExpression), [pe])); + Type enumerableGenericType = targetType.GenericTypeArguments[0]; + TypeInfo typeInfo = enumerableGenericType.GetTypeInfo(); + ParameterExpression localParameter = Parameter(enumerableGenericType); + Expression localBody; + + if (!(typeInfo.IsPrimitive || PrimitiveTypes.Contains(enumerableGenericType))) + { + MemberExpression localProperty = Property(localParameter, fields.Single()); + localBody = ComputeBodyExpression(localProperty, @operator, value); + body = Call(typeof(Enumerable), + nameof(Enumerable.Any), + [enumerableGenericType], + property, + Lambda(localBody, [localParameter]) + ); + } } else { - equals = Equal(property, constantExpression); + body = ComputeBodyExpression(Property((Expression)property ?? pe, fields.Single()), @operator, value); } - - return equals; } - - private static Expression ComputeIsNotEmpty(MemberExpression property) => IsNotAStringAndIsEnumerable(property.Type) - ? Not(ComputeIsEmpty(property)) - : NotEqual(property, Constant(string.Empty)); - - private static bool IsNotAStringAndIsEnumerable(Type propertyType) => propertyType != typeof(string) - && propertyType.IsAssignableToGenericType(typeof(IEnumerable<>)); - - private static Expression ComputeExpression(ParameterExpression pe, IEnumerable fields, Type targetType, FilterOperator @operator, object value, MemberExpression property = null) + else { - Expression body = null; - int i = 0; - - if (fields.Once()) + bool stopComputingExpression = false; + while (!stopComputingExpression && i < fields.Count()) { if (IsNotAStringAndIsEnumerable(targetType)) { + stopComputingExpression = true; Type enumerableGenericType = targetType.GenericTypeArguments[0]; - TypeInfo typeInfo = enumerableGenericType.GetTypeInfo(); ParameterExpression localParameter = Parameter(enumerableGenericType); Expression localBody; - if (!(typeInfo.IsPrimitive || PrimitiveTypes.Contains(enumerableGenericType))) - { - MemberExpression localProperty = Property(localParameter, fields.Single()); - localBody = ComputeBodyExpression(localProperty, @operator, value); - body = Call(typeof(Enumerable), - nameof(Enumerable.Any), - [enumerableGenericType], - property, - Lambda(localBody, [localParameter]) - ); - } + fields = fields.Skip(i) + .ToArray(); + localBody = fields.Any() + ? ComputeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property) + : ComputeBodyExpression(property, @operator, value); + + body = Call(typeof(Enumerable), + nameof(Enumerable.Any), + [enumerableGenericType], + property, + Lambda(localBody, [localParameter]) + ); } else { - body = ComputeBodyExpression(Property((Expression)property ?? pe, fields.Single()), @operator, value); - } - } - else - { - bool stopComputingExpression = false; - while (!stopComputingExpression && i < fields.Count()) - { - if (IsNotAStringAndIsEnumerable(targetType)) + property = Property(body ?? property ?? (Expression)pe, fields.ElementAt(i)); + PropertyInfo pi = (PropertyInfo)property.Member; + TypeInfo propertyTypeInfo = pi.PropertyType.GetTypeInfo(); + + if (propertyTypeInfo.IsPrimitive || PrimitiveTypes.Contains(pi.PropertyType)) { - stopComputingExpression = true; - Type enumerableGenericType = targetType.GenericTypeArguments[0]; - ParameterExpression localParameter = Parameter(enumerableGenericType); - Expression localBody; - - fields = fields.Skip(i) - .ToArray(); - localBody = fields.Any() - ? ComputeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property) - : ComputeBodyExpression(property, @operator, value); - - body = Call(typeof(Enumerable), - nameof(Enumerable.Any), - [enumerableGenericType], - property, - Lambda(localBody, [localParameter]) - ); + body = ComputeBodyExpression(property, @operator, value); } else { - property = Property(body ?? property ?? (Expression)pe, fields.ElementAt(i)); - PropertyInfo pi = (PropertyInfo)property.Member; - TypeInfo propertyTypeInfo = pi.PropertyType.GetTypeInfo(); - - if (propertyTypeInfo.IsPrimitive || PrimitiveTypes.Contains(pi.PropertyType)) - { - body = ComputeBodyExpression(property, @operator, value); - } - else - { - stopComputingExpression = true; - body = ComputeExpression(pe, fields.Skip(i + 1).ToArray(), pi.PropertyType, @operator, value, property); - } + stopComputingExpression = true; + body = ComputeExpression(pe, fields.Skip(i + 1).ToArray(), pi.PropertyType, @operator, value, property); } - - i++; } - } - return body; + i++; + } } - private static Expression ComputeNullSafeExpression(ParameterExpression pe, IEnumerable fields, Type targetType, FilterOperator @operator, object value, MemberExpression property = null) + return body; + } + + private static Expression ComputeNullSafeExpression(ParameterExpression pe, IEnumerable fields, Type targetType, FilterOperator @operator, object value, MemberExpression property = null) + { + Expression body = null; + int i = 0; + + if (fields.Once()) { - Expression body = null; - int i = 0; + if (IsNotAStringAndIsEnumerable(targetType)) + { + Type enumerableGenericType = targetType.GenericTypeArguments[0]; + TypeInfo typeInfo = enumerableGenericType.GetTypeInfo(); + ParameterExpression localParameter = Parameter(enumerableGenericType); + Expression localBody; - if (fields.Once()) + if (!(typeInfo.IsPrimitive || PrimitiveTypes.Contains(enumerableGenericType))) + { + MemberExpression localProperty = Property(localParameter, fields.Single()); + localBody = ComputeNullSafeBodyExpression(localProperty, @operator, value); + body = AndAlso(NotEqual(property, Constant(null)), + Call(typeof(Enumerable), + nameof(Enumerable.Any), + [enumerableGenericType], + property, + Lambda(localBody, [localParameter])) + ); + } + } + else + { + body = ComputeNullSafeBodyExpression(Property((Expression)property ?? pe, fields.Single()), @operator, value); + } + } + else + { + bool stopComputingExpression = false; + while (!stopComputingExpression && i < fields.Count()) { if (IsNotAStringAndIsEnumerable(targetType)) { + stopComputingExpression = true; Type enumerableGenericType = targetType.GenericTypeArguments[0]; - TypeInfo typeInfo = enumerableGenericType.GetTypeInfo(); ParameterExpression localParameter = Parameter(enumerableGenericType); Expression localBody; - if (!(typeInfo.IsPrimitive || PrimitiveTypes.Contains(enumerableGenericType))) - { - MemberExpression localProperty = Property(localParameter, fields.Single()); - localBody = ComputeNullSafeBodyExpression(localProperty, @operator, value); - body = AndAlso(NotEqual(property, Constant(null)), - Call(typeof(Enumerable), - nameof(Enumerable.Any), - [enumerableGenericType], - property, - Lambda(localBody, [localParameter])) - ); - } + fields = fields.Skip(i) + .ToArray(); + localBody = fields.Any() + ? ComputeNullSafeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property) + : ComputeNullSafeBodyExpression(property, @operator, value); + + body = AndAlso(NotEqual(property, Constant(null)), + Call(typeof(Enumerable), + nameof(Enumerable.Any), + [enumerableGenericType], + property, + Lambda(localBody, [localParameter]))); } else { - body = ComputeNullSafeBodyExpression(Property((Expression)property ?? pe, fields.Single()), @operator, value); - } - } - else - { - bool stopComputingExpression = false; - while (!stopComputingExpression && i < fields.Count()) - { - if (IsNotAStringAndIsEnumerable(targetType)) + property = Property(body ?? property ?? (Expression)pe, fields.ElementAt(i)); + PropertyInfo pi = (PropertyInfo)property.Member; + TypeInfo propertyTypeInfo = pi.PropertyType.GetTypeInfo(); + + if (propertyTypeInfo.IsPrimitive || PrimitiveTypes.Contains(pi.PropertyType)) { - stopComputingExpression = true; - Type enumerableGenericType = targetType.GenericTypeArguments[0]; - ParameterExpression localParameter = Parameter(enumerableGenericType); - Expression localBody; - - fields = fields.Skip(i) - .ToArray(); - localBody = fields.Any() - ? ComputeNullSafeExpression(localParameter, fields.ToArray(), enumerableGenericType, @operator, value, property) - : ComputeNullSafeBodyExpression(property, @operator, value); - - body = AndAlso(NotEqual(property, Constant(null)), - Call(typeof(Enumerable), - nameof(Enumerable.Any), - [enumerableGenericType], - property, - Lambda(localBody, [localParameter]))); + body = ComputeNullSafeBodyExpression(property, @operator, value); } else { - property = Property(body ?? property ?? (Expression)pe, fields.ElementAt(i)); - PropertyInfo pi = (PropertyInfo)property.Member; - TypeInfo propertyTypeInfo = pi.PropertyType.GetTypeInfo(); - - if (propertyTypeInfo.IsPrimitive || PrimitiveTypes.Contains(pi.PropertyType)) - { - body = ComputeNullSafeBodyExpression(property, @operator, value); - } - else - { - stopComputingExpression = true; - body = ComputeNullSafeExpression(pe, fields.Skip(i + 1).ToArray(), pi.PropertyType, @operator, value, property); - } + stopComputingExpression = true; + body = ComputeNullSafeExpression(pe, fields.Skip(i + 1).ToArray(), pi.PropertyType, @operator, value, property); } - - i++; } - } - return body; + i++; + } } - private static Expression> MergeExpressions(Expression> left, FilterLogic logic, Expression> right) - => logic switch - { - FilterLogic.And => left.AndAlso(right), - FilterLogic.Or => left.OrElse(right), - _ => throw new NotSupportedException("Unsupported filter logic"), - }; + return body; + } + + private static Expression> MergeExpressions(Expression> left, FilterLogic logic, Expression> right) + => logic switch + { + FilterLogic.And => left.AndAlso(right), + FilterLogic.Or => left.OrElse(right), + _ => throw new NotSupportedException("Unsupported filter logic"), + }; - // Tests if membertype is a "DateTime" type - private static bool IsDatetimeMember(Type memberType) => memberType == typeof(DateTime) - || memberType == typeof(DateTime?) - || memberType == typeof(DateTimeOffset) - || memberType == typeof(DateTimeOffset?); + // Tests if membertype is a "DateTime" type + private static bool IsDatetimeMember(Type memberType) => memberType == typeof(DateTime) + || memberType == typeof(DateTime?) + || memberType == typeof(DateTimeOffset) + || memberType == typeof(DateTimeOffset?); #if NET6_0_OR_GREATER - private static bool IsDateOnly(Type memberType) => memberType == typeof(DateOnly); + private static bool IsDateOnly(Type memberType) => memberType == typeof(DateOnly); #endif - } } \ No newline at end of file diff --git a/src/Datafilters.Expressions/OrderExpression.cs b/src/Datafilters.Expressions/OrderExpression.cs index 62618dc0..a89d9fc6 100644 --- a/src/Datafilters.Expressions/OrderExpression.cs +++ b/src/Datafilters.Expressions/OrderExpression.cs @@ -1,50 +1,49 @@ -namespace DataFilters.Expressions -{ - using System; - using System.Linq.Expressions; +namespace DataFilters.Expressions; + +using System; +using System.Linq.Expressions; - using static DataFilters.OrderDirection; +using static DataFilters.OrderDirection; +/// +/// An instance of this class holds an which defines a property and its related to use to order collections +/// +/// Type of the object which the c +/// +/// An only can be created by calling either +/// or +/// +/// +/// Builds a new instance +/// +/// Lambda expression to the property onto which the sort will be performed. +/// Direction of the sort. +public sealed class OrderExpression(LambdaExpression keySelector, OrderDirection direction) +{ /// - /// An instance of this class holds an which defines a property and its related to use to order collections + /// Creates a new instance of /// - /// Type of the object which the c - /// - /// An only can be created by calling either - /// or - /// - /// - /// Builds a new instance - /// - /// Lambda expression to the property onto which the sort will be performed. - /// Direction of the sort. - public sealed class OrderExpression(LambdaExpression keySelector, OrderDirection direction) - { - /// - /// Creates a new instance of - /// - /// Type of the property which will serve to order collections' items - /// Expression used to select the property to used to order items in a collection - /// Order direction - /// A fully built instance. - public static OrderExpression Create(Expression> keySelector, OrderDirection direction = Ascending) => new(keySelector, direction); + /// Type of the property which will serve to order collections' items + /// Expression used to select the property to used to order items in a collection + /// Order direction + /// A fully built instance. + public static OrderExpression Create(Expression> keySelector, OrderDirection direction = Ascending) => new(keySelector, direction); - /// - /// Creates a new instance of - /// - /// Expression used to select the property to used to order items in a collection - /// Order direction - /// A fully built OrderClause - public static OrderExpression Create(LambdaExpression keySelector, OrderDirection direction = Ascending) => new(keySelector, direction); + /// + /// Creates a new instance of + /// + /// Expression used to select the property to used to order items in a collection + /// Order direction + /// A fully built OrderClause + public static OrderExpression Create(LambdaExpression keySelector, OrderDirection direction = Ascending) => new(keySelector, direction); - /// - /// The lambda expression to use when - /// - public LambdaExpression Expression { get; } = keySelector; + /// + /// The lambda expression to use when + /// + public LambdaExpression Expression { get; } = keySelector; - /// - /// Order direction (either or - /// - public OrderDirection Direction { get; } = direction; - } + /// + /// Order direction (either or + /// + public OrderDirection Direction { get; } = direction; } diff --git a/src/Datafilters.Expressions/OrderExtensions.cs b/src/Datafilters.Expressions/OrderExtensions.cs index 102497dc..5d7792e2 100644 --- a/src/Datafilters.Expressions/OrderExtensions.cs +++ b/src/Datafilters.Expressions/OrderExtensions.cs @@ -1,46 +1,45 @@ -namespace DataFilters -{ - using System; - using System.Collections.Generic; - using DataFilters.Expressions; +namespace DataFilters; + +using System; +using System.Collections.Generic; +using DataFilters.Expressions; +/// +/// Extension methods for instances. +/// +public static class OrderExtensions +{ /// - /// Extension methods for instances. + /// Converts to a collection of . /// - public static class OrderExtensions + /// Type of element onto the sort expression will target. + /// + /// a collection of s + public static IEnumerable> ToOrderClause(this IOrder sort) { - /// - /// Converts to a collection of . - /// - /// Type of element onto the sort expression will target. - /// - /// a collection of s - public static IEnumerable> ToOrderClause(this IOrder sort) + if (sort is not Order and not MultiOrder) { - if (sort is not Order and not MultiOrder) - { - throw new ArgumentOutOfRangeException(nameof(sort), "Unknown sort expression"); - } + throw new ArgumentOutOfRangeException(nameof(sort), "Unknown sort expression"); + } - return BuildOrderClauses(sort); + return BuildOrderClauses(sort); - static IEnumerable> BuildOrderClauses(IOrder sort) + static IEnumerable> BuildOrderClauses(IOrder sort) + { + switch (sort) { - switch (sort) - { - case Order singleSort: - yield return OrderExpression.Create(singleSort.Expression.ToLambda(), singleSort.Direction); - break; - case MultiOrder multiSort: - foreach (Order item in multiSort.Orders) + case Order singleSort: + yield return OrderExpression.Create(singleSort.Expression.ToLambda(), singleSort.Direction); + break; + case MultiOrder multiSort: + foreach (Order item in multiSort.Orders) + { + foreach (OrderExpression order in item.ToOrderClause()) { - foreach (OrderExpression order in item.ToOrderClause()) - { - yield return order; - } + yield return order; } - break; - } + } + break; } } } diff --git a/src/Datafilters.Expressions/QueryableExtensions.cs b/src/Datafilters.Expressions/QueryableExtensions.cs index 1094863a..946918dd 100644 --- a/src/Datafilters.Expressions/QueryableExtensions.cs +++ b/src/Datafilters.Expressions/QueryableExtensions.cs @@ -1,65 +1,69 @@ -namespace System.Linq -{ - using System.Collections.Generic; - using System.Linq.Expressions; - using DataFilters; - using DataFilters.Expressions; +namespace System.Linq; + +using System.Collections.Generic; +using System.Linq.Expressions; +using DataFilters; +using DataFilters.Expressions; +/// +/// Provides extension methods for ordering a collection of entities. +/// +public static class QueryableExtensions +{ /// - /// Provides extension methods for ordering a collection of entities. + /// Orders the based on the specified object. /// - public static class QueryableExtensions + /// The type of the entities in the collection. + /// The collection of entities to be ordered. + /// The order expression object specifying the ordering criteria. + /// An ordered queryable collection of entities. + /// Thrown when either or is . + public static IOrderedQueryable OrderBy(this IQueryable entries, in IOrder orderBy) { - /// - /// Orders the based on the specified object. - /// - /// The type of the entities in the collection. - /// The collection of entities to be ordered. - /// The order expression object specifying the ordering criteria. - /// An ordered queryable collection of entities. - /// Thrown when either or is . - public static IOrderedQueryable OrderBy(this IQueryable entries, in IOrder orderBy) + // Check for null parameters +#if NET8_0_OR_GREATER + ArgumentNullException.ThrowIfNull(entries); + ArgumentNullException.ThrowIfNull(orderBy); +#else + if (entries is null) { - // Check for null parameters - if (entries is null) - { - throw new ArgumentNullException(nameof(entries)); - } - - if (orderBy is null) - { - throw new ArgumentNullException(nameof(orderBy)); - } + throw new ArgumentNullException(nameof(entries)); + } - // Get the list of order expressions - IEnumerable> orders = orderBy.ToOrderClause(); - OrderExpression first = orders.First(); + if (orderBy is null) + { + throw new ArgumentNullException(nameof(orderBy)); + } +#endif - // Build the initial sorting expression - Expression sortExpression = Expression.Call(typeof(Queryable), - first.Direction switch - { - OrderDirection.Ascending => nameof(Queryable.OrderBy), - _ => nameof(Queryable.OrderByDescending) - }, - [entries.ElementType, first.Expression.ReturnType], - entries.Expression, first.Expression); + // Get the list of order expressions + IEnumerable> orders = orderBy.ToOrderClause(); + OrderExpression first = orders.First(); - // Build the subsequent sorting expressions - foreach (OrderExpression order in orders.Skip(1)) - { - sortExpression = Expression.Call(typeof(Queryable), - order.Direction switch - { - OrderDirection.Ascending => nameof(Queryable.ThenBy), - _ => nameof(Queryable.ThenByDescending) - }, - [entries.ElementType, order.Expression.ReturnType], - sortExpression, order.Expression); - } + // Build the initial sorting expression + Expression sortExpression = Expression.Call(typeof(Queryable), + first.Direction switch + { + OrderDirection.Ascending => nameof(Queryable.OrderBy), + _ => nameof(Queryable.OrderByDescending) + }, + [entries.ElementType, first.Expression.ReturnType], + entries.Expression, first.Expression); - // Create the ordered queryable collection - return (IOrderedQueryable)entries.Provider.CreateQuery(sortExpression); + // Build the subsequent sorting expressions + foreach (OrderExpression order in orders.Skip(1)) + { + sortExpression = Expression.Call(typeof(Queryable), + order.Direction switch + { + OrderDirection.Ascending => nameof(Queryable.ThenBy), + _ => nameof(Queryable.ThenByDescending) + }, + [entries.ElementType, order.Expression.ReturnType], + sortExpression, order.Expression); } + + // Create the ordered queryable collection + return (IOrderedQueryable)entries.Provider.CreateQuery(sortExpression); } } diff --git a/test/DataFilters.Expressions.UnitTests/Hero.cs b/test/DataFilters.Expressions.UnitTests/Hero.cs index e346ecb9..9d5ea8bb 100644 --- a/test/DataFilters.Expressions.UnitTests/Hero.cs +++ b/test/DataFilters.Expressions.UnitTests/Hero.cs @@ -1,43 +1,42 @@ -namespace DataFilters.Expressions.UnitTests -{ - using System; - using System.Collections.Generic; +namespace DataFilters.Expressions.UnitTests; - public class Hero : IEquatable - { - public string Name { get; set; } +using System; +using System.Collections.Generic; + +public class Hero : IEquatable +{ + public string Name { get; set; } - public int Age { get; set; } + public int Age { get; set; } - public DateTimeOffset FirstAppearance { get; set; } + public DateTimeOffset FirstAppearance { get; set; } #if NET6_0_OR_GREATER - public DateOnly LastAppearance { get; set; } + public DateOnly LastAppearance { get; set; } #endif - public Hero Acolyte { get; set; } + public Hero Acolyte { get; set; } - /// - public override bool Equals(object obj) => Equals(obj as Hero); + /// + public override bool Equals(object obj) => Equals(obj as Hero); - /// - public bool Equals(Hero other) => other != null && Name == other.Name && Age == other.Age; + /// + public bool Equals(Hero other) => other != null && Name == other.Name && Age == other.Age; - /// + /// #if NETCOREAPP1_0 || NETCOREAPP2_0 - public override int GetHashCode() => (Name, Age).GetHashCode(); + public override int GetHashCode() => (Name, Age).GetHashCode(); #else - public override int GetHashCode() => HashCode.Combine(Name, Age); + public override int GetHashCode() => HashCode.Combine(Name, Age); #endif - public static bool operator ==(Hero hero1, Hero hero2) - { - return EqualityComparer.Default.Equals(hero1, hero2); - } + public static bool operator ==(Hero hero1, Hero hero2) + { + return EqualityComparer.Default.Equals(hero1, hero2); + } - public static bool operator !=(Hero hero1, Hero hero2) - { - return !(hero1 == hero2); - } + public static bool operator !=(Hero hero1, Hero hero2) + { + return !(hero1 == hero2); } } diff --git a/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs b/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs index 1d78b8a2..12ac4cc1 100644 --- a/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs +++ b/test/DataFilters.Expressions.UnitTests/QueryableExtensionsTests.cs @@ -1,59 +1,58 @@ -namespace DataFilters.Expressions.UnitTests +namespace DataFilters.Expressions.UnitTests; + +using System; +using System.Collections.Generic; +using System.Linq; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class QueryableExtensionsTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using System.Linq; - using FluentAssertions; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class QueryableExtensionsTests(ITestOutputHelper outputHelper) + public static IEnumerable ThrowsArgumentNullExceptionCases { - public static IEnumerable ThrowsArgumentNullExceptionCases + get { - get + yield return new object[] + { + null, + new Order(nameof(Hero.Name)) + }; + + yield return new object[] { - yield return new object[] - { - null, - new Order(nameof(Hero.Name)) - }; - - yield return new object[] - { - Enumerable.Empty().AsQueryable(), - null - }; - } + Enumerable.Empty().AsQueryable(), + null + }; } + } - [Theory] - [MemberData(nameof(ThrowsArgumentNullExceptionCases))] - public void Should_Throws_ArgumentNullException_When_Parameter_IsNull(IQueryable heroes, IOrder orderBy) - { - outputHelper.WriteLine($"{nameof(heroes)} is null : {heroes == null}"); - outputHelper.WriteLine($"{nameof(orderBy)} is null : {orderBy == null}"); + [Theory] + [MemberData(nameof(ThrowsArgumentNullExceptionCases))] + public void Should_Throws_ArgumentNullException_When_Parameter_IsNull(IQueryable heroes, IOrder orderBy) + { + outputHelper.WriteLine($"{nameof(heroes)} is null : {heroes == null}"); + outputHelper.WriteLine($"{nameof(orderBy)} is null : {orderBy == null}"); - // Act - Action action = () => heroes.OrderBy(orderBy); + // Act + Action action = () => heroes.OrderBy(orderBy); - // Assert - action.Should() - .ThrowExactly() - .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName)); - } + // Assert + action.Should() + .ThrowExactly() + .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName)); + } - [Fact] - public void Should_Throws_ArgumentNullException_WhenOrderBy_Null() - { - // Act - Action action = () => Enumerable.Empty().AsQueryable().OrderBy(null); + [Fact] + public void Should_Throws_ArgumentNullException_WhenOrderBy_Null() + { + // Act + Action action = () => Enumerable.Empty().AsQueryable().OrderBy(null); - // Assert - action.Should() - .ThrowExactly(); - } + // Assert + action.Should() + .ThrowExactly(); } } diff --git a/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs b/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs index 1bdd0d90..4b2da2dc 100644 --- a/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs +++ b/test/DataFilters.Expressions.UnitTests/ToOrderClauseTests.cs @@ -1,178 +1,183 @@ -namespace DataFilters.Expressions.UnitTests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using FluentAssertions; - using FluentAssertions.Extensions; - using Xunit; - using Xunit.Categories; - using static DataFilters.OrderDirection; +namespace DataFilters.Expressions.UnitTests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using FluentAssertions.Extensions; +using Xunit; +using Xunit.Categories; +using static DataFilters.OrderDirection; - [UnitTest] - [Feature("Expressions")] - public class ToOrderClauseTests +[UnitTest] +[Feature("Expressions")] +public class ToOrderClauseTests +{ + public static IEnumerable ToOrderClauseCases { - public static IEnumerable ToOrderClauseCases + get { - get + yield return new object[] { - yield return new object[] + new[] { - new[] - { - new Hero { Name = "Batman"}, - new Hero { Name = "Flash"} - }, - new Order(nameof(Hero.Name)), - (Expression, bool>>)(heroes => heroes.Exactly(2) - && heroes.First().Name == "Batman" - && heroes.Last().Name == "Flash" - ) - }; + new Hero { Name = "Batman"}, + new Hero { Name = "Flash"} + }, + new Order(nameof(Hero.Name)), + (Expression, bool>>)(heroes => heroes.Exactly(2) + && heroes.First().Name == "Batman" + && heroes.Last().Name == "Flash" + ) + }; - yield return new object[] + yield return new object[] + { + new[] { - new[] - { - new Hero { Name = "Batman"}, - new Hero { Name = "Flash"} - }, - new Order("name"), - (Expression, bool>>)(heroes => heroes.Exactly(2) - && heroes.First().Name == "Batman" - && heroes.Last().Name == "Flash" - ) - }; + new Hero { Name = "Batman"}, + new Hero { Name = "Flash"} + }, + new Order("name"), + (Expression, bool>>)(heroes => heroes.Exactly(2) + && heroes.First().Name == "Batman" + && heroes.Last().Name == "Flash" + ) + }; - yield return new object[] + yield return new object[] + { + new[] { - new[] - { - new Hero { Name = "Batman"}, - new Hero { Name = "Flash"} - }, - new Order(" name"), - (Expression, bool>>)(heroes => heroes.Exactly(2) - && heroes.First().Name == "Batman" - && heroes.Last().Name == "Flash" - ) - }; + new Hero { Name = "Batman"}, + new Hero { Name = "Flash"} + }, + new Order(" name"), + (Expression, bool>>)(heroes => heroes.Exactly(2) + && heroes.First().Name == "Batman" + && heroes.Last().Name == "Flash" + ) + }; - yield return new object[] + yield return new object[] + { + new[] { - new[] - { - new Hero { Name = "Batman"}, - new Hero { Name = "Flash"} - }, - new Order(nameof(Hero.Name), Descending), + new Hero { Name = "Batman"}, + new Hero { Name = "Flash"} + }, + new Order(nameof(Hero.Name), Descending), - (Expression, bool>>)(heroes => heroes.Exactly(2) - && heroes.First().Name == "Flash" - && heroes.Last().Name == "Batman" - ) - }; + (Expression, bool>>)(heroes => heroes.Exactly(2) + && heroes.First().Name == "Flash" + && heroes.Last().Name == "Batman" + ) + }; - yield return new object[] + yield return new object[] + { + new[] { - new[] - { - new Hero { Name = "Batman", Age = 27}, - new Hero { Name = "Wonder Woman", Age = 30}, - new Hero { Name = "Flash", Age = 37} - }, - new MultiOrder - ( - new Order(nameof(Hero.Age), Ascending), - new Order(nameof(Hero.Name), Descending) - ), - (Expression, bool>>)(heroes => heroes.Exactly(3) - && heroes.First().Name == "Batman" - && heroes.Last().Name == "Flash" - ) - }; + new Hero { Name = "Batman", Age = 27}, + new Hero { Name = "Wonder Woman", Age = 30}, + new Hero { Name = "Flash", Age = 37} + }, + new MultiOrder + ( + new Order(nameof(Hero.Age), Ascending), + new Order(nameof(Hero.Name), Descending) + ), + (Expression, bool>>)(heroes => heroes.Exactly(3) + && heroes.First().Name == "Batman" + && heroes.Last().Name == "Flash" + ) + }; - yield return new object[] + yield return new object[] + { + new[] { - new[] - { - new Hero { Name = "Batman", Age = 27}, - new Hero { Name = "Wonder Woman", Age = 30}, - new Hero { Name = "Flash", Age = 37} - }, - new MultiOrder - ( - new Order(nameof(Hero.Name), Descending), - new Order(nameof(Hero.Age), Ascending) - ), - (Expression, bool>>)(heroes => heroes.Exactly(3) - && heroes.First().Name == "Wonder Woman" - && heroes.Last().Name == "Batman" - ) - }; + new Hero { Name = "Batman", Age = 27}, + new Hero { Name = "Wonder Woman", Age = 30}, + new Hero { Name = "Flash", Age = 37} + }, + new MultiOrder + ( + new Order(nameof(Hero.Name), Descending), + new Order(nameof(Hero.Age), Ascending) + ), + (Expression, bool>>)(heroes => heroes.Exactly(3) + && heroes.First().Name == "Wonder Woman" + && heroes.Last().Name == "Batman" + ) + }; - yield return new object[] + yield return new object[] + { + new[] { - new[] - { - new Hero { Name = "Batman", FirstAppearance = 1.May(1939)}, - new Hero { Name = "Flash" , FirstAppearance = 1.May(1940)} - }, - new Order(nameof(Hero.FirstAppearance), Descending), + new Hero { Name = "Batman", FirstAppearance = 1.May(1939)}, + new Hero { Name = "Flash" , FirstAppearance = 1.May(1940)} + }, + new Order(nameof(Hero.FirstAppearance), Descending), - (Expression, bool>>)(heroes => heroes.Exactly(2) - && heroes.First().Name == "Flash" - && heroes.Last().Name == "Batman" - ) - }; + (Expression, bool>>)(heroes => heroes.Exactly(2) + && heroes.First().Name == "Flash" + && heroes.Last().Name == "Batman" + ) + }; - yield return new object[] +#if NET8_0_OR_GREATER +#pragma warning disable CA1861 // Éviter les tableaux constants en tant qu’arguments +#endif + yield return new object[] + { + new[] { - new[] + new Hero { - new Hero - { - Name = "Batman", - FirstAppearance = 1.May(1938), - Acolyte = new Hero { Name = "Robin", Age = 13 } - }, - new Hero - { - Name = "Flash", - FirstAppearance = 1.May(1940), - Acolyte = new Hero { Name = "Kid Flash", Age = 15 } - }, - new Hero - { - Name = "Green Arrow", - FirstAppearance = 1.May(1940), - Acolyte = new Hero { Name = "Red Arrow", Age = 14 } - } + Name = "Batman", + FirstAppearance = 1.May(1938), + Acolyte = new Hero { Name = "Robin", Age = 13 } }, - new MultiOrder( - new Order(nameof(Hero.FirstAppearance), Descending), - new Order($"{nameof(Hero.Acolyte)}.{nameof(Hero.Age)}", Ascending) - ), + new Hero + { + Name = "Flash", + FirstAppearance = 1.May(1940), + Acolyte = new Hero { Name = "Kid Flash", Age = 15 } + }, + new Hero + { + Name = "Green Arrow", + FirstAppearance = 1.May(1940), + Acolyte = new Hero { Name = "Red Arrow", Age = 14 } + } + }, + new MultiOrder( + new Order(nameof(Hero.FirstAppearance), Descending), + new Order($"{nameof(Hero.Acolyte)}.{nameof(Hero.Age)}", Ascending) + ), - (Expression, bool>>)(heroes => heroes.Select(x => x.Name).SequenceEqual(new []{ "Green Arrow", "Flash", "Batman"})) - }; - } + (Expression, bool>>)(heroes => heroes.Select(x => x.Name).SequenceEqual(new[] {"Green Arrow", "Flash", "Batman" })) + }; +#if NET8_0_OR_GREATER +#pragma warning restore CA1861 // Éviter les tableaux constants en tant qu’arguments +#endif } + } - [Theory] - [MemberData(nameof(ToOrderClauseCases))] - public void ToOrderTests(IEnumerable heroes, IOrder order, Expression, bool>> expectation) - { - // Act - IEnumerable actual = heroes.AsQueryable() - .OrderBy(order) - .ToList(); + [Theory] + [MemberData(nameof(ToOrderClauseCases))] + public void ToOrderTests(IEnumerable heroes, IOrder order, Expression, bool>> expectation) + { + // Act + IEnumerable actual = heroes.AsQueryable() + .OrderBy(order) + .ToList(); - // Assert - actual.Should() - .Match(expectation); - } + // Assert + actual.Should() + .Match(expectation); } } diff --git a/test/DataFilters.PerformanceTests/BracketVsOr.cs b/test/DataFilters.PerformanceTests/BracketVsOr.cs index 6847d5c9..25f69d90 100644 --- a/test/DataFilters.PerformanceTests/BracketVsOr.cs +++ b/test/DataFilters.PerformanceTests/BracketVsOr.cs @@ -1,26 +1,25 @@ -namespace DataFilters.PerfomanceTests -{ - using System; - using BenchmarkDotNet.Attributes; - using BenchmarkDotNet.Jobs; +namespace DataFilters.PerfomanceTests; + +using System; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; - [MemoryDiagnoser] - [SimpleJob(RuntimeMoniker.CoreRt31)] - [SimpleJob(RuntimeMoniker.Net50)] - [SimpleJob(RuntimeMoniker.Net60)] - [System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Marquer les membres comme étant static")] - public class BracketVsOr - { - [Benchmark] - [Arguments("Nickname=Br[a-f]")] - public IFilter Bracket(string input) => input.ToFilter(); +[MemoryDiagnoser] +[SimpleJob(RuntimeMoniker.CoreRt31)] +[SimpleJob(RuntimeMoniker.Net50)] +[SimpleJob(RuntimeMoniker.Net60)] +[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Marquer les membres comme étant static")] +public class BracketVsOr +{ + [Benchmark] + [Arguments("Nickname=Br[a-f]")] + public IFilter Bracket(string input) => input.ToFilter(); - [Benchmark] - [Arguments("Nickname=(Bra|Brb)|(Brc|Brd)|(Bre|Brf)")] - public IFilter Or(string input) => input.ToFilter(); + [Benchmark] + [Arguments("Nickname=(Bra|Brb)|(Brc|Brd)|(Bre|Brf)")] + public IFilter Or(string input) => input.ToFilter(); - [Benchmark] - [Arguments("Nickname={Bra|Brb|Brc|Brd|Bre|Brf}")] - public IFilter OneOf(string input) => input.ToFilter(); - } + [Benchmark] + [Arguments("Nickname={Bra|Brb|Brc|Brd|Bre|Brf}")] + public IFilter OneOf(string input) => input.ToFilter(); } diff --git a/test/DataFilters.PerformanceTests/Program.cs b/test/DataFilters.PerformanceTests/Program.cs index 27cf0214..1adb1605 100644 --- a/test/DataFilters.PerformanceTests/Program.cs +++ b/test/DataFilters.PerformanceTests/Program.cs @@ -1,10 +1,9 @@ using BenchmarkDotNet.Running; -namespace DataFilters.PerfomanceTests -{ +namespace DataFilters.PerfomanceTests; + - public class Program - { - static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly) - .Run(args); - } +public class Program +{ + static void Main(string[] args) => BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly) + .Run(args); } diff --git a/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs b/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs deleted file mode 100644 index b1238eac..00000000 --- a/test/DataFilters.PerformanceTests/RawFilterVsFilterService.cs +++ /dev/null @@ -1,63 +0,0 @@ -namespace DataFilters.PerfomanceTests; - -using System; -using BenchmarkDotNet.Attributes; - -[MemoryDiagnoser] -[RPlotExporter] -[System.Diagnostics.CodeAnalysis.SuppressMessage("Performance", "CA1822:Marquer les membres comme étant static")] -public class RawFilterVsFilterService -{ - private FilterService _service; - - [Params("Nickname=((Bat|Sup)|Wonder)*m[ae]n")] - public string Input { get; set; } - - [GlobalSetup] - public void GlobalSetup() - { - _service = new FilterService(new FilterServiceOptions { MaxCacheSize = 10 }); - } - - [Benchmark(Description = "Computing filter with no caching")] - public IFilter WithoutCache() => Input.ToFilter(); - - [Benchmark(Description = "Computing filter with a filter cache")] - public IFilter WithCache() => _service.Compute(Input); - - [Benchmark(Baseline = true)] - public IFilter DirectExpression() - => new MultiFilter - { - Logic = FilterLogic.And, - Filters = new[] - { - new MultiFilter - { - Logic = FilterLogic.Or, - Filters = new IFilter[] - { - new MultiFilter - { - Logic = FilterLogic.Or, - Filters = new IFilter[] - { - new Filter(nameof(SuperHero.Nickname), FilterOperator.StartsWith, "Bat"), - new Filter(nameof(SuperHero.Nickname), FilterOperator.StartsWith, "Sup") - } - }, - new Filter(nameof(SuperHero.Nickname), FilterOperator.StartsWith, "Wonder") - } - }, - new MultiFilter - { - Logic = FilterLogic.Or, - Filters = new[] - { - new Filter(nameof(SuperHero.Nickname), FilterOperator.EndsWith, "man"), - new Filter(nameof(SuperHero.Nickname), FilterOperator.EndsWith, "men") - } - } - } - }; -} \ No newline at end of file diff --git a/test/DataFilters.PerformanceTests/SuperHero.cs b/test/DataFilters.PerformanceTests/SuperHero.cs index 8db09108..bfaa29ac 100644 --- a/test/DataFilters.PerformanceTests/SuperHero.cs +++ b/test/DataFilters.PerformanceTests/SuperHero.cs @@ -1,14 +1,13 @@ -namespace DataFilters.PerfomanceTests -{ - using System.Collections.Generic; +namespace DataFilters.PerfomanceTests; + +using System.Collections.Generic; - public class SuperHero - { - public string Nickname { get; set; } +public class SuperHero +{ + public string Nickname { get; set; } - public string[] Powers { get; set; } + public string[] Powers { get; set; } - public IEnumerable Acolytes { get; set; } + public IEnumerable Acolytes { get; set; } - } } diff --git a/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs b/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs index d0cee984..9d0c85cd 100644 --- a/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs +++ b/test/DataFilters.Queries.UnitTests/FilterToQueriesTests.cs @@ -1,165 +1,164 @@ -namespace DataFilters.Queries.UnitTests +namespace DataFilters.Queries.UnitTests; + +using System; +using System.Collections.Generic; +using FluentAssertions; +using FluentAssertions.Extensions; +using global::Queries.Core.Parts.Clauses; +using Xunit; +using Xunit.Abstractions; +using static DataFilters.FilterOperator; + +public class FilterToQueriesTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using FluentAssertions; - using FluentAssertions.Extensions; - using global::Queries.Core.Parts.Clauses; - using Xunit; - using Xunit.Abstractions; - using static DataFilters.FilterOperator; - - public class FilterToQueriesTests(ITestOutputHelper outputHelper) + public static IEnumerable FilterToWhereCases { - public static IEnumerable FilterToWhereCases + get { - get + yield return new object[] { - yield return new object[] - { - new Filter(field: "Name",@operator: EqualTo, value: "Wayne"), - new WhereClause(column: "Name".Field(), @operator: ClauseOperator.EqualTo, constraint: "Wayne") - }; + new Filter(field: "Name",@operator: EqualTo, value: "Wayne"), + new WhereClause(column: "Name".Field(), @operator: ClauseOperator.EqualTo, constraint: "Wayne") + }; - yield return new object[] - { - new Filter(field: "Name",@operator: NotEqualTo, value: "Wayne"), - new WhereClause(column: "Name".Field(), @operator: ClauseOperator.NotEqualTo, constraint: "Wayne") - }; + yield return new object[] + { + new Filter(field: "Name",@operator: NotEqualTo, value: "Wayne"), + new WhereClause(column: "Name".Field(), @operator: ClauseOperator.NotEqualTo, constraint: "Wayne") + }; - yield return new object[] - { - new Filter(field: "Name",@operator: Contains, value: "W"), - new WhereClause(column: "Name".Field(), @operator: ClauseOperator.Like, constraint: "%W%") - }; + yield return new object[] + { + new Filter(field: "Name",@operator: Contains, value: "W"), + new WhereClause(column: "Name".Field(), @operator: ClauseOperator.Like, constraint: "%W%") + }; - yield return new object[] - { - new Filter(field: "Name",@operator: EndsWith, value: "W"), - new WhereClause(column: "Name".Field(), @operator: ClauseOperator.Like, constraint: "%W") - }; + yield return new object[] + { + new Filter(field: "Name",@operator: EndsWith, value: "W"), + new WhereClause(column: "Name".Field(), @operator: ClauseOperator.Like, constraint: "%W") + }; - yield return new object[] + yield return new object[] + { + new Filter(field: "Name",@operator: NotEndsWith, value: "W"), + new WhereClause(column: "Name".Field(), @operator: ClauseOperator.NotLike, constraint: "%W") + }; + yield return new object[] + { + new MultiFilter { - new Filter(field: "Name",@operator: NotEndsWith, value: "W"), - new WhereClause(column: "Name".Field(), @operator: ClauseOperator.NotLike, constraint: "%W") - }; - yield return new object[] + Logic = FilterLogic.Or, + Filters = new []{ + new Filter(field: "Name",@operator: EndsWith, value: "man"), + new Filter(field: "Name",@operator: StartsWith, value: "Bat") + } + }, + new CompositeWhereClause { - new MultiFilter - { - Logic = FilterLogic.Or, - Filters = new []{ - new Filter(field: "Name",@operator: EndsWith, value: "man"), - new Filter(field: "Name",@operator: StartsWith, value: "Bat") - } - }, - new CompositeWhereClause - { - Logic = ClauseLogic.Or, - Clauses = new []{ - new WhereClause("Name".Field(),@operator: ClauseOperator.Like, constraint: "%man"), - new WhereClause("Name".Field(),@operator: ClauseOperator.Like, constraint: "Bat%") - } + Logic = ClauseLogic.Or, + Clauses = new []{ + new WhereClause("Name".Field(),@operator: ClauseOperator.Like, constraint: "%man"), + new WhereClause("Name".Field(),@operator: ClauseOperator.Like, constraint: "Bat%") } - }; + } + }; - yield return new object[] - { - new Filter(field: "Name", @operator: EndsWith, "*"), - new WhereClause(column: "Name".Field(), @operator: ClauseOperator.Like, "%*"), - }; + yield return new object[] + { + new Filter(field: "Name", @operator: EndsWith, "*"), + new WhereClause(column: "Name".Field(), @operator: ClauseOperator.Like, "%*"), + }; - yield return new object[] - { - new Filter(field: "Superpower", IsNull), - new WhereClause(column: "Superpower".Field(), @operator: ClauseOperator.IsNull) - }; + yield return new object[] + { + new Filter(field: "Superpower", IsNull), + new WhereClause(column: "Superpower".Field(), @operator: ClauseOperator.IsNull) + }; - yield return new object[] - { - new Filter(field: "Superpower", IsNotNull), - new WhereClause(column: "Superpower".Field(), @operator: ClauseOperator.IsNotNull) - }; + yield return new object[] + { + new Filter(field: "Superpower", IsNotNull), + new WhereClause(column: "Superpower".Field(), @operator: ClauseOperator.IsNotNull) + }; - yield return new object[] - { - new Filter(field: "Nickname", NotStartsWith, "Bat"), - new WhereClause(column: "Nickname".Field(), ClauseOperator.NotLike, "Bat%") - }; + yield return new object[] + { + new Filter(field: "Nickname", NotStartsWith, "Bat"), + new WhereClause(column: "Nickname".Field(), ClauseOperator.NotLike, "Bat%") + }; - yield return new object[] - { - new Filter(field: "Nickname", NotContains, "man"), - new WhereClause(column: "Nickname".Field(), ClauseOperator.NotLike, "%man%") - }; + yield return new object[] + { + new Filter(field: "Nickname", NotContains, "man"), + new WhereClause(column: "Nickname".Field(), ClauseOperator.NotLike, "%man%") + }; - yield return new object[] - { - new Filter(field: "XP", FilterOperator.LessThan, 10), - new WhereClause("XP".Field(), ClauseOperator.LessThan, 10) - }; + yield return new object[] + { + new Filter(field: "XP", FilterOperator.LessThan, 10), + new WhereClause("XP".Field(), ClauseOperator.LessThan, 10) + }; - yield return new object[] - { - new Filter(field: "XP", LessThanOrEqualTo, 10), - new WhereClause("XP".Field(), ClauseOperator.LessThanOrEqualTo, 10) - }; + yield return new object[] + { + new Filter(field: "XP", LessThanOrEqualTo, 10), + new WhereClause("XP".Field(), ClauseOperator.LessThanOrEqualTo, 10) + }; - yield return new object[] - { - new Filter(field : "Height", GreaterThan, 6.4), - new WhereClause("Height".Field(), ClauseOperator.GreaterThan, 6.4.Literal()) - }; + yield return new object[] + { + new Filter(field : "Height", GreaterThan, 6.4), + new WhereClause("Height".Field(), ClauseOperator.GreaterThan, 6.4.Literal()) + }; - yield return new object[] - { - new Filter(field : "Height", GreaterThan, 6m), - new WhereClause("Height".Field(), ClauseOperator.GreaterThan, 6m) - }; + yield return new object[] + { + new Filter(field : "Height", GreaterThan, 6m), + new WhereClause("Height".Field(), ClauseOperator.GreaterThan, 6m) + }; - yield return new object[] - { - new Filter(field : "Height", GreaterThanOrEqual, 6), - new WhereClause("Height".Field(), ClauseOperator.GreaterThanOrEqualTo, 6) - }; + yield return new object[] + { + new Filter(field : "Height", GreaterThanOrEqual, 6), + new WhereClause("Height".Field(), ClauseOperator.GreaterThanOrEqualTo, 6) + }; - yield return new object[] - { - new Filter("BirthDate", GreaterThan, 18.January(1983)), - new WhereClause("BirthDate".Field(), ClauseOperator.GreaterThan, 18.January(1983)) - }; + yield return new object[] + { + new Filter("BirthDate", GreaterThan, 18.January(1983)), + new WhereClause("BirthDate".Field(), ClauseOperator.GreaterThan, 18.January(1983)) + }; #if NET6_0_OR_GREATER - yield return new object[] - { - new Filter("BirthDate", GreaterThan, DateOnly.FromDateTime(18.January(1983))), - new WhereClause("BirthDate".Field(), ClauseOperator.GreaterThan, DateOnly.FromDateTime(18.January(1983))) - }; + yield return new object[] + { + new Filter("BirthDate", GreaterThan, DateOnly.FromDateTime(18.January(1983))), + new WhereClause("BirthDate".Field(), ClauseOperator.GreaterThan, DateOnly.FromDateTime(18.January(1983))) + }; - yield return new object[] - { - new Filter("Time", GreaterThan, TimeOnly.FromDateTime(18.January(1983).Add(15.Hours().And(47.Minutes())))), - new WhereClause("Time".Field(), ClauseOperator.GreaterThan, TimeOnly.FromDateTime(18.January(1983).Add(15.Hours().And(47.Minutes())))) - }; + yield return new object[] + { + new Filter("Time", GreaterThan, TimeOnly.FromDateTime(18.January(1983).Add(15.Hours().And(47.Minutes())))), + new WhereClause("Time".Field(), ClauseOperator.GreaterThan, TimeOnly.FromDateTime(18.January(1983).Add(15.Hours().And(47.Minutes())))) + }; #endif - } } + } - [Theory] - [MemberData(nameof(FilterToWhereCases))] - public void FilterToWhere(IFilter filter, IWhereClause expected) - { - outputHelper.WriteLine($"Filter : {filter.Jsonify()}"); - outputHelper.WriteLine($"Expected : {expected.Jsonify()}"); + [Theory] + [MemberData(nameof(FilterToWhereCases))] + public void FilterToWhere(IFilter filter, IWhereClause expected) + { + outputHelper.WriteLine($"Filter : {filter.Jsonify()}"); + outputHelper.WriteLine($"Expected : {expected.Jsonify()}"); - // Act - IWhereClause actualWhere = filter.ToWhere(); - outputHelper.WriteLine($"actualQuery : {actualWhere.Jsonify()}"); + // Act + IWhereClause actualWhere = filter.ToWhere(); + outputHelper.WriteLine($"actualQuery : {actualWhere.Jsonify()}"); - // Assert - actualWhere.Should() - .Be(expected); - } + // Assert + actualWhere.Should() + .Be(expected); } } diff --git a/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs b/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs index 0bb9f161..5f8cbf52 100644 --- a/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs +++ b/test/DataFilters.Queries.UnitTests/OrderToQueriesTests.cs @@ -1,65 +1,64 @@ -namespace DataFilters.Queries.UnitTests -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using FluentAssertions; - using global::Queries.Core.Parts.Sorting; - using Xunit; - using Xunit.Abstractions; - using static global::Queries.Core.Parts.Sorting.OrderDirection; +namespace DataFilters.Queries.UnitTests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using global::Queries.Core.Parts.Sorting; +using Xunit; +using Xunit.Abstractions; +using static global::Queries.Core.Parts.Sorting.OrderDirection; - public class OrderToQueriesTests(ITestOutputHelper outputHelper) +public class OrderToQueriesTests(ITestOutputHelper outputHelper) +{ + public class Person { - public class Person - { - public string Firstname { get; set; } + public string Firstname { get; set; } - public string Lastname { get; set; } - } + public string Lastname { get; set; } + } - public static IEnumerable OrderToOrderCases + public static IEnumerable OrderToOrderCases + { + get { - get + yield return new object[] { + new Order("Name"), + (Expression, bool>>) (sorts => sorts.Once() + && sorts.First().Equals(new OrderExpression("Name".Field(), Ascending))) + }; + + { + MultiOrder multiSort = new + ( + new Order(nameof(Person.Firstname)), + new Order(nameof(Person.Lastname)) + ); yield return new object[] { - new Order("Name"), - (Expression, bool>>) (sorts => sorts.Once() - && sorts.First().Equals(new OrderExpression("Name".Field(), Ascending))) + multiSort, + (Expression, bool>>) (sorts => sorts.Exactly(2) + && sorts.First().Equals(new OrderExpression(nameof(Person.Firstname), Ascending)) + && sorts.Last().Equals(new OrderExpression(nameof(Person.Lastname), Ascending))) }; - - { - MultiOrder multiSort = new - ( - new Order(nameof(Person.Firstname)), - new Order(nameof(Person.Lastname)) - ); - yield return new object[] - { - multiSort, - (Expression, bool>>) (sorts => sorts.Exactly(2) - && sorts.First().Equals(new OrderExpression(nameof(Person.Firstname), Ascending)) - && sorts.Last().Equals(new OrderExpression(nameof(Person.Lastname), Ascending))) - }; - } } } + } - [Theory] - [MemberData(nameof(OrderToOrderCases))] - public void FilterToOrder(IOrder sort, Expression, bool>> expected) - { - outputHelper.WriteLine($"Sort : {sort.Jsonify()}"); + [Theory] + [MemberData(nameof(OrderToOrderCases))] + public void FilterToOrder(IOrder sort, Expression, bool>> expected) + { + outputHelper.WriteLine($"Sort : {sort.Jsonify()}"); - // Act - IEnumerable actual = sort.ToOrder(); - outputHelper.WriteLine($"actual result : {actual.Jsonify()}"); + // Act + IEnumerable actual = sort.ToOrder(); + outputHelper.WriteLine($"actual result : {actual.Jsonify()}"); - // Assert - actual.Should() - .Match(expected); - } + // Assert + actual.Should() + .Match(expected); } } diff --git a/test/DataFilters.TestObjects/Appointment.cs b/test/DataFilters.TestObjects/Appointment.cs index acd718ad..be14629f 100644 --- a/test/DataFilters.TestObjects/Appointment.cs +++ b/test/DataFilters.TestObjects/Appointment.cs @@ -1,10 +1,9 @@ -namespace DataFilters.TestObjects -{ - using System; +namespace DataFilters.TestObjects; + +using System; - public class Appointment - { - public string Name { get; set; } - public DateTimeOffset? Date { get; set; } - } +public class Appointment +{ + public string Name { get; set; } + public DateTimeOffset? Date { get; set; } } diff --git a/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj b/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj index 3592cd93..411789f1 100644 --- a/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj +++ b/test/DataFilters.TestObjects/DataFilters.TestObjects.csproj @@ -2,6 +2,7 @@ netstandard2.0;net6.0;net7.0;net8.0 + latest false diff --git a/test/DataFilters.TestObjects/Henchman.cs b/test/DataFilters.TestObjects/Henchman.cs index 6fbed031..bf6180ce 100644 --- a/test/DataFilters.TestObjects/Henchman.cs +++ b/test/DataFilters.TestObjects/Henchman.cs @@ -1,6 +1,9 @@ -namespace DataFilters.TestObjects +namespace DataFilters.TestObjects; + +#if NET +public class Henchman : SuperHero; +#else +public class Henchman : SuperHero { - public class Henchman : SuperHero - { - } -} +} +#endif diff --git a/test/DataFilters.TestObjects/Model.cs b/test/DataFilters.TestObjects/Model.cs index 04cab93b..636cb446 100644 --- a/test/DataFilters.TestObjects/Model.cs +++ b/test/DataFilters.TestObjects/Model.cs @@ -1,16 +1,15 @@ -namespace DataFilters.TestObjects -{ - using System.Diagnostics.CodeAnalysis; +namespace DataFilters.TestObjects; + +using System.Diagnostics.CodeAnalysis; - [ExcludeFromCodeCoverage] - public class Model - { - public string PascalCaseProperty { get; set; } +[ExcludeFromCodeCoverage] +public class Model +{ + public string PascalCaseProperty { get; set; } #pragma warning disable IDE1006 // Styles d'affectation de noms - public string snake_case_property { get; set; } + public string snake_case_property { get; set; } #pragma warning restore IDE1006 // Styles d'affectation de noms - public string CAPITALCASINGPROPERTY { get; set; } - } + public string CAPITALCASINGPROPERTY { get; set; } } diff --git a/test/DataFilters.TestObjects/Person.cs b/test/DataFilters.TestObjects/Person.cs index 1c922870..da07d991 100644 --- a/test/DataFilters.TestObjects/Person.cs +++ b/test/DataFilters.TestObjects/Person.cs @@ -1,27 +1,26 @@ -namespace DataFilters.TestObjects -{ - using System; - using System.Diagnostics.CodeAnalysis; +namespace DataFilters.TestObjects; + +using System; +using System.Diagnostics.CodeAnalysis; - [ExcludeFromCodeCoverage] - public class Person - { - public string Firstname { get; set; } +[ExcludeFromCodeCoverage] +public class Person +{ + public string Firstname { get; set; } - public string Lastname { get; set; } + public string Lastname { get; set; } - public string Nickname { get; set; } + public string Nickname { get; set; } #if NET6_0_OR_GREATER - public DateOnly BirthDate { get; init; } + public DateOnly BirthDate { get; init; } #else - public DateTime BirthDate { get; set; } + public DateTime BirthDate { get; set; } #endif - public string BattleCry { get; set; } + public string BattleCry { get; set; } - public int Height { get; set; } + public int Height { get; set; } - public DateTimeOffset DateTimeWithOffset { get; set; } - } + public DateTimeOffset DateTimeWithOffset { get; set; } } diff --git a/test/DataFilters.TestObjects/SuperHero.cs b/test/DataFilters.TestObjects/SuperHero.cs index bbf33ae8..19c5cfeb 100644 --- a/test/DataFilters.TestObjects/SuperHero.cs +++ b/test/DataFilters.TestObjects/SuperHero.cs @@ -1,28 +1,27 @@ -namespace DataFilters.TestObjects -{ - using System; - using System.Collections.Generic; - using System.Diagnostics.CodeAnalysis; - using System.Linq; +namespace DataFilters.TestObjects; + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Linq; - [ExcludeFromCodeCoverage] - public class SuperHero : Person - { +[ExcludeFromCodeCoverage] +public class SuperHero : Person +{ #if NET6_0_OR_GREATER - public DateOnly? LastBattleDate { get; init; } + public DateOnly? LastBattleDate { get; init; } - public TimeOnly? PeakShape { get; init; } + public TimeOnly? PeakShape { get; init; } #else - public DateTimeOffset? LastBattleDate { get; set; } + public DateTimeOffset? LastBattleDate { get; set; } #endif - public Henchman Henchman { get; set; } + public Henchman Henchman { get; set; } - public IEnumerable Powers { get; set; } = Enumerable.Empty(); + public IEnumerable Powers { get; set; } = Enumerable.Empty(); - public int Age { get; set; } + public int Age { get; set; } - public IEnumerable Acolytes { get; set; } = Enumerable.Empty(); + public IEnumerable Acolytes { get; set; } = Enumerable.Empty(); - public IEnumerable Weapons { get; set; } = Enumerable.Empty(); - } + public IEnumerable Weapons { get; set; } = Enumerable.Empty(); } diff --git a/test/DataFilters.TestObjects/Weapon.cs b/test/DataFilters.TestObjects/Weapon.cs index 36004486..b013222d 100644 --- a/test/DataFilters.TestObjects/Weapon.cs +++ b/test/DataFilters.TestObjects/Weapon.cs @@ -1,9 +1,8 @@ -namespace DataFilters.TestObjects +namespace DataFilters.TestObjects; + +public class Weapon { - public class Weapon - { - public string Name { get; set; } + public string Name { get; set; } - public int Level { get; set; } - } + public int Level { get; set; } } diff --git a/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs b/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs index c3232614..dc8da7b8 100644 --- a/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs +++ b/test/DataFilters.UnitTests/Converters/DataFilterConvertersTests.cs @@ -1,261 +1,260 @@ #if NETCOREAPP2_1 using static Newtonsoft.Json.JsonConvert; #endif -namespace DataFilters.UnitTests.Converters -{ - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Linq.Expressions; - using FluentAssertions; - using Newtonsoft.Json.Linq; - using Newtonsoft.Json.Schema; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - using static DataFilters.FilterLogic; - using static DataFilters.FilterOperator; +namespace DataFilters.UnitTests.Converters; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Linq.Expressions; +using FluentAssertions; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; +using static DataFilters.FilterLogic; +using static DataFilters.FilterOperator; - [UnitTest] - [Feature("Converters")] - public class DataFilterConverterTests(ITestOutputHelper outputHelper) +[UnitTest] +[Feature("Converters")] +public class DataFilterConverterTests(ITestOutputHelper outputHelper) +{ + private static IImmutableDictionary Operators => new Dictionary { - private static IImmutableDictionary Operators => new Dictionary - { - ["eq"] = EqualTo, - ["neq"] = NotEqualTo, - ["lt"] = FilterOperator.LessThan, - ["gt"] = GreaterThan, - ["lte"] = LessThanOrEqualTo, - ["gte"] = GreaterThanOrEqual, - ["contains"] = Contains, - ["isnull"] = IsNull, - ["isnotnull"] = IsNotNull, - ["isnotempty"] = IsNotEmpty, - ["isempty"] = IsEmpty - }.ToImmutableDictionary(); + ["eq"] = EqualTo, + ["neq"] = NotEqualTo, + ["lt"] = FilterOperator.LessThan, + ["gt"] = GreaterThan, + ["lte"] = LessThanOrEqualTo, + ["gte"] = GreaterThanOrEqual, + ["contains"] = Contains, + ["isnull"] = IsNull, + ["isnotnull"] = IsNotNull, + ["isnotempty"] = IsNotEmpty, + ["isempty"] = IsEmpty + }.ToImmutableDictionary(); - /// - /// Deserialize tests cases - /// - public static IEnumerable SerializeCases + /// + /// Deserialize tests cases + /// + public static IEnumerable SerializeCases + { + get { - get + foreach (KeyValuePair item in Operators.Where(kv => !Filter.UnaryOperators.Any(op => op == kv.Value))) { - foreach (KeyValuePair item in Operators.Where(kv => !Filter.UnaryOperators.Any(op => op == kv.Value))) + yield return new object[] { - yield return new object[] - { - new Filter(field : "Firstname", @operator : item.Value, value : "Bruce"), - (Expression>) ((json) => json != null - && JToken.Parse(json).Type == JTokenType.Object - && JToken.Parse(json).IsValid(Filter.Schema(item.Value)) - && "Firstname".Equals(JToken.Parse(json)[Filter.FieldJsonPropertyName].Value()) - && item.Key.Equals(JToken.Parse(json)[Filter.OperatorJsonPropertyName].Value()) - && "Bruce".Equals(JToken.Parse(json)[Filter.ValueJsonPropertyName].Value())) - }; - } + new Filter(field : "Firstname", @operator : item.Value, value : "Bruce"), + (Expression>) ((json) => json != null + && JToken.Parse(json).Type == JTokenType.Object + && JToken.Parse(json).IsValid(Filter.Schema(item.Value)) + && "Firstname".Equals(JToken.Parse(json)[Filter.FieldJsonPropertyName].Value()) + && item.Key.Equals(JToken.Parse(json)[Filter.OperatorJsonPropertyName].Value()) + && "Bruce".Equals(JToken.Parse(json)[Filter.ValueJsonPropertyName].Value())) + }; + } - foreach (KeyValuePair item in Operators.Where(kv => Filter.UnaryOperators.Any(op => op == kv.Value))) - { - yield return new object[]{ - new Filter(field : "Firstname", @operator : item.Value), - (Expression>) ((json) => json != null - && JToken.Parse(json).Type == JTokenType.Object - && JObject.Parse(json).Properties().Exactly(2) - && "Firstname".Equals(JObject.Parse(json)[Filter.FieldJsonPropertyName].Value()) - && item.Key.Equals(JObject.Parse(json)[Filter.OperatorJsonPropertyName].Value())) - }; - } + foreach (KeyValuePair item in Operators.Where(kv => Filter.UnaryOperators.Any(op => op == kv.Value))) + { + yield return new object[]{ + new Filter(field : "Firstname", @operator : item.Value), + (Expression>) ((json) => json != null + && JToken.Parse(json).Type == JTokenType.Object + && JObject.Parse(json).Properties().Exactly(2) + && "Firstname".Equals(JObject.Parse(json)[Filter.FieldJsonPropertyName].Value()) + && item.Key.Equals(JObject.Parse(json)[Filter.OperatorJsonPropertyName].Value())) + }; } } + } - /// - /// Deserialize tests cases - /// - public static IEnumerable DeserializeCases + /// + /// Deserialize tests cases + /// + public static IEnumerable DeserializeCases + { + get { - get + foreach ((string key, FilterOperator value) in Operators.Where(op => op.Value != IsNull + && op.Value != IsNotNull + && op.Value != IsEmpty + && op.Value != IsNotEmpty)) { - foreach ((string key, FilterOperator value) in Operators.Where(op => op.Value != IsNull - && op.Value != IsNotNull - && op.Value != IsEmpty - && op.Value != IsNotEmpty)) - { - yield return new object[] - { - $@"{{""field"" :""Firstname"", ""op"":""{key}"", ""value"" : ""Bruce""}}", - typeof(Filter), - (Expression>) ((result) => new Filter("Firstname", value, "Bruce").Equals(result)) - }; - } - yield return new object[] { - @"{""field"" :""Firstname"", ""op"":""isnull""}", + $@"{{""field"" :""Firstname"", ""op"":""{key}"", ""value"" : ""Bruce""}}", typeof(Filter), - (Expression>) ((result) => new Filter("Firstname", IsNull, null).Equals(result)) + (Expression>) ((result) => new Filter("Firstname", value, "Bruce").Equals(result)) }; + } - yield return new object[] - { - @"{""field"" :""Firstname"", ""op"":""isnull"", ""value"" : 6}", - typeof(Filter), - (Expression>) ((result) => new Filter("Firstname", IsNull, null).Equals(result)) - }; + yield return new object[] + { + @"{""field"" :""Firstname"", ""op"":""isnull""}", + typeof(Filter), + (Expression>) ((result) => new Filter("Firstname", IsNull, null).Equals(result)) + }; - yield return new object[] - { - @"{""field"" :""Firstname"", ""op"":""isnotnull"", ""value"" : 6}", - typeof(Filter), - (Expression>) ((result) => new Filter("Firstname", IsNotNull, null).Equals(result)) - }; + yield return new object[] + { + @"{""field"" :""Firstname"", ""op"":""isnull"", ""value"" : 6}", + typeof(Filter), + (Expression>) ((result) => new Filter("Firstname", IsNull, null).Equals(result)) + }; - yield return new object[] - { - @$"{{""field"" :""integer"", ""op"":""eq"", ""value"" : {long.MaxValue}}}", - typeof(Filter), - (Expression>) ((result) => new Filter("integer", EqualTo, long.MaxValue).Equals(result)) - }; + yield return new object[] + { + @"{""field"" :""Firstname"", ""op"":""isnotnull"", ""value"" : 6}", + typeof(Filter), + (Expression>) ((result) => new Filter("Firstname", IsNotNull, null).Equals(result)) + }; - yield return new object[] - { - @"{""field"" :""bool"", ""op"":""eq"", ""value"" : true }", - typeof(Filter), - (Expression>) ((result) => new Filter("bool", EqualTo, true ).Equals(result)) - }; + yield return new object[] + { + @$"{{""field"" :""integer"", ""op"":""eq"", ""value"" : {long.MaxValue}}}", + typeof(Filter), + (Expression>) ((result) => new Filter("integer", EqualTo, long.MaxValue).Equals(result)) + }; + yield return new object[] + { + @"{""field"" :""bool"", ""op"":""eq"", ""value"" : true }", + typeof(Filter), + (Expression>) ((result) => new Filter("bool", EqualTo, true ).Equals(result)) + }; + + yield return new object[] + { + @"{""field"" :""EqualNull"", ""op"":""eq"", ""value"" : null }", + typeof(Filter), + (Expression>) ((result) => new Filter("EqualNull", IsNull, null).Equals(result)) + }; + + yield return new object[] + { + @"{""field"" :""NotEqualNull"", ""op"":""neq"", ""value"" : null }", + typeof(Filter), + (Expression>) ((result) => new Filter("NotEqualNull", IsNotNull, null).Equals(result)) + }; + + { + Guid uuid = Guid.NewGuid(); yield return new object[] { - @"{""field"" :""EqualNull"", ""op"":""eq"", ""value"" : null }", + $@"{{""field"" :""guid"", ""op"":""eq"", ""value"" : ""{uuid}"" }}", typeof(Filter), - (Expression>) ((result) => new Filter("EqualNull", IsNull, null).Equals(result)) + (Expression>) ((result) => new Filter("guid", EqualTo, uuid.ToString() ).Equals(result)) }; + } + { yield return new object[] { - @"{""field"" :""NotEqualNull"", ""op"":""neq"", ""value"" : null }", + @"{""field"" :""date"", ""op"":""eq"", ""value"" : ""2019-8-23T00:00"" }", typeof(Filter), - (Expression>) ((result) => new Filter("NotEqualNull", IsNotNull, null).Equals(result)) + (Expression>) ((result) => new Filter("date", EqualTo, "2019-8-23T00:00" ).Equals(result)) }; + } - { - Guid uuid = Guid.NewGuid(); - yield return new object[] - { - $@"{{""field"" :""guid"", ""op"":""eq"", ""value"" : ""{uuid}"" }}", - typeof(Filter), - (Expression>) ((result) => new Filter("guid", EqualTo, uuid.ToString() ).Equals(result)) - }; - } - - { - yield return new object[] + yield return new object[] + { + "{" + + @"""logic"": ""or""," + + @"""filters"": [" + + @"{ ""field"" :""Firstname"", ""op"":""eq"", ""value"":""Bruce""}," + + @"{ ""field"" :""Firstname"", ""op"":""eq"", ""value"":""Clark""}" + + "]" + + "}", + typeof(MultiFilter), + (Expression>) ((result) => + new MultiFilter { - @"{""field"" :""date"", ""op"":""eq"", ""value"" : ""2019-8-23T00:00"" }", - typeof(Filter), - (Expression>) ((result) => new Filter("date", EqualTo, "2019-8-23T00:00" ).Equals(result)) - }; - } - - yield return new object[] - { - "{" + - @"""logic"": ""or""," + - @"""filters"": [" + - @"{ ""field"" :""Firstname"", ""op"":""eq"", ""value"":""Bruce""}," + - @"{ ""field"" :""Firstname"", ""op"":""eq"", ""value"":""Clark""}" + - "]" + - "}", - typeof(MultiFilter), - (Expression>) ((result) => - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", EqualTo, "Bruce"), - new Filter("Firstname", EqualTo, "Clark") - } - }.Equals(result) - ) - }; + new Filter("Firstname", EqualTo, "Bruce"), + new Filter("Firstname", EqualTo, "Clark") + } + }.Equals(result) + ) + }; - yield return new object[] - { - "{" + - @"""logic"": ""and""," + - @"""filters"": [" + - @"{ ""field"" :""HasSuperpower"", ""op"":""eq"", ""value"": false}," + - "{" + - @"""logic"": ""or""," + - @"""filters"": [" + - @"{ ""field"" :""Lastname"", ""op"":""eq"", ""value"":""Wayne""}," + - @"{ ""field"" :""Lastname"", ""op"":""eq"", ""value"":""Kent""}" + - "]" + - "}" + - "]" + - "}", - typeof(MultiFilter), - (Expression>) ((result) => - new MultiFilter + yield return new object[] + { + "{" + + @"""logic"": ""and""," + + @"""filters"": [" + + @"{ ""field"" :""HasSuperpower"", ""op"":""eq"", ""value"": false}," + + "{" + + @"""logic"": ""or""," + + @"""filters"": [" + + @"{ ""field"" :""Lastname"", ""op"":""eq"", ""value"":""Wayne""}," + + @"{ ""field"" :""Lastname"", ""op"":""eq"", ""value"":""Kent""}" + + "]" + + "}" + + "]" + + "}", + typeof(MultiFilter), + (Expression>) ((result) => + new MultiFilter + { + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] + new Filter("HasSuperpower", EqualTo, false), + new MultiFilter { - new Filter("HasSuperpower", EqualTo, false), - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Lastname", EqualTo, "Wayne"), - new Filter("Lastname", EqualTo, "Kent") - } + new Filter("Lastname", EqualTo, "Wayne"), + new Filter("Lastname", EqualTo, "Kent") } } - }.Equals(result) - ) - }; - } + } + }.Equals(result) + ) + }; } + } - /// - /// Tests the deserialization of the to an instance of the specified
- /// The deserialization is done using JsonConvert.DeserializeObject - ///
- /// json to deserialize - /// type the json string will be deserialize into - /// Expectation that result of the deserialization should match - [Theory] - [MemberData(nameof(DeserializeCases))] - public void Deserialize(string json, Type targetType, Expression> expectation) - { - outputHelper.WriteLine($"{nameof(json)} : {json}"); + /// + /// Tests the deserialization of the to an instance of the specified
+ /// The deserialization is done using JsonConvert.DeserializeObject + ///
+ /// json to deserialize + /// type the json string will be deserialize into + /// Expectation that result of the deserialization should match + [Theory] + [MemberData(nameof(DeserializeCases))] + public void Deserialize(string json, Type targetType, Expression> expectation) + { + outputHelper.WriteLine($"{nameof(json)} : {json}"); - object result = System.Text.Json.JsonSerializer.Deserialize(json, targetType); + object result = System.Text.Json.JsonSerializer.Deserialize(json, targetType); - result.Should() - .Match(expectation); - } + result.Should() + .Match(expectation); + } - /// - /// Tests the serialization of the to its string representation - /// The deserialization is done using JsonConvert.DeserializeObject - /// - /// json to deserialize - /// Expectation that result of the deserialization should match - [Theory] - [MemberData(nameof(SerializeCases))] - public void Serialize(IFilter filter, Expression> expectation) - { - outputHelper.WriteLine($"Serializing {filter}"); + /// + /// Tests the serialization of the to its string representation + /// The deserialization is done using JsonConvert.DeserializeObject + /// + /// json to deserialize + /// Expectation that result of the deserialization should match + [Theory] + [MemberData(nameof(SerializeCases))] + public void Serialize(IFilter filter, Expression> expectation) + { + outputHelper.WriteLine($"Serializing {filter}"); - string result = System.Text.Json.JsonSerializer.Serialize(filter, filter.GetType()); + string result = System.Text.Json.JsonSerializer.Serialize(filter, filter.GetType()); - result.Should() - .Match(expectation); - } + result.Should() + .Match(expectation); } } diff --git a/test/DataFilters.UnitTests/FilterExtensionsTests.cs b/test/DataFilters.UnitTests/FilterExtensionsTests.cs index c65bd937..fe56bdcb 100644 --- a/test/DataFilters.UnitTests/FilterExtensionsTests.cs +++ b/test/DataFilters.UnitTests/FilterExtensionsTests.cs @@ -1,561 +1,560 @@ -namespace DataFilters.UnitTests +namespace DataFilters.UnitTests; + +using System; +using System.Collections.Generic; +using DataFilters; +using DataFilters.Casing; +using DataFilters.TestObjects; +using FluentAssertions; +using FluentAssertions.Common; +using FluentAssertions.Extensions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; +using static DataFilters.FilterLogic; +using static DataFilters.FilterOperator; + +[UnitTest] +public class FilterExtensionsTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters; - using DataFilters.Casing; - using DataFilters.TestObjects; - using FluentAssertions; - using FluentAssertions.Common; - using FluentAssertions.Extensions; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - using static DataFilters.FilterLogic; - using static DataFilters.FilterOperator; - - [UnitTest] - public class FilterExtensionsTests(ITestOutputHelper outputHelper) + public static IEnumerable ToFilterCases { - public static IEnumerable ToFilterCases + get { - get + yield return new object[] { - yield return new object[] - { - $"{nameof(Person.Firstname)}=Hal&{nameof(Person.Lastname)}=Jordan", - new MultiFilter{ - Logic = And, - Filters = new IFilter[] - { - new Filter(nameof(Person.Firstname), EqualTo, "Hal"), - new Filter(nameof(Person.Lastname), EqualTo, "Jordan") - } + $"{nameof(Person.Firstname)}=Hal&{nameof(Person.Lastname)}=Jordan", + new MultiFilter{ + Logic = And, + Filters = new IFilter[] + { + new Filter(nameof(Person.Firstname), EqualTo, "Hal"), + new Filter(nameof(Person.Lastname), EqualTo, "Jordan") } - }; + } + }; - yield return new object[] - { - "", - new Filter(field: null, @operator: EqualTo) - }; + yield return new object[] + { + "", + new Filter(field: null, @operator: EqualTo) + }; - yield return new object[] - { - "", - Filter.True - }; + yield return new object[] + { + "", + Filter.True + }; - yield return new object[] - { - $"{nameof(Person.Firstname)}=!*wayne", - new Filter(field: nameof(Person.Firstname), @operator: NotEndsWith, value: "wayne") - }; + yield return new object[] + { + $"{nameof(Person.Firstname)}=!*wayne", + new Filter(field: nameof(Person.Firstname), @operator: NotEndsWith, value: "wayne") + }; - yield return new object[] + yield return new object[] + { + $"{nameof(Person.Firstname)}=Vandal|Gengis", + new MultiFilter { - $"{nameof(Person.Firstname)}=Vandal|Gengis", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter(field: nameof(Person.Firstname), @operator: EqualTo, value: "Vandal"), - new Filter(field: nameof(Person.Firstname), @operator: EqualTo, value: "Gengis") - } + new Filter(field: nameof(Person.Firstname), @operator: EqualTo, value: "Vandal"), + new Filter(field: nameof(Person.Firstname), @operator: EqualTo, value: "Gengis") } - }; + } + }; - yield return new object[] + yield return new object[] + { + $"{nameof(Person.Firstname)}=(V*|G*),(*l|*s)", + new MultiFilter { - $"{nameof(Person.Firstname)}=(V*|G*),(*l|*s)", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] + new MultiFilter { - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter(field: nameof(Person.Firstname), @operator: StartsWith, value: "V"), - new Filter(field: nameof(Person.Firstname), @operator: StartsWith, value: "G") - } - }, - new MultiFilter + new Filter(field: nameof(Person.Firstname), @operator: StartsWith, value: "V"), + new Filter(field: nameof(Person.Firstname), @operator: StartsWith, value: "G") + } + }, + new MultiFilter + { + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter(field: nameof(Person.Firstname), @operator: EndsWith, value: "l"), - new Filter(field: nameof(Person.Firstname), @operator: EndsWith, value: "s") - } + new Filter(field: nameof(Person.Firstname), @operator: EndsWith, value: "l"), + new Filter(field: nameof(Person.Firstname), @operator: EndsWith, value: "s") } } } - }; + } + }; - yield return new object[] - { - string.Empty, - Filter.True - }; + yield return new object[] + { + string.Empty, + Filter.True + }; - yield return new object[] - { - "Firstname=Bruce", - new Filter("Firstname", EqualTo, "Bruce") - }; + yield return new object[] + { + "Firstname=Bruce", + new Filter("Firstname", EqualTo, "Bruce") + }; - yield return new object[] - { - "Firstname=!Bruce", - new Filter("Firstname", NotEqualTo, "Bruce") - }; + yield return new object[] + { + "Firstname=!Bruce", + new Filter("Firstname", NotEqualTo, "Bruce") + }; - yield return new object[] - { - "Firstname=!!Bruce", - new Filter("Firstname", EqualTo, "Bruce") - }; + yield return new object[] + { + "Firstname=!!Bruce", + new Filter("Firstname", EqualTo, "Bruce") + }; - yield return new object[] + yield return new object[] + { + "Firstname=Bruce|Dick", + new MultiFilter { - "Firstname=Bruce|Dick", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", EqualTo, "Bruce"), - new Filter("Firstname", EqualTo, "Dick") - } + new Filter("Firstname", EqualTo, "Bruce"), + new Filter("Firstname", EqualTo, "Dick") } - }; + } + }; - yield return new object[] - { - "Firstname=Bru*", - new Filter("Firstname", StartsWith, "Bru") - }; + yield return new object[] + { + "Firstname=Bru*", + new Filter("Firstname", StartsWith, "Bru") + }; - yield return new object[] - { - "Firstname=*Bru", - new Filter("Firstname", EndsWith, "Bru") - }; + yield return new object[] + { + "Firstname=*Bru", + new Filter("Firstname", EndsWith, "Bru") + }; - yield return new object[] - { - "Height=[100 TO *[", - new Filter("Height", GreaterThanOrEqual, 100) - }; + yield return new object[] + { + "Height=[100 TO *[", + new Filter("Height", GreaterThanOrEqual, 100) + }; - yield return new object[] + yield return new object[] + { + "Height=[100 TO 200]", + new MultiFilter { - "Height=[100 TO 200]", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Height", GreaterThanOrEqual, 100), - new Filter("Height", LessThanOrEqualTo, 200) - } + new Filter("Height", GreaterThanOrEqual, 100), + new Filter("Height", LessThanOrEqualTo, 200) } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Height=]100 TO 200[", + new MultiFilter { - "Height=]100 TO 200[", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Height", GreaterThan, 100), - new Filter("Height", FilterOperator.LessThan, 200) - } + new Filter("Height", GreaterThan, 100), + new Filter("Height", FilterOperator.LessThan, 200) } - }; + } + }; - yield return new object[] - { - "Height=]* TO 200]", - new Filter("Height", LessThanOrEqualTo, 200) - }; + yield return new object[] + { + "Height=]* TO 200]", + new Filter("Height", LessThanOrEqualTo, 200) + }; - yield return new object[] + yield return new object[] + { + "Nickname=Bat*,*man", + new MultiFilter { - "Nickname=Bat*,*man", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Nickname", StartsWith, "Bat"), - new Filter("Nickname", EndsWith, "man"), - } + new Filter("Nickname", StartsWith, "Bat"), + new Filter("Nickname", EndsWith, "man"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Nickname=Bat*man", + new MultiFilter { - "Nickname=Bat*man", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Nickname", StartsWith, "Bat"), - new Filter("Nickname", EndsWith, "man"), - } + new Filter("Nickname", StartsWith, "Bat"), + new Filter("Nickname", EndsWith, "man"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Nickname=Bat*,*man*", + new MultiFilter { - "Nickname=Bat*,*man*", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Nickname", StartsWith, "Bat"), - new Filter("Nickname", Contains, "man"), - } + new Filter("Nickname", StartsWith, "Bat"), + new Filter("Nickname", Contains, "man"), } - }; + } + }; - yield return new object[] - { - "Firstname=!Bru*", - new Filter("Firstname", NotStartsWith, "Bru") - }; + yield return new object[] + { + "Firstname=!Bru*", + new Filter("Firstname", NotStartsWith, "Bru") + }; - yield return new object[] + yield return new object[] + { + "Nickname=!(Bat*man)", + new MultiFilter { - "Nickname=!(Bat*man)", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Nickname", NotStartsWith, "Bat"), - new Filter("Nickname", NotEndsWith, "man"), - } + new Filter("Nickname", NotStartsWith, "Bat"), + new Filter("Nickname", NotEndsWith, "man"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Firstname=Bru*&Lastname=Wayne", + new MultiFilter { - "Firstname=Bru*&Lastname=Wayne", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Firstname", StartsWith, "Bru"), - new Filter("Lastname", EqualTo, "Wayne"), - } + new Filter("Firstname", StartsWith, "Bru"), + new Filter("Lastname", EqualTo, "Wayne"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Firstname=Br[uU]ce", + new MultiFilter { - "Firstname=Br[uU]ce", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", EqualTo, "Bruce"), - new Filter("Firstname", EqualTo, "BrUce"), - } + new Filter("Firstname", EqualTo, "Bruce"), + new Filter("Firstname", EqualTo, "BrUce"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Firstname=*Br[uU]", + new MultiFilter { - "Firstname=*Br[uU]", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", EndsWith, "Bru"), - new Filter("Firstname", EndsWith, "BrU"), - } + new Filter("Firstname", EndsWith, "Bru"), + new Filter("Firstname", EndsWith, "BrU"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Firstname=!(Bruce|Wayne)", + new MultiFilter { - "Firstname=!(Bruce|Wayne)", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("Firstname", NotEqualTo, "Bruce"), - new Filter("Firstname", NotEqualTo, "Wayne") - } + new Filter("Firstname", NotEqualTo, "Bruce"), + new Filter("Firstname", NotEqualTo, "Wayne") } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Firstname=(Bruce|Wayne)", + new MultiFilter { - "Firstname=(Bruce|Wayne)", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", EqualTo, "Bruce"), - new Filter("Firstname", EqualTo, "Wayne") - } + new Filter("Firstname", EqualTo, "Bruce"), + new Filter("Firstname", EqualTo, "Wayne") } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Firstname=(Bat*|Sup*)|(*man|*er)", + new MultiFilter { - "Firstname=(Bat*|Sup*)|(*man|*er)", - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] + new MultiFilter { - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", StartsWith, "Bat"), - new Filter("Firstname", StartsWith, "Sup"), - } - }, - new MultiFilter + new Filter("Firstname", StartsWith, "Bat"), + new Filter("Firstname", StartsWith, "Sup"), + } + }, + new MultiFilter + { + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Firstname", EndsWith, "man"), - new Filter("Firstname", EndsWith, "er"), - } - }, - } + new Filter("Firstname", EndsWith, "man"), + new Filter("Firstname", EndsWith, "er"), + } + }, } - }; + } + }; - yield return new object[] - { - @"BattleCry=*\!", - new Filter(field: "BattleCry", @operator: EndsWith, "!") - }; + yield return new object[] + { + @"BattleCry=*\!", + new Filter(field: "BattleCry", @operator: EndsWith, "!") + }; - yield return new object[] - { - @"BattleCry=\**", - new Filter(field: "BattleCry", @operator: StartsWith, "*") - }; + yield return new object[] + { + @"BattleCry=\**", + new Filter(field: "BattleCry", @operator: StartsWith, "*") + }; - yield return new object[] + yield return new object[] + { + "BirthDate=]2016-10-18 TO 2016-10-25[", + new MultiFilter { - "BirthDate=]2016-10-18 TO 2016-10-25[", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { #if NET6_0_OR_GREATER new Filter(nameof(SuperHero.BirthDate), GreaterThan, DateOnly.FromDateTime(18.October(2016))), - new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, DateOnly.FromDateTime(25.October(2016))) + new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, DateOnly.FromDateTime(25.October(2016))) #else - new Filter(nameof(SuperHero.BirthDate), GreaterThan, 18.October(2016)), - new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, 25.October(2016)) + new Filter(nameof(SuperHero.BirthDate), GreaterThan, 18.October(2016)), + new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, 25.October(2016)) #endif - } } - }; + } + }; - yield return new object[] + yield return new object[] + { + "BirthDate=]2016-10-18T18:00:00 TO 2016-10-25T19:00:00[", + new MultiFilter { - "BirthDate=]2016-10-18T18:00:00 TO 2016-10-25T19:00:00[", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { #if NET6_0_OR_GREATER - new Filter(nameof(SuperHero.BirthDate), GreaterThan, DateOnly.FromDateTime(18.October(2016))), - new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, DateOnly.FromDateTime(25.October(2016))) + new Filter(nameof(SuperHero.BirthDate), GreaterThan, DateOnly.FromDateTime(18.October(2016))), + new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, DateOnly.FromDateTime(25.October(2016))) #else - new Filter(nameof(SuperHero.BirthDate), GreaterThan, 18.October(2016).Add(18.Hours())), - new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, 25.October(2016).Add(19.Hours())) + new Filter(nameof(SuperHero.BirthDate), GreaterThan, 18.October(2016).Add(18.Hours())), + new Filter(nameof(SuperHero.BirthDate), FilterOperator.LessThan, 25.October(2016).Add(19.Hours())) #endif - } } - }; + } + }; - yield return new object[] + yield return new object[] + { + "DateTimeWithOffset=]2016-10-18T18:00:00Z TO 2016-10-18T23:00:00-02:00[", + new MultiFilter { - "DateTimeWithOffset=]2016-10-18T18:00:00Z TO 2016-10-18T23:00:00-02:00[", - new MultiFilter + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] - { - new Filter("DateTimeWithOffset", GreaterThan, 18.October(2016).Add(18.Hours()).ToDateTimeOffset()), - new Filter("DateTimeWithOffset", FilterOperator.LessThan, 18.October(2016).Add(23.Hours()).ToDateTimeOffset(-2.Hours())) - } + new Filter("DateTimeWithOffset", GreaterThan, 18.October(2016).Add(18.Hours()).ToDateTimeOffset()), + new Filter("DateTimeWithOffset", FilterOperator.LessThan, 18.October(2016).Add(23.Hours()).ToDateTimeOffset(-2.Hours())) } - }; + } + }; - yield return new object[] + yield return new object[] + { + "Nickname={Bat|Sup|Wonder}*,*m[ae]n", + new MultiFilter { - "Nickname={Bat|Sup|Wonder}*,*m[ae]n", - new MultiFilter + Logic = And, + Filters = new[] { - Logic = And, - Filters = new[] + new MultiFilter { - new MultiFilter + Logic = Or, + Filters = new[] { - Logic = Or, - Filters = new[] - { - new Filter(nameof(SuperHero.Nickname), StartsWith, "Bat"), - new Filter(nameof(SuperHero.Nickname), StartsWith, "Sup"), - new Filter(nameof(SuperHero.Nickname), StartsWith, "Wonder") - } - }, - new MultiFilter + new Filter(nameof(SuperHero.Nickname), StartsWith, "Bat"), + new Filter(nameof(SuperHero.Nickname), StartsWith, "Sup"), + new Filter(nameof(SuperHero.Nickname), StartsWith, "Wonder") + } + }, + new MultiFilter + { + Logic = Or, + Filters = new[] { - Logic = Or, - Filters = new[] - { - new Filter(nameof(SuperHero.Nickname), EndsWith, "man"), - new Filter(nameof(SuperHero.Nickname), EndsWith, "men") - } + new Filter(nameof(SuperHero.Nickname), EndsWith, "man"), + new Filter(nameof(SuperHero.Nickname), EndsWith, "men") } } } - }; - } + } + }; } + } - [Theory] - [MemberData(nameof(ToFilterCases))] - public void ToFilter(string filter, IFilter expected) - { - outputHelper.WriteLine($"{nameof(filter)} : '{filter}'"); + [Theory] + [MemberData(nameof(ToFilterCases))] + public void ToFilter(string filter, IFilter expected) + { + outputHelper.WriteLine($"{nameof(filter)} : '{filter}'"); - // Act - IFilter actual = filter.ToFilter(); + // Act + IFilter actual = filter.ToFilter(); - // Assert - actual.Should() - .NotBeSameAs(expected).And - .Be(expected); - } + // Assert + actual.Should() + .NotBeSameAs(expected).And + .Be(expected); + } - [Fact] - public void Given_null_ToFilter_should_throw_ArgumentNullException() - { - // Act - Action action = () => ((string)null).ToFilter(); - - // Assert - action.Should() - .Throw().Which - .ParamName.Should() - .NotBeNullOrWhiteSpace(); - } + [Fact] + public void Given_null_ToFilter_should_throw_ArgumentNullException() + { + // Act + Action action = () => ((string)null).ToFilter(); + + // Assert + action.Should() + .Throw().Which + .ParamName.Should() + .NotBeNullOrWhiteSpace(); + } - public static IEnumerable ToFilterWithPropertyNameResolutionStrategyCases + public static IEnumerable ToFilterWithPropertyNameResolutionStrategyCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "SnakeCaseProperty=10", - PropertyNameResolutionStrategy.SnakeCase, - new Filter("snake_case_property", EqualTo, "10") - }; + "SnakeCaseProperty=10", + PropertyNameResolutionStrategy.SnakeCase, + new Filter("snake_case_property", EqualTo, "10") + }; - yield return new object[] - { - "pascal_case_property=10", - PropertyNameResolutionStrategy.PascalCase, - new Filter("PascalCaseProperty", EqualTo, "10") - }; - } + yield return new object[] + { + "pascal_case_property=10", + PropertyNameResolutionStrategy.PascalCase, + new Filter("PascalCaseProperty", EqualTo, "10") + }; } + } - [Theory] - [MemberData(nameof(ToFilterWithPropertyNameResolutionStrategyCases))] - public void Given_input_and_casing_resolution_strategy_ToFilter_should_create_corresponding_filter(string filter, PropertyNameResolutionStrategy propertyNameResolutionStrategy, IFilter expected) - { - // Act - IFilter actual = filter.ToFilter(propertyNameResolutionStrategy); + [Theory] + [MemberData(nameof(ToFilterWithPropertyNameResolutionStrategyCases))] + public void Given_input_and_casing_resolution_strategy_ToFilter_should_create_corresponding_filter(string filter, PropertyNameResolutionStrategy propertyNameResolutionStrategy, IFilter expected) + { + // Act + IFilter actual = filter.ToFilter(propertyNameResolutionStrategy); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - public static IEnumerable ToFilterWithFilterOptionsCases + public static IEnumerable ToFilterWithFilterOptionsCases + { + get { - get - { - FilterLogic[] logics = [And, Or]; + FilterLogic[] logics = [And, Or]; - foreach (FilterLogic logic in logics) + foreach (FilterLogic logic in logics) + { + yield return new object[] { - yield return new object[] + "snake_case_property=10&PascalCaseProperty=value", + new FilterOptions{ Logic = logic }, + new MultiFilter { - "snake_case_property=10&PascalCaseProperty=value", - new FilterOptions{ Logic = logic }, - new MultiFilter + Logic = logic, + Filters = new IFilter[] { - Logic = logic, - Filters = new IFilter[] - { - new Filter("snake_case_property", EqualTo, "10"), - new Filter("PascalCaseProperty", EqualTo, "value"), - } + new Filter("snake_case_property", EqualTo, "10"), + new Filter("PascalCaseProperty", EqualTo, "value"), } - }; + } + }; - yield return new object[] + yield return new object[] + { + "snake_case_property=10&PascalCaseProperty=value", + new FilterOptions{ Logic = logic }, + new MultiFilter { - "snake_case_property=10&PascalCaseProperty=value", - new FilterOptions{ Logic = logic }, - new MultiFilter + Logic = logic, + Filters = new IFilter[] { - Logic = logic, - Filters = new IFilter[] - { - new Filter("snake_case_property", EqualTo, "10"), - new Filter("PascalCaseProperty", EqualTo, "value") - } + new Filter("snake_case_property", EqualTo, "10"), + new Filter("PascalCaseProperty", EqualTo, "value") } - }; - } + } + }; } } + } - [Theory] - [MemberData(nameof(ToFilterWithFilterOptionsCases))] - public void Given_input_and_FilterOptions_ToFilter_should_create_corresponding_filter(string filter, FilterOptions options, IFilter expected) - { - // Act - IFilter actual = filter.ToFilter(options); + [Theory] + [MemberData(nameof(ToFilterWithFilterOptionsCases))] + public void Given_input_and_FilterOptions_ToFilter_should_create_corresponding_filter(string filter, FilterOptions options, IFilter expected) + { + // Act + IFilter actual = filter.ToFilter(options); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); } } \ No newline at end of file diff --git a/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs b/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs deleted file mode 100644 index 43847826..00000000 --- a/test/DataFilters.UnitTests/FilterServiceOptionsTests.cs +++ /dev/null @@ -1,69 +0,0 @@ -namespace DataFilters.UnitTests; - -using System; -using DataFilters.Casing; -using FluentAssertions; -using FsCheck; -using FsCheck.Xunit; -using Xunit.Categories; - -[UnitTest] -public class FilterServiceOptionsTests -{ - [Property] - public void Given_no_parameters_Cosntructor_should_set_properties_with_default_values() - { - // Act - FilterServiceOptions _sut = new(); - - // Assert - _sut.MaxCacheSize.Should().Be(FilterServiceOptions.DefaultCacheSize); - _sut.PropertyNameResolutionStrategy.Should().Be(PropertyNameResolutionStrategy.Default); - } - - [Property(Arbitrary = [typeof(Generators)])] - public void Given_positive_integer_value_And_strategy_Constructor_should_set_properties(PositiveInt input, PropertyNameResolutionStrategy propertyNameResolutionStrategy) - { - // Act - FilterServiceOptions _sut = new() { MaxCacheSize = input.Item, PropertyNameResolutionStrategy = propertyNameResolutionStrategy }; - - // Assert - _sut.MaxCacheSize.Should().Be(input.Item); - _sut.PropertyNameResolutionStrategy.Should().Be(propertyNameResolutionStrategy); - } - - [Property(Arbitrary = [typeof(Generators)])] - public void Given_options_Validate_should_check_all_values_are_valid(int maxCacheSize, PropertyNameResolutionStrategy strategy) - { - // Arrange - Lazy lazy = new(() => - { - FilterServiceOptions _sut = new() - { - PropertyNameResolutionStrategy = strategy, - MaxCacheSize = maxCacheSize - }; - _sut.Validate(); - - return _sut; - }); - - // Act - Action action = () => - { - FilterServiceOptions _sut = new() - { - PropertyNameResolutionStrategy = strategy, - MaxCacheSize = maxCacheSize - }; - _sut.Validate(); - }; - - // Assert - object _ = maxCacheSize switch - { - < 1 => action.Should().Throw(), - _ => action.Should().NotThrow() - }; - } -} \ No newline at end of file diff --git a/test/DataFilters.UnitTests/FilterServiceTests.cs b/test/DataFilters.UnitTests/FilterServiceTests.cs deleted file mode 100644 index 7a7ab2e8..00000000 --- a/test/DataFilters.UnitTests/FilterServiceTests.cs +++ /dev/null @@ -1,52 +0,0 @@ -#if NET5_0_OR_GREATER -namespace DataFilters.UnitTests; - -using System; -using System.Collections.Generic; - -using DataFilters.TestObjects; - -using FluentAssertions; - -using Xunit; - -public class FilterServiceTests -{ - private readonly FilterService _sut = new FilterService(new FilterServiceOptions()); - - public static IEnumerable ComputeCases - { - get - { - yield return new object[] - { - "Nickname=Bat", - new Filter("Nickname", @operator: FilterOperator.EqualTo, "Bat") - }; - } - } - - [Theory] - [MemberData(nameof(ComputeCases))] - public void Given_input_Compute_should_build_expected_Filter(string input, IFilter expected) - { - // Act - IFilter actual = _sut.Compute(input); - - // Assert - actual.Should() - .Be(expected); - } - - [Fact] - public void Given_options_is_null_Constructor_should_throw_ArgumentNullException() - { - // Act - Action ctorWhereOptionsIsNull = () => new FilterService(null); - - // Assert - ctorWhereOptionsIsNull.Should() - .ThrowExactly(); - } -} -#endif \ No newline at end of file diff --git a/test/DataFilters.UnitTests/FilterTests.cs b/test/DataFilters.UnitTests/FilterTests.cs index ae857b60..a068f773 100644 --- a/test/DataFilters.UnitTests/FilterTests.cs +++ b/test/DataFilters.UnitTests/FilterTests.cs @@ -1,366 +1,365 @@ -namespace DataFilters.UnitTests +namespace DataFilters.UnitTests; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.Linq; +using System.Linq.Expressions; +using System.Text.RegularExpressions; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; +using static DataFilters.FilterLogic; +using static DataFilters.FilterOperator; + +[UnitTest] +public class FilterTests(ITestOutputHelper output) { - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Linq; - using System.Linq.Expressions; - using System.Text.RegularExpressions; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Newtonsoft.Json.Linq; - using Newtonsoft.Json.Schema; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - using static DataFilters.FilterLogic; - using static DataFilters.FilterOperator; - - [UnitTest] - public class FilterTests(ITestOutputHelper output) + private static readonly IImmutableDictionary _operators = new Dictionary { - private static readonly IImmutableDictionary _operators = new Dictionary - { - ["contains"] = Contains, - ["endswith"] = EndsWith, - ["eq"] = EqualTo, - ["gt"] = GreaterThan, - ["gte"] = GreaterThanOrEqual, - ["isempty"] = IsEmpty, - ["isnotempty"] = IsNotEmpty, - ["isnotnull"] = IsNotNull, - ["isnull"] = IsNull, - ["lt"] = FilterOperator.LessThan, - ["lte"] = LessThanOrEqualTo, - ["neq"] = NotEqualTo, - ["startswith"] = StartsWith - }.ToImmutableDictionary(); - - /// - /// Deserialization of various json representation into - /// - public static IEnumerable FilterDeserializeCases + ["contains"] = Contains, + ["endswith"] = EndsWith, + ["eq"] = EqualTo, + ["gt"] = GreaterThan, + ["gte"] = GreaterThanOrEqual, + ["isempty"] = IsEmpty, + ["isnotempty"] = IsNotEmpty, + ["isnotnull"] = IsNotNull, + ["isnull"] = IsNull, + ["lt"] = FilterOperator.LessThan, + ["lte"] = LessThanOrEqualTo, + ["neq"] = NotEqualTo, + ["startswith"] = StartsWith + }.ToImmutableDictionary(); + + /// + /// Deserialization of various json representation into + /// + public static IEnumerable FilterDeserializeCases + { + get { - get + foreach (KeyValuePair item in _operators) { - foreach (KeyValuePair item in _operators) + yield return new object[] { - yield return new object[] - { - @$"{{ ""field"" : ""Firstname"", ""op"" : ""{item.Key}"", ""Value"" : ""Batman""}}", - (Expression>)(result => result is Filter - && "Firstname".Equals(((Filter) result).Field) - && item.Value.Equals(((Filter) result).Operator) - && "Batman".Equals(((Filter) result).Value) - ) - }; - } + @$"{{ ""field"" : ""Firstname"", ""op"" : ""{item.Key}"", ""Value"" : ""Batman""}}", + (Expression>)(result => result is Filter + && "Firstname".Equals(((Filter) result).Field) + && item.Value.Equals(((Filter) result).Operator) + && "Batman".Equals(((Filter) result).Value) + ) + }; } } + } - public static IEnumerable CompositeFilterToJsonCases + public static IEnumerable CompositeFilterToJsonCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new MultiFilter { - Logic = Or, - Filters = new [] { - new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), - new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") - } - }, - (Expression>)(json => - JObject.Parse(json).Properties().Count() == 2 - - && "or".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) - - && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) - && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) - && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) - && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) - - ) - }; - - yield return new object[] - { - new MultiFilter { - Filters = new [] { - new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), - new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") - } - }, - (Expression>)(json => - JObject.Parse(json).Properties().Count() == 2 - - && "and".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) - - && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) - && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) - && - "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) - && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) - - ) - }; - } + new MultiFilter { + Logic = Or, + Filters = new [] { + new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), + new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") + } + }, + (Expression>)(json => + JObject.Parse(json).Properties().Count() == 2 + + && "or".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) + + && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) + && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) + && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) + && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) + + ) + }; + + yield return new object[] + { + new MultiFilter { + Filters = new [] { + new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), + new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") + } + }, + (Expression>)(json => + JObject.Parse(json).Properties().Count() == 2 + + && "and".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) + + && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) + && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) + && + "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) + && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) + + ) + }; } + } - public static IEnumerable FilterSchemaTestCases + public static IEnumerable FilterSchemaTestCases + { + get { - get + yield return new object[] { - yield return new object[] - { - $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} :'batman' }}", - EqualTo, - true - }; + $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} :'batman' }}", + EqualTo, + true + }; - yield return new object[] - { - $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : null }}", - EqualTo, - false - }; + yield return new object[] + { + $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : null }}", + EqualTo, + false + }; - yield return new object[] - { - $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq' }}", - EqualTo, - false - }; + yield return new object[] + { + $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq' }}", + EqualTo, + false + }; - yield return new object[] - { - $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'contains', {Filter.ValueJsonPropertyName} : 'br' }}", - Contains, - true - }; + yield return new object[] + { + $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'contains', {Filter.ValueJsonPropertyName} : 'br' }}", + Contains, + true + }; - yield return new object[] - { - $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'contains', {Filter.ValueJsonPropertyName} : 6 }}", - Contains, - false - }; + yield return new object[] + { + $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'contains', {Filter.ValueJsonPropertyName} : 6 }}", + Contains, + false + }; - yield return new object[] - { - $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'isnull', {Filter.ValueJsonPropertyName} : 6 }}", - IsNull, - false - }; - } + yield return new object[] + { + $"{{{Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'isnull', {Filter.ValueJsonPropertyName} : 6 }}", + IsNull, + false + }; } + } - /// - /// Serialization of instance of test cases - /// - public static IEnumerable FilterToJsonCases + /// + /// Serialization of instance of test cases + /// + public static IEnumerable FilterToJsonCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new Filter (field : "Firstname", @operator : EqualTo, value : "Batman"), - (Expression>)(json => - "Firstname".Equals((string) JObject.Parse(json)[Filter.FieldJsonPropertyName]) - && "eq".Equals((string) JObject.Parse(json)[Filter.OperatorJsonPropertyName]) - && "Batman".Equals((string) JObject.Parse(json)[Filter.ValueJsonPropertyName]) - ) - }; - } + new Filter (field : "Firstname", @operator : EqualTo, value : "Batman"), + (Expression>)(json => + "Firstname".Equals((string) JObject.Parse(json)[Filter.FieldJsonPropertyName]) + && "eq".Equals((string) JObject.Parse(json)[Filter.OperatorJsonPropertyName]) + && "Batman".Equals((string) JObject.Parse(json)[Filter.ValueJsonPropertyName]) + ) + }; } + } - [Theory] - [MemberData(nameof(FilterToJsonCases))] - public void FilterToJson(Filter filter, Expression> jsonMatcher) - => ToJson(filter, jsonMatcher); + [Theory] + [MemberData(nameof(FilterToJsonCases))] + public void FilterToJson(Filter filter, Expression> jsonMatcher) + => ToJson(filter, jsonMatcher); - private void ToJson(IFilter filter, Expression> jsonMatcher) - { - output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); + private void ToJson(Filter filter, Expression> jsonMatcher) + { + output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); - // Act - string json = filter.ToJson(); + // Act + string json = filter.ToJson(); - // Assert - output.WriteLine($"ToJson result is '{json}'"); - json.Should().Match(jsonMatcher); - } + // Assert + output.WriteLine($"ToJson result is '{json}'"); + json.Should().Match(jsonMatcher); + } - [Theory] - [MemberData(nameof(FilterSchemaTestCases))] - public void FilterSchema(string json, FilterOperator @operator, bool expectedValidity) - { - output.WriteLine($"{nameof(json)} : {json}"); - output.WriteLine($"{nameof(FilterOperator)} : {@operator}"); + [Theory] + [MemberData(nameof(FilterSchemaTestCases))] + public void FilterSchema(string json, FilterOperator @operator, bool expectedValidity) + { + output.WriteLine($"{nameof(json)} : {json}"); + output.WriteLine($"{nameof(FilterOperator)} : {@operator}"); - // Arrange - JSchema schema = Filter.Schema(@operator); + // Arrange + JSchema schema = Filter.Schema(@operator); - // Act - bool isValid = JObject.Parse(json).IsValid(schema); + // Act + bool isValid = JObject.Parse(json).IsValid(schema); - // Arrange - isValid.Should().Be(expectedValidity); - } + // Arrange + isValid.Should().Be(expectedValidity); + } - public static IEnumerable FilterEquatableCases + public static IEnumerable FilterEquatableCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new Filter("property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - true - }; + new Filter("property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + true + }; - yield return new object[] - { - new Filter("property", IsNull ), - new Filter("property", IsNull, null), - true - }; + yield return new object[] + { + new Filter("property", IsNull ), + new Filter("property", IsNull, null), + true + }; - yield return new object[] - { - new Filter("property", EqualTo, null), - new Filter("property", IsNull), - true - }; + yield return new object[] + { + new Filter("property", EqualTo, null), + new Filter("property", IsNull), + true + }; + + yield return new object[] + { + new Filter("property", EqualTo, "value"), + new Filter("property", NotEqualTo, "value"), + false + }; + yield return new object[] + { + new Filter("Property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + false + }; + + { + Filter first = new("Property", EqualTo, "value"); yield return new object[] { - new Filter("property", EqualTo, "value"), - new Filter("property", NotEqualTo, "value"), - false + first, + first, + true }; + } + { + Guid guid = Guid.NewGuid(); yield return new object[] { - new Filter("Property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - false + new Filter("Prop", EqualTo, guid), + new Filter("Prop", EqualTo, guid), + true }; - - { - Filter first = new("Property", EqualTo, "value"); - yield return new object[] - { - first, - first, - true - }; - } - - { - Guid guid = Guid.NewGuid(); - yield return new object[] - { - new Filter("Prop", EqualTo, guid), - new Filter("Prop", EqualTo, guid), - true - }; - } } } + } - [Theory] - [MemberData(nameof(FilterEquatableCases))] - public void FilterImplementsEquatableProperly(Filter first, Filter second, bool expectedResult) - { - output.WriteLine($"first : {first}"); - output.WriteLine($"second : {second}"); - - // Act - bool result = first.Equals(second); + [Theory] + [MemberData(nameof(FilterEquatableCases))] + public void FilterImplementsEquatableProperly(Filter first, Filter second, bool expectedResult) + { + output.WriteLine($"first : {first}"); + output.WriteLine($"second : {second}"); - // Assert - result.Should() - .Be(expectedResult); - } + // Act + bool result = first.Equals(second); - [Fact] - public void Ctor_should_build_valid_instance() - { - // Act + // Assert + result.Should() + .Be(expectedResult); + } - Regex validFieldNameRegex = new(Filter.ValidFieldNamePattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); + [Fact] + public void Ctor_should_build_valid_instance() + { + // Act - Prop.ForAll((field, @operator, value) => - { - Lazy filterCtor = new(() => new Filter(field, @operator, value)); - Filter filter = null; - Action invokingCtor = () => filter = filterCtor.Value; - // Assertion - ((Action)(() => invokingCtor.Should().Throw())).When(field is not null && !validFieldNameRegex.IsMatch(field)) - .Label($"'{field}' doesnt not match expected regex ") - //.Trivial(field is not null && !validFieldNameRegex.IsMatch(field)) - .Or(() => - { - return filter.Field == field - && filter.Operator == @operator - && filter.Value is null; - }).When(Filter.UnaryOperators.Contains(@operator)).Label("Unary operator") - .Or(() => - { - Filter filter = filterCtor.Value; - - return filter.Field == field - && filter.Operator == @operator - && value.Equals(filter.Value); - }).When(!Filter.UnaryOperators.Contains(@operator)).Label("Binary operator") - .VerboseCheck(output); - }) - .VerboseCheck(output); - } + Regex validFieldNameRegex = new(Filter.ValidFieldNamePattern, RegexOptions.IgnoreCase, TimeSpan.FromSeconds(1)); - [Property(Arbitrary = [typeof(FilterGenerators)])] - public void Given_filter_instance_Negate_should_work_as_expected(NonNull source) + Prop.ForAll((field, @operator, value) => { - // Arrange - Filter original = source.Item; - - // Act - IFilter oppositeFilter = original.Negate(); - - // Assert - oppositeFilter.Should() - .BeOfType() - .Which - .Operator.Should() - .Be(original.Operator switch - { - EqualTo => NotEqualTo, - NotEqualTo => EqualTo, - IsNull => IsNotNull, - IsNotNull => IsNull, - FilterOperator.LessThan => GreaterThan, - GreaterThan => FilterOperator.LessThan, - GreaterThanOrEqual => LessThanOrEqualTo, - StartsWith => NotStartsWith, - NotStartsWith => StartsWith, - EndsWith => NotEndsWith, - NotEndsWith => EndsWith, - Contains => NotContains, - NotContains => Contains, - IsEmpty => IsNotEmpty, - IsNotEmpty => IsEmpty, - LessThanOrEqualTo => GreaterThanOrEqual, - _ => throw new NotSupportedException($"The original {original.Operator} operator is not supported"), - }, "the resulting filter should have the opposite operator"); - } + Lazy filterCtor = new(() => new Filter(field, @operator, value)); + Filter filter = null; + Action invokingCtor = () => filter = filterCtor.Value; + // Assertion + ((Action)(() => invokingCtor.Should().Throw())).When(field is not null && !validFieldNameRegex.IsMatch(field)) + .Label($"'{field}' doesnt not match expected regex ") + //.Trivial(field is not null && !validFieldNameRegex.IsMatch(field)) + .Or(() => + { + return filter.Field == field + && filter.Operator == @operator + && filter.Value is null; + }).When(Filter.UnaryOperators.Contains(@operator)).Label("Unary operator") + .Or(() => + { + Filter filter = filterCtor.Value; + + return filter.Field == field + && filter.Operator == @operator + && value.Equals(filter.Value); + }).When(!Filter.UnaryOperators.Contains(@operator)).Label("Binary operator") + .VerboseCheck(output); + }) + .VerboseCheck(output); + } + + [Property(Arbitrary = [typeof(FilterGenerators)])] + public void Given_filter_instance_Negate_should_work_as_expected(NonNull source) + { + // Arrange + Filter original = source.Item; + + // Act + IFilter oppositeFilter = original.Negate(); + + // Assert + oppositeFilter.Should() + .BeOfType() + .Which + .Operator.Should() + .Be(original.Operator switch + { + EqualTo => NotEqualTo, + NotEqualTo => EqualTo, + IsNull => IsNotNull, + IsNotNull => IsNull, + FilterOperator.LessThan => GreaterThan, + GreaterThan => FilterOperator.LessThan, + GreaterThanOrEqual => LessThanOrEqualTo, + StartsWith => NotStartsWith, + NotStartsWith => StartsWith, + EndsWith => NotEndsWith, + NotEndsWith => EndsWith, + Contains => NotContains, + NotContains => Contains, + IsEmpty => IsNotEmpty, + IsNotEmpty => IsEmpty, + LessThanOrEqualTo => GreaterThanOrEqual, + _ => throw new NotSupportedException($"The original {original.Operator} operator is not supported"), + }, "the resulting filter should have the opposite operator"); } } diff --git a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs index bfb50a7c..6d10cf84 100644 --- a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenParserTests.cs @@ -1,1128 +1,1131 @@ -namespace DataFilters.UnitTests.Grammar.Parsing +namespace DataFilters.UnitTests.Grammar.Parsing; + +using System; +using System.Collections.Generic; +using System.Data; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using DataFilters.Grammar.Parsing; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FluentAssertions.Execution; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Superpower; +using Superpower.Model; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +/// +/// Unit tests for +/// +[UnitTest] +[Feature(nameof(DataFilters.Grammar.Parsing))] +[Feature(nameof(FilterTokenParser))] +public class FilterTokenParserTests : IClassFixture, IDisposable { - using System; - using System.Collections.Generic; - using System.Data; - using System.Globalization; - using System.Linq; - using System.Linq.Expressions; - using DataFilters.Grammar.Parsing; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FluentAssertions.Execution; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Superpower; - using Superpower.Model; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; + private readonly FilterTokenizer _tokenizer; + private readonly ITestOutputHelper _outputHelper; + private readonly CultureSwitcher _cultureSwitcher; - /// - /// Unit tests for - /// - [UnitTest] - [Feature(nameof(DataFilters.Grammar.Parsing))] - [Feature(nameof(FilterTokenParser))] - public class FilterTokenParserTests : IClassFixture, IDisposable + public FilterTokenParserTests(ITestOutputHelper outputHelper, CultureSwitcher cultureSwitcher) { - private readonly FilterTokenizer _tokenizer; - private readonly ITestOutputHelper _outputHelper; - private readonly CultureSwitcher _cultureSwitcher; + _tokenizer = new FilterTokenizer(); + _outputHelper = outputHelper; + _cultureSwitcher = cultureSwitcher; + _cultureSwitcher.DefaultCulture = CultureInfo.GetCultureInfo("fr-FR"); - public FilterTokenParserTests(ITestOutputHelper outputHelper, CultureSwitcher cultureSwitcher) - { - _tokenizer = new FilterTokenizer(); - _outputHelper = outputHelper; - _cultureSwitcher = cultureSwitcher; - _cultureSwitcher.DefaultCulture = CultureInfo.GetCultureInfo("fr-FR"); + _outputHelper.WriteLine($"Current culture is '{_cultureSwitcher.CurrentCulture}'"); + } - _outputHelper.WriteLine($"Current culture is '{_cultureSwitcher.CurrentCulture}'"); - } + /// + void IDisposable.Dispose() + { + _cultureSwitcher?.Dispose(); + GC.SuppressFinalize(this); + } - /// - public void Dispose() => _cultureSwitcher?.Dispose(); - - [Fact] - public void IsParser() => typeof(FilterTokenParser).Should() - .BeStatic().And - .HaveProperty>("AlphaNumeric").And - .HaveProperty>("Property").And - .HaveProperty>("StartsWith").And - .HaveProperty>("EndsWith").And - .HaveProperty>("OneOf").And - .HaveProperty>("Contains").And - .HaveProperty>("Asterisk").And - .HaveProperty>("Group").And - .HaveProperty>("And").And - .HaveProperty>("Interval").And - .HaveProperty>("Not").And - .HaveProperty>("Number").And - .HaveProperty>("GlobalUniqueIdentifier").And - .HaveProperty>("Or"); - - public static IEnumerable AlphaNumericCases + [Fact] + public void IsParser() => typeof(FilterTokenParser).Should() + .BeStatic().And + .HaveProperty>("AlphaNumeric").And + .HaveProperty>("Property").And + .HaveProperty>("StartsWith").And + .HaveProperty>("EndsWith").And + .HaveProperty>("OneOf").And + .HaveProperty>("Contains").And + .HaveProperty>("Asterisk").And + .HaveProperty>("Group").And + .HaveProperty>("And").And + .HaveProperty>("Interval").And + .HaveProperty>("Not").And + .HaveProperty>("Number").And + .HaveProperty>("GlobalUniqueIdentifier").And + .HaveProperty>("Or"); + + public static IEnumerable AlphaNumericCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "Bruce", - new StringValueExpression("Bruce") - }; + "Bruce", + new StringValueExpression("Bruce") + }; - yield return new object[] - { - @"Vandal\*", - new StringValueExpression("Vandal*") - }; + yield return new object[] + { + @"Vandal\*", + new StringValueExpression("Vandal*") + }; - yield return new object[] - { - @"Van\*dal", - new StringValueExpression("Van*dal") - }; - yield return new object[] - { - "1Bruce", - new StringValueExpression("1Bruce") - }; + yield return new object[] + { + @"Van\*dal", + new StringValueExpression("Van*dal") + }; + yield return new object[] + { + "1Bruce", + new StringValueExpression("1Bruce") + }; - yield return new object[] - { - @"0 \ \,i#y", - new StringValueExpression("0 ,i#y") - }; + yield return new object[] + { + @"0 \ \,i#y", + new StringValueExpression("0 ,i#y") + }; - yield return new object[] - { - @"E + yield return new object[] + { + @"E I\&_Oj \.", - new StringValueExpression(@"E + new StringValueExpression(@"E I&_Oj .") - }; + }; - yield return new object[] - { - "39.95173047301258862", - new NumericValueExpression("39.95173047301258862") - }; - } + yield return new object[] + { + "39.95173047301258862", + new NumericValueExpression("39.95173047301258862") + }; } + } - [Theory] - [MemberData(nameof(AlphaNumericCases))] - public void Should_parse_AlphaNumeric(string input, ConstantValueExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); - _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); + [Theory] + [MemberData(nameof(AlphaNumericCases))] + public void Should_parse_AlphaNumeric(string input, ConstantValueExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); + _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); - // Act - FilterExpression actual = FilterTokenParser.AlphaNumeric.Parse(tokens); + // Act + FilterExpression actual = FilterTokenParser.AlphaNumeric.Parse(tokens); - _outputHelper.WriteLine($"actual is '{actual.EscapedParseableString}'"); + _outputHelper.WriteLine($"actual is '{actual.EscapedParseableString}'"); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - [Property] - public void Given_double_as_string_Number_should_parse_to_a_ConstantValueExpression_where_Value_property_holds_the_input(NormalFloat value) - { - // Arrange - NumericValueExpression expected = new(value.Item.ToString(CultureInfo.InvariantCulture)); - string input = expected.EscapedParseableString; + [Property] + public void Given_double_as_string_Number_should_parse_to_a_ConstantValueExpression_where_Value_property_holds_the_input(NormalFloat value) + { + // Arrange + NumericValueExpression expected = new(value.Item.ToString(CultureInfo.InvariantCulture)); + string input = expected.EscapedParseableString; - TokenList tokens = _tokenizer.Tokenize(input); - _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); + TokenList tokens = _tokenizer.Tokenize(input); + _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); - // Act - NumericValueExpression actual = FilterTokenParser.Number.Parse(tokens); + // Act + NumericValueExpression actual = FilterTokenParser.Number.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - [Property] - public void Given_long_as_string_Number_should_parse_to_a_ConstantValueExpression_where_Value_property_holds_the_input(long value) - { - // Arrange - NumericValueExpression expected = new(value.ToString()); - string input = expected.EscapedParseableString; + [Property] + public void Given_long_as_string_Number_should_parse_to_a_ConstantValueExpression_where_Value_property_holds_the_input(long value) + { + // Arrange + NumericValueExpression expected = new(value.ToString()); + string input = expected.EscapedParseableString; - TokenList tokens = _tokenizer.Tokenize(input); - _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); + TokenList tokens = _tokenizer.Tokenize(input); + _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); - // Act - NumericValueExpression actual = FilterTokenParser.Number.Parse(tokens); + // Act + NumericValueExpression actual = FilterTokenParser.Number.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - [Property] - public void Given_int_as_string_Number_should_parse_to_a_ConstantValueExpression_where_Value_property_holds_the_input(int value) - { - // Arrange - NumericValueExpression expected = new(value.ToString()); - string input = expected.EscapedParseableString; + [Property] + public void Given_int_as_string_Number_should_parse_to_a_ConstantValueExpression_where_Value_property_holds_the_input(int value) + { + // Arrange + NumericValueExpression expected = new(value.ToString()); + string input = expected.EscapedParseableString; - TokenList tokens = _tokenizer.Tokenize(input); - _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); + TokenList tokens = _tokenizer.Tokenize(input); + _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); - // Act - NumericValueExpression actual = FilterTokenParser.Number.Parse(tokens); + // Act + NumericValueExpression actual = FilterTokenParser.Number.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_StartsWith(StartsWithExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_StartsWith(StartsWithExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); - // Act - StartsWithExpression expression = FilterTokenParser.StartsWith.Parse(tokens); + // Act + StartsWithExpression expression = FilterTokenParser.StartsWith.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expected); - } + // Assert + AssertThatShould_parse(expression, expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_EndsWith(EndsWithExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_EndsWith(EndsWithExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + _outputHelper.WriteLine($"Tokens : ${StringifyTokens(tokens)}"); - // Act - EndsWithExpression expression = FilterTokenParser.EndsWith.Parse(tokens); + // Act + EndsWithExpression expression = FilterTokenParser.EndsWith.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expected); - } + // Assert + AssertThatShould_parse(expression, expected); + } - private static string StringifyTokens(TokenList tokens) - => tokens.Select(token => new { token.Kind, Value = token.ToStringValue() }).Jsonify(); + private static string StringifyTokens(TokenList tokens) + => tokens.Select(token => new { token.Kind, Value = token.ToStringValue() }).Jsonify(); - public static IEnumerable ContainsCases + public static IEnumerable ContainsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "*bat*", - new ContainsExpression("bat") - }; + "*bat*", + new ContainsExpression("bat") + }; - yield return new object[] - { - @"*bat\*man*", - new ContainsExpression("bat*man") - }; + yield return new object[] + { + @"*bat\*man*", + new ContainsExpression("bat*man") + }; - yield return new object[] - { - "*d3aa022d-ec52-47aa-be13-6823c478c60a*", - new ContainsExpression("d3aa022d-ec52-47aa-be13-6823c478c60a") - }; + yield return new object[] + { + "*d3aa022d-ec52-47aa-be13-6823c478c60a*", + new ContainsExpression("d3aa022d-ec52-47aa-be13-6823c478c60a") + }; - string[] punctuations = [".", "-", ":", "_"]; + string[] punctuations = [".", "-", ":", "_"]; - foreach (string punctuation in punctuations) + foreach (string punctuation in punctuations) + { + yield return new object[] { - yield return new object[] - { - $"*{punctuation}*", - new ContainsExpression(punctuation) - }; - } + $"*{punctuation}*", + new ContainsExpression(punctuation) + }; } } + } - [Theory] - [MemberData(nameof(ContainsCases))] - public void Should_parse_Contains(string input, ContainsExpression expectedContains) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + [Theory] + [MemberData(nameof(ContainsCases))] + public void Should_parse_Contains(string input, ContainsExpression expectedContains) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - // Act - ContainsExpression expression = FilterTokenParser.Contains.Parse(tokens); + // Act + ContainsExpression expression = FilterTokenParser.Contains.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expectedContains); - } + // Assert + AssertThatShould_parse(expression, expectedContains); + } - public static IEnumerable OrExpressionCases + public static IEnumerable OrExpressionCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "Bruce|bat*", - new OrExpression(new StringValueExpression("Bruce"), new StartsWithExpression("bat")) - }; + "Bruce|bat*", + new OrExpression(new StringValueExpression("Bruce"), new StartsWithExpression("bat")) + }; - yield return new object[] - { - "Bruce|Wayne", - new OrExpression(new StringValueExpression("Bruce"), new StringValueExpression("Wayne")) - }; + yield return new object[] + { + "Bruce|Wayne", + new OrExpression(new StringValueExpression("Bruce"), new StringValueExpression("Wayne")) + }; - yield return new object[] - { - "Bru*|Di*", - new OrExpression(new StartsWithExpression("Bru"), new StartsWithExpression("Di")) - }; + yield return new object[] + { + "Bru*|Di*", + new OrExpression(new StartsWithExpression("Bru"), new StartsWithExpression("Di")) + }; - yield return new object[] - { - "!(Bat*|Sup*)|!*man", - new OrExpression( - left : new NotExpression( - new GroupExpression( - new OrExpression(new StartsWithExpression("Bat"), new StartsWithExpression("Sup")) - ) - ), - right: new NotExpression(new EndsWithExpression("man")) - ) - }; + yield return new object[] + { + "!(Bat*|Sup*)|!*man", + new OrExpression( + left : new NotExpression( + new GroupExpression( + new OrExpression(new StartsWithExpression("Bat"), new StartsWithExpression("Sup")) + ) + ), + right: new NotExpression(new EndsWithExpression("man")) + ) + }; - yield return new object[] - { - "Bat|Wonder|Sup", - new OrExpression(new OrExpression(new StringValueExpression("Bat"), - new StringValueExpression("Wonder")), - new StringValueExpression("Sup")) - }; - } + yield return new object[] + { + "Bat|Wonder|Sup", + new OrExpression(new OrExpression(new StringValueExpression("Bat"), + new StringValueExpression("Wonder")), + new StringValueExpression("Sup")) + }; } + } - [Theory] - [MemberData(nameof(OrExpressionCases))] - public void Should_parse_OrExpression(string input, OrExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + [Theory] + [MemberData(nameof(OrExpressionCases))] + public void Should_parse_OrExpression(string input, OrExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - // Act - FilterExpression expression = FilterTokenParser.Or.Parse(tokens); + // Act + FilterExpression expression = FilterTokenParser.Or.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expected); - } + // Assert + AssertThatShould_parse(expression, expected); + } - public static IEnumerable AndExpressionCases + public static IEnumerable AndExpressionCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "bat*,man*", - new AndExpression(new StartsWithExpression("bat"), new StartsWithExpression("man")) - }; + "bat*,man*", + new AndExpression(new StartsWithExpression("bat"), new StartsWithExpression("man")) + }; - yield return new object[] - { - "!(Bat*|Sup*),!*man", - new AndExpression( - left : new NotExpression( - new GroupExpression( - new OrExpression(new StartsWithExpression("Bat"), new StartsWithExpression("Sup")) - ) - ), - right: new NotExpression(new EndsWithExpression("man")) - ) - }; + yield return new object[] + { + "!(Bat*|Sup*),!*man", + new AndExpression( + left : new NotExpression( + new GroupExpression( + new OrExpression(new StartsWithExpression("Bat"), new StartsWithExpression("Sup")) + ) + ), + right: new NotExpression(new EndsWithExpression("man")) + ) + }; - yield return new object[] - { - "bat*man", - new AndExpression(new StartsWithExpression("bat"), new EndsWithExpression("man")) - }; - } + yield return new object[] + { + "bat*man", + new AndExpression(new StartsWithExpression("bat"), new EndsWithExpression("man")) + }; } + } - [Theory] - [MemberData(nameof(AndExpressionCases))] - public void Should_parse_AndExpression(string input, AndExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + [Theory] + [MemberData(nameof(AndExpressionCases))] + public void Should_parse_AndExpression(string input, AndExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - // Act - FilterExpression expression = FilterTokenParser.And.Parse(tokens); + // Act + FilterExpression expression = FilterTokenParser.And.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expected); - } + // Assert + AssertThatShould_parse(expression, expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_NotExpression(NotExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - _outputHelper.WriteLine($"Tokens : '{StringifyTokens(tokens)}'"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_NotExpression(NotExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + _outputHelper.WriteLine($"Tokens : '{StringifyTokens(tokens)}'"); - // Act - NotExpression expression = FilterTokenParser.Not.Parse(tokens); + // Act + NotExpression expression = FilterTokenParser.Not.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expected); - } + // Assert + AssertThatShould_parse(expression, expected); + } - public static IEnumerable NotParsingCases + public static IEnumerable NotParsingCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "!!!!!(w%A*@,2088-08-10)", + "!!!!!(w%A*@,2088-08-10)", + new NotExpression( new NotExpression( new NotExpression( new NotExpression( new NotExpression( - new NotExpression( - new GroupExpression( - new AndExpression( - new AndExpression(new StartsWithExpression("w%A"), - new EndsWithExpression("@")), - new DateExpression(2088, 8, 10) - ) + new GroupExpression( + new AndExpression( + new AndExpression(new StartsWithExpression("w%A"), + new EndsWithExpression("@")), + new DateExpression(2088, 8, 10) ) ) ) ) ) ) - }; + ) + }; - yield return new object[] - { - "!![50 TO 50]", - new NotExpression - (new NotExpression( - new IntervalExpression(min: new (new NumericValueExpression("50"), true), - new(new NumericValueExpression("60"), true)))) - }; - } + yield return new object[] + { + "!![50 TO 50]", + new NotExpression + (new NotExpression( + new IntervalExpression(min: new (new NumericValueExpression("50"), true), + new(new NumericValueExpression("60"), true)))) + }; } + } - [Theory] - [MemberData(nameof(NotParsingCases))] - public void Given_NotExpression_as_parseable_string_Not_should_parse_the_input(string input, NotExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + [Theory] + [MemberData(nameof(NotParsingCases))] + public void Given_NotExpression_as_parseable_string_Not_should_parse_the_input(string input, NotExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - NotExpression expression = FilterTokenParser.Not.Parse(tokens); + // Act + NotExpression expression = FilterTokenParser.Not.Parse(tokens); - // Assert - AssertThatShould_parse(expression, expected); - } + // Assert + AssertThatShould_parse(expression, expected); + } - public static IEnumerable OneOfExpressionCases + public static IEnumerable OneOfExpressionCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "[Bb]", - new OneOfExpression(new StringValueExpression("B"), new StringValueExpression("b")) - }; - - yield return new object[] - { - "[Bb]ruce", - new OneOfExpression(new StringValueExpression("Bruce"), new StringValueExpression("bruce")) - }; - - yield return new object[] - { - "[Bb]at*", - new OneOfExpression(new StartsWithExpression("Bat"), new StartsWithExpression("bat")) - }; - - yield return new object[] - { - "Ma*[Nn]", - new OneOfExpression( - new AndExpression(new StartsWithExpression("Ma"), new EndsWithExpression("N")), - new AndExpression(new StartsWithExpression("Ma"), new EndsWithExpression("n")) - ) - }; - - yield return new object[] - { - "cat[Ww]oman", - new OneOfExpression(new StringValueExpression("catWoman"), new StringValueExpression("catwoman")) - }; - - yield return new object[] - { - "*Br[Uu]", - new OneOfExpression(new EndsWithExpression("BrU"), new EndsWithExpression("Bru")) - }; - - yield return new object[] - { - "Bo[Bb]", - new OneOfExpression(new StringValueExpression("BoB"), new StringValueExpression("Bob")) - }; - - yield return new object[] - { - "[Bb]*ob", - new OneOfExpression(new AndExpression(new StartsWithExpression("B"), new EndsWithExpression("ob")), - new AndExpression(new StartsWithExpression("b"), new EndsWithExpression("ob"))) - }; - - yield return new object[] - { - "*[Mm]an", - new OneOfExpression(new EndsWithExpression("Man"), new EndsWithExpression("man")) - }; - - yield return new object[] - { - "*[Mm]", - new OneOfExpression(new EndsWithExpression("M"), new EndsWithExpression("m")) - }; - - yield return new object[] - { - "[a-z]", - new OneOfExpression(GetCharacters('a', 'z').Select(chr => new StringValueExpression(chr.ToString())).ToArray()) - }; - - yield return new object[] - { - "[a-zA-Z0-9]", - new OneOfExpression(GetCharacters('a', 'z').Concat(GetCharacters('A', 'Z')) - .Concat(GetCharacters('0', '9')) - .Select(chr => new StringValueExpression(chr.ToString())) - .ToArray()) - }; - - yield return new object[] - { - "{Bat|Sup|Wonder}*", - new OneOfExpression(new StartsWithExpression("Bat"), - new StartsWithExpression("Sup"), - new StartsWithExpression("Wonder")) - }; + "[Bb]", + new OneOfExpression(new StringValueExpression("B"), new StringValueExpression("b")) + }; - yield return new object[] - { - "[ab][cd]", - new OneOfExpression(new StringValueExpression("ac"), - new StringValueExpression("ad"), - new StringValueExpression("bc"), - new StringValueExpression("bd")) - }; + yield return new object[] + { + "[Bb]ruce", + new OneOfExpression(new StringValueExpression("Bruce"), new StringValueExpression("bruce")) + }; - yield return new object[] - { - "{Bat|Sup|Wonder}", - new OneOfExpression(new StringValueExpression("Bat"), - new StringValueExpression("Sup"), - new StringValueExpression("Wonder")) - }; - } - } + yield return new object[] + { + "[Bb]at*", + new OneOfExpression(new StartsWithExpression("Bat"), new StartsWithExpression("bat")) + }; - private static IEnumerable GetCharacters(char regexStart, char regexEnd) - => Enumerable.Range(regexStart, regexEnd - regexStart + 1) - .Select(ascii => (char)ascii); + yield return new object[] + { + "Ma*[Nn]", + new OneOfExpression( + new AndExpression(new StartsWithExpression("Ma"), new EndsWithExpression("N")), + new AndExpression(new StartsWithExpression("Ma"), new EndsWithExpression("n")) + ) + }; - [Theory] - [MemberData(nameof(OneOfExpressionCases))] - public void Should_parse_OneOfExpression(string input, OneOfExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + yield return new object[] + { + "cat[Ww]oman", + new OneOfExpression(new StringValueExpression("catWoman"), new StringValueExpression("catwoman")) + }; - // Act - OneOfExpression expression = FilterTokenParser.OneOf.Parse(tokens); + yield return new object[] + { + "*Br[Uu]", + new OneOfExpression(new EndsWithExpression("BrU"), new EndsWithExpression("Bru")) + }; - // Assert - AssertThatShould_parse(expression, expected); - } + yield return new object[] + { + "Bo[Bb]", + new OneOfExpression(new StringValueExpression("BoB"), new StringValueExpression("Bob")) + }; - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_bracket_expression_OneOf_can_parse_input(NonNull bracketExpression) - { - // Arrange - string input = bracketExpression.Item.EscapedParseableString; - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + yield return new object[] + { + "[Bb]*ob", + new OneOfExpression(new AndExpression(new StartsWithExpression("B"), new EndsWithExpression("ob")), + new AndExpression(new StartsWithExpression("b"), new EndsWithExpression("ob"))) + }; - OneOfExpression expected = bracketExpression.Item switch + yield return new object[] { - ConstantBracketValue constant => new(constant.Value.Select(chr => new StringValueExpression(chr.ToString())).ToArray()), - RangeBracketValue range => new(Enumerable.Range(range.Start, range.End - range.Start + 1) - .Select(ascii => (char)ascii) - .Select(chr => new StringValueExpression(chr.ToString())).ToArray()), - _ => throw new NotSupportedException() + "*[Mm]an", + new OneOfExpression(new EndsWithExpression("Man"), new EndsWithExpression("man")) }; - // Act - OneOfExpression expression = FilterTokenParser.OneOf.Parse(tokens); + yield return new object[] + { + "*[Mm]", + new OneOfExpression(new EndsWithExpression("M"), new EndsWithExpression("m")) + }; - // Assert - expression.IsEquivalentTo(expected).ToProperty(); - } + yield return new object[] + { + "[a-z]", + new OneOfExpression(GetCharacters('a', 'z').Select(chr => new StringValueExpression(chr.ToString())).ToArray()) + }; - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_Interval(CultureInfo culture, IntervalExpression expected) - { - _cultureSwitcher.Run(culture, () => + yield return new object[] { - // Arrange - _outputHelper.WriteLine($"Culture : '{culture.Name}'"); - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + "[a-zA-Z0-9]", + new OneOfExpression(GetCharacters('a', 'z').Concat(GetCharacters('A', 'Z')) + .Concat(GetCharacters('0', '9')) + .Select(chr => new StringValueExpression(chr.ToString())) + .ToArray()) + }; - // Act - IntervalExpression expression = FilterTokenParser.Interval.Parse(tokens); + yield return new object[] + { + "{Bat|Sup|Wonder}*", + new OneOfExpression(new StartsWithExpression("Bat"), + new StartsWithExpression("Sup"), + new StartsWithExpression("Wonder")) + }; - // Assert - AssertThatShould_parse(expression, expected); - }); - } + yield return new object[] + { + "[ab][cd]", + new OneOfExpression(new StringValueExpression("ac"), + new StringValueExpression("ad"), + new StringValueExpression("bc"), + new StringValueExpression("bd")) + }; - public static IEnumerable IntervalCases - { - get + yield return new object[] { - string[] cultures = ["fr-FR", "en-GB", "en-US"]; - foreach (string culture in cultures) - { - yield return new object[] - { - culture, - "[5 TO 5[", - new IntervalExpression(new BoundaryExpression(new NumericValueExpression("5"), true) , new BoundaryExpression(new NumericValueExpression("5"), false)) - }; - - yield return new object[] - { - culture, - "[1993-08-04T00:25:05.155Z TO 1908-06-08T03:18:46.745-09:50[", - new IntervalExpression(new BoundaryExpression(new DateTimeExpression(new (1993, 8, 4), new(0, 25, 5, 155), OffsetExpression.Zero), true), - new BoundaryExpression(new DateTimeExpression(new (1908, 6, 8), new(3, 18, 46,745), new(NumericSign.Minus, 9, 50)), false)) - }; - - yield return new object[] - { - culture, - "]* TO 1963-06-03T15:53:44.609Z]", - new IntervalExpression(max:new BoundaryExpression(new DateTimeExpression(new (1963, 6, 3), new (15, 53, 44, 609), OffsetExpression.Zero), true)) - }; - } - } + "{Bat|Sup|Wonder}", + new OneOfExpression(new StringValueExpression("Bat"), + new StringValueExpression("Sup"), + new StringValueExpression("Wonder")) + }; } + } - [Theory] - [MemberData(nameof(IntervalCases))] - public void Given_interval_as_string_Parser_should_parse_to_IntervalExpression(string culture, string input, IntervalExpression expected) - { - _cultureSwitcher.Run(culture, () => - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + private static IEnumerable GetCharacters(char regexStart, char regexEnd) + => Enumerable.Range(regexStart, regexEnd - regexStart + 1) + .Select(ascii => (char)ascii); + + [Theory] + [MemberData(nameof(OneOfExpressionCases))] + public void Should_parse_OneOfExpression(string input, OneOfExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - _outputHelper.WriteLine($"Tokens : {StringifyTokens(tokens)}"); + // Act + OneOfExpression expression = FilterTokenParser.OneOf.Parse(tokens); - // Act - IntervalExpression expression = FilterTokenParser.Interval.Parse(tokens); + // Assert + AssertThatShould_parse(expression, expected); + } - // Assert - AssertThatShould_parse(expression, expected); - }); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_bracket_expression_OneOf_can_parse_input(NonNull bracketExpression) + { + // Arrange + string input = bracketExpression.Item.EscapedParseableString; + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_text(CultureInfo culture, TextExpression expected) + OneOfExpression expected = bracketExpression.Item switch { - _cultureSwitcher.Run(culture, () => - { - // Arrange - _outputHelper.WriteLine($"Culture : '{culture.Name}'"); - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + ConstantBracketValue constant => new(constant.Value.Select(chr => new StringValueExpression(chr.ToString())).ToArray()), + RangeBracketValue range => new(Enumerable.Range(range.Start, range.End - range.Start + 1) + .Select(ascii => (char)ascii) + .Select(chr => new StringValueExpression(chr.ToString())).ToArray()), + _ => throw new NotSupportedException() + }; + + // Act + OneOfExpression expression = FilterTokenParser.OneOf.Parse(tokens); + + // Assert + expression.IsEquivalentTo(expected).ToProperty(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_Interval(CultureInfo culture, IntervalExpression expected) + { + _cultureSwitcher.Run(culture, () => + { + // Arrange + _outputHelper.WriteLine($"Culture : '{culture.Name}'"); + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - TextExpression actual = FilterTokenParser.Text.Parse(tokens); + // Act + IntervalExpression expression = FilterTokenParser.Interval.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - }); - } + // Assert + AssertThatShould_parse(expression, expected); + }); + } - public static IEnumerable PropertyNameCases + public static IEnumerable IntervalCases + { + get { - get + string[] cultures = ["fr-FR", "en-GB", "en-US"]; + foreach (string culture in cultures) { yield return new object[] { - "Firstname=Bruce", - new PropertyName("Firstname") - }; - - yield return new object[] - { - @"Henchmen[""Name""]=*rob*", - new PropertyName(@"Henchmen[""Name""]") - }; - - yield return new object[] - { - @"Henchmen[""Powers""][""Description""]=*strength*", - new PropertyName(@"Henchmen[""Powers""][""Description""]") + culture, + "[5 TO 5[", + new IntervalExpression(new BoundaryExpression(new NumericValueExpression("5"), true) , new BoundaryExpression(new NumericValueExpression("5"), false)) }; yield return new object[] { - @"Henchmen[""first_name""]=*rob*", - new PropertyName(@"Henchmen[""first_name""]") + culture, + "[1993-08-04T00:25:05.155Z TO 1908-06-08T03:18:46.745-09:50[", + new IntervalExpression(new BoundaryExpression(new DateTimeExpression(new (1993, 8, 4), new(0, 25, 5, 155), OffsetExpression.Zero), true), + new BoundaryExpression(new DateTimeExpression(new (1908, 6, 8), new(3, 18, 46,745), new(NumericSign.Minus, 9, 50)), false)) }; yield return new object[] { - "first_name=Bruce", - new PropertyName("first_name") + culture, + "]* TO 1963-06-03T15:53:44.609Z]", + new IntervalExpression(max:new BoundaryExpression(new DateTimeExpression(new (1963, 6, 3), new (15, 53, 44, 609), OffsetExpression.Zero), true)) }; } } + } - [Theory] - [MemberData(nameof(PropertyNameCases))] - public void Should_parse_PropertyNameExpression(string input, PropertyName expected) + [Theory] + [MemberData(nameof(IntervalCases))] + public void Given_interval_as_string_Parser_should_parse_to_IntervalExpression(string culture, string input, IntervalExpression expected) + { + _cultureSwitcher.Run(culture, () => { // Arrange _outputHelper.WriteLine($"input : '{input}'"); TokenList tokens = _tokenizer.Tokenize(input); + _outputHelper.WriteLine($"Tokens : {StringifyTokens(tokens)}"); + // Act - PropertyName expression = FilterTokenParser.Property.Parse(tokens); + IntervalExpression expression = FilterTokenParser.Interval.Parse(tokens); // Assert - expression.Should() - .NotBeSameAs(expected).And - .Be(expected); + AssertThatShould_parse(expression, expected); + }); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_text(CultureInfo culture, TextExpression expected) + { + _cultureSwitcher.Run(culture, () => + { + // Arrange + _outputHelper.WriteLine($"Culture : '{culture.Name}'"); + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + + // Act + TextExpression actual = FilterTokenParser.Text.Parse(tokens); + + // Assert + AssertThatShould_parse(actual, expected); + }); + } + + public static IEnumerable PropertyNameCases + { + get + { + yield return new object[] + { + "Firstname=Bruce", + new PropertyName("Firstname") + }; + + yield return new object[] + { + @"Henchmen[""Name""]=*rob*", + new PropertyName(@"Henchmen[""Name""]") + }; + + yield return new object[] + { + @"Henchmen[""Powers""][""Description""]=*strength*", + new PropertyName(@"Henchmen[""Powers""][""Description""]") + }; + + yield return new object[] + { + @"Henchmen[""first_name""]=*rob*", + new PropertyName(@"Henchmen[""first_name""]") + }; + + yield return new object[] + { + "first_name=Bruce", + new PropertyName("first_name") + }; } + } + + [Theory] + [MemberData(nameof(PropertyNameCases))] + public void Should_parse_PropertyNameExpression(string input, PropertyName expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); + + // Act + PropertyName expression = FilterTokenParser.Property.Parse(tokens); + + // Assert + expression.Should() + .NotBeSameAs(expected).And + .Be(expected); + } - public static IEnumerable CriterionCases + public static IEnumerable CriterionCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "Name=Vandal", - ( - new PropertyName("Name"), - (FilterExpression) new StringValueExpression("Vandal") - ) - }; + "Name=Vandal", + ( + new PropertyName("Name"), + (FilterExpression) new StringValueExpression("Vandal") + ) + }; - yield return new object[] - { - "first_name=Vandal", - ( - new PropertyName("first_name"), - (FilterExpression) new StringValueExpression("Vandal") - ) - }; + yield return new object[] + { + "first_name=Vandal", + ( + new PropertyName("first_name"), + (FilterExpression) new StringValueExpression("Vandal") + ) + }; - yield return new object[] - { - "Name=Vandal|Banner", - ( - new PropertyName("Name"), - (FilterExpression) new OrExpression(new StringValueExpression("Vandal"), new StringValueExpression("Banner")) - ) - }; + yield return new object[] + { + "Name=Vandal|Banner", + ( + new PropertyName("Name"), + (FilterExpression) new OrExpression(new StringValueExpression("Vandal"), new StringValueExpression("Banner")) + ) + }; - yield return new object[] - { - "Name=Vandal|Banner", - ( - new PropertyName("Name"), - (FilterExpression) new OrExpression(new StringValueExpression("Vandal"), new StringValueExpression("Banner")) - ) - }; + yield return new object[] + { + "Name=Vandal|Banner", + ( + new PropertyName("Name"), + (FilterExpression) new OrExpression(new StringValueExpression("Vandal"), new StringValueExpression("Banner")) + ) + }; - yield return new object[] - { - "Size=[10 TO 20]", - ( - new PropertyName("Size"), - (FilterExpression) new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), - included: true), - max: new BoundaryExpression(new NumericValueExpression("20"), - included: true)) - ) - }; + yield return new object[] + { + "Size=[10 TO 20]", + ( + new PropertyName("Size"), + (FilterExpression) new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), + included: true), + max: new BoundaryExpression(new NumericValueExpression("20"), + included: true)) + ) + }; - yield return new object[] - { - @"Acolytes[""Name""][""Superior""]=Vandal|Banner", - ( - new PropertyName(@"Acolytes[""Name""][""Superior""]"), - (FilterExpression) new OrExpression(new StringValueExpression("Vandal"), new StringValueExpression("Banner")) - ) - }; + yield return new object[] + { + @"Acolytes[""Name""][""Superior""]=Vandal|Banner", + ( + new PropertyName(@"Acolytes[""Name""][""Superior""]"), + (FilterExpression) new OrExpression(new StringValueExpression("Vandal"), new StringValueExpression("Banner")) + ) + }; - yield return new object[] - { - @"Appointment[""Date""]=]2012-10-19T15:03:45Z TO 2012-10-19T15:30:45+01:00[", - ( - new PropertyName(@"Appointment[""Date""]"), - (FilterExpression) new IntervalExpression(min: new BoundaryExpression(new DateTimeExpression(new DateExpression(year: 2012, month: 10, day: 19 ), - new TimeExpression(hours : 15, minutes: 03, seconds: 45), - OffsetExpression.Zero), - included: false), - max: new BoundaryExpression(new DateTimeExpression(new DateExpression(year: 2012, month: 10, day: 19 ), - new TimeExpression(hours : 15, minutes: 30, seconds: 45), - new OffsetExpression(hours: 1)), - included: false) - ) + yield return new object[] + { + @"Appointment[""Date""]=]2012-10-19T15:03:45Z TO 2012-10-19T15:30:45+01:00[", + ( + new PropertyName(@"Appointment[""Date""]"), + (FilterExpression) new IntervalExpression(min: new BoundaryExpression(new DateTimeExpression(new DateExpression(year: 2012, month: 10, day: 19 ), + new TimeExpression(hours : 15, minutes: 03, seconds: 45), + OffsetExpression.Zero), + included: false), + max: new BoundaryExpression(new DateTimeExpression(new DateExpression(year: 2012, month: 10, day: 19 ), + new TimeExpression(hours : 15, minutes: 30, seconds: 45), + new OffsetExpression(hours: 1)), + included: false) ) - }; + ) + }; - yield return new object[] - { - "Name=*[Mm]an", - ( - new PropertyName("Name"), - (FilterExpression) new OneOfExpression(new EndsWithExpression("Man"), - new EndsWithExpression("man")) - ) - }; + yield return new object[] + { + "Name=*[Mm]an", + ( + new PropertyName("Name"), + (FilterExpression) new OneOfExpression(new EndsWithExpression("Man"), + new EndsWithExpression("man")) + ) + }; - yield return new object[] - { - "Name=[Bb]o[Bb]", - ( - new PropertyName("Name"), - (FilterExpression)new OneOfExpression(new StringValueExpression("BoB"), - new StringValueExpression("Bob"), - new StringValueExpression("boB"), - new StringValueExpression("bob")) - ) - }; + yield return new object[] + { + "Name=[Bb]o[Bb]", + ( + new PropertyName("Name"), + (FilterExpression)new OneOfExpression(new StringValueExpression("BoB"), + new StringValueExpression("Bob"), + new StringValueExpression("boB"), + new StringValueExpression("bob")) + ) + }; - yield return new object[] - { - "prop=!(((1908-09-30T00:31:33.127+05:13|2000-06-19)|(00:01:40.443|* ))|((2003-05-27|89ae5244-2a98-16cc-6f99-d17658594604)|(s*|*0$)))", - ( - new PropertyName("prop"), - (FilterExpression) new NotExpression( - new OrExpression( - new OrExpression( - - new OrExpression(new DateTimeExpression(new DateExpression(1908, 9, 30), - new TimeExpression(0, 31, 33, 127), - new OffsetExpression(hours: 5, minutes: 13)), - new DateExpression(2000, 6, 19)), - new OrExpression(new TimeExpression(minutes: 1, seconds: 40, milliseconds: 443), - new EndsWithExpression(" ")) - ), - new OrExpression(new OrExpression(new DateExpression(2003, 5, 27), - new GuidValueExpression("89ae5244-2a98-16cc-6f99-d17658594604")), - new OrExpression(new StartsWithExpression("s"), new EndsWithExpression("0$")) - ) - )) - ) - }; + yield return new object[] + { + "prop=!(((1908-09-30T00:31:33.127+05:13|2000-06-19)|(00:01:40.443|* ))|((2003-05-27|89ae5244-2a98-16cc-6f99-d17658594604)|(s*|*0$)))", + ( + new PropertyName("prop"), + (FilterExpression) new NotExpression( + new OrExpression( + new OrExpression( + + new OrExpression(new DateTimeExpression(new DateExpression(1908, 9, 30), + new TimeExpression(0, 31, 33, 127), + new OffsetExpression(hours: 5, minutes: 13)), + new DateExpression(2000, 6, 19)), + new OrExpression(new TimeExpression(minutes: 1, seconds: 40, milliseconds: 443), + new EndsWithExpression(" ")) + ), + new OrExpression(new OrExpression(new DateExpression(2003, 5, 27), + new GuidValueExpression("89ae5244-2a98-16cc-6f99-d17658594604")), + new OrExpression(new StartsWithExpression("s"), new EndsWithExpression("0$")) + ) + )) + ) + }; - yield return new object[] - { - "Nickname={Bat|Sup|Wonder}*", - ( - new PropertyName("Nickname"), - (FilterExpression) new OneOfExpression(new StartsWithExpression("Bat"), - new StartsWithExpression("Sup"), - new StartsWithExpression("Wonder")) - ) - }; - } + yield return new object[] + { + "Nickname={Bat|Sup|Wonder}*", + ( + new PropertyName("Nickname"), + (FilterExpression) new OneOfExpression(new StartsWithExpression("Bat"), + new StartsWithExpression("Sup"), + new StartsWithExpression("Wonder")) + ) + }; } + } - /// - /// Tests if can parse a criteria - /// - /// - /// - [Theory] - [MemberData(nameof(CriterionCases))] - public void Should_parse_Criterion(string input, (PropertyName prop, FilterExpression expression) expected) - { - _outputHelper.WriteLine($"{nameof(input)} : '{input}'"); + /// + /// Tests if can parse a criteria + /// + /// + /// + [Theory] + [MemberData(nameof(CriterionCases))] + public void Should_parse_Criterion(string input, (PropertyName prop, FilterExpression expression) expected) + { + _outputHelper.WriteLine($"{nameof(input)} : '{input}'"); - // Arrange - TokenList tokens = _tokenizer.Tokenize(input); + // Arrange + TokenList tokens = _tokenizer.Tokenize(input); - // Act - (PropertyName prop, FilterExpression expression) = FilterTokenParser.Criterion.Parse(tokens); + // Act + (PropertyName prop, FilterExpression expression) = FilterTokenParser.Criterion.Parse(tokens); - // Assert - using (new AssertionScope()) - { - prop.Should() - .NotBeSameAs(expected.prop).And - .Be(expected.prop); + // Assert + using (new AssertionScope()) + { + prop.Should() + .NotBeSameAs(expected.prop).And + .Be(expected.prop); - expression.Should() - .NotBeSameAs(expected.expression).And - .Be(expected.expression); - } + expression.Should() + .NotBeSameAs(expected.expression).And + .Be(expected.expression); } + } - public static IEnumerable CriteriaCases + public static IEnumerable CriteriaCases + { + get { - get + yield return new object[] + { + "Firstname=Vandal&Lastname=Savage", + (Expression, bool>>)( + expressions => expressions.Exactly(2) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("Firstname")) && expr.expression.Equals(new StringValueExpression("Vandal"))) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("Lastname")) && expr.expression.Equals(new StringValueExpression("Savage"))) + ) + }; + + yield return new object[] + { + "Firstname=[Vv]andal", + (Expression, bool>>)( + expressions => expressions.Exactly(1) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("Firstname")) + && expr.expression.Equals(new OneOfExpression(new StringValueExpression("Vandal"), + new StringValueExpression("vandal")))) + ) + }; + + foreach (char c in FilterTokenizer.SpecialCharacters) { yield return new object[] { - "Firstname=Vandal&Lastname=Savage", + $@"Firstname=Vand\{c}al&Lastname=Savage", (Expression, bool>>)( expressions => expressions.Exactly(2) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("Firstname")) && expr.expression.Equals(new StringValueExpression("Vandal"))) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("Lastname")) && expr.expression.Equals(new StringValueExpression("Savage"))) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("Firstname")) && expr.expression.Equals(new StringValueExpression($"Vand{c}al"))) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("Lastname")) && expr.expression.Equals(new StringValueExpression("Savage"))) ) }; + } + foreach (char c in FilterTokenizer.SpecialCharacters) + { yield return new object[] { - "Firstname=[Vv]andal", + $@"first_name=Vand\{c}al&last_name=Savage", (Expression, bool>>)( - expressions => expressions.Exactly(1) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("Firstname")) - && expr.expression.Equals(new OneOfExpression(new StringValueExpression("Vandal"), - new StringValueExpression("vandal")))) + expressions => expressions.Exactly(2) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("first_name")) && expr.expression.Equals(new StringValueExpression($"Vand{c}al"))) + && expressions.Once(expr => expr.prop.Equals(new PropertyName("last_name")) && expr.expression.Equals(new StringValueExpression("Savage"))) ) }; - - foreach (char c in FilterTokenizer.SpecialCharacters) - { - yield return new object[] - { - $@"Firstname=Vand\{c}al&Lastname=Savage", - (Expression, bool>>)( - expressions => expressions.Exactly(2) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("Firstname")) && expr.expression.Equals(new StringValueExpression($"Vand{c}al"))) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("Lastname")) && expr.expression.Equals(new StringValueExpression("Savage"))) - ) - }; - } - - foreach (char c in FilterTokenizer.SpecialCharacters) - { - yield return new object[] - { - $@"first_name=Vand\{c}al&last_name=Savage", - (Expression, bool>>)( - expressions => expressions.Exactly(2) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("first_name")) && expr.expression.Equals(new StringValueExpression($"Vand{c}al"))) - && expressions.Once(expr => expr.prop.Equals(new PropertyName("last_name")) && expr.expression.Equals(new StringValueExpression("Savage"))) - ) - }; - } } } + } - /// - /// Tests if can parse a criteria - /// - /// - /// - [Theory] - [MemberData(nameof(CriteriaCases))] - public void Should_parse_Criteria(string input, Expression, bool>> expectation) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + /// + /// Tests if can parse a criteria + /// + /// + /// + [Theory] + [MemberData(nameof(CriteriaCases))] + public void Should_parse_Criteria(string input, Expression, bool>> expectation) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - // Act - IEnumerable<(PropertyName prop, FilterExpression expression)> actual = FilterTokenParser.Criteria.Parse(tokens); + // Act + IEnumerable<(PropertyName prop, FilterExpression expression)> actual = FilterTokenParser.Criteria.Parse(tokens); - // Assert - actual.Should() - .Match(expectation); - } + // Assert + actual.Should() + .Match(expectation); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_DateAndTime(DateTimeExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_DateAndTime(DateTimeExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - DateTimeExpression actual = FilterTokenParser.DateAndTime.Parse(tokens); + // Act + DateTimeExpression actual = FilterTokenParser.DateAndTime.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - public static IEnumerable DateCases + public static IEnumerable DateCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "2019-01-12", - new DateExpression(year : 2019, month: 01, day: 12) - }; - } + "2019-01-12", + new DateExpression(year : 2019, month: 01, day: 12) + }; } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_DateCases(DateExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_DateCases(DateExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - DateExpression actual = FilterTokenParser.Date.Parse(tokens); + // Act + DateExpression actual = FilterTokenParser.Date.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_Groups(GroupExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_Groups(GroupExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - GroupExpression actual = FilterTokenParser.Group.Parse(tokens); + // Act + GroupExpression actual = FilterTokenParser.Group.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - public static IEnumerable ParseGroupCases + public static IEnumerable ParseGroupCases + { + get { - get + string[] cultures = ["fr-FR", "en-GB", "en-US"]; + foreach (string culture in cultures) { - string[] cultures = ["fr-FR", "en-GB", "en-US"]; - foreach (string culture in cultures) + yield return new object[] + { + new GroupExpression(new DateTimeExpression(new(2090, 10, 10), new(3, 0, 40, 583), OffsetExpression.Zero)), + culture + }; + + yield return new object[] { - yield return new object[] - { - new GroupExpression(new DateTimeExpression(new(2090, 10, 10), new(3, 0, 40, 583), OffsetExpression.Zero)), - culture - }; - - yield return new object[] - { - new GroupExpression(new DateTimeExpression(new(2010, 06, 02), new(23, 45, 54, 331), OffsetExpression.Zero)), - culture - }; - } + new GroupExpression(new DateTimeExpression(new(2010, 06, 02), new(23, 45, 54, 331), OffsetExpression.Zero)), + culture + }; } } + } - [Theory] - [MemberData(nameof(ParseGroupCases))] - public void Given_GroupExpression_EscapedParseableString_as_input_Parser_should_return_GroupExpression_that_is_equivalent_to_input(GroupExpression expected, string culture) + [Theory] + [MemberData(nameof(ParseGroupCases))] + public void Given_GroupExpression_EscapedParseableString_as_input_Parser_should_return_GroupExpression_that_is_equivalent_to_input(GroupExpression expected, string culture) + { + _cultureSwitcher.Run(culture, () => { - _cultureSwitcher.Run(culture, () => - { - _outputHelper.WriteLine($"Current culture is '{_cultureSwitcher.CurrentCulture}'"); - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + _outputHelper.WriteLine($"Current culture is '{_cultureSwitcher.CurrentCulture}'"); + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - GroupExpression actual = FilterTokenParser.Group.Parse(tokens); + // Act + GroupExpression actual = FilterTokenParser.Group.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - }); - } + // Assert + AssertThatShould_parse(actual, expected); + }); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_TimeExpression(NonNull expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.Item.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.Item.EscapedParseableString); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_TimeExpression(NonNull expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.Item.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.Item.EscapedParseableString); - // Act - TimeExpression actual = FilterTokenParser.Time.Parse(tokens); + // Act + TimeExpression actual = FilterTokenParser.Time.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected.Item); - } + // Assert + AssertThatShould_parse(actual, expected.Item); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Should_parse_DurationExpression(DurationExpression expected) - { - // Arrange - _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); - TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Should_parse_DurationExpression(DurationExpression expected) + { + // Arrange + _outputHelper.WriteLine($"input : '{expected.EscapedParseableString}'"); + TokenList tokens = _tokenizer.Tokenize(expected.EscapedParseableString); - // Act - DurationExpression actual = FilterTokenParser.Duration.Parse(tokens); + // Act + DurationExpression actual = FilterTokenParser.Duration.Parse(tokens); - // Assert - AssertThatShould_parse(actual, expected); - } + // Assert + AssertThatShould_parse(actual, expected); + } - [Theory] - [InlineData("P", "time separator and duration unit are mising")] - [InlineData("P1Y", "the time separator 'T' is missing after the 'year' duration")] - [InlineData("P0S", "the time separator 'T' is missing before the 'second' duration")] - [InlineData("PT", "the duration string does not contain any duration")] - public void Given_incorrect_input_for_duration_Parser_should_throw_ParseException(string input, string reason) - { - // Arrange - _outputHelper.WriteLine($"input : '{input}'"); - TokenList tokens = _tokenizer.Tokenize(input); + [Theory] + [InlineData("P", "time separator and duration unit are mising")] + [InlineData("P1Y", "the time separator 'T' is missing after the 'year' duration")] + [InlineData("P0S", "the time separator 'T' is missing before the 'second' duration")] + [InlineData("PT", "the duration string does not contain any duration")] + public void Given_incorrect_input_for_duration_Parser_should_throw_ParseException(string input, string reason) + { + // Arrange + _outputHelper.WriteLine($"input : '{input}'"); + TokenList tokens = _tokenizer.Tokenize(input); - // Act - Action callingParserWithInvalidStringInput = () => FilterTokenParser.Duration.Parse(tokens); + // Act + void CallingParserWithInvalidStringInput() => _ = FilterTokenParser.Duration.Parse(tokens); - // Assert - AssertThatParseExceptionIsThrown(callingParserWithInvalidStringInput, reason); - } + // Assert + AssertThatParseExceptionIsThrown(CallingParserWithInvalidStringInput, reason); + } - private static void AssertThatShould_parse(FilterExpression actual, FilterExpression expected, string reason = "") - => actual.Should() - .NotBeSameAs(expected).And - .Be(expected, reason); + private static void AssertThatShould_parse(FilterExpression actual, FilterExpression expected, string reason = "") + => actual.Should() + .NotBeSameAs(expected).And + .Be(expected, reason); - private static void AssertThatParseExceptionIsThrown(Action action, string reason) - { - // Act - action.Should().ThrowExactly(reason); - } + private static void AssertThatParseExceptionIsThrown(Action action, string reason) + { + // Act + action.Should().ThrowExactly(reason); } } diff --git a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs index 17477b12..90796593 100644 --- a/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Parsing/FilterTokenizerTests.cs @@ -1,378 +1,377 @@ -namespace DataFilters.UnitTests.Grammar.Parsing -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using DataFilters.Grammar.Parsing; - using FluentAssertions; - using Superpower; - using Superpower.Model; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - using static DataFilters.Grammar.Parsing.FilterToken; +namespace DataFilters.UnitTests.Grammar.Parsing; - [UnitTest] - [Feature(nameof(DataFilters.Grammar.Parsing))] - [Feature(nameof(FilterTokenizer))] - public class FilterTokenizerTests(ITestOutputHelper outputHelper) - { - private readonly FilterTokenizer _sut = new FilterTokenizer(); +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using DataFilters.Grammar.Parsing; +using FluentAssertions; +using Superpower; +using Superpower.Model; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; +using static DataFilters.Grammar.Parsing.FilterToken; + +[UnitTest] +[Feature(nameof(DataFilters.Grammar.Parsing))] +[Feature(nameof(FilterTokenizer))] +public class FilterTokenizerTests(ITestOutputHelper outputHelper) +{ + private readonly FilterTokenizer _sut = new(); - [Fact] - public void IsTokenizer() => typeof(FilterTokenizer).Should() - .HaveDefaultConstructor().And - .BeAssignableTo>(); + [Fact] + public void IsTokenizer() => typeof(FilterTokenizer).Should() + .HaveDefaultConstructor().And + .BeAssignableTo>(); - public static IEnumerable RecognizeTokensCases + public static IEnumerable RecognizeTokensCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "Firstname=Bruce", - (Expression, bool>>)(results => results.Exactly(15) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 9) - && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 10) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 11) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 12) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 13) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 14) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 15) - ) - }; + "Firstname=Bruce", + (Expression, bool>>)(results => results.Exactly(15) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 9) + && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 10) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 11) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 12) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 13) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 14) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 15) + ) + }; - yield return new object[] - { - "_Firstname=Bruce", - (Expression, bool>>)(results => results.Exactly(16) - && results.Once(result => result.Kind == Underscore && result.Span.EqualsValue("_") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 9) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 10) - && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 11) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 12) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 13) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 14) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 15) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 16) - ) - }; - - yield return new object[] - { - "Firstname=Bru*", - (Expression, bool>>)(results => results.Exactly(14) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 9) - && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 10) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 11) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 12) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 13) - && results.Once(result => result.Kind == Asterisk && result.Span.EqualsValue("*") && result.Span.Position.Column == 14) - ) - }; + yield return new object[] + { + "_Firstname=Bruce", + (Expression, bool>>)(results => results.Exactly(16) + && results.Once(result => result.Kind == Underscore && result.Span.EqualsValue("_") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 9) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 10) + && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 11) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 12) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 13) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 14) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 15) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 16) + ) + }; - yield return new object[] - { - "prop1=Bruce", - (Expression, bool>>)(results => results.Exactly(11) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("p") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("o") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("p") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 9) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 10) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 11) - ) - }; + yield return new object[] + { + "Firstname=Bru*", + (Expression, bool>>)(results => results.Exactly(14) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 9) + && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 10) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 11) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 12) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 13) + && results.Once(result => result.Kind == Asterisk && result.Span.EqualsValue("*") && result.Span.Position.Column == 14) + ) + }; - yield return new object[] - { - "val1|val2", - (Expression, bool>>)(results => results.Exactly(9) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Or && result.Span.EqualsValue("|") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Span.Position.Column == 9) - ) - }; + yield return new object[] + { + "prop1=Bruce", + (Expression, bool>>)(results => results.Exactly(11) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("p") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("o") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("p") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 9) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 10) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 11) + ) + }; - yield return new object[] - { - "val1,val2", - (Expression, bool>>)(results => results.Exactly(9) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == And && result.Span.EqualsValue(",") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Span.Position.Column == 9) - ) - }; + yield return new object[] + { + "val1|val2", + (Expression, bool>>)(results => results.Exactly(9) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Or && result.Span.EqualsValue("|") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Span.Position.Column == 9) + ) + }; - yield return new object[] - { - "!Bruce", - (Expression, bool>>)(results => results.Exactly(6) - && results.Once(result => result.Kind == Bang && result.Span.EqualsValue("!") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 6) - ) - }; + yield return new object[] + { + "val1,val2", + (Expression, bool>>)(results => results.Exactly(9) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == And && result.Span.EqualsValue(",") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("v") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("l") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Span.Position.Column == 9) + ) + }; - yield return new object[] - { - "[", - (Expression, bool>>)(results => results.Once() - && results.Once(result => result.Kind == OpenSquaredBracket && result.Span.EqualsValue("[")) - ) - }; + yield return new object[] + { + "!Bruce", + (Expression, bool>>)(results => results.Exactly(6) + && results.Once(result => result.Kind == Bang && result.Span.EqualsValue("!") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("c") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 6) + ) + }; - yield return new object[] - { - "]", - (Expression, bool>>)(results => results.Once() - && results.Once(result => result.Kind == CloseSquaredBracket && result.Span.EqualsValue("]")) - ) - }; + yield return new object[] + { + "[", + (Expression, bool>>)(results => results.Once() + && results.Once(result => result.Kind == OpenSquaredBracket && result.Span.EqualsValue("[")) + ) + }; - yield return new object[] - { - "10-20", - (Expression, bool>>)(results => results.Exactly(5) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Position.Column == 1) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 2) - && results.Once(result => result.Kind == Dash && result.Span.EqualsValue("-")) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 4) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 5) - ) - }; + yield return new object[] + { + "]", + (Expression, bool>>)(results => results.Once() + && results.Once(result => result.Kind == CloseSquaredBracket && result.Span.EqualsValue("]")) + ) + }; - yield return new object[] - { - " ", - (Expression, bool>>)(results => results.Once() - && results.Once(result => result.Kind == Whitespace && result.Span.EqualsValue(" ")) - ) - }; + yield return new object[] + { + "10-20", + (Expression, bool>>)(results => results.Exactly(5) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Position.Column == 1) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 2) + && results.Once(result => result.Kind == Dash && result.Span.EqualsValue("-")) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 4) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 5) + ) + }; - yield return new object[] - { - ":", - (Expression, bool>>)(results => - results.Exactly(1) - && results.Once(result => result.Kind == Colon && result.Span.EqualsValue(":")) - ) - }; + yield return new object[] + { + " ", + (Expression, bool>>)(results => results.Once() + && results.Once(result => result.Kind == Whitespace && result.Span.EqualsValue(" ")) + ) + }; - yield return new object[] - { - "2019-10-22", - (Expression, bool>>)(results => - results.Exactly(10) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 1) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 2) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Position.Column == 3) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("9") && result.Position.Column == 4) - && results.Once(result => result.Kind == Dash && result.Span.EqualsValue("-") && result.Position.Column == 5) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Position.Column == 6) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 7) - && results.Once(result => result.Kind == Dash && result.Span.EqualsValue("-") && result.Position.Column == 8) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 9) - && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 10) - ) - }; + yield return new object[] + { + ":", + (Expression, bool>>)(results => + results.Exactly(1) + && results.Once(result => result.Kind == Colon && result.Span.EqualsValue(":")) + ) + }; - yield return new object[] - { - "*", - (Expression, bool>>)(results => - results.Exactly(1) - && results.Exactly(result => result.Kind == Asterisk && result.Span.EqualsValue("*"), 1) - ) - }; + yield return new object[] + { + "2019-10-22", + (Expression, bool>>)(results => + results.Exactly(10) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 1) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 2) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Position.Column == 3) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("9") && result.Position.Column == 4) + && results.Once(result => result.Kind == Dash && result.Span.EqualsValue("-") && result.Position.Column == 5) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("1") && result.Position.Column == 6) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("0") && result.Position.Column == 7) + && results.Once(result => result.Kind == Dash && result.Span.EqualsValue("-") && result.Position.Column == 8) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 9) + && results.Once(result => result.Kind == Digit && result.Span.EqualsValue("2") && result.Position.Column == 10) + ) + }; - yield return new object[] - { - "_a", - (Expression, bool>>)(results => - results.Exactly(2) - && results.Exactly(result => result.Kind == Underscore && result.Span.EqualsValue("_"), 1) - && results.Exactly(result => result.Kind == Letter && result.Span.EqualsValue("a"), 1) - ) - }; + yield return new object[] + { + "*", + (Expression, bool>>)(results => + results.Exactly(1) + && results.Exactly(result => result.Kind == Asterisk && result.Span.EqualsValue("*"), 1) + ) + }; - foreach (char c in FilterTokenizer.SpecialCharacters) - { - yield return new object[] - { - $"Firstname=Bru{FilterTokenizer.BackSlash}{c}", - (Expression, bool>>) (results => results.Exactly(14) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 2) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 4) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 5) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 6) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 8) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 9) - && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 10) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 11) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 12) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 13) - && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue(c.ToString()) && result.Span.Position.Column == 15) - ) - }; - } + yield return new object[] + { + "_a", + (Expression, bool>>)(results => + results.Exactly(2) + && results.Exactly(result => result.Kind == Underscore && result.Span.EqualsValue("_"), 1) + && results.Exactly(result => result.Kind == Letter && result.Span.EqualsValue("a"), 1) + ) + }; + foreach (char c in FilterTokenizer.SpecialCharacters) + { yield return new object[] { - @"\", - (Expression, bool>>)(results => - results.Exactly(1) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue(@"\")) + $"Firstname=Bru{FilterTokenizer.BackSlash}{c}", + (Expression, bool>>) (results => results.Exactly(14) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("F") && result.Span.Position.Column == 1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("i") && result.Span.Position.Column == 2) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 3) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("s") && result.Span.Position.Column == 4) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t") && result.Span.Position.Column == 5) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("n") && result.Span.Position.Column == 6) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("a") && result.Span.Position.Column == 7) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("m") && result.Span.Position.Column == 8) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("e") && result.Span.Position.Column == 9) + && results.Once(result => result.Kind == Equal && result.Span.EqualsValue("=") && result.Span.Position.Column == 10) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("B") && result.Span.Position.Column == 11) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("r") && result.Span.Position.Column == 12) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("u") && result.Span.Position.Column == 13) + && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue(c.ToString()) && result.Span.Position.Column == 15) ) }; + } - yield return new object[] - { - @"\\t", - (Expression, bool>>)(results => - results.Exactly(2) - && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue("\\")) - && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t")) - ) - }; + yield return new object[] + { + @"\", + (Expression, bool>>)(results => + results.Exactly(1) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue(@"\")) + ) + }; - yield return new object[] - { - @"""", - (Expression, bool>>)(results => - results.Exactly(1) - && results.Once(result => result.Kind == DoubleQuote && result.Span.EqualsValue(@"""")) - ) - }; + yield return new object[] + { + @"\\t", + (Expression, bool>>)(results => + results.Exactly(2) + && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue("\\")) + && results.Once(result => result.Kind == Letter && result.Span.EqualsValue("t")) + ) + }; - yield return new object[] - { - "+", - (Expression, bool>>)(results => results.Once() - && results.Once(result => result.Kind == None && result.Span.EqualsValue("+")) - ) - }; + yield return new object[] + { + @"""", + (Expression, bool>>)(results => + results.Exactly(1) + && results.Once(result => result.Kind == DoubleQuote && result.Span.EqualsValue(@"""")) + ) + }; - foreach (char chr in FilterTokenizer.SpecialCharacters) - { - switch (chr) - { - case FilterTokenizer.DoubleQuote: - yield return new object[] - { - @$"""\{chr}""", - (Expression, bool>>)(results => results.Exactly(3) - && results.Once(result => result.Kind == DoubleQuote - && result.Position.Column == 1) - && results.Once(result => result.Kind == Escaped - && result.Position.Column == 3 - && result.Span.EqualsValue(@"""")) - && results.Once(result => result.Kind == DoubleQuote - && result.Position.Column == 4) - ) - }; - break; - case FilterTokenizer.BackSlash: - yield return new object[] - { - @$"""\{chr}""", - (Expression, bool>>)(results => results.Exactly(3) - && results.Once(result => result.Kind == DoubleQuote && result.Position.Column == 1) - && results.Once(result => result.Kind == Escaped - && result.Position.Column == 3 - && result.Span.EqualsValue(@"\")) - && results.Once(result => result.Kind == DoubleQuote - && result.Position.Column == 4) - ) - }; - break; - default: - yield return new object[] - { - @$"""\{chr}""", - (Expression, bool>>)(results => results.Exactly(4) - && results.Once(result => result.Kind == DoubleQuote && result.Position.Column == 1) - && results.Once(result => result.Kind == Escaped && result.Position.Column == 2 && result.Span.EqualsValue("\\")) - && results.Once(result => result.Kind == Escaped && result.Position.Column == 3 && result.Span.EqualsValue($"{chr}")) - && results.Once(result => result.Kind == DoubleQuote && result.Position.Column == 4) - ) - }; - break; - } - } + yield return new object[] + { + "+", + (Expression, bool>>)(results => results.Once() + && results.Once(result => result.Kind == None && result.Span.EqualsValue("+")) + ) + }; - yield return new object[] + foreach (char chr in FilterTokenizer.SpecialCharacters) + { + switch (chr) { - @"""\[", - (Expression, bool>>)(results => results.Exactly(3) - && results.Once(result => result.Kind == DoubleQuote && result.Span.EqualsValue(@"""")) - && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue(@"\")) - && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue("[")) - ) - }; + case FilterTokenizer.DoubleQuote: + yield return new object[] + { + @$"""\{chr}""", + (Expression, bool>>)(results => results.Exactly(3) + && results.Once(result => result.Kind == DoubleQuote + && result.Position.Column == 1) + && results.Once(result => result.Kind == Escaped + && result.Position.Column == 3 + && result.Span.EqualsValue(@"""")) + && results.Once(result => result.Kind == DoubleQuote + && result.Position.Column == 4) + ) + }; + break; + case FilterTokenizer.BackSlash: + yield return new object[] + { + @$"""\{chr}""", + (Expression, bool>>)(results => results.Exactly(3) + && results.Once(result => result.Kind == DoubleQuote && result.Position.Column == 1) + && results.Once(result => result.Kind == Escaped + && result.Position.Column == 3 + && result.Span.EqualsValue(@"\")) + && results.Once(result => result.Kind == DoubleQuote + && result.Position.Column == 4) + ) + }; + break; + default: + yield return new object[] + { + @$"""\{chr}""", + (Expression, bool>>)(results => results.Exactly(4) + && results.Once(result => result.Kind == DoubleQuote && result.Position.Column == 1) + && results.Once(result => result.Kind == Escaped && result.Position.Column == 2 && result.Span.EqualsValue("\\")) + && results.Once(result => result.Kind == Escaped && result.Position.Column == 3 && result.Span.EqualsValue($"{chr}")) + && results.Once(result => result.Kind == DoubleQuote && result.Position.Column == 4) + ) + }; + break; + } } + + yield return new object[] + { + @"""\[", + (Expression, bool>>)(results => results.Exactly(3) + && results.Once(result => result.Kind == DoubleQuote && result.Span.EqualsValue(@"""")) + && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue(@"\")) + && results.Once(result => result.Kind == Escaped && result.Span.EqualsValue("[")) + ) + }; } + } - [Theory] - [MemberData(nameof(RecognizeTokensCases))] - public void RecognizeTokens(string input, Expression, bool>> expectation) - { - outputHelper.WriteLine($"input : '{input}'"); - // Act - TokenList tokens = _sut.Tokenize(input); + [Theory] + [MemberData(nameof(RecognizeTokensCases))] + public void RecognizeTokens(string input, Expression, bool>> expectation) + { + outputHelper.WriteLine($"input : '{input}'"); + // Act + TokenList tokens = _sut.Tokenize(input); - // Assert - outputHelper.WriteLine($"Tokens : {tokens.Select(token => new { Value = token.ToStringValue(), Kind = token.Kind.ToString(), token.Position.Column }).Jsonify()}"); + // Assert + outputHelper.WriteLine($"Tokens : {tokens.Select(token => new { Value = token.ToStringValue(), Kind = token.Kind.ToString(), token.Position.Column }).Jsonify()}"); - tokens.Should() - .Match(expectation); - } + tokens.Should() + .Match(expectation); } } \ No newline at end of file diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs index e1b6f529..87435eeb 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/AndExpressionTests.cs @@ -1,237 +1,236 @@  -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +[Feature(nameof(AndExpression))] +public class AndExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using System.Linq; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - [Feature(nameof(AndExpression))] - public class AndExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(AndExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement().And + .HaveConstructor(new[] { typeof(FilterExpression), typeof(FilterExpression) }).And + .HaveProperty("Left").And + .HaveProperty("Right"); + + public static IEnumerable ArgumentNullExceptionCases { - [Fact] - public void IsFilterExpression() => typeof(AndExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement().And - .HaveConstructor(new[] { typeof(FilterExpression), typeof(FilterExpression) }).And - .HaveProperty("Left").And - .HaveProperty("Right"); - - public static IEnumerable ArgumentNullExceptionCases + get { - get - { - FilterExpression[] left = [new StartsWithExpression("ce"), null]; + FilterExpression[] left = [new StartsWithExpression("ce"), null]; - return left.CrossJoin(left, (left, right) => (left, right)) - .Where(tuple => tuple.left == null || tuple.right is null) - .Select(tuple => new object[] { tuple.left, tuple.right }); - } + return left.CrossJoin(left, (left, right) => (left, right)) + .Where(tuple => tuple.left == null || tuple.right is null) + .Select(tuple => new object[] { tuple.left, tuple.right }); } + } - [Theory] - [MemberData(nameof(ArgumentNullExceptionCases))] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null(FilterExpression left, FilterExpression right) - { - // Act - Action action = () => new AndExpression(left, right); + [Theory] + [MemberData(nameof(ArgumentNullExceptionCases))] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null(FilterExpression left, FilterExpression right) + { + // Act + Action action = () => _ = new AndExpression(left, right); - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be null"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - true, - "operands are the same and in the same order" - }; - - yield return new object[] - { - new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop3")), - false, - "one operand is different" - }; - - yield return new object[] - { - new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new AndExpression(new StartsWithExpression("prop2"), new StartsWithExpression("prop1")), - true, - "operands are the same but their order differs" - }; - } + new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + true, + "operands are the same and in the same order" + }; + + yield return new object[] + { + new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop3")), + false, + "one operand is different" + }; + + yield return new object[] + { + new AndExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new AndExpression(new StartsWithExpression("prop2"), new StartsWithExpression("prop1")), + true, + "operands are the same but their order differs" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_work_as_expected(AndExpression first, object other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_work_as_expected(AndExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = first.Equals(other); + // Act + bool actual = first.Equals(other); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_AndExpression_GetComplexity_should_return_left_complexity_multiply_by_right_complexity(AndExpression and) - { - // Arrange - double expected = and.Left.Complexity * and.Right.Complexity; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_AndExpression_GetComplexity_should_return_left_complexity_multiply_by_right_complexity(AndExpression and) + { + // Arrange + double expected = and.Left.Complexity * and.Right.Complexity; - // Act - double actual = and.Complexity; + // Act + double actual = and.Complexity; - // Assert - actual.Should().Be(expected); - } + // Assert + actual.Should().Be(expected); + } - public static IEnumerable SimplifyCases + public static IEnumerable SimplifyCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new AndExpression(new StringValueExpression("val"), new StringValueExpression("val")), - new StringValueExpression("val") - }; - - yield return new object[] - { - new AndExpression(new StringValueExpression("val"), new OrExpression(new StringValueExpression("val"), new StringValueExpression("val"))), - new StringValueExpression("val") - }; - - yield return new object[] - { - new AndExpression(new OrExpression(new StringValueExpression("val"), new StringValueExpression("val")), new StringValueExpression("val")), - new StringValueExpression("val") - }; - - yield return new object[] - { - new AndExpression(new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), - new BoundaryExpression(new NumericValueExpression("-1"), true)), - new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), - new BoundaryExpression(new NumericValueExpression("-1"), true))), - new NumericValueExpression("-1"), - }; - } - } + new AndExpression(new StringValueExpression("val"), new StringValueExpression("val")), + new StringValueExpression("val") + }; - [Theory] - [MemberData(nameof(SimplifyCases))] - public void Given_AndExpression_Simplify_should_return_the_expected_expression(AndExpression andExpression, FilterExpression expected) - { - // Act - FilterExpression actual = andExpression.Simplify(); + yield return new object[] + { + new AndExpression(new StringValueExpression("val"), new OrExpression(new StringValueExpression("val"), new StringValueExpression("val"))), + new StringValueExpression("val") + }; + + yield return new object[] + { + new AndExpression(new OrExpression(new StringValueExpression("val"), new StringValueExpression("val")), new StringValueExpression("val")), + new StringValueExpression("val") + }; - // Assert - actual.Should() - .Be(expected); + yield return new object[] + { + new AndExpression(new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), + new BoundaryExpression(new NumericValueExpression("-1"), true)), + new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), + new BoundaryExpression(new NumericValueExpression("-1"), true))), + new NumericValueExpression("-1"), + }; } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_AndExpression_instances_one_and_two_where_oneU002Eleft_eq_twoU002Eright_and_oneU002Eright_eq_twoU002Eleft_IsEquivalentTo_should_return_true(FilterExpression first, FilterExpression second) - { - // Arrange - AndExpression one = new(first, second); - AndExpression two = new(second, first); + [Theory] + [MemberData(nameof(SimplifyCases))] + public void Given_AndExpression_Simplify_should_return_the_expected_expression(AndExpression andExpression, FilterExpression expected) + { + // Act + FilterExpression actual = andExpression.Simplify(); - // Act - bool actual = one.IsEquivalentTo(two); + // Assert + actual.Should() + .Be(expected); + } - // Assert - actual.Should() - .BeTrue(); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_AndExpression_instances_one_and_two_where_oneU002Eleft_eq_twoU002Eright_and_oneU002Eright_eq_twoU002Eleft_IsEquivalentTo_should_return_true(FilterExpression first, FilterExpression second) + { + // Arrange + AndExpression one = new(first, second); + AndExpression two = new(second, first); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_reflexive(AndExpression and) - => and.IsEquivalentTo(and).Should().BeTrue(); + // Act + bool actual = one.IsEquivalentTo(two); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_AndExpression_instances_first_and_second_and_firstU002ERight_eq_secondU002Eright_and_firstU002ELeft_eq_secondU002ELeft_Equals_should_returns_true(FilterExpression left, FilterExpression right) - { - // Arrange - AndExpression first = new(left, right); - AndExpression second = new(left, right); + // Assert + actual.Should() + .BeTrue(); + } - // Assert - first.Should().Be(second); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_reflexive(AndExpression and) + => and.IsEquivalentTo(and).Should().BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void An_AndExpression_Equals_should_neq_false(FilterExpression left, FilterExpression right) - { - // Arrange - AndExpression first = new(left, right); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_AndExpression_instances_first_and_second_and_firstU002ERight_eq_secondU002Eright_and_firstU002ELeft_eq_secondU002ELeft_Equals_should_returns_true(FilterExpression left, FilterExpression right) + { + // Arrange + AndExpression first = new(left, right); + AndExpression second = new(left, right); - // Act - bool actual = first.Equals(null); + // Assert + first.Should().Be(second); + } - // Assert - actual.Should() - .BeFalse(); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void An_AndExpression_Equals_should_neq_false(FilterExpression left, FilterExpression right) + { + // Arrange + AndExpression first = new(left, right); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_AndExpression_instance_where_instanceU002ELeft_is_equivalent_to_instanceU002ERight_Simplify_should_return_the_expression_with_the_lowest_Complexity(FilterExpression expression, PositiveInt count) - { - // Arrange - OneOfExpression oneOfExpression = new(Enumerable.Repeat(expression, count.Item + 1) // if count == 1 - .ToArray()); + // Act + bool actual = first.Equals(null); - outputHelper.WriteLine($"{nameof(oneOfExpression)} : '{oneOfExpression.EscapedParseableString}'"); - outputHelper.WriteLine($"{nameof(oneOfExpression.Complexity)} : {oneOfExpression.Complexity}"); + // Assert + actual.Should() + .BeFalse(); + } - AndExpression and = new(oneOfExpression, expression); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_AndExpression_instance_where_instanceU002ELeft_is_equivalent_to_instanceU002ERight_Simplify_should_return_the_expression_with_the_lowest_Complexity(FilterExpression expression, PositiveInt count) + { + // Arrange + OneOfExpression oneOfExpression = new(Enumerable.Repeat(expression, count.Item + 1) // if count == 1 + .ToArray()); - // Act - FilterExpression actual = and.Simplify(); - bool isEquivalent = actual.IsEquivalentTo(oneOfExpression); - double actualComplexity = actual.Complexity; + outputHelper.WriteLine($"{nameof(oneOfExpression)} : '{oneOfExpression.EscapedParseableString}'"); + outputHelper.WriteLine($"{nameof(oneOfExpression.Complexity)} : {oneOfExpression.Complexity}"); - // Assert - outputHelper.WriteLine($"actual : {actual.EscapedParseableString} (Complexity : {actual.Complexity})"); - outputHelper.WriteLine($"actual is equivalent to expression : {isEquivalent})"); + AndExpression and = new(oneOfExpression, expression); - isEquivalent.Should().BeTrue(); - actualComplexity.Should().BeLessThan(oneOfExpression.Complexity); - } + // Act + FilterExpression actual = and.Simplify(); + bool isEquivalent = actual.IsEquivalentTo(oneOfExpression); + double actualComplexity = actual.Complexity; - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_operand_is_a_AndFilterExpression_instance_When_right_is_not_null_Constructor_should_wrap_left_inside_a_GroupExpression_instance(NonNull left, NonNull right) - { - // Act - AndExpression and = new(left.Item, right.Item); + // Assert + outputHelper.WriteLine($"actual : {actual.EscapedParseableString} (Complexity : {actual.Complexity})"); + outputHelper.WriteLine($"actual is equivalent to expression : {isEquivalent})"); - // Assert - and.Left.Should() - .BeOfType($"Left instance is a '{nameof(AndExpression)}'"); - } + isEquivalent.Should().BeTrue(); + actualComplexity.Should().BeLessThan(oneOfExpression.Complexity); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_operand_is_a_AndFilterExpression_instance_When_right_is_not_null_Constructor_should_wrap_left_inside_a_GroupExpression_instance(NonNull left, NonNull right) + { + // Act + AndExpression and = new(left.Item, right.Item); + + // Assert + and.Left.Should() + .BeOfType($"Left instance is a '{nameof(AndExpression)}'"); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs index 8a0e184e..2dfdd51f 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/AsteriskExpressionTests.cs @@ -1,92 +1,91 @@ -namespace DataFilters.UnitTests.Grammar.Syntax -{ - using System; - using System.Collections.Generic; +namespace DataFilters.UnitTests.Grammar.Syntax; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; +using System; +using System.Collections.Generic; - using FluentAssertions; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; - using FsCheck; - using FsCheck.Xunit; +using FluentAssertions; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; +using FsCheck; +using FsCheck.Xunit; - [UnitTest] - [Feature(nameof(AsteriskExpression))] - public class AsteriskExpressionTests(ITestOutputHelper outputHelper) - { - [Fact] - public void IsFilterExpression() => typeof(AsteriskExpression).Should() - .BeAssignableTo().And - .Implement>().And - .HaveDefaultConstructor(); +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; - public static IEnumerable EqualsCases +[UnitTest] +[Feature(nameof(AsteriskExpression))] +public class AsteriskExpressionTests(ITestOutputHelper outputHelper) +{ + [Fact] + public void IsFilterExpression() => typeof(AsteriskExpression).Should() + .BeAssignableTo().And + .Implement>().And + .HaveDefaultConstructor(); + + public static IEnumerable EqualsCases + { + get { - get - { - yield return new object[] { AsteriskExpression.Instance, null, false, "Comparing to null" }; - yield return new object[] { AsteriskExpression.Instance, AsteriskExpression.Instance, true, "Comparing to null" }; - } + yield return new object[] { AsteriskExpression.Instance, null, false, "Comparing to null" }; + yield return new object[] { AsteriskExpression.Instance, AsteriskExpression.Instance, true, "Comparing to null" }; } + } + + [Theory] + [MemberData(nameof(EqualsCases))] + public void TestEquals(AsteriskExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - [Theory] - [MemberData(nameof(EqualsCases))] - public void TestEquals(AsteriskExpression first, object other, bool expected, string reason) + // Act + bool actual = first.Equals(other); + int actualHashCode = first.GetHashCode(); + + // Assert + actual.Should() + .Be(expected, reason); + if (expected) { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); - - // Act - bool actual = first.Equals(other); - int actualHashCode = first.GetHashCode(); - - // Assert - actual.Should() - .Be(expected, reason); - if (expected) - { - actualHashCode.Should() - .Be(other?.GetHashCode(), reason); - } - else - { - actualHashCode.Should() - .NotBe(other?.GetHashCode(), reason); - } + actualHashCode.Should() + .Be(other?.GetHashCode(), reason); } + else + { + actualHashCode.Should() + .NotBe(other?.GetHashCode(), reason); + } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_AsteriskExpression_GetComplexity_should_return_1() => AsteriskExpression.Instance.Complexity.Should().Be(1); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_AsteriskExpression_GetComplexity_should_return_1() => AsteriskExpression.Instance.Complexity.Should().Be(1); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_AsteriskExpression_When_adding_ConstantValueExpression_Should_returns_EndsWithExpression(NonNull constantExpression) - { - // Arrange - AsteriskExpression asterisk = AsteriskExpression.Instance; - EndsWithExpression expected = new(constantExpression.Item.Value); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_AsteriskExpression_When_adding_ConstantValueExpression_Should_returns_EndsWithExpression(NonNull constantExpression) + { + // Arrange + AsteriskExpression asterisk = AsteriskExpression.Instance; + EndsWithExpression expected = new(constantExpression.Item.Value); - // Act - EndsWithExpression actual = asterisk + constantExpression.Item; + // Act + EndsWithExpression actual = asterisk + constantExpression.Item; - actual.Should().Be(expected); - } + actual.Should().Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_ConstantValueExpresion_When_adding_AsteriskExpression_Should_returns_StartsWithWithExpression(NonNull constantExpression) - { - // Arrange - AsteriskExpression asterisk = AsteriskExpression.Instance; - StartsWithExpression expected = new(constantExpression.Item.Value); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_ConstantValueExpresion_When_adding_AsteriskExpression_Should_returns_StartsWithWithExpression(NonNull constantExpression) + { + // Arrange + AsteriskExpression asterisk = AsteriskExpression.Instance; + StartsWithExpression expected = new(constantExpression.Item.Value); - // Act - StartsWithExpression actual = constantExpression.Item + asterisk; + // Act + StartsWithExpression actual = constantExpression.Item + asterisk; - actual.Should().Be(expected); - } + actual.Should().Be(expected); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs index 1acad179..6b5ef32a 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/BoundaryExpressionTests.cs @@ -1,121 +1,120 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Categories; + +[UnitTest] +[Feature(nameof(DataFilters.Grammar.Syntax))] +[Feature(nameof(IntervalExpression))] +public class BoundaryExpressionTests { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Categories; - - [UnitTest] - [Feature(nameof(DataFilters.Grammar.Syntax))] - [Feature(nameof(IntervalExpression))] - public class BoundaryExpressionTests + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_a_BoundaryExpression_instance_should_be_equals_to_itself(BoundaryExpression input) { - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_a_BoundaryExpression_instance_should_be_equals_to_itself(BoundaryExpression input) - { - // Act - bool actual = input.Equals(input); + // Act + bool actual = input.Equals(input); - // Assert - actual.Should().BeTrue("'equals' implementation should be reflexive"); - } + // Assert + actual.Should().BeTrue("'equals' implementation should be reflexive"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void A_BoundaryExpression_instance_should_not_be_equal_to_null(BoundaryExpression input) - { - // Act - bool actual = input.Equals(null); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void A_BoundaryExpression_instance_should_not_be_equal_to_null(BoundaryExpression input) + { + // Act + bool actual = input.Equals(null); - // Assert - actual.Should().BeFalse("a not null boundaryexpression is not equal to null"); - } + // Assert + actual.Should().BeFalse("a not null boundaryexpression is not equal to null"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Two_BoundaryExpressions_instances_with_same_expression_and_included_should_be_equal(BoundaryExpression source) - { - // Arrange - BoundaryExpression first = new(source.Expression, source.Included); - BoundaryExpression other = new(source.Expression, source.Included); - - // Act - bool actual = first.Equals(other); - int firstHashCode = first.GetHashCode(); - int otherHashCode = other.GetHashCode(); - - // Assert - actual.Should().BeTrue(); - firstHashCode.Should().Be(otherHashCode); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Two_BoundaryExpressions_instances_with_same_expression_and_included_should_be_equal(BoundaryExpression source) + { + // Arrange + BoundaryExpression first = new(source.Expression, source.Included); + BoundaryExpression other = new(source.Expression, source.Included); + + // Act + bool actual = first.Equals(other); + int firstHashCode = first.GetHashCode(); + int otherHashCode = other.GetHashCode(); + + // Assert + actual.Should().BeTrue(); + firstHashCode.Should().Be(otherHashCode); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Ctor_should_throw_ArgumentNullException_when_expression_is_null(bool included) - { - // Act - Action buildInstanceWithNullExpression = () => new BoundaryExpression(null, included); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Ctor_should_throw_ArgumentNullException_when_expression_is_null(bool included) + { + // Act + Action buildInstanceWithNullExpression = () => _ = new BoundaryExpression(null, included); - // Assert - buildInstanceWithNullExpression.Should() - .ThrowExactly(); - } + // Assert + buildInstanceWithNullExpression.Should() + .ThrowExactly(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - { - // Act - bool actual = expression.Item.Equals(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + { + // Act + bool actual = expression.Item.Equals(expression.Item); - // Assert - actual.Should().BeTrue("'equals' implementation must be reflexive"); - } + // Assert + actual.Should().BeTrue("'equals' implementation must be reflexive"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull group, NonNull otherExpression) - { - // Act - bool groupEqualOtherExpression = group.Item.Equals(otherExpression.Item); - bool otherExpressionEqualGroup = otherExpression.Item.Equals(group); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull group, NonNull otherExpression) + { + // Act + bool groupEqualOtherExpression = group.Item.Equals(otherExpression.Item); + bool otherExpressionEqualGroup = otherExpression.Item.Equals(group); - // Assert - groupEqualOtherExpression.Should().Be(otherExpressionEqualGroup, "'equals' impelemntation must be symetric"); - } + // Assert + groupEqualOtherExpression.Should().Be(otherExpressionEqualGroup, "'equals' impelemntation must be symetric"); + } - public static IEnumerable EqualsBehaviourCases + public static IEnumerable EqualsBehaviourCases + { + get { - get + yield return new object[] + { + new BoundaryExpression(new DateExpression(), true), + new BoundaryExpression(new DateExpression(), true), + true, + $"Two instances with {nameof(DateExpression)} that are equal" + }; + + yield return new object[] { - yield return new object[] - { - new BoundaryExpression(new DateExpression(), true), - new BoundaryExpression(new DateExpression(), true), - true, - $"Two instances with {nameof(DateExpression)} that are equal" - }; - - yield return new object[] - { - new BoundaryExpression(new DateTimeExpression(new (), new (), new()), true), - new BoundaryExpression(new DateTimeExpression(new (), new (), new()), true), - true, - $"Two instances with {nameof(DateExpression)} that are equal" - }; - } + new BoundaryExpression(new DateTimeExpression(new (), new (), new()), true), + new BoundaryExpression(new DateTimeExpression(new (), new (), new()), true), + true, + $"Two instances with {nameof(DateExpression)} that are equal" + }; } + } - [Theory] - [MemberData(nameof(EqualsBehaviourCases))] - public void Equals_should_behave_as_expected(BoundaryExpression expression, object other, bool expected, string reason) - { - // Act - bool actual = expression.Equals(other); + [Theory] + [MemberData(nameof(EqualsBehaviourCases))] + public void Equals_should_behave_as_expected(BoundaryExpression expression, object other, bool expected, string reason) + { + // Act + bool actual = expression.Equals(other); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs index cc0dea2d..703756f8 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/BracketExpressionTests.cs @@ -1,140 +1,139 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; + +public class BracketExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using System.Linq; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - - public class BracketExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(BracketExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement(); + + [Fact] + public void Ctor_Throws_ArgumentNullException_If_Value_Is_Null() { - [Fact] - public void IsFilterExpression() => typeof(BracketExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement(); - - [Fact] - public void Ctor_Throws_ArgumentNullException_If_Value_Is_Null() - { - // Act - Action action = () => new BracketExpression(null); + // Act + Action action = () => _ = new BracketExpression(null); - // Assert - action.Should() - .ThrowExactly($"{nameof(BracketExpression)}.{nameof(BracketExpression.Values)} cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"{nameof(BracketExpression)}.{nameof(BracketExpression.Values)} cannot be null"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new BracketExpression(new ConstantBracketValue("aBc")), - new BracketExpression(new ConstantBracketValue("aBc")), - true, - $"Two {nameof(BracketExpression)} instances built with inputs that are equals" - }; - - yield return new object[] - { - new BracketExpression(new ConstantBracketValue("aBc")), - new BracketExpression(new ConstantBracketValue("aBc")), - true, - $"Two {nameof(BracketExpression)} instances built with inputs that are equals" - }; - } + new BracketExpression(new ConstantBracketValue("aBc")), + new BracketExpression(new ConstantBracketValue("aBc")), + true, + $"Two {nameof(BracketExpression)} instances built with inputs that are equals" + }; + + yield return new object[] + { + new BracketExpression(new ConstantBracketValue("aBc")), + new BracketExpression(new ConstantBracketValue("aBc")), + true, + $"Two {nameof(BracketExpression)} instances built with inputs that are equals" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_behave_as_expected(BracketExpression expression, object obj, bool expected, string reason) - { - // Act - bool actual = expression.Equals(obj); + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_behave_as_expected(BracketExpression expression, object obj, bool expected, string reason) + { + // Act + bool actual = expression.Equals(obj); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Two_BracketExpression_instances_built_with_different_inputs_should_not_be_equal(NonEmptyArray one, - NonEmptyArray two) - { - // Arrange - BracketExpression first = new(one.Item); - BracketExpression second = new(two.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Two_BracketExpression_instances_built_with_different_inputs_should_not_be_equal(NonEmptyArray one, + NonEmptyArray two) + { + // Arrange + BracketExpression first = new(one.Item); + BracketExpression second = new(two.Item); - first.Equals(second).ToProperty().When(one.Item.Equals(two.Item)).VerboseCheck(outputHelper); - } + first.Equals(second).ToProperty().When(one.Item.Equals(two.Item)).VerboseCheck(outputHelper); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Complexity_should_depends_on_input(NonEmptyArray values) - { - // Arrange - BracketExpression bracketExpression = new(values.Item); - double expected = values.Item.Select(value => value.Complexity) - .Aggregate((initial, next) => initial * next); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Complexity_should_depends_on_input(NonEmptyArray values) + { + // Arrange + BracketExpression bracketExpression = new(values.Item); + double expected = values.Item.Select(value => value.Complexity) + .Aggregate((initial, next) => initial * next); - // Act - double complexity = bracketExpression.Complexity; + // Act + double complexity = bracketExpression.Complexity; - // Assert - complexity.Should().Be(expected); - } + // Assert + complexity.Should().Be(expected); + } - public static IEnumerable ComplexityCases + public static IEnumerable ComplexityCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new BracketExpression - ( - new ConstantBracketValue("aa"), - new RangeBracketValue('a', 'c') - ), - new ConstantBracketValue("aa").Complexity * new RangeBracketValue('a', 'c').Complexity - }; - } + new BracketExpression + ( + new ConstantBracketValue("aa"), + new RangeBracketValue('a', 'c') + ), + new ConstantBracketValue("aa").Complexity * new RangeBracketValue('a', 'c').Complexity + }; } + } - [Theory] - [MemberData(nameof(ComplexityCases))] - public void Complexity_should_behave_as_expected(BracketExpression expression, double expected) - { - // Act - double actual = expression.Complexity; + [Theory] + [MemberData(nameof(ComplexityCases))] + public void Complexity_should_behave_as_expected(BracketExpression expression, double expected) + { + // Act + double actual = expression.Complexity; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_BracketRangeValue_IsEquivalentTo_should_be_equivalent_to_many_OrExpression_where_each_expression_contains_one_charater(NonNull rangeBracketValue) - { - // Arrange - BracketExpression rangeBracketExpression = new(rangeBracketValue.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_BracketRangeValue_IsEquivalentTo_should_be_equivalent_to_many_OrExpression_where_each_expression_contains_one_charater(NonNull rangeBracketValue) + { + // Arrange + BracketExpression rangeBracketExpression = new(rangeBracketValue.Item); - OneOfExpression oneOf = new(Enumerable.Range(rangeBracketValue.Item.Start, - rangeBracketValue.Item.End - rangeBracketValue.Item.Start + 1) - .Select(ascii => new StringValueExpression(((char)ascii).ToString())) - .ToArray()); + OneOfExpression oneOf = new(Enumerable.Range(rangeBracketValue.Item.Start, + rangeBracketValue.Item.End - rangeBracketValue.Item.Start + 1) + .Select(ascii => new StringValueExpression(((char)ascii).ToString())) + .ToArray()); - // Act - bool actual = rangeBracketExpression.IsEquivalentTo(oneOf); + // Act + bool actual = rangeBracketExpression.IsEquivalentTo(oneOf); - // Assert - actual.Should().BeTrue($"Range expression : {rangeBracketValue.Item}"); - } + // Assert + actual.Should().BeTrue($"Range expression : {rangeBracketValue.Item}"); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs index c6b99534..5d1b89de 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/ConstantBracketValueTests.cs @@ -1,80 +1,79 @@ -namespace DataFilters.UnitTests.Grammar.Syntax -{ - using System; - using System.Linq; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Linq; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; - public class ConstantBracketValueTests +public class ConstantBracketValueTests +{ + [Property] + public void Value_should_be_set_by_the_parameter_of_the_constructor(NonNull input) { - [Property] - public void Value_should_be_set_by_the_parameter_of_the_constructor(NonNull input) - { - // Act - ConstantBracketValue constantBracketValue = new(input.Item); + // Act + ConstantBracketValue constantBracketValue = new(input.Item); - // Assert - constantBracketValue.Value.Should() - .Be(input.Item); - } + // Assert + constantBracketValue.Value.Should() + .Be(input.Item); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_RangeBracketValue_Equals_should_returns_true_when_ConstantBracketValue_contains_all_letters_of_the_interval(NonNull rangeBracketValue) - { - // Arrange - ConstantBracketValue constantBracketValue = new(Enumerable.Range(rangeBracketValue.Item.Start, rangeBracketValue.Item.End - rangeBracketValue.Item.Start + 1) - .Select(ascii => ((char)ascii).ToString()) - .Aggregate((accumulate, current) => $"{accumulate}{current}")); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_RangeBracketValue_Equals_should_returns_true_when_ConstantBracketValue_contains_all_letters_of_the_interval(NonNull rangeBracketValue) + { + // Arrange + ConstantBracketValue constantBracketValue = new(Enumerable.Range(rangeBracketValue.Item.Start, rangeBracketValue.Item.End - rangeBracketValue.Item.Start + 1) + .Select(ascii => ((char)ascii).ToString()) + .Aggregate((accumulate, current) => $"{accumulate}{current}")); - // Act - bool actual = constantBracketValue.Equals(rangeBracketValue.Item); + // Act + bool actual = constantBracketValue.Equals(rangeBracketValue.Item); - actual.Should().BeTrue($"Range expression : {rangeBracketValue} and Constant expression is {constantBracketValue.Value}"); - } + actual.Should().BeTrue($"Range expression : {rangeBracketValue} and Constant expression is {constantBracketValue.Value}"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_ConstantBracketValues_left_eq_right_should_be_returns_same_value_as_Equals(ConstantBracketValue left, ConstantBracketValue right) - => (left == right).When(left.Equals(right)).Label($"Left, Right : {(left, right)}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_ConstantBracketValues_left_eq_right_should_be_returns_same_value_as_Equals(ConstantBracketValue left, ConstantBracketValue right) + => (left == right).When(left.Equals(right)).Label($"Left, Right : {(left, right)}"); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_ConstantBracketValues_left_neq_right_should_be_returns_same_value_as_Equals(ConstantBracketValue left, ConstantBracketValue right) - => (left != right).When(!left.Equals(right)).Label($"Left, Right : {(left, right)}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_ConstantBracketValues_left_neq_right_should_be_returns_same_value_as_Equals(ConstantBracketValue left, ConstantBracketValue right) + => (left != right).When(!left.Equals(right)).Label($"Left, Right : {(left, right)}"); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - { - // Act - bool actual = expression.Item.Equals(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + { + // Act + bool actual = expression.Item.Equals(expression.Item); - // Assert - actual.Should().BeTrue("'equals' implementation is reflexive"); - } + // Assert + actual.Should().BeTrue("'equals' implementation is reflexive"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - { - // Act - bool actual = expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + { + // Act + bool actual = expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item); - // Assert - actual.Should().BeTrue("'equals' implementation should be symetric"); - } + // Assert + actual.Should().BeTrue("'equals' implementation should be symetric"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_ConstantBracketValue_Complexity_should_be_equal_to_inner_expression_complexity(ConstantBracketValue value) - { - // Arrange - double expected = 1 + Math.Pow(2, value.Value.Length); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_ConstantBracketValue_Complexity_should_be_equal_to_inner_expression_complexity(ConstantBracketValue value) + { + // Arrange + double expected = 1 + Math.Pow(2, value.Value.Length); - // Act - double actual = value.Complexity; + // Act + double actual = value.Complexity; - // Assert - actual.Should().Be(expected); - } + // Assert + actual.Should().Be(expected); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs index 2073a7c2..1c6b3eec 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/ContainsExpressionTests.cs @@ -1,183 +1,182 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; + +public class ContainsExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - - public class ContainsExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(ContainsExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement().And + .Implement(); + + [Fact] + public void Given_string_argument_is_null_Constructor_should_thros_ArgumentNullException() { - [Fact] - public void IsFilterExpression() => typeof(ContainsExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement().And - .Implement(); - - [Fact] - public void Given_string_argument_is_null_Constructor_should_thros_ArgumentNullException() - { - // Act - Action action = () => _ = new ContainsExpression((string)null); + // Act + Action action = () => _ = new ContainsExpression((string)null); - // Assert - action.Should() - .ThrowExactly(); - } + // Assert + action.Should() + .ThrowExactly(); + } - [Fact] - public void Given_TextExpression_is_null_Constructor_should_thros_ArgumentNullException() - { - // Act - Action action = () => _ = new ContainsExpression((TextExpression)null); + [Fact] + public void Given_TextExpression_is_null_Constructor_should_thros_ArgumentNullException() + { + // Act + Action action = () => _ = new ContainsExpression((TextExpression)null); - // Assert - action.Should() - .ThrowExactly(); - } + // Assert + action.Should() + .ThrowExactly(); + } - [Fact] - public void Given_TextExpression_argument_is_null_Constructor_should_thros_ArgumentNullException() - { - // Act - Action action = () => _ = new ContainsExpression(string.Empty); + [Fact] + public void Given_TextExpression_argument_is_null_Constructor_should_thros_ArgumentNullException() + { + // Act + Action action = () => _ = new ContainsExpression(string.Empty); - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be empty"); - } + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be empty"); + } - [Fact] - public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_WhitespaceOnly() - { - // Act - Action action = () => _ = new ContainsExpression(" "); - - // Assert - action.Should() - .NotThrow("The parameter of the constructor can be whitespace only"); - action.Should() - .NotThrow("The parameter of the constructor can be whitespace only"); - } + [Fact] + public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_WhitespaceOnly() + { + // Act + Action action = () => _ = new ContainsExpression(" "); + + // Assert + action.Should() + .NotThrow("The parameter of the constructor can be whitespace only"); + action.Should() + .NotThrow("The parameter of the constructor can be whitespace only"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] + { + new ContainsExpression("prop1"), + new ContainsExpression("prop1"), + true, + "comparing two different instances with same property name" + }; + + yield return new object[] { - yield return new object[] - { - new ContainsExpression("prop1"), - new ContainsExpression("prop1"), - true, - "comparing two different instances with same property name" - }; - - yield return new object[] - { - new ContainsExpression("prop1"), - new ContainsExpression("prop2"), - false, - "comparing two different instances with different property name" - }; - } + new ContainsExpression("prop1"), + new ContainsExpression("prop2"), + false, + "comparing two different instances with different property name" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void ImplementsEqualsCorrectly(ContainsExpression first, object other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void ImplementsEqualsCorrectly(ContainsExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = first.Equals(other); + // Act + bool actual = first.Equals(other); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_ContainsExpression_Complexity_eq_1U002E5(ContainsExpression contains) - { - // Act - double actual = contains.Complexity; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_ContainsExpression_Complexity_eq_1U002E5(ContainsExpression contains) + { + // Act + double actual = contains.Complexity; - // Assert - actual.Should().Be(1.5); - } + // Assert + actual.Should().Be(1.5); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_reflexive(ContainsExpression contains) - => contains.IsEquivalentTo(contains).Should().BeTrue(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_reflexive(ContainsExpression contains) + => contains.IsEquivalentTo(contains).Should().BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) - { - // Arrange - ContainsExpression expression = new(text.Item); - string expected = $"*{text.Item.EscapedParseableString}*"; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) + { + // Arrange + ContainsExpression expression = new(text.Item); + string expected = $"*{text.Item.EscapedParseableString}*"; - // Act - string actual = expression.EscapedParseableString; + // Act + string actual = expression.EscapedParseableString; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property] - public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString_should_be_correct(NonWhiteSpaceString text) - { - // Arrange - ContainsExpression expression = new(text.Item); - StringValueExpression stringValueExpression = new(text.Item); - string expected = $"*{stringValueExpression.EscapedParseableString}*"; + [Property] + public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString_should_be_correct(NonWhiteSpaceString text) + { + // Arrange + ContainsExpression expression = new(text.Item); + StringValueExpression stringValueExpression = new(text.Item); + string expected = $"*{stringValueExpression.EscapedParseableString}*"; - // Act - string actual = expression.EscapedParseableString; + // Act + string actual = expression.EscapedParseableString; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - { - // Act - bool firstEqualsSecond = first.Equals(second); - bool secondEqualsFirst = second.Equals(first); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + { + // Act + bool firstEqualsSecond = first.Equals(second); + bool secondEqualsFirst = second.Equals(first); - // Assert - firstEqualsSecond.Should().Be(secondEqualsFirst, "'equals' implementation must be commutative"); - } + // Assert + firstEqualsSecond.Should().Be(secondEqualsFirst, "'equals' implementation must be commutative"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - { - // Act - bool actual = expression.Item.Equals(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + { + // Act + bool actual = expression.Item.Equals(expression.Item); - // Assert - actual.Should().BeTrue("'equals' implementation must be reflexive"); - } + // Assert + actual.Should().BeTrue("'equals' implementation must be reflexive"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - { - // Act + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + { + // Act - bool expressionEqualsOther = expression.Item.Equals(otherExpression.Item); - bool otherEqualsExpression = otherExpression.Item.Equals(expression.Item); + bool expressionEqualsOther = expression.Item.Equals(otherExpression.Item); + bool otherEqualsExpression = otherExpression.Item.Equals(expression.Item); - // Assert - expressionEqualsOther.Should().Be(otherEqualsExpression, "'equals' implementation must be symetric"); - } + // Assert + expressionEqualsOther.Should().Be(otherEqualsExpression, "'equals' implementation must be symetric"); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs index 01587317..9b5ad4e7 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateExpressionTests.cs @@ -1,115 +1,114 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; + +public class DateExpressionTests { - using System; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - - public class DateExpressionTests + [Fact] + public void IsFilterExpression() => typeof(DateExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement(); + + [Property] + public void Given_DateExpression_instance_instance_eq_instance_should_be_true(PositiveInt year, PositiveInt month, PositiveInt day) { - [Fact] - public void IsFilterExpression() => typeof(DateExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement(); - - [Property] - public void Given_DateExpression_instance_instance_eq_instance_should_be_true(PositiveInt year, PositiveInt month, PositiveInt day) - { - // Arrange - DateExpression instance = new(year.Item, month.Item, day.Item); + // Arrange + DateExpression instance = new(year.Item, month.Item, day.Item); - // Act - bool actual = instance.Equals(instance); + // Act + bool actual = instance.Equals(instance); - // Assert - actual.Should() - .BeTrue(); - } + // Assert + actual.Should() + .BeTrue(); + } - [Property] - public void Given_DateExpression_instance_instance_eq_null_should_be_false(PositiveInt year, PositiveInt month, PositiveInt day) - { - // Arrange - DateExpression instance = new(year.Item, month.Item, day.Item); + [Property] + public void Given_DateExpression_instance_instance_eq_null_should_be_false(PositiveInt year, PositiveInt month, PositiveInt day) + { + // Arrange + DateExpression instance = new(year.Item, month.Item, day.Item); + + // Act + bool actual = instance.Equals(null); - // Act - bool actual = instance.Equals(null); + // Assert + actual.Should() + .BeFalse(); + } - // Assert - actual.Should() - .BeFalse(); - } + [Property] + public void Given_two_DateExpression_instances_first_and_other_with_same_data_first_eq_other_should_be_true(PositiveInt year, PositiveInt month, PositiveInt day) + { + // Arrange + DateExpression first = new(year.Item, month.Item, day.Item); + DateExpression other = new(year.Item, month.Item, day.Item); + + // Act + bool actual = first.Equals(other); + int firstHashCode = first.GetHashCode(); + int otherHashCode = other.GetHashCode(); + + // Assert + actual.Should().BeTrue(); + firstHashCode.Should().Be(otherHashCode); + } - [Property] - public void Given_two_DateExpression_instances_first_and_other_with_same_data_first_eq_other_should_be_true(PositiveInt year, PositiveInt month, PositiveInt day) - { - // Arrange - DateExpression first = new(year.Item, month.Item, day.Item); - DateExpression other = new(year.Item, month.Item, day.Item); - - // Act - bool actual = first.Equals(other); - int firstHashCode = first.GetHashCode(); - int otherHashCode = other.GetHashCode(); - - // Assert - actual.Should().BeTrue(); - firstHashCode.Should().Be(otherHashCode); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateExpression_GetComplexity_should_return_1(NonNull date) - => date.Item.Complexity.Should().Be(1); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty(); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.IsEquivalentTo(second).Should().Be(second.IsEquivalentTo(first.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_reflexive(NonNull expression) - => expression.Item.IsEquivalentTo(expression.Item).Should().BeTrue(); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.IsEquivalentTo(otherExpression.Item).Should().Be(otherExpression.Item.IsEquivalentTo(expression.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_operands_Equal_operator_should_return_same_result_as_using_Equals(NonNull left, NonNull right) - => (left.Item == right.Item).Should().Be(left.Item.Equals(right.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_operands_NotEquals_operator_should_return_opposite_result_of_Equals(NonNull left, NonNull right) - => (left.Item != right.Item).Should().Be(!left.Item.Equals(right.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_years_month_and_days_When_either_years_or_months_or_days_is_less_than_1_Then_constructor_should_throw_ArgumentOutOfRangeException(int years, int months, int days) + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateExpression_GetComplexity_should_return_1(NonNull date) + => date.Item.Complexity.Should().Be(1); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty(); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.IsEquivalentTo(second).Should().Be(second.IsEquivalentTo(first.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_reflexive(NonNull expression) + => expression.Item.IsEquivalentTo(expression.Item).Should().BeTrue(); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.IsEquivalentTo(otherExpression.Item).Should().Be(otherExpression.Item.IsEquivalentTo(expression.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_operands_Equal_operator_should_return_same_result_as_using_Equals(NonNull left, NonNull right) + => (left.Item == right.Item).Should().Be(left.Item.Equals(right.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_operands_NotEquals_operator_should_return_opposite_result_of_Equals(NonNull left, NonNull right) + => (left.Item != right.Item).Should().Be(!left.Item.Equals(right.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_years_month_and_days_When_either_years_or_months_or_days_is_less_than_1_Then_constructor_should_throw_ArgumentOutOfRangeException(int years, int months, int days) + { + // Act + Action invokingConstructor = () => _ = new DateExpression(years, months, days); + + // Assert + object __ = (years, months, days) switch { - // Act - Action invokingConstructor = () => _ = new DateExpression(years, months, days); - - // Assert - object __ = (years, months, days) switch - { - (int y, int m, int d) when y < 1 || m < 1 || d < 1 => invokingConstructor.Should().Throw("because years, months and days must be greater than or equal to 1"), - _ => invokingConstructor.Should().NotThrow("because years, months and days are greater than or equal to 1") - }; - } + (int y, int m, int d) when y < 1 || m < 1 || d < 1 => invokingConstructor.Should().Throw("because years, months and days must be greater than or equal to 1"), + _ => invokingConstructor.Should().NotThrow("because years, months and days are greater than or equal to 1") + }; } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs index 3e625060..f787d716 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DateTimeExpressionTests.cs @@ -1,245 +1,244 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FluentAssertions.Extensions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[Feature(nameof(DataFilters.Grammar.Syntax))] +public class DateTimeExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FluentAssertions.Extensions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [Feature(nameof(DataFilters.Grammar.Syntax))] - public class DateTimeExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(DateTimeExpression).Should() + .BeAssignableTo().And + .Implement>().And + .HaveConstructor(new[] { typeof(DateExpression), typeof(TimeExpression), typeof(OffsetExpression) }).And + .HaveConstructor(new[] { typeof(DateExpression), typeof(TimeExpression) }).And + .HaveConstructor(new[] { typeof(TimeExpression) }).And + .HaveConstructor(new[] { typeof(DateExpression) }).And + .HaveProperty("Date").And + .HaveProperty("Time").And + .HaveProperty("Offset"); + + [Fact] + public void Given_date_and_time_parameters_are_null_Constructor_should_throws_ArgumentException() { - [Fact] - public void IsFilterExpression() => typeof(DateTimeExpression).Should() - .BeAssignableTo().And - .Implement>().And - .HaveConstructor(new[] { typeof(DateExpression), typeof(TimeExpression), typeof(OffsetExpression) }).And - .HaveConstructor(new[] { typeof(DateExpression), typeof(TimeExpression) }).And - .HaveConstructor(new[] { typeof(TimeExpression) }).And - .HaveConstructor(new[] { typeof(DateExpression) }).And - .HaveProperty("Date").And - .HaveProperty("Time").And - .HaveProperty("Offset"); - - [Fact] - public void Given_date_and_time_parameters_are_null_Constructor_should_throws_ArgumentException() - { - // Act - Action ctor = () => new DateTimeExpression(null, null); + // Act + Action ctor = () => _ = new DateTimeExpression(null, null); - // Assert - ctor.Should() - .ThrowExactly("both date and time are null"); - } + // Assert + ctor.Should() + .ThrowExactly("both date and time are null"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void A_non_null_DateTimeExpression_instance_should_never_be_equal_to_null(DateTimeExpression instance) - { - // Act - bool actual = instance.Equals(null); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void A_non_null_DateTimeExpression_instance_should_never_be_equal_to_null(DateTimeExpression instance) + { + // Act + bool actual = instance.Equals(null); - // Assert - actual.Should() - .BeFalse(); - } + // Assert + actual.Should() + .BeFalse(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Two_DateTimeExpression_instances_which_have_same_data_should_be_equal(DateExpression date, TimeExpression time, OffsetExpression offset) - { - // Arrange - DateTimeExpression first = new(date, time, offset); - DateTimeExpression other = new(date, time, offset); - - // Act - bool actual = first.Equals(other); - - // Assert - actual.Should() - .BeTrue(); - first.GetHashCode().Should() - .Be(other.GetHashCode(), "Two instances that are equal should have same hashcode"); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Two_DateTimeExpression_instances_which_have_same_data_should_be_equal(DateExpression date, TimeExpression time, OffsetExpression offset) + { + // Arrange + DateTimeExpression first = new(date, time, offset); + DateTimeExpression other = new(date, time, offset); + + // Act + bool actual = first.Equals(other); + + // Assert + actual.Should() + .BeTrue(); + first.GetHashCode().Should() + .Be(other.GetHashCode(), "Two instances that are equal should have same hashcode"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void A_DateTimeExpression_built_from_variables_obtained_from_deconstructing_a_DateTimeExpression_should_equal_the_original_DateTimeExpression_value(DateTimeExpression source) - { - outputHelper.WriteLine(message: $"DateTimeExpression is {source}"); - (DateExpression date, TimeExpression time, OffsetExpression offset, _) = source; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void A_DateTimeExpression_built_from_variables_obtained_from_deconstructing_a_DateTimeExpression_should_equal_the_original_DateTimeExpression_value(DateTimeExpression source) + { + outputHelper.WriteLine(message: $"DateTimeExpression is {source}"); + (DateExpression date, TimeExpression time, OffsetExpression offset, _) = source; - // Act - DateTimeExpression clone = new(date, time, offset); + // Act + DateTimeExpression clone = new(date, time, offset); - // Assert - clone.Should() - .Be(source); - } + // Assert + clone.Should() + .Be(source); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateTimeExpression_Complexity_Should_be_the_sum_of_complexity_of_each_member(NonNull date, NonNull time, NonNull offset) - { - // Arrange - DateTimeExpression dateTimeExpression = new(date.Item, time.Item, offset.Item); - double expected = date.Item.Complexity + time.Item.Complexity + offset.Item.Complexity; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateTimeExpression_Complexity_Should_be_the_sum_of_complexity_of_each_member(NonNull date, NonNull time, NonNull offset) + { + // Arrange + DateTimeExpression dateTimeExpression = new(date.Item, time.Item, offset.Item); + double expected = date.Item.Complexity + time.Item.Complexity + offset.Item.Complexity; - // Act - double actual = dateTimeExpression.Complexity; + // Act + double actual = dateTimeExpression.Complexity; - // Assert - actual.Should() - .BeGreaterThanOrEqualTo(3).And - .Be(expected); - } + // Assert + actual.Should() + .BeGreaterThanOrEqualTo(3).And + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateTimeExpression_where_only_date_part_is_set_When_comparing_to_DateExpression_IsEquivalentTo_should_be_true(NonNull dateExpression) - { - // Arrange - DateTimeExpression dateTimeExpression = new(dateExpression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateTimeExpression_where_only_date_part_is_set_When_comparing_to_DateExpression_IsEquivalentTo_should_be_true(NonNull dateExpression) + { + // Arrange + DateTimeExpression dateTimeExpression = new(dateExpression.Item); - // Act - bool actual = dateTimeExpression.IsEquivalentTo(dateExpression.Item); + // Act + bool actual = dateTimeExpression.IsEquivalentTo(dateExpression.Item); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateTimeExpression_where_only_time_part_is_set_When_comparing_to_that_TimeExpression(NonNull timeExpression) - { - // Arrange - DateTimeExpression dateTimeExpression = new(timeExpression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateTimeExpression_where_only_time_part_is_set_When_comparing_to_that_TimeExpression(NonNull timeExpression) + { + // Arrange + DateTimeExpression dateTimeExpression = new(timeExpression.Item); - // Act - bool actual = dateTimeExpression.IsEquivalentTo(timeExpression.Item); + // Act + bool actual = dateTimeExpression.IsEquivalentTo(timeExpression.Item); - // Assert - actual.Should().BeTrue(); - dateTimeExpression.Complexity.Should().BeGreaterThan(timeExpression.Item.Complexity); - } + // Assert + actual.Should().BeTrue(); + dateTimeExpression.Complexity.Should().BeGreaterThan(timeExpression.Item.Complexity); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new DateTimeExpression(new TimeExpression()), - new TimeExpression(), - true, - $"{nameof(DateTimeExpression.Date)} and {nameof(DateTimeExpression.Date)} are null and TimeExpression are equal" - }; - - yield return new object[] - { - new DateTimeExpression(new (), new (), new()), - new DateTimeExpression(new (), new (), new()), - true, - $"Two instances with {nameof(DateTimeExpression)} that are equal" - }; - - yield return new object[] - { - new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero), - new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero), - true, - $"Two instances with {nameof(DateTimeExpression.Date)}, {nameof(DateTimeExpression.Time)}, {nameof(DateTimeExpression.Offset)} are equal and not null" - }; - } - } + new DateTimeExpression(new TimeExpression()), + new TimeExpression(), + true, + $"{nameof(DateTimeExpression.Date)} and {nameof(DateTimeExpression.Date)} are null and TimeExpression are equal" + }; - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_work_as_expected(DateTimeExpression dateTime, object obj, bool expected, string reason) - { - // Act - bool actual = dateTime.Equals(obj); + yield return new object[] + { + new DateTimeExpression(new (), new (), new()), + new DateTimeExpression(new (), new (), new()), + true, + $"Two instances with {nameof(DateTimeExpression)} that are equal" + }; - // Assert - actual.Should() - .Be(expected, reason); + yield return new object[] + { + new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero), + new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero), + true, + $"Two instances with {nameof(DateTimeExpression.Date)}, {nameof(DateTimeExpression.Time)}, {nameof(DateTimeExpression.Offset)} are equal and not null" + }; } + } + + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_work_as_expected(DateTimeExpression dateTime, object obj, bool expected, string reason) + { + // Act + bool actual = dateTime.Equals(obj); + + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - public static IEnumerable EqualsTransitivityCases + public static IEnumerable EqualsTransitivityCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new DateTimeExpression(1.January(2010)), - new DateTimeExpression(1.January(2010)), - new DateTimeExpression(1.January(2010)) - }; - - yield return new object[] - { - new DateTimeExpression(new TimeExpression(1)), - new DateTimeExpression(new TimeExpression(minutes: 60)), - new DateTimeExpression(new TimeExpression(seconds: 3600)) - }; - - yield return new object[] - { - new DateTimeExpression(new TimeExpression(1)), - new DateTimeExpression(new TimeExpression(minutes: 60)), - new DateTimeExpression(new TimeExpression(seconds: 3600)) - }; - } + new DateTimeExpression(1.January(2010)), + new DateTimeExpression(1.January(2010)), + new DateTimeExpression(1.January(2010)) + }; + + yield return new object[] + { + new DateTimeExpression(new TimeExpression(1)), + new DateTimeExpression(new TimeExpression(minutes: 60)), + new DateTimeExpression(new TimeExpression(seconds: 3600)) + }; + + yield return new object[] + { + new DateTimeExpression(new TimeExpression(1)), + new DateTimeExpression(new TimeExpression(minutes: 60)), + new DateTimeExpression(new TimeExpression(seconds: 3600)) + }; } + } - [Theory] - [MemberData(nameof(EqualsTransitivityCases))] - public void Equals_should_be_transitive(DateTimeExpression first, object second, object third) - { - // Arrange - bool firstEqualsSecond = first.Equals(second); - bool secondEqualsThird = second.Equals(third); + [Theory] + [MemberData(nameof(EqualsTransitivityCases))] + public void Equals_should_be_transitive(DateTimeExpression first, object second, object third) + { + // Arrange + bool firstEqualsSecond = first.Equals(second); + bool secondEqualsThird = second.Equals(third); - // Act - bool actual = first.Equals(third); + // Act + bool actual = first.Equals(third); - // Assert - actual.Should() - .Be(firstEqualsSecond && secondEqualsThird); - } + // Assert + actual.Should() + .Be(firstEqualsSecond && secondEqualsThird); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_operands_Equal_operator_should_return_same_result_as_using_Equals(NonNull left, NonNull right) - { - // Act - bool actual = left.Item == right.Item; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_operands_Equal_operator_should_return_same_result_as_using_Equals(NonNull left, NonNull right) + { + // Act + bool actual = left.Item == right.Item; - // Assert - actual.Should() - .Be(left.Item.Equals(right.Item)); - } + // Assert + actual.Should() + .Be(left.Item.Equals(right.Item)); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_operands_NotEqual_operator_should_return_opposite_result_of_Equal_operator(NonNull left, NonNull right) - { - // Act - bool actual = left.Item != right.Item; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_operands_NotEqual_operator_should_return_opposite_result_of_Equal_operator(NonNull left, NonNull right) + { + // Act + bool actual = left.Item != right.Item; - // Assert - actual.Should() - .Be(!left.Item.Equals(right.Item)); - } + // Assert + actual.Should() + .Be(!left.Item.Equals(right.Item)); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs index b8b6c5f8..2331714c 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/DurationExpressionTests.cs @@ -1,188 +1,187 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class DurationExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class DurationExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(DurationExpression).Should() + .BeDerivedFrom(); + + [Property] + public void Throws_ArgumentOutOfRangeException_when_any_ctor_input_is_negative(int years, int months, int weeks, int days, int hours, int minutes, int seconds) { - [Fact] - public void IsFilterExpression() => typeof(DurationExpression).Should() - .BeDerivedFrom(); + Action invokingConstructor = () => _ = new DurationExpression(years, months, weeks, days, hours, minutes, seconds); - [Property] - public void Throws_ArgumentOutOfRangeException_when_any_ctor_input_is_negative(int years, int months, int weeks, int days, int hours, int minutes, int seconds) + object __ = (years, months, weeks, days, hours, minutes, seconds) switch { - Action invokingConstructor = () => _ = new DurationExpression(years, months, weeks, days, hours, minutes, seconds); - - object __ = (years, months, weeks, days, hours, minutes, seconds) switch - { - var tuple when tuple.years < 0 - || tuple.months < 0 - || tuple.weeks < 0 - || tuple.days < 0 - || tuple.hours < 0 - || tuple.minutes < 0 - || tuple.seconds < 0 => invokingConstructor.Should().ThrowExactly(), - _ => invokingConstructor.Should().NotThrow() - }; - } + var tuple when tuple.years < 0 + || tuple.months < 0 + || tuple.weeks < 0 + || tuple.days < 0 + || tuple.hours < 0 + || tuple.minutes < 0 + || tuple.seconds < 0 => invokingConstructor.Should().ThrowExactly(), + _ => invokingConstructor.Should().NotThrow() + }; + } - [Property] - public void Set_properties_accordingly(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) - { - DurationExpression duration = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); - - duration.Years.Should().Be(years.Item); - duration.Months.Should().Be(months.Item); - duration.Days.Should().Be(days.Item); - duration.Hours.Should().Be(hours.Item); - duration.Minutes.Should().Be(minutes.Item); - duration.Seconds.Should().Be(seconds.Item); - } + [Property] + public void Set_properties_accordingly(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) + { + DurationExpression duration = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); + + duration.Years.Should().Be(years.Item); + duration.Months.Should().Be(months.Item); + duration.Days.Should().Be(days.Item); + duration.Hours.Should().Be(hours.Item); + duration.Minutes.Should().Be(minutes.Item); + duration.Seconds.Should().Be(seconds.Item); + } - [Property] - public void Equals_depends_on_values_only(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) - { - DurationExpression first = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); - DurationExpression other = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); + [Property] + public void Equals_depends_on_values_only(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) + { + DurationExpression first = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); + DurationExpression other = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); - // Act - bool actual = first.Equals(other); - int firstHashCode = first.GetHashCode(); - int otherHashCode = other.GetHashCode(); + // Act + bool actual = first.Equals(other); + int firstHashCode = first.GetHashCode(); + int otherHashCode = other.GetHashCode(); - // Assert - actual.Should().BeTrue(); - firstHashCode.Should().Be(otherHashCode); - } + // Assert + actual.Should().BeTrue(); + firstHashCode.Should().Be(otherHashCode); + } - [Property] - public void Two_durations_that_are_equal_are_equivalent(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) - { - DurationExpression first = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); - DurationExpression other = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); + [Property] + public void Two_durations_that_are_equal_are_equivalent(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) + { + DurationExpression first = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); + DurationExpression other = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); - // Act - bool firstEqualsOther = first.Equals(other); - bool firstIsEquivalentToOther = first.IsEquivalentTo(other); + // Act + bool firstEqualsOther = first.Equals(other); + bool firstIsEquivalentToOther = first.IsEquivalentTo(other); - // Assert - firstEqualsOther.Should().BeTrue(); - firstIsEquivalentToOther.Should().BeTrue(); - } + // Assert + firstEqualsOther.Should().BeTrue(); + firstIsEquivalentToOther.Should().BeTrue(); + } - [Property] - public void Two_durations_are_equivalent_when_they_represent_same_timespan() - { - // Arrange - DurationExpression first = new(hours: 24); - DurationExpression other = new(days: 1); + [Property] + public void Two_durations_are_equivalent_when_they_represent_same_timespan() + { + // Arrange + DurationExpression first = new(hours: 24); + DurationExpression other = new(days: 1); - // Act - bool firstEqualsOther = first.Equals(other); - bool firstIsEquivalentToOther = first.IsEquivalentTo(other); + // Act + bool firstEqualsOther = first.Equals(other); + bool firstIsEquivalentToOther = first.IsEquivalentTo(other); - // Assert - firstEqualsOther.Should().BeFalse("two durations initialized with timespans that are not in the same unit of time"); - firstIsEquivalentToOther.Should().BeTrue("two durations initialized from timespans that are equal should be equivalent"); - } + // Assert + firstEqualsOther.Should().BeFalse("two durations initialized with timespans that are not in the same unit of time"); + firstIsEquivalentToOther.Should().BeTrue("two durations initialized from timespans that are equal should be equivalent"); + } - [Property] - public void Equals_to_null_always_returns_false(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) - { - // Arrange - DurationExpression duration = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); + [Property] + public void Equals_to_null_always_returns_false(PositiveInt years, PositiveInt months, PositiveInt weeks, PositiveInt days, PositiveInt hours, PositiveInt minutes, PositiveInt seconds) + { + // Arrange + DurationExpression duration = new(years.Item, months.Item, weeks.Item, days.Item, hours.Item, minutes.Item, seconds.Item); - // Act - bool durationEqualsToNull = duration.Equals(null); + // Act + bool durationEqualsToNull = duration.Equals(null); - // Assert - durationEqualsToNull.Should().BeFalse(); - } + // Assert + durationEqualsToNull.Should().BeFalse(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).Should().BeTrue(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Equals(expression.Item).Should().BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DurationExpression_GetComplexity_should_return_1(DurationExpression duration) - => duration.Complexity.Should().Be(1); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DurationExpression_GetComplexity_should_return_1(DurationExpression duration) + => duration.Complexity.Should().Be(1); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_transitive(DurationExpression duration, NonNull other, NonNull third) - { - // Arrange - outputHelper.WriteLine($"Duration : {duration}"); - outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); - outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_transitive(DurationExpression duration, NonNull other, NonNull third) + { + // Arrange + outputHelper.WriteLine($"Duration : {duration}"); + outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); + outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); - bool first = duration.IsEquivalentTo(other.Item); - bool second = other.Item.IsEquivalentTo(third.Item); + bool first = duration.IsEquivalentTo(other.Item); + bool second = other.Item.IsEquivalentTo(third.Item); - // Act - bool actual = duration.IsEquivalentTo(third.Item); + // Act + bool actual = duration.IsEquivalentTo(third.Item); - // Assert - if (first && second) - { - actual.Should().BeTrue(); - } + // Assert + if (first && second) + { + actual.Should().BeTrue(); } + } - public static IEnumerable IsEquivalentToCases + public static IEnumerable IsEquivalentToCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new DurationExpression(), - new StartsWithExpression("a"), - new StartsWithExpression("a"), - ( - expected: false, - reason: "A is not equivalent to B and B is equivalent to C => A is not equivalent to C" - ) - }; - } + new DurationExpression(), + new StartsWithExpression("a"), + new StartsWithExpression("a"), + ( + expected: false, + reason: "A is not equivalent to B and B is equivalent to C => A is not equivalent to C" + ) + }; } + } - [Theory] - [MemberData(nameof(IsEquivalentToCases))] - public void IsEquivalent_should_behave_as_expected(DurationExpression duration, FilterExpression other, FilterExpression third, (bool expected, string reason) expectation) - { - // Arrange - outputHelper.WriteLine($"Duration : {duration}"); - outputHelper.WriteLine($"Other : {other.EscapedParseableString}"); - outputHelper.WriteLine($"Third : {third.EscapedParseableString}"); + [Theory] + [MemberData(nameof(IsEquivalentToCases))] + public void IsEquivalent_should_behave_as_expected(DurationExpression duration, FilterExpression other, FilterExpression third, (bool expected, string reason) expectation) + { + // Arrange + outputHelper.WriteLine($"Duration : {duration}"); + outputHelper.WriteLine($"Other : {other.EscapedParseableString}"); + outputHelper.WriteLine($"Third : {third.EscapedParseableString}"); - bool durationIsEquivalentToOther = duration.IsEquivalentTo(other); - bool otherIsEquivalentToThird = other.IsEquivalentTo(third); + bool durationIsEquivalentToOther = duration.IsEquivalentTo(other); + bool otherIsEquivalentToThird = other.IsEquivalentTo(third); - outputHelper.WriteLine($"{duration} is equivalent to {other} : {durationIsEquivalentToOther}"); - outputHelper.WriteLine($"{other} is equivalent to {third} : {otherIsEquivalentToThird}"); + outputHelper.WriteLine($"{duration} is equivalent to {other} : {durationIsEquivalentToOther}"); + outputHelper.WriteLine($"{other} is equivalent to {third} : {otherIsEquivalentToThird}"); - // Act - bool actual = duration.IsEquivalentTo(third); + // Act + bool actual = duration.IsEquivalentTo(third); - // Assert - actual.Should().Be(expectation.expected, expectation.reason); - } + // Assert + actual.Should().Be(expectation.expected, expectation.reason); } } \ No newline at end of file diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs index 75a55e89..085b74b5 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/EndsWithExpressionTests.cs @@ -1,165 +1,164 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class EndsWithExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class EndsWithExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(EndsWithExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement(); + + [Fact] + public void Given_string_input_is_null_Constructor_should_throws_ArgumentNullException() { - [Fact] - public void IsFilterExpression() => typeof(EndsWithExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement(); - - [Fact] - public void Given_string_input_is_null_Constructor_should_throws_ArgumentNullException() - { - // Act - Action action = () => new EndsWithExpression((string)null); + // Act + Action action = () => _ = new EndsWithExpression((string)null); - // Assert - action.Should() - .ThrowExactly($"The value of {nameof(EndsWithExpression)}'s constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"The value of {nameof(EndsWithExpression)}'s constructor cannot be null"); + } - [Fact] - public void Given_TextExpression_input_is_null_Constructor_should_throws_ArgumentNullException() - { - // Act - Action action = () => new EndsWithExpression((TextExpression)null); + [Fact] + public void Given_TextExpression_input_is_null_Constructor_should_throws_ArgumentNullException() + { + // Act + Action action = () => _ = new EndsWithExpression((TextExpression)null); - // Assert - action.Should() - .ThrowExactly($"The value of {nameof(EndsWithExpression)}'s constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"The value of {nameof(EndsWithExpression)}'s constructor cannot be null"); + } - [Fact] - public void Given_string_input_is_an_empty_Constructor_should_throws_ArgumentOutOfRangeException() - { - // Act - Action action = () => new EndsWithExpression(string.Empty); + [Fact] + public void Given_string_input_is_an_empty_Constructor_should_throws_ArgumentOutOfRangeException() + { + // Act + Action action = () => _ = new EndsWithExpression(string.Empty); - // Assert - action.Should() - .ThrowExactly($"The value of {nameof(EndsWithExpression)}'s constructor cannot be empty"); - } + // Assert + action.Should() + .ThrowExactly($"The value of {nameof(EndsWithExpression)}'s constructor cannot be empty"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] + { + new EndsWithExpression("prop1"), + new EndsWithExpression("prop1"), + true, + "comparing two different instances with same property name" + }; + + yield return new object[] { - yield return new object[] - { - new EndsWithExpression("prop1"), - new EndsWithExpression("prop1"), - true, - "comparing two different instances with same property name" - }; - - yield return new object[] - { - new EndsWithExpression("prop1"), - new EndsWithExpression("prop2"), - false, - "comparing two different instances with different property name" - }; - } + new EndsWithExpression("prop1"), + new EndsWithExpression("prop2"), + false, + "comparing two different instances with different property name" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void ImplementsEqualsCorrectly(EndsWithExpression first, object other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void ImplementsEqualsCorrectly(EndsWithExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = first.Equals(other); - int actualHashCode = first.GetHashCode(); + // Act + bool actual = first.Equals(other); + int actualHashCode = first.GetHashCode(); - // Assert - actual.Should() - .Be(expected, reason); - if (expected) - { - actualHashCode.Should() - .Be(other?.GetHashCode(), reason); - } - else - { - actualHashCode.Should() - .NotBe(other?.GetHashCode(), reason); - } + // Assert + actual.Should() + .Be(expected, reason); + if (expected) + { + actualHashCode.Should() + .Be(other?.GetHashCode(), reason); + } + else + { + actualHashCode.Should() + .NotBe(other?.GetHashCode(), reason); } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_EndsWithExpression_Complexity_should_return_1U002E5(EndsWithExpression endsWith) - => endsWith.Complexity.Should().Be(1.5); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_EndsWithExpression_Complexity_should_return_1U002E5(EndsWithExpression endsWith) + => endsWith.Complexity.Should().Be(1.5); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) - { - // Arrange - EndsWithExpression expression = new(text.Item); - string expected = $"*{text.Item.EscapedParseableString}"; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) + { + // Arrange + EndsWithExpression expression = new(text.Item); + string expected = $"*{text.Item.EscapedParseableString}"; - // Act - string actual = expression.EscapedParseableString; + // Act + string actual = expression.EscapedParseableString; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property] - public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString_should_be_correct(NonWhiteSpaceString text) - { - // Arrange - EndsWithExpression expression = new(text.Item); - StringValueExpression stringValueExpression = new(text.Item); - string expected = $"*{stringValueExpression.EscapedParseableString}"; + [Property] + public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString_should_be_correct(NonWhiteSpaceString text) + { + // Arrange + EndsWithExpression expression = new(text.Item); + StringValueExpression stringValueExpression = new(text.Item); + string expected = $"*{stringValueExpression.EscapedParseableString}"; - // Act - string actual = expression.EscapedParseableString; + // Act + string actual = expression.EscapedParseableString; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).Should().BeTrue(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Equals(expression.Item).Should().BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_EndsWithExpression_When_adding_AsteriskExpression_should_returns_ContainsExpression(NonNull endsWith) - { - // Arrange - ContainsExpression expected = new(endsWith.Item.Value); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_EndsWithExpression_When_adding_AsteriskExpression_should_returns_ContainsExpression(NonNull endsWith) + { + // Arrange + ContainsExpression expected = new(endsWith.Item.Value); - // Act - ContainsExpression actual = endsWith.Item + AsteriskExpression.Instance; + // Act + ContainsExpression actual = endsWith.Item + AsteriskExpression.Instance; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs index edcaead1..1ee1c40e 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/GroupExpressionTests.cs @@ -1,231 +1,230 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; + +public class GroupExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - - public class GroupExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(GroupExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement().And + .Implement(); + + [Fact] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { - [Fact] - public void IsFilterExpression() => typeof(GroupExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement().And - .Implement(); - - [Fact] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() - { - // Act - Action action = () => new GroupExpression(null); + // Act + Action action = () => _ = new GroupExpression(null); - // Assert - action.Should() - .ThrowExactly($"The parameter of {nameof(GroupExpression)}'s constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"The parameter of {nameof(GroupExpression)}'s constructor cannot be null"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_current_instance_is_not_null_and_other_is_null_Equals_should_return_false(NonNull group) - { - // Act - bool actual = group.Item.Equals(null); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_current_instance_is_not_null_and_other_is_null_Equals_should_return_false(NonNull group) + { + // Act + bool actual = group.Item.Equals(null); - // Assert - actual.Should() - .BeFalse(); - } + // Assert + actual.Should() + .BeFalse(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull group) - => group.Item.Equals(group.Item).Should().BeTrue(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull group) + => group.Item.Equals(group.Item).Should().BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull group, NonNull otherExpression) - => group.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(group.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull group, NonNull otherExpression) + => group.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(group.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_GroupExpression_Complexity_should_be_linear_to_inner_expression_complexity(GroupExpression group) => group.Complexity.Should().Be(0.1 + group.Expression.Complexity); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_GroupExpression_Complexity_should_be_linear_to_inner_expression_complexity(GroupExpression group) => group.Complexity.Should().Be(0.1 + group.Expression.Complexity); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_comparing_to_that_arbitrary_expression_IsEquivalent_should_return_true(NonNull filterExpression) - { - // Arrange - GroupExpression group = new(filterExpression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_comparing_to_that_arbitrary_expression_IsEquivalent_should_return_true(NonNull filterExpression) + { + // Arrange + GroupExpression group = new(filterExpression.Item); - // Act - bool actual = group.IsEquivalentTo(filterExpression.Item); + // Act + bool actual = group.IsEquivalentTo(filterExpression.Item); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_comparing_to_that_arbitrary_expression_IsEquivalentTo_should_return_true(NonNull filterExpression) - { - // Arrange - GroupExpression group = new(filterExpression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_comparing_to_that_arbitrary_expression_IsEquivalentTo_should_return_true(NonNull filterExpression) + { + // Arrange + GroupExpression group = new(filterExpression.Item); - // Act - bool actual = group.IsEquivalentTo(filterExpression.Item); + // Act + bool actual = group.IsEquivalentTo(filterExpression.Item); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_wrapping_that_group_inside_a_group_expression_Should_not_change_its_meaning(NonNull expression) - { - // Arrange - GroupExpression group = new(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_GroupExpression_which_contains_an_arbitrary_expression_When_wrapping_that_group_inside_a_group_expression_Should_not_change_its_meaning(NonNull expression) + { + // Arrange + GroupExpression group = new(expression.Item); - // Act - bool isEquivalent = group.IsEquivalentTo(expression.Item); + // Act + bool isEquivalent = group.IsEquivalentTo(expression.Item); - // Assert - isEquivalent.Should().BeTrue(); - } + // Assert + isEquivalent.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_GroupExpression_instances_that_wrap_equivalent_expressions_IsEquivalent_should_be_true(NonNull filterExpression) - { - // Arrange - GroupExpression first = new(filterExpression.Item); - GroupExpression other = new(filterExpression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_GroupExpression_instances_that_wrap_equivalent_expressions_IsEquivalent_should_be_true(NonNull filterExpression) + { + // Arrange + GroupExpression first = new(filterExpression.Item); + GroupExpression other = new(filterExpression.Item); - // Act - bool actual = first.IsEquivalentTo(other); + // Act + bool actual = first.IsEquivalentTo(other); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new GroupExpression(new StartsWithExpression("prop1")), - new GroupExpression(new StartsWithExpression("prop1")), - true, - "comparing two different instances with same property name" - }; - - yield return new object[] - { - new GroupExpression(new StartsWithExpression("prop1")), - new GroupExpression(new StartsWithExpression("prop2")), - false, - "comparing two different instances with different inner expressions" - }; - - yield return new object[] - { - new GroupExpression(new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero)), - new GroupExpression(new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero)), - true, - "Two instances with inner expressions that are equal" - }; - } - } + new GroupExpression(new StartsWithExpression("prop1")), + new GroupExpression(new StartsWithExpression("prop1")), + true, + "comparing two different instances with same property name" + }; - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_behave_as_expected(GroupExpression expression, object obj, bool expected, string reason) - { - // Act - bool actual = expression.Equals(obj); + yield return new object[] + { + new GroupExpression(new StartsWithExpression("prop1")), + new GroupExpression(new StartsWithExpression("prop2")), + false, + "comparing two different instances with different inner expressions" + }; - // Assert - actual.Should() - .Be(expected, reason); + yield return new object[] + { + new GroupExpression(new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero)), + new GroupExpression(new DateTimeExpression(new(2090, 10, 10), new (03,00,40, 583), OffsetExpression.Zero)), + true, + "Two instances with inner expressions that are equal" + }; } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_GroupExpression_wraps_an_arbitrary_FilterExpression_When_that_group_is_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression) - { - // Arrange - GroupExpression group = new(filterExpression.Item); - GroupExpression extraGroup = new(group); + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_behave_as_expected(GroupExpression expression, object obj, bool expected, string reason) + { + // Act + bool actual = expression.Equals(obj); - // Act - bool isEquivalent = group.IsEquivalentTo(extraGroup); + // Assert + actual.Should() + .Be(expected, reason); + } - // Assert - isEquivalent.Should().BeTrue(); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_GroupExpression_wraps_an_arbitrary_FilterExpression_When_that_group_is_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression) + { + // Arrange + GroupExpression group = new(filterExpression.Item); + GroupExpression extraGroup = new(group); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_any_BinaryExpression_When_that_expression_is_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression) - { - // Arrange - GroupExpression group = new(filterExpression.Item); + // Act + bool isEquivalent = group.IsEquivalentTo(extraGroup); - // Act - bool isEquivalent = group.IsEquivalentTo(filterExpression.Item); + // Assert + isEquivalent.Should().BeTrue(); + } - // Assert - isEquivalent.Should().BeTrue(); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_any_BinaryExpression_When_that_expression_is_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression) + { + // Arrange + GroupExpression group = new(filterExpression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_reflexive(GroupExpression group) - => group.IsEquivalentTo(group).Should().BeTrue(); + // Act + bool isEquivalent = group.IsEquivalentTo(filterExpression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_symetric(GroupExpression group, NonNull other) - { - outputHelper.WriteLine($"Group : {group}"); - outputHelper.WriteLine($"Other : {other}"); + // Assert + isEquivalent.Should().BeTrue(); + } - group.IsEquivalentTo(other.Item).Should().Be(other.Item.IsEquivalentTo(group)); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_reflexive(GroupExpression group) + => group.IsEquivalentTo(group).Should().BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)]/*, Replay = "(1500570200792655435,5263687413201141279)"*/)] - public void IsEquivalent_should_be_transitive(GroupExpression group, NonNull other, NonNull third) - { - // Arrange - outputHelper.WriteLine($"Group : {group}"); - outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); - outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_symetric(GroupExpression group, NonNull other) + { + outputHelper.WriteLine($"Group : {group}"); + outputHelper.WriteLine($"Other : {other}"); - bool first = group.IsEquivalentTo(other.Item); - bool second = other.Item.IsEquivalentTo(third.Item); + group.IsEquivalentTo(other.Item).Should().Be(other.Item.IsEquivalentTo(group)); + } - // Act - bool actual = group.IsEquivalentTo(third.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)]/*, Replay = "(1500570200792655435,5263687413201141279)"*/)] + public void IsEquivalent_should_be_transitive(GroupExpression group, NonNull other, NonNull third) + { + // Arrange + outputHelper.WriteLine($"Group : {group}"); + outputHelper.WriteLine($"Other : {other.Item.EscapedParseableString}"); + outputHelper.WriteLine($"Third : {third.Item.EscapedParseableString}"); - // Assert - if (first && second) - { - actual.Should().BeTrue(); - } - } + bool first = group.IsEquivalentTo(other.Item); + bool second = other.Item.IsEquivalentTo(third.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_a_non_null_filter_expression_When_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression, PositiveInt count) - { - // Arrange - int depth = count.Item / 2; - GroupExpression initialGroup = new(filterExpression.Item); - GroupExpression otherGroup = new(filterExpression.Item); + // Act + bool actual = group.IsEquivalentTo(third.Item); - for (int i = 0; i < depth; i++) - { - otherGroup = new(otherGroup); - } + // Assert + if (first && second) + { + actual.Should().BeTrue(); + } + } - // Act - bool isEquivalent = initialGroup.IsEquivalentTo(otherGroup); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_a_non_null_filter_expression_When_wrapped_inside_a_group_expression_Should_not_change_its_meaning(NonNull filterExpression, PositiveInt count) + { + // Arrange + int depth = count.Item / 2; + GroupExpression initialGroup = new(filterExpression.Item); + GroupExpression otherGroup = new(filterExpression.Item); - // Assert - isEquivalent.Should().BeTrue(); + for (int i = 0; i < depth; i++) + { + otherGroup = new(otherGroup); } + + // Act + bool isEquivalent = initialGroup.IsEquivalentTo(otherGroup); + + // Assert + isEquivalent.Should().BeTrue(); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs index 6a6e67f5..214cd120 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/IntervalExpressionTests.cs @@ -1,453 +1,452 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Exceptions; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FluentAssertions.Extensions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; + +public class IntervalExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Exceptions; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FluentAssertions.Extensions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - - public class IntervalExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(IntervalExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement(); + + [Fact] + public void Given_min_and_max_are_null_Ctor_should_throws_IncorrectBoundaryException() { - [Fact] - public void IsFilterExpression() => typeof(IntervalExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement(); - - [Fact] - public void Given_min_and_max_are_null_Ctor_should_throws_IncorrectBoundaryException() - { - // Act - Action action = () => new IntervalExpression(null, null); + // Act + Action action = () => _ = new IntervalExpression(null, null); - // Assert - action.Should() - .ThrowExactly($"Either {nameof(IntervalExpression.Min)}/{nameof(IntervalExpression.Max)} must not be null"); - } + // Assert + action.Should() + .ThrowExactly($"Either {nameof(IntervalExpression.Min)}/{nameof(IntervalExpression.Max)} must not be null"); + } - public static IEnumerable IncorrectBoundariesCases + public static IEnumerable IncorrectBoundariesCases + { + get { - get - { - yield return new object[] { - new BoundaryExpression(AsteriskExpression.Instance, included: true), - new BoundaryExpression(AsteriskExpression.Instance, included: true), - $"min and max cannot both be {nameof(AsteriskExpression)} instances" - }; + yield return new object[] { + new BoundaryExpression(AsteriskExpression.Instance, included: true), + new BoundaryExpression(AsteriskExpression.Instance, included: true), + $"min and max cannot both be {nameof(AsteriskExpression)} instances" + }; - yield return new object[] { - new BoundaryExpression(AsteriskExpression.Instance, included : true), - null, - $"max cannot be null when min is {nameof(AsteriskExpression)} instance" - }; - } + yield return new object[] { + new BoundaryExpression(AsteriskExpression.Instance, included : true), + null, + $"max cannot be null when min is {nameof(AsteriskExpression)} instance" + }; } + } - public static IEnumerable BoundariesTypeMismatchCases + public static IEnumerable BoundariesTypeMismatchCases + { + get { - get - { - yield return new object[] { - new BoundaryExpression(new DateExpression(), included: true), new BoundaryExpression(new NumericValueExpression("10"), included: true), - $"min holds {nameof(DateExpression)} and max holds {nameof(ConstantValueExpression)}" - }; + yield return new object[] { + new BoundaryExpression(new DateExpression(), included: true), new BoundaryExpression(new NumericValueExpression("10"), included: true), + $"min holds {nameof(DateExpression)} and max holds {nameof(ConstantValueExpression)}" + }; - yield return new object[] { - new BoundaryExpression(new NumericValueExpression("10"), included : true), new BoundaryExpression(new DateExpression(), included : true), - $"min holds {nameof(ConstantValueExpression)} and max holds {nameof(DateExpression)}" - }; + yield return new object[] { + new BoundaryExpression(new NumericValueExpression("10"), included : true), new BoundaryExpression(new DateExpression(), included : true), + $"min holds {nameof(ConstantValueExpression)} and max holds {nameof(DateExpression)}" + }; - yield return new object[] - { - new BoundaryExpression(new TimeExpression(), included: true), - new BoundaryExpression(new DateExpression(), included: true), - $"min holds { nameof(TimeExpression) } and max holds { nameof(DateExpression) }" - }; - } + yield return new object[] + { + new BoundaryExpression(new TimeExpression(), included: true), + new BoundaryExpression(new DateExpression(), included: true), + $"min holds { nameof(TimeExpression) } and max holds { nameof(DateExpression) }" + }; } + } - [Theory] - [MemberData(nameof(IncorrectBoundariesCases))] - public void Given_incorrect_boundaries_Ctor_should_throws_IncorrectBoundaryException(BoundaryExpression min, BoundaryExpression max, string reason) - { - // Act - Action action = () => new IntervalExpression(min, max); + [Theory] + [MemberData(nameof(IncorrectBoundariesCases))] + public void Given_incorrect_boundaries_Ctor_should_throws_IncorrectBoundaryException(BoundaryExpression min, BoundaryExpression max, string reason) + { + // Act + Action action = () => _ = new IntervalExpression(min, max); - // Assert - action.Should() - .ThrowExactly(reason); - } + // Assert + action.Should() + .ThrowExactly(reason); + } - [Theory] - [MemberData(nameof(BoundariesTypeMismatchCases))] - public void Given_boundaries_that_are_not_compatible_Ctor_should_throws_BoundaryTypeMismatchException(BoundaryExpression min, BoundaryExpression max, string reason) - { - // Act - Action action = () => new IntervalExpression(min, max); + [Theory] + [MemberData(nameof(BoundariesTypeMismatchCases))] + public void Given_boundaries_that_are_not_compatible_Ctor_should_throws_BoundaryTypeMismatchException(BoundaryExpression min, BoundaryExpression max, string reason) + { + // Act + Action action = () => _ = new IntervalExpression(min, max); - // Assert - action.Should() - .ThrowExactly(reason) - .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName)); - } + // Assert + action.Should() + .ThrowExactly(reason) + .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName)); + } - public static IEnumerable ValidCtorCases + public static IEnumerable ValidCtorCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new BoundaryExpression(new DateExpression(), included: false), - new BoundaryExpression(new TimeExpression(), included: false) - }; + new BoundaryExpression(new DateExpression(), included: false), + new BoundaryExpression(new TimeExpression(), included: false) + }; - yield return new object[] - { - new BoundaryExpression(AsteriskExpression.Instance, included: false), - new BoundaryExpression(new TimeExpression(), included: false) - }; + yield return new object[] + { + new BoundaryExpression(AsteriskExpression.Instance, included: false), + new BoundaryExpression(new TimeExpression(), included: false) + }; - yield return new object[] - { - new BoundaryExpression(new TimeExpression(), included: false), - new BoundaryExpression(AsteriskExpression.Instance, included: false) - }; - } + yield return new object[] + { + new BoundaryExpression(new TimeExpression(), included: false), + new BoundaryExpression(AsteriskExpression.Instance, included: false) + }; } + } - [Theory] - [MemberData(nameof(ValidCtorCases))] - public void Given_valid_min_and_max_boundaries_Ctor_should_not_throws(BoundaryExpression min, BoundaryExpression max) - { - // Act - Action ctor = () => new IntervalExpression(min, max); + [Theory] + [MemberData(nameof(ValidCtorCases))] + public void Given_valid_min_and_max_boundaries_Ctor_should_not_throws(BoundaryExpression min, BoundaryExpression max) + { + // Act + Action ctor = () => _ = new IntervalExpression(min, max); - ctor.Should() - .NotThrow(); - } + ctor.Should() + .NotThrow(); + } - public static IEnumerable CtorLogicCases + public static IEnumerable CtorLogicCases + { + get { - get + DateTime dateTime = 12.March(2019); + yield return new object[] { - DateTime dateTime = 12.March(2019); - yield return new object[] - { - new BoundaryExpression(new DateTimeExpression(dateTime), included : true), - new BoundaryExpression(new TimeExpression(hours: 10), included : true), - new IntervalExpression(min: new BoundaryExpression(new DateTimeExpression(dateTime), included : true), - max: new BoundaryExpression(new DateTimeExpression(dateTime.Add(10.Hours())), included: true)), - "max date part should be deduced from min date part when max date part is not specified" - }; - } + new BoundaryExpression(new DateTimeExpression(dateTime), included : true), + new BoundaryExpression(new TimeExpression(hours: 10), included : true), + new IntervalExpression(min: new BoundaryExpression(new DateTimeExpression(dateTime), included : true), + max: new BoundaryExpression(new DateTimeExpression(dateTime.Add(10.Hours())), included: true)), + "max date part should be deduced from min date part when max date part is not specified" + }; } + } - [Theory] - [MemberData(nameof(CtorLogicCases))] - public void Given_min_and_max_bounds_Constructor_logic_shoud_work_as_expected(BoundaryExpression min, BoundaryExpression max, IntervalExpression expected, string reason) - { - // Act - IntervalExpression actual = new(min, max); + [Theory] + [MemberData(nameof(CtorLogicCases))] + public void Given_min_and_max_bounds_Constructor_logic_shoud_work_as_expected(BoundaryExpression min, BoundaryExpression max, IntervalExpression expected, string reason) + { + // Act + IntervalExpression actual = new(min, max); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - public static IEnumerable EqualCases + public static IEnumerable EqualCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), - new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), - true, - $"comparing two {nameof(IntervalExpression)} instances with same min and max" - }; - - yield return new object[] - { - new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), - new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : false)), - false, - $"comparing two {nameof(IntervalExpression)} instances with same min and max but not same {nameof(BoundaryExpression.Included)}" - }; + new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), + new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), + true, + $"comparing two {nameof(IntervalExpression)} instances with same min and max" + }; - yield return new object[] - { - new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), - null, - false, - "comparing to null" - }; + yield return new object[] + { + new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), + new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : false)), + false, + $"comparing two {nameof(IntervalExpression)} instances with same min and max but not same {nameof(BoundaryExpression.Included)}" + }; - yield return new object[] - { - new IntervalExpression(max: new BoundaryExpression(new NumericValueExpression("10"), included : true)), - new IntervalExpression(max: new BoundaryExpression(new NumericValueExpression("10"), included : true)), - true, - $"comparing two {nameof(IntervalExpression)} instances with same min and max" - }; + yield return new object[] + { + new IntervalExpression(min: new BoundaryExpression(new NumericValueExpression("10"), included : true)), + null, + false, + "comparing to null" + }; - yield return new object[] - { - new IntervalExpression(max: new BoundaryExpression( new DateTimeExpression(new DateExpression()), included : true)), - new IntervalExpression(max: new BoundaryExpression( new DateTimeExpression(new DateExpression()), included : true)), - true, - $"comparing two {nameof(IntervalExpression)} instances with same {nameof(DateTimeExpression)} min and max" - }; + yield return new object[] + { + new IntervalExpression(max: new BoundaryExpression(new NumericValueExpression("10"), included : true)), + new IntervalExpression(max: new BoundaryExpression(new NumericValueExpression("10"), included : true)), + true, + $"comparing two {nameof(IntervalExpression)} instances with same min and max" + }; - { - IntervalExpression interval = new(min: new BoundaryExpression(new DateTimeExpression(new DateExpression(2012, 10, 19), new TimeExpression(15, 03, 45), OffsetExpression.Zero), included: false), - max: new BoundaryExpression(new DateTimeExpression(new DateExpression(2012, 10, 19), new TimeExpression(15, 03, 45), new(hours: 1)), included: false)); - yield return new object[] - { - interval, - interval, - true, - "Comparing two ranges" - }; - } + yield return new object[] + { + new IntervalExpression(max: new BoundaryExpression( new DateTimeExpression(new DateExpression()), included : true)), + new IntervalExpression(max: new BoundaryExpression( new DateTimeExpression(new DateExpression()), included : true)), + true, + $"comparing two {nameof(IntervalExpression)} instances with same {nameof(DateTimeExpression)} min and max" + }; + { + IntervalExpression interval = new(min: new BoundaryExpression(new DateTimeExpression(new DateExpression(2012, 10, 19), new TimeExpression(15, 03, 45), OffsetExpression.Zero), included: false), + max: new BoundaryExpression(new DateTimeExpression(new DateExpression(2012, 10, 19), new TimeExpression(15, 03, 45), new(hours: 1)), included: false)); yield return new object[] { - new IntervalExpression(new BoundaryExpression(new DateTimeExpression(new (1973, 09, 02), new (18, 50, 17, 403), OffsetExpression.Zero), true), - new BoundaryExpression(new DateExpression(1944, 09, 06), true)), - new IntervalExpression(new BoundaryExpression(new DateTimeExpression(new (1973, 09, 02), new (18, 50, 17, 403), OffsetExpression.Zero), true), - new BoundaryExpression(new DateExpression(1944, 09, 06), true)), + interval, + interval, true, - "Two intervals with same data" + "Comparing two ranges" }; } + + yield return new object[] + { + new IntervalExpression(new BoundaryExpression(new DateTimeExpression(new (1973, 09, 02), new (18, 50, 17, 403), OffsetExpression.Zero), true), + new BoundaryExpression(new DateExpression(1944, 09, 06), true)), + new IntervalExpression(new BoundaryExpression(new DateTimeExpression(new (1973, 09, 02), new (18, 50, 17, 403), OffsetExpression.Zero), true), + new BoundaryExpression(new DateExpression(1944, 09, 06), true)), + true, + "Two intervals with same data" + }; } + } - [Theory] - [MemberData(nameof(EqualCases))] - public void Equals_should_work_as_expected(IntervalExpression first, object other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + [Theory] + [MemberData(nameof(EqualCases))] + public void Equals_should_work_as_expected(IntervalExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = first.Equals(other); + // Act + bool actual = first.Equals(other); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_min_bound_is_DateTimeExpression_When_min_does_not_hold_TimeExpression_Constructor_should_converts_Min_to_DateExpression(DateExpression date, bool included) - { - // Arrange - DateTimeExpression dateTimeExpression = new(date); - BoundaryExpression minBoundary = new(dateTimeExpression, included); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_min_bound_is_DateTimeExpression_When_min_does_not_hold_TimeExpression_Constructor_should_converts_Min_to_DateExpression(DateExpression date, bool included) + { + // Arrange + DateTimeExpression dateTimeExpression = new(date); + BoundaryExpression minBoundary = new(dateTimeExpression, included); - // Act - IntervalExpression sut = new(minBoundary); + // Act + IntervalExpression sut = new(minBoundary); - // Assert - sut.Min.Expression.Should().Be(date); - } + // Assert + sut.Min.Expression.Should().Be(date); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_Min_boundary_is_a_DateTimeExpression_with_only_time_specified_Ctor_should_convert_min_boundary_to_only_holds_the_specified_TimeExpression(NonNull time, bool included) - { - // Arrange - DateTimeExpression dateTimeExpression = new(time.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_Min_boundary_is_a_DateTimeExpression_with_only_time_specified_Ctor_should_convert_min_boundary_to_only_holds_the_specified_TimeExpression(NonNull time, bool included) + { + // Arrange + DateTimeExpression dateTimeExpression = new(time.Item); - // Act - IntervalExpression interval = new(min: new BoundaryExpression(dateTimeExpression, included)); + // Act + IntervalExpression interval = new(min: new BoundaryExpression(dateTimeExpression, included)); - // Assert - interval.Min.Expression.Should() - .BeOfType() - .Which.Should().Be(time.Item); - } + // Assert + interval.Min.Expression.Should() + .BeOfType() + .Which.Should().Be(time.Item); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_Min_boundary_is_a_DateTimeExpression_with_only_date_specified_Ctor_should_convert_min_boundary_to_only_holds_the_specified_DateExpression(NonNull date, bool included) - { - // Arrange - // Arrange - DateTimeExpression dateTimeExpression = new(date.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_Min_boundary_is_a_DateTimeExpression_with_only_date_specified_Ctor_should_convert_min_boundary_to_only_holds_the_specified_DateExpression(NonNull date, bool included) + { + // Arrange + // Arrange + DateTimeExpression dateTimeExpression = new(date.Item); - // Act - IntervalExpression interval = new(min: new BoundaryExpression(dateTimeExpression, included)); + // Act + IntervalExpression interval = new(min: new BoundaryExpression(dateTimeExpression, included)); - // Assert - interval.Min.Expression.Should() - .BeOfType() - .Which.Should().Be(date.Item); - } + // Assert + interval.Min.Expression.Should() + .BeOfType() + .Which.Should().Be(date.Item); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Ctor_should_converts_Max_DateTimeExpression_to_TimeExpression_when_DateExpression_is_not_provided(PositiveInt hours, - PositiveInt minutes, - PositiveInt seconds, - PositiveInt milliseconds, - bool included) - { - // Arrange - TimeExpression time = new(hours.Item, minutes.Item, seconds.Item, milliseconds.Item); - DateTimeExpression dateTimeExpression = new(time); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Ctor_should_converts_Max_DateTimeExpression_to_TimeExpression_when_DateExpression_is_not_provided(PositiveInt hours, + PositiveInt minutes, + PositiveInt seconds, + PositiveInt milliseconds, + bool included) + { + // Arrange + TimeExpression time = new(hours.Item, minutes.Item, seconds.Item, milliseconds.Item); + DateTimeExpression dateTimeExpression = new(time); - // Act - IntervalExpression interval = new(max: new BoundaryExpression(dateTimeExpression, included)); - (IBoundaryExpression expression, _) = interval.Max; + // Act + IntervalExpression interval = new(max: new BoundaryExpression(dateTimeExpression, included)); + (IBoundaryExpression expression, _) = interval.Max; - // Assert - expression.Should() - .Be(time); - } + // Assert + expression.Should() + .Be(time); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_a_ConstantExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_ConstantExpression(NumericValueExpression constant) - { - CreateIsEquivalentPropeprty(constant, constant, minIncluded: true, maxIsIncluded: true); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_a_ConstantExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_ConstantExpression(NumericValueExpression constant) + { + CreateIsEquivalentPropeprty(constant, constant, minIncluded: true, maxIsIncluded: true); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_reflexive(NonNull interval) - { - // Arrange - IntervalExpression source = interval.Item; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_reflexive(NonNull interval) + { + // Arrange + IntervalExpression source = interval.Item; - // Act - bool actual = source.IsEquivalentTo(source); + // Act + bool actual = source.IsEquivalentTo(source); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_a_DateExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_DateExpression(DateExpression date) - { - CreateIsEquivalentPropeprty(date, date, minIncluded: true, maxIsIncluded: true); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_a_DateExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_DateExpression(DateExpression date) + { + CreateIsEquivalentPropeprty(date, date, minIncluded: true, maxIsIncluded: true); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_a_DateTimeExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_DateTimeExpression(DateTimeExpression dateTime) - { - CreateIsEquivalentPropeprty(dateTime, dateTime, minIncluded: true, maxIsIncluded: true); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_a_DateTimeExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_DateTimeExpression(DateTimeExpression dateTime) + { + CreateIsEquivalentPropeprty(dateTime, dateTime, minIncluded: true, maxIsIncluded: true); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_a_DateTimeExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_TimeExpression(TimeExpression dateTime) - { - CreateIsEquivalentPropeprty(dateTime, dateTime, minIncluded: true, maxIsIncluded: true); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_a_DateTimeExpression_that_is_equal_to_min_and_min_and_max_are_equal_and_min_and_max_are_included_IsEquivalent_should_return_true_when_comparing_with_TimeExpression(TimeExpression dateTime) + { + CreateIsEquivalentPropeprty(dateTime, dateTime, minIncluded: true, maxIsIncluded: true); + } - private static void CreateIsEquivalentPropeprty(FilterExpression filterExpression, IBoundaryExpression boundaryExpression, bool minIncluded, bool maxIsIncluded) - { - // Arrange - IntervalExpression range = new(new BoundaryExpression(boundaryExpression, minIncluded), new BoundaryExpression(boundaryExpression, maxIsIncluded)); + private static void CreateIsEquivalentPropeprty(FilterExpression filterExpression, IBoundaryExpression boundaryExpression, bool minIncluded, bool maxIsIncluded) + { + // Arrange + IntervalExpression range = new(new BoundaryExpression(boundaryExpression, minIncluded), new BoundaryExpression(boundaryExpression, maxIsIncluded)); - // Act - bool actual = range.IsEquivalentTo(filterExpression); + // Act + bool actual = range.IsEquivalentTo(filterExpression); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_RangeExpression_GetComplexity_should_return_sum_of_min_and_max_complexity(NonNull rangeExpressionGenerator) - { - // Arrange - IntervalExpression rangeExpression = rangeExpressionGenerator.Item; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_RangeExpression_GetComplexity_should_return_sum_of_min_and_max_complexity(NonNull rangeExpressionGenerator) + { + // Arrange + IntervalExpression rangeExpression = rangeExpressionGenerator.Item; - // Act - double actualComplexity = rangeExpression.Complexity; + // Act + double actualComplexity = rangeExpression.Complexity; - // Assert - object _ = (rangeExpression.Min, rangeExpression.Max) switch - { - ({ Expression: { } minExpression }, { Expression: { } maxExpression }) => actualComplexity.Should().Be(minExpression.Complexity + maxExpression.Complexity), - ({ Expression: { } minExpression }, null) => actualComplexity.Should().Be(minExpression.Complexity), - (null, { Expression: { } maxExpression }) => actualComplexity.Should().Be(maxExpression.Complexity), - _ => actualComplexity.Should().Be(0), - }; - } + // Assert + object _ = (rangeExpression.Min, rangeExpression.Max) switch + { + ({ Expression: { } minExpression }, { Expression: { } maxExpression }) => actualComplexity.Should().Be(minExpression.Complexity + maxExpression.Complexity), + ({ Expression: { } minExpression }, null) => actualComplexity.Should().Be(minExpression.Complexity), + (null, { Expression: { } maxExpression }) => actualComplexity.Should().Be(maxExpression.Complexity), + _ => actualComplexity.Should().Be(0), + }; + } - public static IEnumerable SimplifyCases + public static IEnumerable SimplifyCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new IntervalExpression(new (new NumericValueExpression("10"), true), new (new NumericValueExpression("10"), true)), - new NumericValueExpression("10"), - "Lower and upper bound are equal" - }; + new IntervalExpression(new (new NumericValueExpression("10"), true), new (new NumericValueExpression("10"), true)), + new NumericValueExpression("10"), + "Lower and upper bound are equal" + }; - yield return new object[] - { - new IntervalExpression(new (new NumericValueExpression("10"), true), new (new NumericValueExpression("12"), true)), - new IntervalExpression(new (new NumericValueExpression("10"), true), new (new NumericValueExpression("12"), true)), - "Lower and upper bound are not equals and not equivalent" - }; + yield return new object[] + { + new IntervalExpression(new (new NumericValueExpression("10"), true), new (new NumericValueExpression("12"), true)), + new IntervalExpression(new (new NumericValueExpression("10"), true), new (new NumericValueExpression("12"), true)), + "Lower and upper bound are not equals and not equivalent" + }; - yield return new object[] - { - new IntervalExpression(new (new TimeExpression(1), true), new (new TimeExpression(minutes: 60), true)), - new TimeExpression(1), - "Lower and upper bound are equivalent" - }; + yield return new object[] + { + new IntervalExpression(new (new TimeExpression(1), true), new (new TimeExpression(minutes: 60), true)), + new TimeExpression(1), + "Lower and upper bound are equivalent" + }; - yield return new object[] - { - new IntervalExpression(new (new NumericValueExpression("-32"), true), new (new NumericValueExpression("-32"), true)), - new NumericValueExpression("-32"), - "Lower and upper bound are equal" - }; - } + yield return new object[] + { + new IntervalExpression(new (new NumericValueExpression("-32"), true), new (new NumericValueExpression("-32"), true)), + new NumericValueExpression("-32"), + "Lower and upper bound are equal" + }; } + } - [Theory] - [MemberData(nameof(SimplifyCases))] - public void Given_IntervalExpression_Simplify_should_return_expected_expression(IntervalExpression rangeExpression, FilterExpression expected, string reason) - { - outputHelper.WriteLine($"Range expression : {rangeExpression}"); + [Theory] + [MemberData(nameof(SimplifyCases))] + public void Given_IntervalExpression_Simplify_should_return_expected_expression(IntervalExpression rangeExpression, FilterExpression expected, string reason) + { + outputHelper.WriteLine($"Range expression : {rangeExpression}"); - // Act - FilterExpression actual = rangeExpression.Simplify(); + // Act + FilterExpression actual = rangeExpression.Simplify(); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void An_IntervalExpression_instance_built_from_data_from_a_previously_deconstructed_instance_should_be_equal(IntervalExpression expected) - { - // Arrange - (BoundaryExpression min, BoundaryExpression max) = expected; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void An_IntervalExpression_instance_built_from_data_from_a_previously_deconstructed_instance_should_be_equal(IntervalExpression expected) + { + // Arrange + (BoundaryExpression min, BoundaryExpression max) = expected; - // Act - IntervalExpression actual = new(min, max); + // Act + IntervalExpression actual = new(min, max); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs index 5b64d5c3..3e70f796 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NotExpressionTests.cs @@ -1,163 +1,162 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; + +public class NotExpressionTests { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - - public class NotExpressionTests + [Fact] + public void IsFilterExpression() => typeof(NotExpression).Should() + .BeAssignableTo().And + .Implement>().And + .HaveConstructor(new[] { typeof(FilterExpression) }).And + .HaveProperty("Expression"); + + [Fact] + public void Ctor_should_throws_ArgumentNullException_when_argument_is_null() { - [Fact] - public void IsFilterExpression() => typeof(NotExpression).Should() - .BeAssignableTo().And - .Implement>().And - .HaveConstructor(new[] { typeof(FilterExpression) }).And - .HaveProperty("Expression"); - - [Fact] - public void Ctor_should_throws_ArgumentNullException_when_argument_is_null() - { - // Act - Action action = () => new NotExpression(null); + // Act + Action action = () => _ = new NotExpression(null); - // Assert - action.Should() - .ThrowExactly($"The parameter of {nameof(NotExpression)}'s constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"The parameter of {nameof(NotExpression)}'s constructor cannot be null"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_NotExpression_GetComplexity_should_return_same_complexity_as_embedded_expression(NonNull notExpression) - => notExpression.Item.Complexity.Should().Be(notExpression.Item.Expression.Complexity); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_NotExpression_GetComplexity_should_return_same_complexity_as_embedded_expression(NonNull notExpression) + => notExpression.Item.Complexity.Should().Be(notExpression.Item.Expression.Complexity); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_NotExpression_Equals_should_be_reflexive(NonNull notExpression) - => notExpression.Item.Should().Be(notExpression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_NotExpression_Equals_should_be_reflexive(NonNull notExpression) + => notExpression.Item.Should().Be(notExpression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_NotExpression_and_a_filter_expression_that_is_not_null_Equals_should_be_symetric(NonNull notExpression, NonNull filterExpression) - => notExpression.Item.Equals(filterExpression.Item).Should().Be(filterExpression.Item.Equals(notExpression.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_NotExpression_and_a_filter_expression_that_is_not_null_Equals_should_be_symetric(NonNull notExpression, NonNull filterExpression) + => notExpression.Item.Equals(filterExpression.Item).Should().Be(filterExpression.Item.Equals(notExpression.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_instances_holding_same_Expressions_Equals_should_return_true(NonNull expression) - { - // Arrange - NotExpression first = new(expression.Item); - NotExpression other = new(expression.Item); - - // Act - bool actual = first.Equals(other); - int firstHashCode = first.GetHashCode(); - int otherHashCode = other.GetHashCode(); - - // Assert - actual.Should().BeTrue(); - firstHashCode.Should().Be(otherHashCode); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_instances_holding_same_Expressions_Equals_should_return_true(NonNull expression) + { + // Arrange + NotExpression first = new(expression.Item); + NotExpression other = new(expression.Item); + + // Act + bool actual = first.Equals(other); + int firstHashCode = first.GetHashCode(); + int otherHashCode = other.GetHashCode(); + + // Assert + actual.Should().BeTrue(); + firstHashCode.Should().Be(otherHashCode); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_argument_is_AndExpression_Constructor_should_wrap_it_inside_a_GroupExpression(NonNull expression) - { - // Act - NotExpression not = new(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_argument_is_AndExpression_Constructor_should_wrap_it_inside_a_GroupExpression(NonNull expression) + { + // Act + NotExpression not = new(expression.Item); - // Assert - not.Expression.Should() - .BeOfType("the parameter is a BinaryFilterExpression"); - } + // Assert + not.Expression.Should() + .BeOfType("the parameter is a BinaryFilterExpression"); + } - public static IEnumerable EscapedParseableStringCases + public static IEnumerable EscapedParseableStringCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new NotExpression( - new OrExpression(new DateTimeExpression(new TimeExpression(2, 53, 39, 827)), - new DateTimeExpression(new DateExpression(1901,06,17), - new TimeExpression(09, 13,44, 17), - OffsetExpression.Zero)) - ), - - "!(T02:53:39.827|1901-06-17T09:13:44.17Z)" - }; - - yield return new object[] - { - new NotExpression( - new OrExpression(new TimeExpression(2, 53, 39, 827), - new DateTimeExpression(new DateExpression(1901,06,17), - new TimeExpression(09, 13,44, 17), - OffsetExpression.Zero)) - ), - - "!(02:53:39.827|1901-06-17T09:13:44.17Z)" - }; - } - } + new NotExpression( + new OrExpression(new DateTimeExpression(new TimeExpression(2, 53, 39, 827)), + new DateTimeExpression(new DateExpression(1901,06,17), + new TimeExpression(09, 13,44, 17), + OffsetExpression.Zero)) + ), - [Theory] - [MemberData(nameof(EscapedParseableStringCases))] - public void Given_NotExpression_EscapedParseableString_should_match_expected(NotExpression expression, string expected) - { - // Act - string actual = expression.EscapedParseableString; + "!(T02:53:39.827|1901-06-17T09:13:44.17Z)" + }; - // Assert - actual.Should() - .Be(expected); + yield return new object[] + { + new NotExpression( + new OrExpression(new TimeExpression(2, 53, 39, 827), + new DateTimeExpression(new DateExpression(1901,06,17), + new TimeExpression(09, 13,44, 17), + OffsetExpression.Zero)) + ), + + "!(02:53:39.827|1901-06-17T09:13:44.17Z)" + }; } + } + + [Theory] + [MemberData(nameof(EscapedParseableStringCases))] + public void Given_NotExpression_EscapedParseableString_should_match_expected(NotExpression expression, string expected) + { + // Act + string actual = expression.EscapedParseableString; - public static IEnumerable EqualsCases + // Assert + actual.Should() + .Be(expected); + } + + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new NotExpression( - new OrExpression(new DateTimeExpression(new TimeExpression(2, 53, 39, 827)), - new DateTimeExpression(new DateExpression(1901,06,17), - new TimeExpression(09, 13,44, 17), - OffsetExpression.Zero)) - ), - new NotExpression( - new OrExpression(new TimeExpression(2, 53, 39, 827), - new DateTimeExpression(new DateExpression(1901,06,17), - new TimeExpression(09, 13,44, 17), - OffsetExpression.Zero)) - ), - true, - $"{nameof(DateTimeExpression.Date)} and {nameof(DateTimeExpression.Date)} are null and TimeExpression are equal" - }; - } + new NotExpression( + new OrExpression(new DateTimeExpression(new TimeExpression(2, 53, 39, 827)), + new DateTimeExpression(new DateExpression(1901,06,17), + new TimeExpression(09, 13,44, 17), + OffsetExpression.Zero)) + ), + new NotExpression( + new OrExpression(new TimeExpression(2, 53, 39, 827), + new DateTimeExpression(new DateExpression(1901,06,17), + new TimeExpression(09, 13,44, 17), + OffsetExpression.Zero)) + ), + true, + $"{nameof(DateTimeExpression.Date)} and {nameof(DateTimeExpression.Date)} are null and TimeExpression are equal" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_work_as_expected(NotExpression expression, object obj, bool expected, string reason) - { - // Act - bool actual = expression.Equals(obj); + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_work_as_expected(NotExpression expression, object obj, bool expected, string reason) + { + // Act + bool actual = expression.Equals(obj); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).ToProperty(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Equals(expression.Item).ToProperty(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs index 61d25a06..7233eb88 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/NumericValueExpressionTests.cs @@ -1,115 +1,114 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class NumericValueExpressionTests(ITestOutputHelper outputHelper) { - using System; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class NumericValueExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(NumericValueExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement(); + + [Fact] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { - [Fact] - public void IsFilterExpression() => typeof(NumericValueExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement(); - - [Fact] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() - { - // Act - Action action = () => _ = new NumericValueExpression(null); - - // Assert - action.Should() - .ThrowExactly($"The parameter of {nameof(NumericValueExpression)}'s constructor cannot be null"); - } - - [Fact] - public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() - { - // Act - Action action = () => _ = new NumericValueExpression(string.Empty); - - // Assert - action.Should() - .ThrowExactly($"The parameter of {nameof(StringValueExpression)}'s constructor cannot be empty"); - } - - [Fact] - public void Given_parameter_is_whitespace_Ctor_should_not_throw() - { - // Act - Action action = () => _ = new NumericValueExpression(" "); - - // Assert - action.Should() - .NotThrow($"The parameter of {nameof(StringValueExpression)}'s constructor can be whitespace"); - } - - [Property] - public void Two_instances_are_equals_when_holding_same_values(NonEmptyString value) - { - // Arrange - NumericValueExpression first = new(value.Item); - NumericValueExpression other = new(value.Item); - - // Act - bool actual = first.Equals(other); - - // Assert - actual.Should().BeTrue(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_is_commutative(NumericValueExpression first, NumericValueExpression second) - => first.Equals(second).Should().Be(second.Equals(first)); - - [Property] - public void Given_two_NumericValueExpressions_Equals_should_depends_on_string_input_only(NonWhiteSpaceString input) - { - // Arrange - NumericValueExpression first = new(input.Get); - NumericValueExpression second = new(input.Get); - - // Act - first.Equals(second).Should().Be(Equals(first.Value, second.Value)); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_NumericValueExpression_GetComplexity_should_return_1(NumericValueExpression constant) - => constant.Complexity.Should().Be(1); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_IntervalExpression_with_min_equals_max_is_equivalent_should_return_true(NumericValueExpression input) - { - // Arrange - IntervalExpression interval = new(new BoundaryExpression(input, true), - new BoundaryExpression(input, true)); - - // Act - bool actual = input.IsEquivalentTo(interval); - - // Assert - actual.Should() - .BeTrue(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); + // Act + Action action = () => _ = new NumericValueExpression(null); + + // Assert + action.Should() + .ThrowExactly($"The parameter of {nameof(NumericValueExpression)}'s constructor cannot be null"); + } + + [Fact] + public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() + { + // Act + Action action = () => _ = new NumericValueExpression(string.Empty); + + // Assert + action.Should() + .ThrowExactly($"The parameter of {nameof(StringValueExpression)}'s constructor cannot be empty"); + } + + [Fact] + public void Given_parameter_is_whitespace_Ctor_should_not_throw() + { + // Act + Action action = () => _ = new NumericValueExpression(" "); + + // Assert + action.Should() + .NotThrow($"The parameter of {nameof(StringValueExpression)}'s constructor can be whitespace"); + } + + [Property] + public void Two_instances_are_equals_when_holding_same_values(NonEmptyString value) + { + // Arrange + NumericValueExpression first = new(value.Item); + NumericValueExpression other = new(value.Item); + + // Act + bool actual = first.Equals(other); + + // Assert + actual.Should().BeTrue(); } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_is_commutative(NumericValueExpression first, NumericValueExpression second) + => first.Equals(second).Should().Be(second.Equals(first)); + + [Property] + public void Given_two_NumericValueExpressions_Equals_should_depends_on_string_input_only(NonWhiteSpaceString input) + { + // Arrange + NumericValueExpression first = new(input.Get); + NumericValueExpression second = new(input.Get); + + // Act + first.Equals(second).Should().Be(Equals(first.Value, second.Value)); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_NumericValueExpression_GetComplexity_should_return_1(NumericValueExpression constant) + => constant.Complexity.Should().Be(1); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_IntervalExpression_with_min_equals_max_is_equivalent_should_return_true(NumericValueExpression input) + { + // Arrange + IntervalExpression interval = new(new BoundaryExpression(input, true), + new BoundaryExpression(input, true)); + + // Act + bool actual = input.IsEquivalentTo(interval); + + // Assert + actual.Should() + .BeTrue(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs index 98709ea6..31b16f8c 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OffsetExpressionTests.cs @@ -1,66 +1,65 @@ -namespace DataFilters.UnitTests.Grammar.Syntax -{ - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; +namespace DataFilters.UnitTests.Grammar.Syntax; + +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; - using FluentAssertions; +using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; - using Xunit; - using Xunit.Categories; +using Xunit; +using Xunit.Categories; - [UnitTest] - public class OffsetExpressionTests +[UnitTest] +public class OffsetExpressionTests +{ + [Theory] + [InlineData(NumericSign.Minus, 0, 0, "Z")] + [InlineData(NumericSign.Plus, 0, 0, "Z")] + [InlineData(NumericSign.Minus, 1, 0, "-01:00")] + [InlineData(NumericSign.Plus, 2, 0, "+02:00")] + [InlineData(NumericSign.Minus, 2, 0, "-02:00")] + public void Given_input_EscapedParseableString_should_be_correct(NumericSign sign, uint hours, uint minutes, string expected) { - [Theory] - [InlineData(NumericSign.Minus, 0, 0, "Z")] - [InlineData(NumericSign.Plus, 0, 0, "Z")] - [InlineData(NumericSign.Minus, 1, 0, "-01:00")] - [InlineData(NumericSign.Plus, 2, 0, "+02:00")] - [InlineData(NumericSign.Minus, 2, 0, "-02:00")] - public void Given_input_EscapedParseableString_should_be_correct(NumericSign sign, uint hours, uint minutes, string expected) - { - // Arrange - OffsetExpression offset = new(sign, hours, minutes); + // Arrange + OffsetExpression offset = new(sign, hours, minutes); - // Act - string actual = offset.EscapedParseableString; + // Act + string actual = offset.EscapedParseableString; - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_an_OffsetExpression_instance_Equals_should_be_reflective(NonNull offset) => offset.Item.Equals(offset.Item).ToProperty(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_an_OffsetExpression_instance_Equals_should_be_reflective(NonNull offset) => offset.Item.Equals(offset.Item).ToProperty(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - [Property] - public void Given_offset_is_Zero_When_comparing_same_offset_should_not_take_into_account_the_numeric_sign(NumericSign sign) - { - // Arrange - OffsetExpression zero = OffsetExpression.Zero; - OffsetExpression zeroWithNegativeSign = new(sign, (uint)zero.Hours, (uint)zero.Minutes); + [Property] + public void Given_offset_is_Zero_When_comparing_same_offset_should_not_take_into_account_the_numeric_sign(NumericSign sign) + { + // Arrange + OffsetExpression zero = OffsetExpression.Zero; + OffsetExpression zeroWithNegativeSign = new(sign, (uint)zero.Hours, (uint)zero.Minutes); - // Act - bool actual = zero == zeroWithNegativeSign; + // Act + bool actual = zero == zeroWithNegativeSign; - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs index 2236bf71..460125d9 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OneOfExpressionTests.cs @@ -1,336 +1,335 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; + +public class OneOfExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using System.Linq; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - - public class OneOfExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(OneOfExpression).Should() + .BeAssignableTo().And + .Implement>().And + .HaveConstructor(new[] { typeof(FilterExpression[]) }).And + .HaveProperty>("Values"); + + [Fact] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { - [Fact] - public void IsFilterExpression() => typeof(OneOfExpression).Should() - .BeAssignableTo().And - .Implement>().And - .HaveConstructor(new[] { typeof(FilterExpression[]) }).And - .HaveProperty>("Values"); - - [Fact] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() - { - // Act - Action action = () => _ = new OneOfExpression(null); + // Act + Action action = () => _ = new OneOfExpression(null); - // Assert - action.Should() - .ThrowExactly($"The parameter {nameof(OneOfExpression)}'s constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"The parameter {nameof(OneOfExpression)}'s constructor cannot be null"); + } + + [Fact] + public void Constructor_throws_InvalidOperationException_when_values_is_empty() + { + // Act + Action ctorWithEmptyArray = () => _ = new OneOfExpression([]); - [Fact] - public void Constructor_throws_InvalidOperationException_when_values_is_empty() + // Assert + ctorWithEmptyArray.Should() + .ThrowExactly($"The parameter {nameof(OneOfExpression)}'s constructor cannot be a empty array"); + } + + public static IEnumerable EqualsCases + { + get { - // Act - Action ctorWithEmptyArray = () => _ = new OneOfExpression(Array.Empty()); + yield return new object[] + { + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + true, + "comparing two different instances with same data in same order" + }; - // Assert - ctorWithEmptyArray.Should() - .ThrowExactly($"The parameter {nameof(OneOfExpression)}'s constructor cannot be a empty array"); + yield return new object[] + { + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop2"), new StringValueExpression("prop1")), + false, + "comparing two different instances with same data but the order does not matter" + }; + + yield return new object[] + { + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop3")), + false, + "comparing two different instances with different data" + }; } + } - public static IEnumerable EqualsCases + [Theory] + [MemberData(nameof(EqualsCases))] + public void ImplementsEqualsCorrectly(OneOfExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); + + // Act + bool actual = first.Equals(other); + int actualHashCode = first.GetHashCode(); + + // Assert + actual.Should() + .Be(expected, reason); + if (expected) { - get - { - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - true, - "comparing two different instances with same data in same order" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop2"), new StringValueExpression("prop1")), - false, - "comparing two different instances with same data but the order does not matter" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop3")), - false, - "comparing two different instances with different data" - }; - } + actualHashCode.Should() + .Be(other?.GetHashCode(), reason); } + else + { + actualHashCode.Should() + .NotBe(other?.GetHashCode(), reason); + } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void ImplementsEqualsCorrectly(OneOfExpression first, object other, bool expected, string reason) + public static IEnumerable IsEquivalentToCases + { + get { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + yield return new object[] + { + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + true, + "comparing two different instances with same data in same order" + }; - // Act - bool actual = first.Equals(other); - int actualHashCode = first.GetHashCode(); + yield return new object[] + { + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop2"), new StringValueExpression("prop1")), + true, + "comparing two different instances with same data but the order does not matter" + }; - // Assert - actual.Should() - .Be(expected, reason); - if (expected) + yield return new object[] { - actualHashCode.Should() - .Be(other?.GetHashCode(), reason); - } - else + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop3")), + false, + "comparing two different instances with different data" + }; + + yield return new object[] { - actualHashCode.Should() - .NotBe(other?.GetHashCode(), reason); - } - } + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2"), new StringValueExpression("prop3")), + false, + "the other instance contains all data of the first instance and one item that is not in the current instance" + }; - public static IEnumerable IsEquivalentToCases - { - get + yield return new object[] { - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - true, - "comparing two different instances with same data in same order" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop2"), new StringValueExpression("prop1")), - true, - "comparing two different instances with same data but the order does not matter" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop3")), - false, - "comparing two different instances with different data" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2"), new StringValueExpression("prop3")), - false, - "the other instance contains all data of the first instance and one item that is not in the current instance" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - true, - $"a {nameof(OneOfExpression)} instance that holds duplicates is equivalent a {nameof(OneOfExpression)} with no duplicate" - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), - true, - $"a {nameof(OneOfExpression)} instance that holds two distinct value is equivalent to an {nameof(OrExpression)} with the same values" - }; - - yield return new object[] - { - new OneOfExpression(new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), - new BoundaryExpression(new NumericValueExpression("-1"), true)), - new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), - new BoundaryExpression(new NumericValueExpression("-1"), true))), - new NumericValueExpression("-1"), - true, - $"a {nameof(OneOfExpression)} instance that holds two distinct value is equivalent to its simplified version" - }; - } - } + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + true, + $"a {nameof(OneOfExpression)} instance that holds duplicates is equivalent a {nameof(OneOfExpression)} with no duplicate" + }; - [Theory] - [MemberData(nameof(IsEquivalentToCases))] - public void Implements_IsEquivalentTo_Properly(OneOfExpression first, FilterExpression other, bool expected, string reason) - { - // Act - bool actual = first.IsEquivalentTo(other); + yield return new object[] + { + new OneOfExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop2")), + true, + $"a {nameof(OneOfExpression)} instance that holds two distinct value is equivalent to an {nameof(OrExpression)} with the same values" + }; - // Assert - actual.Should() - .Be(expected, reason); + yield return new object[] + { + new OneOfExpression(new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), + new BoundaryExpression(new NumericValueExpression("-1"), true)), + new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), + new BoundaryExpression(new NumericValueExpression("-1"), true))), + new NumericValueExpression("-1"), + true, + $"a {nameof(OneOfExpression)} instance that holds two distinct value is equivalent to its simplified version" + }; } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_ConstantExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(StringValueExpression filterExpression, PositiveInt n) - => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); + [Theory] + [MemberData(nameof(IsEquivalentToCases))] + public void Implements_IsEquivalentTo_Properly(OneOfExpression first, FilterExpression other, bool expected, string reason) + { + // Act + bool actual = first.IsEquivalentTo(other); + + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateExpression filterExpression, PositiveInt n) - => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_ConstantExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(StringValueExpression filterExpression, PositiveInt n) + => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateTimeExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateTimeExpression filterExpression, PositiveInt n) - => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateExpression filterExpression, PositiveInt n) + => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_AsteriskExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(PositiveInt n) - => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(AsteriskExpression.Instance, n.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateTimeExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateTimeExpression filterExpression, PositiveInt n) + => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(filterExpression, n.Item); - private static void Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(FilterExpression filterExpression, int n) - { - // Arrange - IEnumerable filterExpressions = Enumerable.Repeat(filterExpression, n); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_AsteriskExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(PositiveInt n) + => Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(AsteriskExpression.Instance, n.Item); - OneOfExpression oneOfExpression = new(filterExpressions.ToArray()); + private static void Given_OneOfExpression_contains_the_same_epxression_repeated_many_time_When_comparing_to_that_expression_IsEquivalentTo_should_return_true(FilterExpression filterExpression, int n) + { + // Arrange + IEnumerable filterExpressions = Enumerable.Repeat(filterExpression, n); - // Act - bool actual = oneOfExpression.IsEquivalentTo(filterExpression); + OneOfExpression oneOfExpression = new(filterExpressions.ToArray()); - // Assert - actual.Should().BeTrue(); - } + // Act + bool actual = oneOfExpression.IsEquivalentTo(filterExpression); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OneOfExpression_Complexity_should_return_sum_of_inner_expressions(NonEmptyArray expressions) - { - // Arrange - OneOfExpression oneOfExpression = new(expressions.Item); - double expected = oneOfExpression.Values.Sum(expr => expr.Complexity); + // Assert + actual.Should().BeTrue(); + } - // Act - double actual = oneOfExpression.Complexity; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OneOfExpression_Complexity_should_return_sum_of_inner_expressions(NonEmptyArray expressions) + { + // Arrange + OneOfExpression oneOfExpression = new(expressions.Item); + double expected = oneOfExpression.Values.Sum(expr => expr.Complexity); - // Assert - actual.Should().Be(expected); - } + // Act + double actual = oneOfExpression.Complexity; - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OneOfExpression_with_same_values_repetead_more_than_once_Simplify_should_filter_out_duplicated_values(NonEmptyArray expressions) - { - // Arrange - OneOfExpression oneOf = new(expressions.Item); + // Assert + actual.Should().Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OneOfExpression_with_same_values_repetead_more_than_once_Simplify_should_filter_out_duplicated_values(NonEmptyArray expressions) + { + // Arrange + OneOfExpression oneOf = new(expressions.Item); - double complexityBeforeCallingSimplify = oneOf.Complexity; + double complexityBeforeCallingSimplify = oneOf.Complexity; - // Act - FilterExpression simplifiedExpression = oneOf.Simplify(); + // Act + FilterExpression simplifiedExpression = oneOf.Simplify(); - // Assert - simplifiedExpression.Complexity.Should().BeLessThanOrEqualTo(complexityBeforeCallingSimplify); - } + // Assert + simplifiedExpression.Complexity.Should().BeLessThanOrEqualTo(complexityBeforeCallingSimplify); + } - public static IEnumerable SimplifyCases + public static IEnumerable SimplifyCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new OneOfExpression(new StringValueExpression("val1"), new StringValueExpression("val2")), - new OrExpression(new StringValueExpression("val1"), new StringValueExpression("val2")) - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("val1"), new StringValueExpression("val1")), - new StringValueExpression("val1") - }; - - yield return new object[] - { - new OneOfExpression(new StringValueExpression("val1"), new OrExpression(new StringValueExpression("val1"), new StringValueExpression("val1"))), - new StringValueExpression("val1") - }; - - yield return new object[] - { - new OneOfExpression(new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), - new BoundaryExpression(new NumericValueExpression("-1"), true)), - new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), - new BoundaryExpression(new NumericValueExpression("-1"), true))), - new NumericValueExpression("-1"), - }; - } - } + new OneOfExpression(new StringValueExpression("val1"), new StringValueExpression("val2")), + new OrExpression(new StringValueExpression("val1"), new StringValueExpression("val2")) + }; - [Theory] - [MemberData(nameof(SimplifyCases))] - public void Given_OneOfExpression_Simplify_should_output_expected_expression(OneOfExpression input, FilterExpression expected) - { - // Act - FilterExpression actual = input.Simplify(); + yield return new object[] + { + new OneOfExpression(new StringValueExpression("val1"), new StringValueExpression("val1")), + new StringValueExpression("val1") + }; + + yield return new object[] + { + new OneOfExpression(new StringValueExpression("val1"), new OrExpression(new StringValueExpression("val1"), new StringValueExpression("val1"))), + new StringValueExpression("val1") + }; - // Assert - actual.Should().Be(expected); + yield return new object[] + { + new OneOfExpression(new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), + new BoundaryExpression(new NumericValueExpression("-1"), true)), + new IntervalExpression(new BoundaryExpression(new NumericValueExpression("-1"), true), + new BoundaryExpression(new NumericValueExpression("-1"), true))), + new NumericValueExpression("-1"), + }; } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OneOfExpression_instance_which_contains_only_one_expression_Simplify_should_return_an_expression_that_is_requivalent_to_the_inner_expression(NonNull expression) - { - // Arrange - outputHelper.WriteLine($"input : {expression.Item}"); - OneOfExpression oneOf = new(expression.Item); + [Theory] + [MemberData(nameof(SimplifyCases))] + public void Given_OneOfExpression_Simplify_should_output_expected_expression(OneOfExpression input, FilterExpression expected) + { + // Act + FilterExpression actual = input.Simplify(); - // Act - FilterExpression simplified = oneOf.Simplify(); + // Assert + actual.Should().Be(expected); + } - outputHelper.WriteLine($"Simplified : {simplified}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OneOfExpression_instance_which_contains_only_one_expression_Simplify_should_return_an_expression_that_is_requivalent_to_the_inner_expression(NonNull expression) + { + // Arrange + outputHelper.WriteLine($"input : {expression.Item}"); + OneOfExpression oneOf = new(expression.Item); - // Assert - simplified.IsEquivalentTo(expression.Item) - .Should() - .BeTrue(); - } + // Act + FilterExpression simplified = oneOf.Simplify(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OneOfExpression_Simplify_should_return_an_expression_that_cannot_be_further_simplified(PositiveInt count, NonNull expression) - { - // Arrange - FilterExpression[] expressions = Enumerable.Repeat(expression.Item, count.Item + 2) - .ToArray(); + outputHelper.WriteLine($"Simplified : {simplified}"); - OneOfExpression oneOfExpression = new(expressions); + // Assert + simplified.IsEquivalentTo(expression.Item) + .Should() + .BeTrue(); + } - double complexityBeforeFirstSimplification = oneOfExpression.Complexity; + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OneOfExpression_Simplify_should_return_an_expression_that_cannot_be_further_simplified(PositiveInt count, NonNull expression) + { + // Arrange + FilterExpression[] expressions = Enumerable.Repeat(expression.Item, count.Item + 2) + .ToArray(); - // Act - FilterExpression simplified = oneOfExpression.Simplify(); + OneOfExpression oneOfExpression = new(expressions); - // Assert - simplified.Complexity.Should() - .BeLessThan(complexityBeforeFirstSimplification, "The expression must be simpler"); - } + double complexityBeforeFirstSimplification = oneOfExpression.Complexity; - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OneExpression_that_contains_inner_OneOfExpressions_Simplify_should_flatten_them(NonEmptyArray first, NonEmptyArray second, NonEmptyArray third) - { - // Arrange - FilterExpression expected = new OneOfExpression([.. first.Item, .. second.Item, .. third.Item]).Simplify(); + // Act + FilterExpression simplified = oneOfExpression.Simplify(); + + // Assert + simplified.Complexity.Should() + .BeLessThan(complexityBeforeFirstSimplification, "The expression must be simpler"); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OneExpression_that_contains_inner_OneOfExpressions_Simplify_should_flatten_them(NonEmptyArray first, NonEmptyArray second, NonEmptyArray third) + { + // Arrange + FilterExpression expected = new OneOfExpression([.. first.Item, .. second.Item, .. third.Item]).Simplify(); - OneOfExpression initial = new(new OneOfExpression(first.Item), - new OneOfExpression(second.Item), - new OneOfExpression(third.Item)); + OneOfExpression initial = new(new OneOfExpression(first.Item), + new OneOfExpression(second.Item), + new OneOfExpression(third.Item)); - // Act - FilterExpression actual = initial.Simplify(); + // Act + FilterExpression actual = initial.Simplify(); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs index 3f8741e0..0ca16290 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/OrExpressionTests.cs @@ -1,364 +1,363 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using System.Linq; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class OrExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using System.Linq; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class OrExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(OrExpression).Should() + .BeAssignableTo().And + .Implement>().And + .Implement().And + .HaveProperty("Left").And + .HaveProperty("Right"); + + public static IEnumerable ArgumentNullExceptionCases { - [Fact] - public void IsFilterExpression() => typeof(OrExpression).Should() - .BeAssignableTo().And - .Implement>().And - .Implement().And - .HaveProperty("Left").And - .HaveProperty("Right"); - - public static IEnumerable ArgumentNullExceptionCases + get { - get - { - FilterExpression[] expression = [new StartsWithExpression("ce"), null]; + FilterExpression[] expression = [new StartsWithExpression("ce"), null]; - return expression.CrossJoin(expression, (left, right) => (left, right)) - .Where(tuple => tuple.left == null || tuple.right is null) - .Select(tuple => new object[] { tuple.left, tuple.right }); - } + return expression.CrossJoin(expression, (left, right) => (left, right)) + .Where(tuple => tuple.left == null || tuple.right is null) + .Select(tuple => new object[] { tuple.left, tuple.right }); } + } - [Theory] - [MemberData(nameof(ArgumentNullExceptionCases))] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null(FilterExpression left, FilterExpression right) - { - // Act - Action action = () => new OrExpression(left, right); + [Theory] + [MemberData(nameof(ArgumentNullExceptionCases))] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null(FilterExpression left, FilterExpression right) + { + // Act + Action action = () => _ = new OrExpression(left, right); - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be null"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] + { + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + true, + "comparing two different instances with same property name" + }; + + yield return new object[] + { + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop3")), + false, + "comparing two different instances with different property name" + }; + + yield return new object[] { - yield return new object[] - { - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - true, - "comparing two different instances with same property name" - }; - - yield return new object[] - { - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop3")), - false, - "comparing two different instances with different property name" - }; - - yield return new object[] - { - new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1")), - new StringValueExpression("prop1"), - false, - "comparing to a filter expression that is semantically equivalent" - }; - } + new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1")), + new StringValueExpression("prop1"), + false, + "comparing to a filter expression that is semantically equivalent" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_behave_has_expected(OrExpression first, object other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_behave_has_expected(OrExpression first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = first.Equals(other); + // Act + bool actual = first.Equals(other); - // Assert - actual.Should() - .Be(expected, reason); - } + // Assert + actual.Should() + .Be(expected, reason); + } - public static IEnumerable IsEquivalentToCases + public static IEnumerable IsEquivalentToCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - true, - $"both {nameof(OrExpression)} instances are identical" - }; - - yield return new object[] - { - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop3")), - false, - $"the two {nameof(OrExpression)} instances contain different data." - }; - - yield return new object[] - { - new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), - new OrExpression(new StartsWithExpression("prop2"), new StartsWithExpression("prop1")), - true, - $"both {nameof(OrExpression)} contains same data but not in the same order" - }; - - yield return new object[] - { - new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1")), - new StringValueExpression("prop1"), - true, - "comparing to a filter expression that is semantically equivalent" - }; - } - } + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + true, + $"both {nameof(OrExpression)} instances are identical" + }; - [Theory] - [MemberData(nameof(IsEquivalentToCases))] - public void Implements_IsEquivalentTo_Correctly(OrExpression first, FilterExpression other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + yield return new object[] + { + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop3")), + false, + $"the two {nameof(OrExpression)} instances contain different data." + }; - // Act - bool actual = first.IsEquivalentTo(other); + yield return new object[] + { + new OrExpression(new StartsWithExpression("prop1"), new StartsWithExpression("prop2")), + new OrExpression(new StartsWithExpression("prop2"), new StartsWithExpression("prop1")), + true, + $"both {nameof(OrExpression)} contains same data but not in the same order" + }; - // Assert - actual.Should() - .Be(expected, reason); + yield return new object[] + { + new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1")), + new StringValueExpression("prop1"), + true, + "comparing to a filter expression that is semantically equivalent" + }; } + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_OrExpression_instances_that_left_and_right_expressions_are_both_equals_IsEquivalentTo_should_return_true(FilterExpression left, FilterExpression right) - { - // Arrange - OrExpression one = new(left, right); - OrExpression two = new(left: right, right: left); + [Theory] + [MemberData(nameof(IsEquivalentToCases))] + public void Implements_IsEquivalentTo_Correctly(OrExpression first, FilterExpression other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = one.IsEquivalentTo(two); + // Act + bool actual = first.IsEquivalentTo(other); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should() + .Be(expected, reason); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OrExpression_and_OneOfExpression_that_contains_the_same_two_expression_then_calling_IsEquivalentTo_with_that_OneOfExpression_should_return_true(FilterExpression left, FilterExpression right) - { - // Arrange - OneOfExpression oneOf = new(left, right); - OrExpression or = new(left, right); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_OrExpression_instances_that_left_and_right_expressions_are_both_equals_IsEquivalentTo_should_return_true(FilterExpression left, FilterExpression right) + { + // Arrange + OrExpression one = new(left, right); + OrExpression two = new(left: right, right: left); - // Act - bool actual = or.IsEquivalentTo(oneOf); + // Act + bool actual = one.IsEquivalentTo(two); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_ConstantExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(ConstantValueExpression filterExpression) - => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OrExpression_and_OneOfExpression_that_contains_the_same_two_expression_then_calling_IsEquivalentTo_with_that_OneOfExpression_should_return_true(FilterExpression left, FilterExpression right) + { + // Arrange + OneOfExpression oneOf = new(left, right); + OrExpression or = new(left, right); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateExpression filterExpression) - => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); + // Act + bool actual = or.IsEquivalentTo(oneOf); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_DateTimeExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateTimeExpression filterExpression) - => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); + // Assert + actual.Should().BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_AsteriskExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true() - => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(AsteriskExpression.Instance); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_ConstantExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(ConstantValueExpression filterExpression) + => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_eq_right_eq_OrExpression_Simplify_should_returns_the_inner_OrExpression(OrExpression filterExpression) - { - // Arrange - OrExpression orExpression = new(filterExpression, filterExpression); - FilterExpression expected = filterExpression.Simplify(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateExpression filterExpression) + => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); - // Act - FilterExpression actual = orExpression.Simplify(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_DateTimeExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true(DateTimeExpression filterExpression) + => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(filterExpression); - // Assert - actual.Should().Be(expected); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_AsteriskExpression_equals_left_and_left_equals_right_expression_IsEquivalentTo_should_be_true() + => Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(AsteriskExpression.Instance); - private static void Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(FilterExpression expression) - { - // Arrange - OrExpression filterExpression = new(expression, expression); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_eq_right_eq_OrExpression_Simplify_should_returns_the_inner_OrExpression(OrExpression filterExpression) + { + // Arrange + OrExpression orExpression = new(filterExpression, filterExpression); + FilterExpression expected = filterExpression.Simplify(); - // Act - bool actual = filterExpression.IsEquivalentTo(expression); + // Act + FilterExpression actual = orExpression.Simplify(); - // Assert - actual.Should().BeTrue(); - } + // Assert + actual.Should().Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_OrExpression_GetComplexity_should_return_sum_of_left_and_right_complexity(OrExpression orExpression) - => (orExpression.Complexity == orExpression.Left.Complexity + orExpression.Right.Complexity).ToProperty(); + private static void Given_FilterExpression_equals_left_and_right_IsEquivalentTo_should_return_true(FilterExpression expression) + { + // Arrange + OrExpression filterExpression = new(expression, expression); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_eq_right_Simplify_should_return_left(FilterExpression expected) - { - // Arrange - OrExpression orExpression = new(expected, expected); + // Act + bool actual = filterExpression.IsEquivalentTo(expression); - // Act - FilterExpression actual = orExpression.Simplify(); + // Assert + actual.Should().BeTrue(); + } - outputHelper.WriteLine($"Simplifiied expression : {actual}"); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_OrExpression_GetComplexity_should_return_sum_of_left_and_right_complexity(OrExpression orExpression) + => (orExpression.Complexity == orExpression.Left.Complexity + orExpression.Right.Complexity).ToProperty(); - bool isEquivalent = actual.IsEquivalentTo(expected); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_eq_right_Simplify_should_return_left(FilterExpression expected) + { + // Arrange + OrExpression orExpression = new(expected, expected); - // Assert - isEquivalent.Should() - .BeTrue("the meaning of the expression should remain the same even after being simplified"); - } + // Act + FilterExpression actual = orExpression.Simplify(); - public static IEnumerable SimplifyCases + outputHelper.WriteLine($"Simplifiied expression : {actual}"); + + bool isEquivalent = actual.IsEquivalentTo(expected); + + // Assert + isEquivalent.Should() + .BeTrue("the meaning of the expression should remain the same even after being simplified"); + } + + public static IEnumerable SimplifyCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new OrExpression(new NumericValueExpression("1"), new OrExpression(new NumericValueExpression("1"), new NumericValueExpression("1"))), - new NumericValueExpression("1") - }; - - yield return new object[] - { - new OrExpression(new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1")), new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1"))), - new StringValueExpression("prop1") - }; - } + new OrExpression(new NumericValueExpression("1"), new OrExpression(new NumericValueExpression("1"), new NumericValueExpression("1"))), + new NumericValueExpression("1") + }; + + yield return new object[] + { + new OrExpression(new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1")), new OrExpression(new StringValueExpression("prop1"), new StringValueExpression("prop1"))), + new StringValueExpression("prop1") + }; } + } - [Theory] - [MemberData(nameof(SimplifyCases))] - public void Given_OrExpression_Simplify_should_return_expected_result(OrExpression orExpression, FilterExpression expected) - { - // Act - FilterExpression actual = orExpression.Simplify(); + [Theory] + [MemberData(nameof(SimplifyCases))] + public void Given_OrExpression_Simplify_should_return_expected_result(OrExpression orExpression, FilterExpression expected) + { + // Act + FilterExpression actual = orExpression.Simplify(); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_OrExpression_instances_one_and_two_where_oneU002Eleft_eq_twoU002Eright_and_oneU002Eright_eq_twoU002Eleft_IsEquivalentTo_should_return_true(FilterExpression first, FilterExpression second) - { - // Arrange - OrExpression one = new(first, second); - OrExpression two = new(second, first); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_OrExpression_instances_one_and_two_where_oneU002Eleft_eq_twoU002Eright_and_oneU002Eright_eq_twoU002Eleft_IsEquivalentTo_should_return_true(FilterExpression first, FilterExpression second) + { + // Arrange + OrExpression one = new(first, second); + OrExpression two = new(second, first); - // Act - bool actual = one.IsEquivalentTo(two); + // Act + bool actual = one.IsEquivalentTo(two); - // Assert - actual.Should() - .BeTrue(); - } + // Assert + actual.Should() + .BeTrue(); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_reflexive(OrExpression or) - => or.IsEquivalentTo(or).Should() - .BeTrue(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_reflexive(OrExpression or) + => or.IsEquivalentTo(or).Should() + .BeTrue(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_symetric(OrExpression or) - { - // Arrange - OrExpression other = new(or.Left, or.Right); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_symetric(OrExpression or) + { + // Arrange + OrExpression other = new(or.Left, or.Right); - // Act - bool actual = or.IsEquivalentTo(other); - bool expected = other.IsEquivalentTo(or); + // Act + bool actual = or.IsEquivalentTo(other); + bool expected = other.IsEquivalentTo(or); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_transitive(OrExpression or) - { - // Arrange - OrExpression other = new(or.Left, or.Right); - OrExpression yetAnother = new(other.Left, other.Right); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_transitive(OrExpression or) + { + // Arrange + OrExpression other = new(or.Left, or.Right); + OrExpression yetAnother = new(other.Left, other.Right); - // Act - bool actual = or.IsEquivalentTo(other); - bool expected = other.IsEquivalentTo(yetAnother); + // Act + bool actual = or.IsEquivalentTo(other); + bool expected = other.IsEquivalentTo(yetAnother); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_right_and_left_are_OrExpression_Constructor_should_wrap_right_and_left_inside_a_GroupExpression_instance(NonNull left, - NonNull right) - { - // Act - OrExpression or = new(left.Item, right.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_right_and_left_are_OrExpression_Constructor_should_wrap_right_and_left_inside_a_GroupExpression_instance(NonNull left, + NonNull right) + { + // Act + OrExpression or = new(left.Item, right.Item); - // Assert - or.Right.Should() - .BeOfType($"Left instance is a '{nameof(BinaryFilterExpression)}'"); + // Assert + or.Right.Should() + .BeOfType($"Left instance is a '{nameof(BinaryFilterExpression)}'"); - or.Left.Should() - .BeOfType($"Right instance is a '{nameof(BinaryFilterExpression)}'"); - } + or.Left.Should() + .BeOfType($"Right instance is a '{nameof(BinaryFilterExpression)}'"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_is_ConstantValueExpression_and_right_is_ConstantValueExpression_Constructor_should_leave_right_and_left_untouched(NonNull left, - NonNull right) - { - // Act - OrExpression or = new(left.Item, right.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_is_ConstantValueExpression_and_right_is_ConstantValueExpression_Constructor_should_leave_right_and_left_untouched(NonNull left, + NonNull right) + { + // Act + OrExpression or = new(left.Item, right.Item); - // Assert - or.Left.Should() - .NotBeOfType($"Right a instance is a '{nameof(ConstantValueExpression)}'"); + // Assert + or.Left.Should() + .NotBeOfType($"Right a instance is a '{nameof(ConstantValueExpression)}'"); - or.Right.Should() - .NotBeOfType($"Left instance is a '{nameof(ConstantValueExpression)}'"); - } + or.Right.Should() + .NotBeOfType($"Left instance is a '{nameof(ConstantValueExpression)}'"); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); - } + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.Equals(otherExpression.Item).Should().Be(otherExpression.Item.Equals(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs index de18ee4d..ddbe8b74 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/PropertyNameExpressionTests.cs @@ -1,89 +1,88 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; + +public class PropertyNameTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using FluentAssertions; - using Xunit; - using Xunit.Abstractions; + [Fact] + public void IsFilterExpression() => typeof(PropertyName).Should() + .HaveConstructor(new[] { typeof(string) }).And + .HaveProperty("Name"); - public class PropertyNameTests(ITestOutputHelper outputHelper) + [Fact] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() { - [Fact] - public void IsFilterExpression() => typeof(PropertyName).Should() - .HaveConstructor(new[] { typeof(string) }).And - .HaveProperty("Name"); + // Act + Action action = () => _ = new PropertyName(null); - [Fact] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() - { - // Act - Action action = () => new PropertyName(null); - - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be null"); + } - [Theory] - [InlineData("")] - [InlineData(" ")] - public void Ctor_Throws_ArgumentOutyException_When_Argument_Is_Null(string name) - { - // Act - Action action = () => new PropertyName(name); + [Theory] + [InlineData("")] + [InlineData(" ")] + public void Ctor_Throws_ArgumentOutyException_When_Argument_Is_Null(string name) + { + // Act + Action action = () => _ = new PropertyName(name); - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be empty or whitespace only"); - } + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be empty or whitespace only"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new PropertyName("prop1"), - new PropertyName("prop1"), - true, - "comparing two different instances with same property name" - }; + new PropertyName("prop1"), + new PropertyName("prop1"), + true, + "comparing two different instances with same property name" + }; - yield return new object[] - { - new PropertyName("prop1"), - new PropertyName("prop2"), - false, - "comparing two different instances with different property name" - }; - } + yield return new object[] + { + new PropertyName("prop1"), + new PropertyName("prop2"), + false, + "comparing two different instances with different property name" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void ImplementsEqualsCorrectly(PropertyName first, object other, bool expected, string reason) - { - outputHelper.WriteLine($"First instance : {first}"); - outputHelper.WriteLine($"Second instance : {other}"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void ImplementsEqualsCorrectly(PropertyName first, object other, bool expected, string reason) + { + outputHelper.WriteLine($"First instance : {first}"); + outputHelper.WriteLine($"Second instance : {other}"); - // Act - bool actual = first.Equals(other); - int actualHashCode = first.GetHashCode(); + // Act + bool actual = first.Equals(other); + int actualHashCode = first.GetHashCode(); - // Assert - actual.Should() - .Be(expected, reason); - if (expected) - { - actualHashCode.Should() - .Be(other?.GetHashCode(), reason); - } - else - { - actualHashCode.Should() - .NotBe(other?.GetHashCode(), reason); - } + // Assert + actual.Should() + .Be(expected, reason); + if (expected) + { + actualHashCode.Should() + .Be(other?.GetHashCode(), reason); + } + else + { + actualHashCode.Should() + .NotBe(other?.GetHashCode(), reason); } } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs index ac2d4836..b5447063 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/RangeBracketValueTests.cs @@ -1,132 +1,131 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Linq; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; + +public class RangeBracketValueTests(ITestOutputHelper outputHelper) { - using System; - using System.Linq; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - - public class RangeBracketValueTests(ITestOutputHelper outputHelper) + [Property] + public void Value_should_be_set_by_the_parameter_of_the_constructor(char start, char end) { - [Property] - public void Value_should_be_set_by_the_parameter_of_the_constructor(char start, char end) - { - // Act - RangeBracketValue rangeBracketValue = new(start, end); - - // Assert - rangeBracketValue.Start.Should().Be(start); - rangeBracketValue.End.Should().Be(end); - } - - [Property] - public void Given_ConstantBracketExpression_contains_consecutive_characters_When_comparing_to_RangeBracketValue_that_has_head_and_tail_Equals_should_returns_true() - { - // Arrange - IArbMap arbMap = ArbMap.Default; - Prop.ForAll(arbMap.ArbFor().Filter(char.IsLetter).Filter(char.IsLower), arbMap.ArbFor().Filter(char.IsLetter).Filter(char.IsLower), - (start, end) => - { - char[] chrs = [.. new[] { start, end }.OrderBy(x => x) + // Act + RangeBracketValue rangeBracketValue = new(start, end); + + // Assert + rangeBracketValue.Start.Should().Be(start); + rangeBracketValue.End.Should().Be(end); + } + + [Property] + public void Given_ConstantBracketExpression_contains_consecutive_characters_When_comparing_to_RangeBracketValue_that_has_head_and_tail_Equals_should_returns_true() + { + // Arrange + IArbMap arbMap = ArbMap.Default; + Prop.ForAll(arbMap.ArbFor().Filter(char.IsLetter).Filter(char.IsLower), arbMap.ArbFor().Filter(char.IsLetter).Filter(char.IsLower), + (start, end) => + { + char[] chrs = [.. new[] { start, end }.OrderBy(x => x) ]; - char head = chrs[0]; - char tail = chrs[1]; - ConstantBracketValue constantBracketValue = new(Enumerable.Range(head, tail - head + 1) - .Select(ascii => ((char)ascii).ToString()) - .Aggregate((accumulate, current) => $"{accumulate}{current}")); - - // Act - bool actual = new RangeBracketValue(head, tail).Equals(constantBracketValue); - - return actual.When(start < end); - }) - .QuickCheckThrowOnFailure(outputHelper); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_RangeBracketValues_left_ne_right_should_be_same_as_not_left_Equals_right(RangeBracketValue left, RangeBracketValue right) - { - // Arrange - bool expected = !left.Equals(right); - - // Act - bool actual = left != right; - - // Assert - actual.Should().Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_RangeBracketValues_left_gt_right_should_be_same_as_left_Start_gt_right_Start(RangeBracketValue left, RangeBracketValue right) - { - // Arrange - bool expected = left.Start > right.Start; - - // Act - bool actual = left > right; - - // Assert - actual.Should().Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_RangeBracketValues_left_lte_right_should_be_same_as_left_lt_right_or_left_eq_right(RangeBracketValue left, RangeBracketValue right) - { - // Arrange - bool expected = left < right || left == right; - - // Act - bool actual = left <= right; - - // Assert - actual.Should().Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_left_and_right_RangeBracketValues_left_gte_right_should_be_same_as_left_gt_right_or_left_eq_right(RangeBracketValue left, RangeBracketValue right) - { - // Arrange - bool expected = left > right || left == right; - - // Act - bool actual = left >= right; - - // Assert - actual.Should().Be(expected); - } - - [Theory] - [InlineData('a', 'a', 2)] - public void Complexity_should_behave_as_expected(char start, char end, double expected) - { - // Arrange - RangeBracketValue bracketValue = new(start, end); - - // Act - double actual = bracketValue.Complexity; - - // Assert - actual.Should() - .Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_RangeBracketValue_Complexity_should_depends_on_start_and_end(RangeBracketValue bracketValue) - { - // Arrange - double expected = 1 + Math.Pow(2, bracketValue.End - bracketValue.Start); - - // Act - double actual = bracketValue.Complexity; - - // Assert - actual.Should() - .Be(expected); - } + char head = chrs[0]; + char tail = chrs[1]; + ConstantBracketValue constantBracketValue = new(Enumerable.Range(head, tail - head + 1) + .Select(ascii => ((char)ascii).ToString()) + .Aggregate((accumulate, current) => $"{accumulate}{current}")); + + // Act + bool actual = new RangeBracketValue(head, tail).Equals(constantBracketValue); + + return actual.When(start < end); + }) + .QuickCheckThrowOnFailure(outputHelper); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_RangeBracketValues_left_ne_right_should_be_same_as_not_left_Equals_right(RangeBracketValue left, RangeBracketValue right) + { + // Arrange + bool expected = !left.Equals(right); + + // Act + bool actual = left != right; + + // Assert + actual.Should().Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_RangeBracketValues_left_gt_right_should_be_same_as_left_Start_gt_right_Start(RangeBracketValue left, RangeBracketValue right) + { + // Arrange + bool expected = left.Start > right.Start; + + // Act + bool actual = left > right; + + // Assert + actual.Should().Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_RangeBracketValues_left_lte_right_should_be_same_as_left_lt_right_or_left_eq_right(RangeBracketValue left, RangeBracketValue right) + { + // Arrange + bool expected = left < right || left == right; + + // Act + bool actual = left <= right; + + // Assert + actual.Should().Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_left_and_right_RangeBracketValues_left_gte_right_should_be_same_as_left_gt_right_or_left_eq_right(RangeBracketValue left, RangeBracketValue right) + { + // Arrange + bool expected = left > right || left == right; + + // Act + bool actual = left >= right; + + // Assert + actual.Should().Be(expected); + } + + [Theory] + [InlineData('a', 'a', 2)] + public void Complexity_should_behave_as_expected(char start, char end, double expected) + { + // Arrange + RangeBracketValue bracketValue = new(start, end); + + // Act + double actual = bracketValue.Complexity; + + // Assert + actual.Should() + .Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_RangeBracketValue_Complexity_should_depends_on_start_and_end(RangeBracketValue bracketValue) + { + // Arrange + double expected = 1 + Math.Pow(2, bracketValue.End - bracketValue.Start); + + // Act + double actual = bracketValue.Complexity; + + // Assert + actual.Should() + .Be(expected); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs index 7cf596e1..b614005a 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StartsWithExpressionTests.cs @@ -1,182 +1,181 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Categories; + +[UnitTest("StartsWith")] +public class StartsWithExpressionTests { - using System; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Categories; - - [UnitTest("StartsWith")] - public class StartsWithExpressionTests + [Fact] + public void IsFilterExpression() => typeof(StartsWithExpression).Should() + .NotBeAbstract().And + .BeAssignableTo().And + .Implement>().And + .Implement().And + .NotImplement(); + + [Fact] + public void Given_string_argument_is_null_Constructor_should_throw_ArgumentNullException() { - [Fact] - public void IsFilterExpression() => typeof(StartsWithExpression).Should() - .NotBeAbstract().And - .BeAssignableTo().And - .Implement>().And - .Implement().And - .NotImplement(); - - [Fact] - public void Given_string_argument_is_null_Constructor_should_throw_ArgumentNullException() - { - // Act - Action action = () => _ = new StartsWithExpression((string)null); - - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be null"); - } - - [Fact] - public void Given_TextExpression_argument_is_null_Constructor_should_throw_ArgumentNullException() - { - // Act - Action action = () => _ = new StartsWithExpression((TextExpression)null); - - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be null"); - } - - [Fact] - public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() - { - // Act - Action action = () => _ = new StartsWithExpression(string.Empty); - - // Assert - action.Should() - .ThrowExactly("The parameter of the constructor cannot be empty"); - } - - [Fact] - public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_WhitespaceOnly() - { - // Act - Action action = () => _ = new StartsWithExpression(" "); - - // Assert - action.Should() - .NotThrow("The parameter of the constructor can be whitespace only"); - action.Should() - .NotThrow("The parameter of the constructor can be whitespace only"); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Should().Be(expression.Item); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) - { - // Arrange - StartsWithExpression expression = new(text.Item); - string expected = $"{text.Item.EscapedParseableString}*"; - - // Act - string actual = expression.EscapedParseableString; - - // Assert - actual.Should() - .Be(expected); - } - - [Property] - public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString_should_be_correct(NonWhiteSpaceString text) - { - // Arrange - StartsWithExpression expression = new(text.Item); - StringValueExpression stringValueExpression = new(text.Item); - string expected = $"{stringValueExpression.EscapedParseableString}*"; - - // Act - string actual = expression.EscapedParseableString; - - // Assert - actual.Should() - .Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_StartsWithExpression_When_right_operand_is_EndsWithExpression_Plus_operator_should_return_expected_AndExpression(NonNull startsWithGen, NonNull endsWithGen) - { - // Arrange - StartsWithExpression startsWith = startsWithGen.Item; - EndsWithExpression endsWith = endsWithGen.Item; - - AndExpression expected = new(startsWith, endsWith); - - // Act - AndExpression actual = startsWith + endsWith; - - // Assert - actual.IsEquivalentTo(expected).Should().BeTrue(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_StartsWithExpression_When_right_operand_is_StartsWithExpression_Plus_operator_should_return_OneOfExpression(NonNull leftOperandGen, NonNull rightOperandGen) - { - // Arrange - StartsWithExpression leftStartsWith = leftOperandGen.Item; - StartsWithExpression rightStartsWith = rightOperandGen.Item; - - // bat*man* - OneOfExpression expected = new(new StringValueExpression(leftStartsWith.Value + rightStartsWith.Value), - new AndExpression(leftStartsWith, new ContainsExpression(rightStartsWith.Value)), - new StartsWithExpression(leftStartsWith.Value + rightStartsWith.Value)); - - // Act - OneOfExpression actual = leftStartsWith + rightStartsWith; - - // Assert - actual.IsEquivalentTo(expected).Should().BeTrue(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_StartsWithExpression_When_right_operand_is_Contains_Plus_operator_should_return_OneOfExpression(NonNull leftOperandGen, NonNull rightOperandGen) - { - // Arrange - StartsWithExpression leftStartsWith = leftOperandGen.Item; - ContainsExpression rightStartsWith = rightOperandGen.Item; - - OneOfExpression expected = new(new StringValueExpression(leftStartsWith.Value + rightStartsWith.Value), - new AndExpression(leftStartsWith, new ContainsExpression(rightStartsWith.Value)), - new StartsWithExpression(leftStartsWith.Value + rightStartsWith.Value)); - - // Act - OneOfExpression actual = leftStartsWith + rightStartsWith; - - // Assert - actual.IsEquivalentTo(expected).Should().BeTrue(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_StartsWithExpression_When_right_operand_is_StringValueExpression_Plus_operator_should_return_expected_AndExpression(NonNull leftOperandGen, NonNull rightOperandGen) - { - // Arrange - StartsWithExpression leftStartsWith = leftOperandGen.Item; - StringValueExpression rightStartsWith = rightOperandGen.Item; - - AndExpression expected = leftStartsWith + new EndsWithExpression(rightStartsWith.Value); - - // Act - AndExpression actual = leftStartsWith + rightStartsWith; - - // Assert - actual.IsEquivalentTo(expected).Should().BeTrue(); - } + // Act + Action action = () => _ = new StartsWithExpression((string)null); + + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be null"); + } + + [Fact] + public void Given_TextExpression_argument_is_null_Constructor_should_throw_ArgumentNullException() + { + // Act + Action action = () => _ = new StartsWithExpression((TextExpression)null); + + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be null"); + } + + [Fact] + public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() + { + // Act + Action action = () => _ = new StartsWithExpression(string.Empty); + + // Assert + action.Should() + .ThrowExactly("The parameter of the constructor cannot be empty"); + } + + [Fact] + public void Ctor_DoesNot_Throws_ArgumentOutOfRangeException_When_Argument_Is_WhitespaceOnly() + { + // Act + Action action = () => _ = new StartsWithExpression(" "); + + // Assert + action.Should() + .NotThrow("The parameter of the constructor can be whitespace only"); + action.Should() + .NotThrow("The parameter of the constructor can be whitespace only"); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => first.Item.Equals(second).Should().Be(second.Equals(first.Item)); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Should().Be(expression.Item); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_TextExpression_as_input_EscapedParseableString_should_be_correct(NonNull text) + { + // Arrange + StartsWithExpression expression = new(text.Item); + string expected = $"{text.Item.EscapedParseableString}*"; + + // Act + string actual = expression.EscapedParseableString; + + // Assert + actual.Should() + .Be(expected); + } + + [Property] + public void Given_non_whitespace_string_as_input_as_input_EscapedParseableString_should_be_correct(NonWhiteSpaceString text) + { + // Arrange + StartsWithExpression expression = new(text.Item); + StringValueExpression stringValueExpression = new(text.Item); + string expected = $"{stringValueExpression.EscapedParseableString}*"; + + // Act + string actual = expression.EscapedParseableString; + + // Assert + actual.Should() + .Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_StartsWithExpression_When_right_operand_is_EndsWithExpression_Plus_operator_should_return_expected_AndExpression(NonNull startsWithGen, NonNull endsWithGen) + { + // Arrange + StartsWithExpression startsWith = startsWithGen.Item; + EndsWithExpression endsWith = endsWithGen.Item; + + AndExpression expected = new(startsWith, endsWith); + + // Act + AndExpression actual = startsWith + endsWith; + + // Assert + actual.IsEquivalentTo(expected).Should().BeTrue(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_StartsWithExpression_When_right_operand_is_StartsWithExpression_Plus_operator_should_return_OneOfExpression(NonNull leftOperandGen, NonNull rightOperandGen) + { + // Arrange + StartsWithExpression leftStartsWith = leftOperandGen.Item; + StartsWithExpression rightStartsWith = rightOperandGen.Item; + + // bat*man* + OneOfExpression expected = new(new StringValueExpression(leftStartsWith.Value + rightStartsWith.Value), + new AndExpression(leftStartsWith, new ContainsExpression(rightStartsWith.Value)), + new StartsWithExpression(leftStartsWith.Value + rightStartsWith.Value)); + + // Act + OneOfExpression actual = leftStartsWith + rightStartsWith; + + // Assert + actual.IsEquivalentTo(expected).Should().BeTrue(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_StartsWithExpression_When_right_operand_is_Contains_Plus_operator_should_return_OneOfExpression(NonNull leftOperandGen, NonNull rightOperandGen) + { + // Arrange + StartsWithExpression leftStartsWith = leftOperandGen.Item; + ContainsExpression rightStartsWith = rightOperandGen.Item; + + OneOfExpression expected = new(new StringValueExpression(leftStartsWith.Value + rightStartsWith.Value), + new AndExpression(leftStartsWith, new ContainsExpression(rightStartsWith.Value)), + new StartsWithExpression(leftStartsWith.Value + rightStartsWith.Value)); + + // Act + OneOfExpression actual = leftStartsWith + rightStartsWith; + + // Assert + actual.IsEquivalentTo(expected).Should().BeTrue(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_StartsWithExpression_When_right_operand_is_StringValueExpression_Plus_operator_should_return_expected_AndExpression(NonNull leftOperandGen, NonNull rightOperandGen) + { + // Arrange + StartsWithExpression leftStartsWith = leftOperandGen.Item; + StringValueExpression rightStartsWith = rightOperandGen.Item; + + AndExpression expected = leftStartsWith + new EndsWithExpression(rightStartsWith.Value); + + // Act + AndExpression actual = leftStartsWith + rightStartsWith; + + // Assert + actual.IsEquivalentTo(expected).Should().BeTrue(); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs index ac91331d..d96509b7 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/StringValueExpressionTests.cs @@ -1,154 +1,153 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using System.Collections.Generic; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class StringValueExpressionTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class StringValueExpressionTests(ITestOutputHelper outputHelper) - { - [Fact] - public void IsFilterExpression() => typeof(StringValueExpression).Should() - .BeAssignableTo().And - .Implement>(); + [Fact] + public void IsFilterExpression() => typeof(StringValueExpression).Should() + .BeAssignableTo().And + .Implement>(); - [Fact] - public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() - { - // Act - Action action = () => _ = new StringValueExpression(null); + [Fact] + public void Ctor_Throws_ArgumentNullException_When_Argument_Is_Null() + { + // Act + Action action = () => _ = new StringValueExpression(null); - // Assert - action.Should() - .ThrowExactly($"The parameter of {nameof(StringValueExpression)}'s constructor cannot be null"); - } + // Assert + action.Should() + .ThrowExactly($"The parameter of {nameof(StringValueExpression)}'s constructor cannot be null"); + } - [Fact] - public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() - { - // Act - Action action = () => _ = new StringValueExpression(string.Empty); + [Fact] + public void Ctor_Throws_ArgumentOutOfRangeException_When_Argument_Is_Empty() + { + // Act + Action action = () => _ = new StringValueExpression(string.Empty); - // Assert - action.Should() - .ThrowExactly($"The parameter of {nameof(StringValueExpression)}'s constructor cannot be empty"); - } + // Assert + action.Should() + .ThrowExactly($"The parameter of {nameof(StringValueExpression)}'s constructor cannot be empty"); + } - [Fact] - public void Given_parameter_is_whitespace_Ctor_should_not_throw() - { - // Act - Action action = () => _ = new StringValueExpression(" "); + [Fact] + public void Given_parameter_is_whitespace_Ctor_should_not_throw() + { + // Act + Action action = () => _ = new StringValueExpression(" "); - // Assert - action.Should() - .NotThrow($"The parameter of {nameof(StringValueExpression)}'s constructor can be whitespace"); - } + // Assert + action.Should() + .NotThrow($"The parameter of {nameof(StringValueExpression)}'s constructor can be whitespace"); + } - [Property] - public void Given_two_instances_that_hold_values_that_are_equals_Equals_shoud_return_true(NonEmptyString value) - { - // Arrange - StringValueExpression first = new(value.Item); - StringValueExpression other = new(value.Item); + [Property] + public void Given_two_instances_that_hold_values_that_are_equals_Equals_shoud_return_true(NonEmptyString value) + { + // Arrange + StringValueExpression first = new(value.Item); + StringValueExpression other = new(value.Item); - // Act - bool actual = first.Equals(other); + // Act + bool actual = first.Equals(other); - // Assert - actual.Should() - .BeTrue(); - } + // Assert + actual.Should() + .BeTrue(); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new StringValueExpression("True"), - new OrExpression(new StringValueExpression("True"), new StringValueExpression("True")), - false - }; - } + new StringValueExpression("True"), + new OrExpression(new StringValueExpression("True"), new StringValueExpression("True")), + false + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void Equals_should_behave_as_expected(StringValueExpression stringValue, object obj, bool expected) - { - // Act - bool actual = stringValue.Equals(obj); + [Theory] + [MemberData(nameof(EqualsCases))] + public void Equals_should_behave_as_expected(StringValueExpression stringValue, object obj, bool expected) + { + // Act + bool actual = stringValue.Equals(obj); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(StringValueExpression first, FilterExpression second) - => (first.Equals(second) == second.Equals(first)).ToProperty().QuickCheckThrowOnFailure(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(StringValueExpression first, FilterExpression second) + => (first.Equals(second) == second.Equals(first)).ToProperty().QuickCheckThrowOnFailure(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).ToProperty(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Equals(expression.Item).ToProperty(); - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); - [Property] - public void Given_two_StringValueExpresssions_Equals_should_depends_on_string_input_only(NonWhiteSpaceString input) - { - // Arrange - StringValueExpression first = new(input.Get); - StringValueExpression second = new(input.Get); - - // Act - (first.Equals(second) == Equals(first.Value, second.Value)) - .ToProperty() - .QuickCheckThrowOnFailure(outputHelper); - } + [Property] + public void Given_two_StringValueExpresssions_Equals_should_depends_on_string_input_only(NonWhiteSpaceString input) + { + // Arrange + StringValueExpression first = new(input.Get); + StringValueExpression second = new(input.Get); + + // Act + (first.Equals(second) == Equals(first.Value, second.Value)) + .ToProperty() + .QuickCheckThrowOnFailure(outputHelper); + } - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_StringValueExpression_GetComplexity_should_return_1(StringValueExpression constant) => (constant.Complexity == 1).ToProperty(); + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_StringValueExpression_GetComplexity_should_return_1(StringValueExpression constant) => (constant.Complexity == 1).ToProperty(); - [Property] - public void Given_StringValueExpression_and_TextExpression_are_based_on_same_value_IsEquivalentTo_should_be_true(NonWhiteSpaceString input) - { - // Arrange - StringValueExpression stringValue = new(input.Item); - TextExpression textExpression = new(input.Item); + [Property] + public void Given_StringValueExpression_and_TextExpression_are_based_on_same_value_IsEquivalentTo_should_be_true(NonWhiteSpaceString input) + { + // Arrange + StringValueExpression stringValue = new(input.Item); + TextExpression textExpression = new(input.Item); - // Act - bool isEquivalent = stringValue.IsEquivalentTo(textExpression); + // Act + bool isEquivalent = stringValue.IsEquivalentTo(textExpression); - // Assert - isEquivalent.Should() - .BeTrue($"{nameof(TextExpression)} was created from the same value"); - } + // Assert + isEquivalent.Should() + .BeTrue($"{nameof(TextExpression)} was created from the same value"); + } - [Property] - public void Given_StringValueExpression_when_GroupExpression_holds_an_expression_that_is_equals_to_that_StringValueExpression_IsEquivalentTo_should_return_true(NonWhiteSpaceString input) - { - // Arrange - StringValueExpression stringValue = new(input.Item); - GroupExpression group = new(new StringValueExpression(input.Item)); + [Property] + public void Given_StringValueExpression_when_GroupExpression_holds_an_expression_that_is_equals_to_that_StringValueExpression_IsEquivalentTo_should_return_true(NonWhiteSpaceString input) + { + // Arrange + StringValueExpression stringValue = new(input.Item); + GroupExpression group = new(new StringValueExpression(input.Item)); - // Act - bool actual = stringValue.IsEquivalentTo(group); + // Act + bool actual = stringValue.IsEquivalentTo(group); - // Assert - actual.Should() - .BeTrue(); - } + // Assert + actual.Should() + .BeTrue(); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs index 67db12f9..2b8793be 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/TextExpressionTests.cs @@ -11,84 +11,83 @@ using Xunit.Abstractions; using Xunit.Categories; -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +[UnitTest] +public class TextExpressionTests(ITestOutputHelper outputHelper) { - [UnitTest] - public class TextExpressionTests(ITestOutputHelper outputHelper) + [Theory] + [InlineData(@"<\\Y!A", @"""<\\\\Y!A""")] + public void Given_input_EscapedParseableString_should_be_correct(string input, string expected) + { + // Arrange + TextExpression text = new(input); + + // Act + string actual = text.EscapedParseableString; + + // Assert + actual.Should() + .Be(expected); + } + + [Property] + public void Equals_to_null_always_returns_false(NonEmptyString input) + { + // Arrange + TextExpression textExpression = new(input.Item); + + // Act + bool actual = textExpression.Equals(null); + + // Assert + actual.Should() + .BeFalse(); + } + + [Property] + public void Given_two_instances_with_same_input_Equals_should_return_true(NonWhiteSpaceString input) + { + // Arrange + outputHelper.WriteLine($"input : '{input.Item}'"); + TextExpression first = new(input.Item); + TextExpression other = new(input.Item); + + // Act + bool actual = first.Equals(other); + + // Assert + actual.Should() + .BeTrue(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty() + .QuickCheckThrowOnFailure(outputHelper); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Equals(expression.Item).ToProperty() + .QuickCheckThrowOnFailure(outputHelper); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_non_null_TextExpression_EscapedParseableString_should_be_correct(NonNull text) { - [Theory] - [InlineData(@"<\\Y!A", @"""<\\\\Y!A""")] - public void Given_input_EscapedParseableString_should_be_correct(string input, string expected) - { - // Arrange - TextExpression text = new(input); - - // Act - string actual = text.EscapedParseableString; - - // Assert - actual.Should() - .Be(expected); - } - - [Property] - public void Equals_to_null_always_returns_false(NonEmptyString input) - { - // Arrange - TextExpression textExpression = new(input.Item); - - // Act - bool actual = textExpression.Equals(null); - - // Assert - actual.Should() - .BeFalse(); - } - - [Property] - public void Given_two_instances_with_same_input_Equals_should_return_true(NonWhiteSpaceString input) - { - // Arrange - outputHelper.WriteLine($"input : '{input.Item}'"); - TextExpression first = new(input.Item); - TextExpression other = new(input.Item); - - // Act - bool actual = first.Equals(other); - - // Assert - actual.Should() - .BeTrue(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty() - .QuickCheckThrowOnFailure(outputHelper); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).ToProperty() - .QuickCheckThrowOnFailure(outputHelper); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty(); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_non_null_TextExpression_EscapedParseableString_should_be_correct(NonNull text) - { - // Arrange - TextExpression textExpression = text.Item; - string expected = $@"""{textExpression.OriginalString.Replace("\\", @"\\").Replace(@"""", @"\""")}"""; - // Act - string escapedParseableString = textExpression.EscapedParseableString; - - // Assert - escapedParseableString.Should() - .StartWith(@"""").And - .EndWith(@"""").And - .Be(expected); - } + // Arrange + TextExpression textExpression = text.Item; + string expected = $@"""{textExpression.OriginalString.Replace("\\", @"\\").Replace(@"""", @"\""")}"""; + // Act + string escapedParseableString = textExpression.EscapedParseableString; + + // Assert + escapedParseableString.Should() + .StartWith(@"""").And + .EndWith(@"""").And + .Be(expected); } } diff --git a/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs b/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs index f8e8de71..ff57391a 100644 --- a/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs +++ b/test/DataFilters.UnitTests/Grammar/Syntax/TimeExpressionTests.cs @@ -1,144 +1,143 @@ -namespace DataFilters.UnitTests.Grammar.Syntax +namespace DataFilters.UnitTests.Grammar.Syntax; + +using System; +using DataFilters.Grammar.Syntax; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Fluent; +using FsCheck.Xunit; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; + +[UnitTest] +public class TimeExpressionTests(ITestOutputHelper outputHelper) { - using System; - using DataFilters.Grammar.Syntax; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Fluent; - using FsCheck.Xunit; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - - [UnitTest] - public class TimeExpressionTests(ITestOutputHelper outputHelper) + [Fact] + public void IsFilterExpression() => typeof(TimeExpression).Should() + .BeAssignableTo().And + .Implement>().And + .HaveConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int) }).And + .HaveProperty("Hours").And + .HaveProperty("Minutes").And + .HaveProperty("Seconds").And + .HaveProperty("Milliseconds"); + + [Property] + public void Ctor_should_build_valid_instance(IntWithMinMax hours, + IntWithMinMax minutes, + IntWithMinMax seconds, + IntWithMinMax milliseconds) { - [Fact] - public void IsFilterExpression() => typeof(TimeExpression).Should() - .BeAssignableTo().And - .Implement>().And - .HaveConstructor(new[] { typeof(int), typeof(int), typeof(int), typeof(int) }).And - .HaveProperty("Hours").And - .HaveProperty("Minutes").And - .HaveProperty("Seconds").And - .HaveProperty("Milliseconds"); - - [Property] - public void Ctor_should_build_valid_instance(IntWithMinMax hours, - IntWithMinMax minutes, - IntWithMinMax seconds, - IntWithMinMax milliseconds) - { - outputHelper.WriteLine($"hours : {hours.Item}"); - outputHelper.WriteLine($"minutes : {minutes.Item}"); - outputHelper.WriteLine($"seconds : {seconds.Item}"); - outputHelper.WriteLine($"milliseconds : {milliseconds.Item}"); - - // Arrange - Lazy timeExpressionBuilder = new(() => new TimeExpression(hours.Item, minutes.Item, - seconds.Item, milliseconds.Item)); - - Action invokingCtor = () => { TimeExpression value = timeExpressionBuilder.Value; }; - - ((Action)(() => invokingCtor.Should().ThrowExactly())).When(hours.Item < 0 - || minutes.Item < 0 || 59 < minutes.Item - || seconds.Item < 0 || 60 < seconds.Item - || (seconds.Item == 60 && minutes.Item != 59 && hours.Item != 23) - ) - .Label("Invalid TimeExpression").Trivial(hours.Item < 0 - || minutes.Item < 0 || 59 < minutes.Item - || seconds.Item < 0 || 60 < seconds.Item - || (seconds.Item == 60 && minutes.Item != 59 && hours.Item != 23)) - .And(() => - { - TimeExpression timeExpression = timeExpressionBuilder.Value; - return timeExpression.Hours == hours.Item - && timeExpression.Minutes == minutes.Item - && timeExpression.Seconds == seconds.Item; - }) - .VerboseCheck(outputHelper); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_commutative(NonNull first, FilterExpression second) - => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty() - .QuickCheckThrowOnFailure(outputHelper); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_reflexive(NonNull expression) - => expression.Item.Equals(expression.Item).ToProperty().QuickCheckThrowOnFailure(outputHelper); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) - => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty().QuickCheckThrowOnFailure(outputHelper); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_non_null_TimeExpression_instance_should_never_be_equal_to_null(TimeExpression instance) - { - // Act - bool actual = instance.Equals(null); - - // Assert - actual.Should() - .BeFalse(); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void Given_two_TimeExpression_instances_that_have_same_values_should_be_equal(NonNegativeInt hours, NonNegativeInt minutes, NonNegativeInt seconds, NonNegativeInt milliseconds) - { - // Arrange - TimeExpression first = new(hours.Item, minutes.Item, seconds.Item, milliseconds.Item); - TimeExpression other = new(hours.Item, minutes.Item, seconds.Item, milliseconds.Item); - - // Act - bool actual = first.Equals(other); - - // Assert - actual.Should() - .BeTrue(); - first.GetHashCode().Should() - .Be(other.GetHashCode()); - } - - [Bug(32)] - [Theory] - [InlineData(00, 00, 00, 00, "00:00:00")] - [InlineData(02, 53, 39, 987, "02:53:39.987")] - public void Given_TimeExpression_instance_EscapedParseableString_should_be_in_expected_form(int hours, - int minutes, - int seconds, - int milliseconds, - string expected) - { - // Arrange - TimeExpression timeExpression = new(hours, minutes, seconds, milliseconds); - - // Act - string actual = timeExpression.EscapedParseableString; - - // Assert - actual.Should() - .Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalent_should_be_commutative(NonNull first, FilterExpression second) - { - // Act - bool actual = first.Item.IsEquivalentTo(second); - bool expected = second.IsEquivalentTo(first.Item); - - // Assert - actual.Should().Be(expected); - } - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_reflexive(NonNull expression) - => expression.Item.IsEquivalentTo(expression.Item).Should().BeTrue(); - - [Property(Arbitrary = [typeof(ExpressionsGenerators)])] - public void IsEquivalentTo_should_be_symetric(NonNull expression, NonNull otherExpression) - => expression.Item.IsEquivalentTo(otherExpression.Item).Should().Be(otherExpression.Item.IsEquivalentTo(expression.Item)); + outputHelper.WriteLine($"hours : {hours.Item}"); + outputHelper.WriteLine($"minutes : {minutes.Item}"); + outputHelper.WriteLine($"seconds : {seconds.Item}"); + outputHelper.WriteLine($"milliseconds : {milliseconds.Item}"); + + // Arrange + Lazy timeExpressionBuilder = new(() => new TimeExpression(hours.Item, minutes.Item, + seconds.Item, milliseconds.Item)); + + Action invokingCtor = () => { TimeExpression value = timeExpressionBuilder.Value; }; + + ((Action)(() => invokingCtor.Should().ThrowExactly())).When(hours.Item < 0 + || minutes.Item < 0 || 59 < minutes.Item + || seconds.Item < 0 || 60 < seconds.Item + || (seconds.Item == 60 && minutes.Item != 59 && hours.Item != 23) + ) + .Label("Invalid TimeExpression").Trivial(hours.Item < 0 + || minutes.Item < 0 || 59 < minutes.Item + || seconds.Item < 0 || 60 < seconds.Item + || (seconds.Item == 60 && minutes.Item != 59 && hours.Item != 23)) + .And(() => + { + TimeExpression timeExpression = timeExpressionBuilder.Value; + return timeExpression.Hours == hours.Item + && timeExpression.Minutes == minutes.Item + && timeExpression.Seconds == seconds.Item; + }) + .VerboseCheck(outputHelper); } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_commutative(NonNull first, FilterExpression second) + => (first.Item.Equals(second) == second.Equals(first.Item)).ToProperty() + .QuickCheckThrowOnFailure(outputHelper); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_reflexive(NonNull expression) + => expression.Item.Equals(expression.Item).ToProperty().QuickCheckThrowOnFailure(outputHelper); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Equals_should_be_symetric(NonNull expression, NonNull otherExpression) + => (expression.Item.Equals(otherExpression.Item) == otherExpression.Item.Equals(expression.Item)).ToProperty().QuickCheckThrowOnFailure(outputHelper); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_non_null_TimeExpression_instance_should_never_be_equal_to_null(TimeExpression instance) + { + // Act + bool actual = instance.Equals(null); + + // Assert + actual.Should() + .BeFalse(); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void Given_two_TimeExpression_instances_that_have_same_values_should_be_equal(NonNegativeInt hours, NonNegativeInt minutes, NonNegativeInt seconds, NonNegativeInt milliseconds) + { + // Arrange + TimeExpression first = new(hours.Item, minutes.Item, seconds.Item, milliseconds.Item); + TimeExpression other = new(hours.Item, minutes.Item, seconds.Item, milliseconds.Item); + + // Act + bool actual = first.Equals(other); + + // Assert + actual.Should() + .BeTrue(); + first.GetHashCode().Should() + .Be(other.GetHashCode()); + } + + [Bug(32)] + [Theory] + [InlineData(00, 00, 00, 00, "00:00:00")] + [InlineData(02, 53, 39, 987, "02:53:39.987")] + public void Given_TimeExpression_instance_EscapedParseableString_should_be_in_expected_form(int hours, + int minutes, + int seconds, + int milliseconds, + string expected) + { + // Arrange + TimeExpression timeExpression = new(hours, minutes, seconds, milliseconds); + + // Act + string actual = timeExpression.EscapedParseableString; + + // Assert + actual.Should() + .Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalent_should_be_commutative(NonNull first, FilterExpression second) + { + // Act + bool actual = first.Item.IsEquivalentTo(second); + bool expected = second.IsEquivalentTo(first.Item); + + // Assert + actual.Should().Be(expected); + } + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_reflexive(NonNull expression) + => expression.Item.IsEquivalentTo(expression.Item).Should().BeTrue(); + + [Property(Arbitrary = [typeof(ExpressionsGenerators)])] + public void IsEquivalentTo_should_be_symetric(NonNull expression, NonNull otherExpression) + => expression.Item.IsEquivalentTo(otherExpression.Item).Should().Be(otherExpression.Item.IsEquivalentTo(expression.Item)); } diff --git a/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs b/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs index 83e24745..458f1a75 100644 --- a/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs +++ b/test/DataFilters.UnitTests/Helpers/CultureSwitcher.cs @@ -1,71 +1,70 @@ -namespace DataFilters.UnitTests.Helpers -{ - using System; - using System.Diagnostics.CodeAnalysis; - using System.Globalization; +namespace DataFilters.UnitTests.Helpers; + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +/// +/// A helper class to control the value during tests. +/// +[ExcludeFromCodeCoverage] +public sealed class CultureSwitcher : IDisposable +{ /// - /// A helper class to control the value during tests. + /// The current culture used by the current instance. /// - [ExcludeFromCodeCoverage] - public sealed class CultureSwitcher : IDisposable - { - /// - /// The current culture used by the current instance. - /// - public CultureInfo CurrentCulture { get; private set; } + public CultureInfo CurrentCulture { get; private set; } - /// - /// Gets/Sets the default culture used throughout the lifecycle of the current instance. - /// - public CultureInfo DefaultCulture { get; set; } + /// + /// Gets/Sets the default culture used throughout the lifecycle of the current instance. + /// + public CultureInfo DefaultCulture { get; set; } - /// - /// Bulds a new instance. - /// - public CultureSwitcher() - { - CurrentCulture = CultureInfo.CurrentCulture; - DefaultCulture = CurrentCulture; - } + /// + /// Bulds a new instance. + /// + public CultureSwitcher() + { + CurrentCulture = CultureInfo.CurrentCulture; + DefaultCulture = CurrentCulture; + } - /// - /// Performs the specified AFTER switching - /// to the specified . - /// - /// Name of the culture to use when running the specified . - /// The action to run with the specified . - /// - /// will be used as Default - /// - public void Run(string cultureToUse, Action action) => Run(CultureInfo.CreateSpecificCulture(cultureToUse), action); + /// + /// Performs the specified AFTER switching + /// to the specified . + /// + /// Name of the culture to use when running the specified . + /// The action to run with the specified . + /// + /// will be used as Default + /// + public void Run(string cultureToUse, Action action) => Run(CultureInfo.CreateSpecificCulture(cultureToUse), action); - /// - /// Performs the specified AFTER switching and - /// to the specified . - /// - /// The culture to use when running the specified . - /// The action to run - public void Run(CultureInfo cultureToUse, Action action) - { - CurrentCulture = cultureToUse; - CultureInfo.CurrentCulture = CurrentCulture; - action.Invoke(); - Dispose(); - } + /// + /// Performs the specified AFTER switching and + /// to the specified . + /// + /// The culture to use when running the specified . + /// The action to run + public void Run(CultureInfo cultureToUse, Action action) + { + CurrentCulture = cultureToUse; + CultureInfo.CurrentCulture = CurrentCulture; + action.Invoke(); + Dispose(); + } - /// - /// Performs the specified AFTER switching - /// to . - /// - /// Action to run with - /// - /// and will be reverted - /// to when the current instance get disposed. - /// - public void Run(Action action) => Run(DefaultCulture.Name, action); + /// + /// Performs the specified AFTER switching + /// to . + /// + /// Action to run with + /// + /// and will be reverted + /// to when the current instance get disposed. + /// + public void Run(Action action) => Run(DefaultCulture.Name, action); - /// - public void Dispose() => CultureInfo.CurrentCulture = DefaultCulture; - } + /// + public void Dispose() => CultureInfo.CurrentCulture = DefaultCulture; } \ No newline at end of file diff --git a/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs b/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs index 0d3f2999..126efbc8 100644 --- a/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs +++ b/test/DataFilters.UnitTests/Helpers/ExpressionsGenerators.cs @@ -1,446 +1,445 @@ -namespace DataFilters.UnitTests.Helpers +namespace DataFilters.UnitTests.Helpers; + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using DataFilters.Grammar.Parsing; +using DataFilters.Grammar.Syntax; +using FsCheck; +using FsCheck.Fluent; +using static GeneratorHelper; + +public static class ExpressionsGenerators { - using System; - using System.Collections.Generic; - using System.Globalization; - using System.Linq; - using DataFilters.Grammar.Parsing; - using DataFilters.Grammar.Syntax; - using FsCheck; - using FsCheck.Fluent; - using static GeneratorHelper; - - public static class ExpressionsGenerators + public static Arbitrary DateTimeExpressions() { - public static Arbitrary DateTimeExpressions() - { - return GetArbitraryFor() - .Filter(dateTime => dateTime.Hour >= 0 - && dateTime.Minute >= 0 - && dateTime.Second >= 0 - && dateTime.Millisecond >= 0) - .Generator - .Zip(TimeExpressions().Generator) - .Select(val => (date: val.Item1, time: val.Item2)) - .Zip(OffsetExpressions().Generator) - .Select(val => (val.Item1.date, val.Item1.time, offset: val.Item2)) - .Where(val => val.time != null || val.offset != null) - .Select(dateTime => new DateTimeExpression(date: new(year: dateTime.date.Year, month: dateTime.date.Month, day: dateTime.date.Day), - time: new(hours: dateTime.date.Hour, minutes: dateTime.date.Minute, seconds: dateTime.date.Second, milliseconds: dateTime.date.Millisecond), - offset: dateTime.offset)) - .ToArbitrary(); - } + return GetArbitraryFor() + .Filter(dateTime => dateTime.Hour >= 0 + && dateTime.Minute >= 0 + && dateTime.Second >= 0 + && dateTime.Millisecond >= 0) + .Generator + .Zip(TimeExpressions().Generator) + .Select(val => (date: val.Item1, time: val.Item2)) + .Zip(OffsetExpressions().Generator) + .Select(val => (val.Item1.date, val.Item1.time, offset: val.Item2)) + .Where(val => val.time != null || val.offset != null) + .Select(dateTime => new DateTimeExpression(date: new(year: dateTime.date.Year, month: dateTime.date.Month, day: dateTime.date.Day), + time: new(hours: dateTime.date.Hour, minutes: dateTime.date.Minute, seconds: dateTime.date.Second, milliseconds: dateTime.date.Millisecond), + offset: dateTime.offset)) + .ToArbitrary(); + } - public static Arbitrary TimeExpressions() - { - return GetArbitraryFor() - .Filter(timespan => timespan.Hours >= 0 && timespan.Minutes >= 0 && timespan.Seconds >= 0 && timespan.Milliseconds >= 0) - .Generator - .Select(timespan => new TimeExpression(hours: timespan.Hours, - minutes: timespan.Minutes, - seconds: timespan.Seconds, - milliseconds: timespan.Milliseconds)) - .ToArbitrary(); - } + public static Arbitrary TimeExpressions() + { + return GetArbitraryFor() + .Filter(timespan => timespan.Hours >= 0 && timespan.Minutes >= 0 && timespan.Seconds >= 0 && timespan.Milliseconds >= 0) + .Generator + .Select(timespan => new TimeExpression(hours: timespan.Hours, + minutes: timespan.Minutes, + seconds: timespan.Seconds, + milliseconds: timespan.Milliseconds)) + .ToArbitrary(); + } - public static Arbitrary OffsetExpressions() - { - Gen hours = Gen.Choose(0, 23); - Gen minutes = Gen.Choose(0, 59); - Gen sign = Gen.OneOf(Gen.Constant(NumericSign.Plus), Gen.Constant(NumericSign.Minus)); - - return hours.Zip(minutes, (hours, minutes) => (hours, minutes)) - .Zip(sign, (offset, sign) => (offset.hours, offset.minutes, sign)) - .Select(val => (val.hours, val.minutes, val.sign)) - .Select(val => new OffsetExpression(val.sign, (uint)val.hours, (uint)val.minutes)) - .OrNull() - .ToArbitrary(); - } + public static Arbitrary OffsetExpressions() + { + Gen hours = Gen.Choose(0, 23); + Gen minutes = Gen.Choose(0, 59); + Gen sign = Gen.OneOf(Gen.Constant(NumericSign.Plus), Gen.Constant(NumericSign.Minus)); + + return hours.Zip(minutes, (hours, minutes) => (hours, minutes)) + .Zip(sign, (offset, sign) => (offset.hours, offset.minutes, sign)) + .Select(val => (val.hours, val.minutes, val.sign)) + .Select(val => new OffsetExpression(val.sign, (uint)val.hours, (uint)val.minutes)) + .OrNull() + .ToArbitrary(); + } - public static Arbitrary DateExpressions() - { - Gen dateTimeGenerator = GetArbitraryFor() - .Generator - .Select(dateTime => new DateExpression(year: dateTime.Year, month: dateTime.Month, day: dateTime.Day)); + public static Arbitrary DateExpressions() + { + Gen dateTimeGenerator = GetArbitraryFor() + .Generator + .Select(dateTime => new DateExpression(year: dateTime.Year, month: dateTime.Month, day: dateTime.Day)); #if NET6_0_OR_GREATER - Gen dateOnlyGenerator = GetArbitraryFor() - .Generator - .Select(dateTime => DateOnly.FromDateTime(dateTime)) - .Select(date => new DateExpression(year: date.Year, month: date.Month, day: date.Day)); + Gen dateOnlyGenerator = GetArbitraryFor() + .Generator + .Select(dateTime => DateOnly.FromDateTime(dateTime)) + .Select(date => new DateExpression(year: date.Year, month: date.Month, day: date.Day)); #endif #if !NET6_0_OR_GREATER - return dateTimeGenerator - .ToArbitrary(); + return dateTimeGenerator + .ToArbitrary(); #else - return Gen.OneOf(dateTimeGenerator, dateOnlyGenerator) - .ToArbitrary(); + return Gen.OneOf(dateTimeGenerator, dateOnlyGenerator) + .ToArbitrary(); #endif - } + } - public static Arbitrary ConstantValueExpressions() + public static Arbitrary ConstantValueExpressions() + { + IList> generators = new List> { - IList> generators = new List> - { - TextExpressions().Generator.Select(value => (ConstantValueExpression) value), - StringValueExpressions().Generator.Select(value => (ConstantValueExpression) value), - GetArbitraryFor().Generator.Select(value => (ConstantValueExpression) new StringValueExpression(value.ToString())), - GetArbitraryFor().Generator.Select(value => (ConstantValueExpression) new GuidValueExpression(value.ToString("d"))), - NumericValueExpressions().Generator.Select(value => (ConstantValueExpression) value) - }; - - return Gen.OneOf(generators) - .ToArbitrary(); - } + TextExpressions().Generator.Select(value => (ConstantValueExpression) value), + StringValueExpressions().Generator.Select(value => (ConstantValueExpression) value), + GetArbitraryFor().Generator.Select(value => (ConstantValueExpression) new StringValueExpression(value.ToString())), + GetArbitraryFor().Generator.Select(value => (ConstantValueExpression) new GuidValueExpression(value.ToString("d"))), + NumericValueExpressions().Generator.Select(value => (ConstantValueExpression) value) + }; + + return Gen.OneOf(generators) + .ToArbitrary(); + } - public static Arbitrary NumericValueExpressions() + public static Arbitrary NumericValueExpressions() + { + IEnumerable> generators = new Gen[] { - IEnumerable> generators = new Gen[] - { - GetArbitraryFor().Generator.Select(value => new NumericValueExpression(value.ToString(CultureInfo.InvariantCulture))), - GetArbitraryFor().Generator.Select(value => new NumericValueExpression(value.ToString(CultureInfo.InvariantCulture))), - GetArbitraryFor().Generator.Select(value => new NumericValueExpression(value.Item.ToString("G19", CultureInfo.InvariantCulture))) - }; + GetArbitraryFor().Generator.Select(value => new NumericValueExpression(value.ToString(CultureInfo.InvariantCulture))), + GetArbitraryFor().Generator.Select(value => new NumericValueExpression(value.ToString(CultureInfo.InvariantCulture))), + GetArbitraryFor().Generator.Select(value => new NumericValueExpression(value.Item.ToString("G19", CultureInfo.InvariantCulture))) + }; - return Gen.OneOf(generators) - .ToArbitrary(); - } + return Gen.OneOf(generators) + .ToArbitrary(); + } - public static Arbitrary StringValueExpressions() - => GetArbitraryFor().Generator - .Select(value => value.Item.Any(chr => FilterTokenizer.SpecialCharacters.Contains(chr)) - ? new TextExpression(value.Item) - : new StringValueExpression(value.Item) - ) - .ToArbitrary(); + public static Arbitrary StringValueExpressions() + => GetArbitraryFor().Generator + .Select(value => value.Item.Any(chr => FilterTokenizer.SpecialCharacters.Contains(chr)) + ? new TextExpression(value.Item) + : new StringValueExpression(value.Item) + ) + .ToArbitrary(); - public static Arbitrary DurationExpressions() - { - Arbitrary<(((PositiveInt years, PositiveInt months, PositiveInt days) date, (PositiveInt hours, PositiveInt minutes, PositiveInt seconds) time) dateTime, PositiveInt milliseconds)> arb = GetArbitraryFor().Generator - .Three() - .Two() - .Zip(GetArbitraryFor().Generator) - .ToArbitrary(); - - return arb.Generator.Select(tuple => new DurationExpression(years: tuple.dateTime.date.years.Item, - months: tuple.dateTime.date.months.Item, - weeks: tuple.dateTime.date.days.Item, - days: tuple.dateTime.time.hours.Item, - hours: tuple.dateTime.time.minutes.Item, - minutes: tuple.dateTime.time.seconds.Item, - seconds: tuple.milliseconds.Item)) - .ToArbitrary(); - } + public static Arbitrary DurationExpressions() + { + Arbitrary<(((PositiveInt years, PositiveInt months, PositiveInt days) date, (PositiveInt hours, PositiveInt minutes, PositiveInt seconds) time) dateTime, PositiveInt milliseconds)> arb = GetArbitraryFor().Generator + .Three() + .Two() + .Zip(GetArbitraryFor().Generator) + .ToArbitrary(); + + return arb.Generator.Select(tuple => new DurationExpression(years: tuple.dateTime.date.years.Item, + months: tuple.dateTime.date.months.Item, + weeks: tuple.dateTime.date.days.Item, + days: tuple.dateTime.time.hours.Item, + hours: tuple.dateTime.time.minutes.Item, + minutes: tuple.dateTime.time.seconds.Item, + seconds: tuple.milliseconds.Item)) + .ToArbitrary(); + } - public static Arbitrary EndsWithExpressions() - => Gen.OneOf(GetArbitraryFor().Generator - .Select(nonWhiteSpaceString => new EndsWithExpression(nonWhiteSpaceString.Item)), - TextExpressions().Generator.Select(text => new EndsWithExpression(text)) - ) - .ToArbitrary(); + public static Arbitrary EndsWithExpressions() + => Gen.OneOf(GetArbitraryFor().Generator + .Select(nonWhiteSpaceString => new EndsWithExpression(nonWhiteSpaceString.Item)), + TextExpressions().Generator.Select(text => new EndsWithExpression(text)) + ) + .ToArbitrary(); - public static Arbitrary StartsWithExpressions() - => Gen.OneOf(GetArbitraryFor().Generator - .Select(nonWhiteSpaceString => new StartsWithExpression(nonWhiteSpaceString.Item)), - TextExpressions().Generator.Select(text => new StartsWithExpression(text)) - ) - .ToArbitrary(); + public static Arbitrary StartsWithExpressions() + => Gen.OneOf(GetArbitraryFor().Generator + .Select(nonWhiteSpaceString => new StartsWithExpression(nonWhiteSpaceString.Item)), + TextExpressions().Generator.Select(text => new StartsWithExpression(text)) + ) + .ToArbitrary(); - public static Arbitrary OrExpressions() => Gen.Sized(SafeOrExpressionGenerator).ToArbitrary(); + public static Arbitrary OrExpressions() => Gen.Sized(SafeOrExpressionGenerator).ToArbitrary(); - public static Arbitrary AndExpressions() => Gen.Sized(SafeAndExpressionGenerator).ToArbitrary(); + public static Arbitrary AndExpressions() => Gen.Sized(SafeAndExpressionGenerator).ToArbitrary(); - public static Arbitrary GroupExpressions() => Gen.Sized(SafeGroupExpressionGenerator).ToArbitrary(); + public static Arbitrary GroupExpressions() => Gen.Sized(SafeGroupExpressionGenerator).ToArbitrary(); - private static Gen SafeGroupExpressionGenerator(int size) + private static Gen SafeGroupExpressionGenerator(int size) + { + Gen gen; + switch (size) { - Gen gen; - switch (size) - { - case 0: - { - gen = GenerateFilterExpressions().Generator - .Select(expr => new GroupExpression(expr)); - break; - } - - default: - { - Gen subtree = SafeGroupExpressionGenerator(size / 2); - gen = Gen.OneOf(GenerateFilterExpressions().Generator.Select(expr => new GroupExpression(expr)), - subtree.Select(expr => new GroupExpression(expr))); - break; - } - } + case 0: + { + gen = GenerateFilterExpressions().Generator + .Select(expr => new GroupExpression(expr)); + break; + } - return gen; + default: + { + Gen subtree = SafeGroupExpressionGenerator(size / 2); + gen = Gen.OneOf(GenerateFilterExpressions().Generator.Select(expr => new GroupExpression(expr)), + subtree.Select(expr => new GroupExpression(expr))); + break; + } } - /// - /// Generates an arbitrary random . - /// - public static Arbitrary GenerateFilterExpressions() - { - Gen[] generators = - [ - EndsWithExpressions().Generator.Select(item => (FilterExpression) item), - StartsWithExpressions().Generator.Select(item => (FilterExpression) item), - ContainsExpressions().Generator.Select(item => (FilterExpression) item), - IntervalExpressions().Generator.Select(item => (FilterExpression) item), - DateExpressions().Generator.Select(item => (FilterExpression) item), - DateTimeExpressions().Generator.Select(item => (FilterExpression) item), - TimeExpressions().Generator.Select(item => (FilterExpression) item), - DurationExpressions().Generator.Select(item => (FilterExpression) item), - ConstantValueExpressions().Generator.Select(item => (FilterExpression) item), - GroupExpressions().Generator.Select(item => (FilterExpression) item) - ]; + return gen; + } - return Gen.OneOf(generators).ToArbitrary(); - } + /// + /// Generates an arbitrary random . + /// + public static Arbitrary GenerateFilterExpressions() + { + Gen[] generators = + [ + EndsWithExpressions().Generator.Select(item => (FilterExpression) item), + StartsWithExpressions().Generator.Select(item => (FilterExpression) item), + ContainsExpressions().Generator.Select(item => (FilterExpression) item), + IntervalExpressions().Generator.Select(item => (FilterExpression) item), + DateExpressions().Generator.Select(item => (FilterExpression) item), + DateTimeExpressions().Generator.Select(item => (FilterExpression) item), + TimeExpressions().Generator.Select(item => (FilterExpression) item), + DurationExpressions().Generator.Select(item => (FilterExpression) item), + ConstantValueExpressions().Generator.Select(item => (FilterExpression) item), + GroupExpressions().Generator.Select(item => (FilterExpression) item) + ]; + + return Gen.OneOf(generators).ToArbitrary(); + } - /// - /// Generates an arbitrary random . - /// - /// - public static Arbitrary BinaryFilterExpressions() + /// + /// Generates an arbitrary random . + /// + /// + public static Arbitrary BinaryFilterExpressions() + { + Gen[] generators = + [ + AndExpressions().Generator.Select(item => (BinaryFilterExpression) item), + OrExpressions().Generator.Select(item => (BinaryFilterExpression) item) + ]; + + return Gen.OneOf(generators).ToArbitrary(); + } + + private static Gen SafeOrExpressionGenerator(int size) + { + Gen gen; + switch (size) { - Gen[] generators = - [ - AndExpressions().Generator.Select(item => (BinaryFilterExpression) item), - OrExpressions().Generator.Select(item => (BinaryFilterExpression) item) - ]; + case 0: + { + gen = GenerateFilterExpressions().Generator.Two() + .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), tuple => new OrExpression(tuple.Item1, tuple.Item2))); + break; + } - return Gen.OneOf(generators).ToArbitrary(); + default: + { + Gen subtree = SafeOrExpressionGenerator(size / 2); + gen = Gen.OneOf(GenerateFilterExpressions().Generator.Two() + .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), + tuple => new OrExpression(tuple.Item1, tuple.Item2))), + subtree.Two().Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), + tuple => new OrExpression(tuple.Item1, tuple.Item2)))); + break; + } } - private static Gen SafeOrExpressionGenerator(int size) - { - Gen gen; - switch (size) - { - case 0: - { - gen = GenerateFilterExpressions().Generator.Two() - .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), tuple => new OrExpression(tuple.Item1, tuple.Item2))); - break; - } + return gen; + } - default: - { - Gen subtree = SafeOrExpressionGenerator(size / 2); - gen = Gen.OneOf(GenerateFilterExpressions().Generator.Two() - .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), - tuple => new OrExpression(tuple.Item1, tuple.Item2))), - subtree.Two().Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), - tuple => new OrExpression(tuple.Item1, tuple.Item2)))); - break; - } - } + private static Gen SafeAndExpressionGenerator(int size) + { + Gen gen; + switch (size) + { + case 0: + { + gen = GenerateFilterExpressions().Generator.Two() + .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), + tuple => new AndExpression(tuple.Item1, tuple.Item2))); + break; + } - return gen; + default: + { + Gen subtree = SafeAndExpressionGenerator(size / 2); + gen = Gen.OneOf(GenerateFilterExpressions().Generator.Two() + .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), tuple => new AndExpression(tuple.Item1, tuple.Item2))), + subtree.Two().Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), tuple => new AndExpression(tuple.Item1, tuple.Item2)))); + break; + } } - private static Gen SafeAndExpressionGenerator(int size) + return gen; + } + + public static Arbitrary NotExpressions() + { + return Gen.Sized(SafeNotExpressionGenerator).ToArbitrary(); + + static Gen SafeNotExpressionGenerator(int size) { - Gen gen; + Gen[] generators = + [ + AndExpressions().Generator.Select(expr => (FilterExpression)expr), + OrExpressions().Generator.Select(expr => (FilterExpression)expr), + ConstantValueExpressions().Generator.Select(expr => (FilterExpression)expr), + DurationExpressions().Generator.Select(expr => (FilterExpression)expr), + DateExpressions().Generator.Select(expr => (FilterExpression)expr), + TimeExpressions().Generator.Select(expr => (FilterExpression)expr), + DateTimeExpressions().Generator.Select(expr => (FilterExpression)expr), + IntervalExpressions().Generator.Select(expr => (FilterExpression)expr) + ]; + + Gen gen; switch (size) { case 0: { - gen = GenerateFilterExpressions().Generator.Two() - .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), - tuple => new AndExpression(tuple.Item1, tuple.Item2))); + gen = Gen.OneOf(generators).Select(expr => new NotExpression(expr)); break; } default: { - Gen subtree = SafeAndExpressionGenerator(size / 2); - gen = Gen.OneOf(GenerateFilterExpressions().Generator.Two() - .Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), tuple => new AndExpression(tuple.Item1, tuple.Item2))), - subtree.Two().Select(tuple => CreateFilterExpression((tuple.Item1, tuple.Item2), tuple => new AndExpression(tuple.Item1, tuple.Item2)))); + Gen subtree = SafeNotExpressionGenerator(size / 2); + gen = Gen.OneOf(Gen.OneOf(generators).Select(exp => new NotExpression(exp)), + subtree.Select(expr => new NotExpression(expr))); break; } } return gen; } + } - public static Arbitrary NotExpressions() - { - return Gen.Sized(SafeNotExpressionGenerator).ToArbitrary(); - - static Gen SafeNotExpressionGenerator(int size) - { - Gen[] generators = - [ - AndExpressions().Generator.Select(expr => (FilterExpression)expr), - OrExpressions().Generator.Select(expr => (FilterExpression)expr), - ConstantValueExpressions().Generator.Select(expr => (FilterExpression)expr), - DurationExpressions().Generator.Select(expr => (FilterExpression)expr), - DateExpressions().Generator.Select(expr => (FilterExpression)expr), - TimeExpressions().Generator.Select(expr => (FilterExpression)expr), - DateTimeExpressions().Generator.Select(expr => (FilterExpression)expr), - IntervalExpressions().Generator.Select(expr => (FilterExpression)expr) - ]; - - Gen gen; - switch (size) - { - case 0: - { - gen = Gen.OneOf(generators).Select(expr => new NotExpression(expr)); - break; - } - - default: - { - Gen subtree = SafeNotExpressionGenerator(size / 2); - gen = Gen.OneOf(Gen.OneOf(generators).Select(exp => new NotExpression(exp)), - subtree.Select(expr => new NotExpression(expr))); - break; - } - } - - return gen; - } - } - - private static TFilterExpression CreateFilterExpression((FilterExpression, FilterExpression) input, Func<(FilterExpression, FilterExpression), TFilterExpression> func) - => func.Invoke(input); + private static TFilterExpression CreateFilterExpression((FilterExpression, FilterExpression) input, Func<(FilterExpression, FilterExpression), TFilterExpression> func) + => func.Invoke(input); - public static Arbitrary IntervalExpressions() + public static Arbitrary IntervalExpressions() + { + Gen boolGenerator = GetArbitraryFor().Generator; + (Gen gen, Gen included)[] datesGen = new[] { - Gen boolGenerator = GetArbitraryFor().Generator; - (Gen gen, Gen included)[] datesGen = new[] - { - (DateExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), - (DateTimeExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator) - }; + (DateExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), + (DateTimeExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator) + }; - (Gen gen, Gen included)[] numericsGen = new[] - { - (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.ToString())), boolGenerator), - (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.ToString())), boolGenerator), - (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.ToString())), boolGenerator), - (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.Item.ToString("G19", CultureInfo.InvariantCulture))), boolGenerator) - }; - - (Gen gen, Gen included) timeGen = (TimeExpressions().Generator.Select(item => (IBoundaryExpression)item), boolGenerator); - (Gen gen, Gen included) asteriskGen = (Gen.Constant((IBoundaryExpression)AsteriskExpression.Instance), Gen.Constant(false)); - - IEnumerable> generatorsWithMinAndMax = datesGen.CrossJoin(datesGen) - .Concat(datesGen.CrossJoin(new[] { timeGen })) - .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) - .Select(tuple => CreateIntervalExpressionGenerator((tuple.min.gen, tuple.min.included), - (tuple.max.gen, tuple.max.included))) - .Concat(numericsGen.CrossJoin(numericsGen) - .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) - .Select(tuple => CreateIntervalExpressionGenerator((tuple.min.gen, tuple.min.included), - (tuple.max.gen, tuple.max.included)))) - ; - - IEnumerable> generatorsWithMinOrMaxOnly = datesGen.CrossJoin(new[] { asteriskGen }) - .Concat(new[] { asteriskGen }.CrossJoin(datesGen)) - .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) - .Select(tuple => CreateIntervalExpressionGenerator((tuple.min.gen, tuple.min.included), - (tuple.max.gen, tuple.max.included))); - - return Gen.OneOf(generatorsWithMinAndMax.Concat(generatorsWithMinOrMaxOnly)) - .ToArbitrary(); - } - - public static Arbitrary BoundariesExpressions() + (Gen gen, Gen included)[] numericsGen = new[] { - Gen boolGenerator = GetArbitraryFor().Generator; - - IList> generators = new List> - { - CreateBoundaryGenerator(DateExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), - CreateBoundaryGenerator(DateTimeExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), - CreateBoundaryGenerator(TimeExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), - CreateBoundaryGenerator(Gen.Constant((IBoundaryExpression)AsteriskExpression.Instance), Gen.Constant(false)), - }; - - return Gen.OneOf(generators) - .ToArbitrary(); - } + (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.ToString())), boolGenerator), + (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.ToString())), boolGenerator), + (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.ToString())), boolGenerator), + (GetArbitraryFor().Generator.Select(value => (IBoundaryExpression) new NumericValueExpression(value.Item.ToString("G19", CultureInfo.InvariantCulture))), boolGenerator) + }; + + (Gen gen, Gen included) timeGen = (TimeExpressions().Generator.Select(item => (IBoundaryExpression)item), boolGenerator); + (Gen gen, Gen included) asteriskGen = (Gen.Constant((IBoundaryExpression)AsteriskExpression.Instance), Gen.Constant(false)); + + IEnumerable> generatorsWithMinAndMax = datesGen.CrossJoin(datesGen) + .Concat(datesGen.CrossJoin(new[] { timeGen })) + .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) + .Select(tuple => CreateIntervalExpressionGenerator((tuple.min.gen, tuple.min.included), + (tuple.max.gen, tuple.max.included))) + .Concat(numericsGen.CrossJoin(numericsGen) + .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) + .Select(tuple => CreateIntervalExpressionGenerator((tuple.min.gen, tuple.min.included), + (tuple.max.gen, tuple.max.included)))) + ; + + IEnumerable> generatorsWithMinOrMaxOnly = datesGen.CrossJoin(new[] { asteriskGen }) + .Concat(new[] { asteriskGen }.CrossJoin(datesGen)) + .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) + .Select(tuple => CreateIntervalExpressionGenerator((tuple.min.gen, tuple.min.included), + (tuple.max.gen, tuple.max.included))); + + return Gen.OneOf(generatorsWithMinAndMax.Concat(generatorsWithMinOrMaxOnly)) + .ToArbitrary(); + } - private static Gen CreateIntervalExpressionGenerator((Gen boundaryGenerator, Gen includedGenerator) genMin, (Gen boundaryGenerator, Gen includedGenerator) genMax) - { - return CreateBoundaryGenerator(genMin.boundaryGenerator, genMin.includedGenerator) - .Zip(CreateBoundaryGenerator(genMax.boundaryGenerator, genMax.includedGenerator)) - .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) - .Select(tuple => new IntervalExpression(min: new(tuple.min.Expression, tuple.min.Included), - max: new(tuple.max.Expression, tuple.max.Included))); - } + public static Arbitrary BoundariesExpressions() + { + Gen boolGenerator = GetArbitraryFor().Generator; - private static Gen CreateBoundaryGenerator(Gen boundaryGenerator, Gen includedGenerator) + IList> generators = new List> { - return boundaryGenerator.Zip(includedGenerator) - .Select(tuple => new BoundaryExpression(expression: tuple.Item1, - included: tuple.Item2)); - } + CreateBoundaryGenerator(DateExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), + CreateBoundaryGenerator(DateTimeExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), + CreateBoundaryGenerator(TimeExpressions().Generator.Select(item => (IBoundaryExpression) item), boolGenerator), + CreateBoundaryGenerator(Gen.Constant((IBoundaryExpression)AsteriskExpression.Instance), Gen.Constant(false)), + }; + + return Gen.OneOf(generators) + .ToArbitrary(); + } - public static Arbitrary ContainsExpressions() - => Gen.OneOf(GetArbitraryFor().Generator - .Select(nonWhiteSpaceString => new ContainsExpression(nonWhiteSpaceString.Item)), - TextExpressions().Generator.Select(text => new ContainsExpression(text)) - ) - .ToArbitrary(); + private static Gen CreateIntervalExpressionGenerator((Gen boundaryGenerator, Gen includedGenerator) genMin, (Gen boundaryGenerator, Gen includedGenerator) genMax) + { + return CreateBoundaryGenerator(genMin.boundaryGenerator, genMin.includedGenerator) + .Zip(CreateBoundaryGenerator(genMax.boundaryGenerator, genMax.includedGenerator)) + .Select(tuple => (min: tuple.Item1, max: tuple.Item2)) + .Select(tuple => new IntervalExpression(min: new(tuple.min.Expression, tuple.min.Included), + max: new(tuple.max.Expression, tuple.max.Included))); + } - public static Arbitrary GenerateRegexValues() - { - Gen regexRangeGenerator = RangeBracketValues().Convert(range => (BracketValue)range, - bracket => (RangeBracketValue)bracket) - .Generator; - Gen regexConstantGenerator = ConstantBracketValues().Convert(range => (BracketValue)range, - bracket => (ConstantBracketValue)bracket) - .Generator; - - return Gen.OneOf(regexConstantGenerator, regexRangeGenerator).ToArbitrary(); - } + private static Gen CreateBoundaryGenerator(Gen boundaryGenerator, Gen includedGenerator) + { + return boundaryGenerator.Zip(includedGenerator) + .Select(tuple => new BoundaryExpression(expression: tuple.Item1, + included: tuple.Item2)); + } - private static Arbitrary ConstantBracketValues() - => GetArbitraryFor().Filter(input => !string.IsNullOrWhiteSpace(input) - && input.Length > 1 - && input.All(chr => char.IsLetterOrDigit(chr))) - .Generator - .Select(item => new ConstantBracketValue(item)) - .ToArbitrary(); + public static Arbitrary ContainsExpressions() + => Gen.OneOf(GetArbitraryFor().Generator + .Select(nonWhiteSpaceString => new ContainsExpression(nonWhiteSpaceString.Item)), + TextExpressions().Generator.Select(text => new ContainsExpression(text)) + ) + .ToArbitrary(); - private static Arbitrary RangeBracketValues() - { - return GetArbitraryFor() - .Filter(chr => char.IsLetterOrDigit(chr)).Generator - .Two() - .Select(tuple => (start: tuple.Item1, end: tuple.Item2)) - .Where(tuple => (tuple.start < tuple.end) && (TupleContainsLetter(tuple) || TupleContainsDigits(tuple))) - .Select(tuple => new RangeBracketValue(tuple.start, tuple.end)) - .ToArbitrary(); - - static bool TupleContainsLetter((char start, char end) tuple) => char.IsLetter(tuple.start) && char.IsLetter(tuple.end) - && ((char.IsLower(tuple.start) && char.IsLower(tuple.end)) || (char.IsUpper(tuple.start) && char.IsUpper(tuple.end))); - - static bool TupleContainsDigits((char start, char end) tuple) => char.IsDigit(tuple.start) && char.IsDigit(tuple.end); - } + public static Arbitrary GenerateRegexValues() + { + Gen regexRangeGenerator = RangeBracketValues().Convert(range => (BracketValue)range, + bracket => (RangeBracketValue)bracket) + .Generator; + Gen regexConstantGenerator = ConstantBracketValues().Convert(range => (BracketValue)range, + bracket => (ConstantBracketValue)bracket) + .Generator; + + return Gen.OneOf(regexConstantGenerator, regexRangeGenerator).ToArbitrary(); + } - /// - /// generator - /// - /// - public static Arbitrary BracketExpressions() - => Gen.OneOf(RangeBracketValues().Generator.Select(x => (BracketValue)x), - ConstantBracketValues().Generator.Select(x => (BracketValue)x)) - .ArrayOf() - .Select(brackets => new BracketExpression(brackets)) - .ToArbitrary(); + private static Arbitrary ConstantBracketValues() + => GetArbitraryFor().Filter(input => !string.IsNullOrWhiteSpace(input) + && input.Length > 1 + && input.All(chr => char.IsLetterOrDigit(chr))) + .Generator + .Select(item => new ConstantBracketValue(item)) + .ToArbitrary(); - public static Arbitrary TextExpressions() - => ArbMap.Default.ArbFor() - .Generator - .Select(val => new TextExpression(val.Item)) - .ToArbitrary(); + private static Arbitrary RangeBracketValues() + { + return GetArbitraryFor() + .Filter(chr => char.IsLetterOrDigit(chr)).Generator + .Two() + .Select(tuple => (start: tuple.Item1, end: tuple.Item2)) + .Where(tuple => (tuple.start < tuple.end) && (TupleContainsLetter(tuple) || TupleContainsDigits(tuple))) + .Select(tuple => new RangeBracketValue(tuple.start, tuple.end)) + .ToArbitrary(); + + static bool TupleContainsLetter((char start, char end) tuple) => char.IsLetter(tuple.start) && char.IsLetter(tuple.end) + && ((char.IsLower(tuple.start) && char.IsLower(tuple.end)) || (char.IsUpper(tuple.start) && char.IsUpper(tuple.end))); + + static bool TupleContainsDigits((char start, char end) tuple) => char.IsDigit(tuple.start) && char.IsDigit(tuple.end); } + + /// + /// generator + /// + /// + public static Arbitrary BracketExpressions() + => Gen.OneOf(RangeBracketValues().Generator.Select(x => (BracketValue)x), + ConstantBracketValues().Generator.Select(x => (BracketValue)x)) + .ArrayOf() + .Select(brackets => new BracketExpression(brackets)) + .ToArbitrary(); + + public static Arbitrary TextExpressions() + => ArbMap.Default.ArbFor() + .Generator + .Select(val => new TextExpression(val.Item)) + .ToArbitrary(); } \ No newline at end of file diff --git a/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs b/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs index 8d0b42c7..2bd75d3c 100644 --- a/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs +++ b/test/DataFilters.UnitTests/Helpers/FilterGenerators.cs @@ -5,160 +5,159 @@ using FsCheck; using FsCheck.Fluent; -namespace DataFilters.UnitTests.Helpers +namespace DataFilters.UnitTests.Helpers; + +using static GeneratorHelper; + +/// +/// Helper class which contains usefull FsCheck generators. +/// +internal static class FilterGenerators { - using static GeneratorHelper; + private readonly static Faker Faker = new(); /// - /// Helper class which contains usefull FsCheck generators. + /// Generates a where the value of the filter is a string /// - internal static class FilterGenerators + internal static Arbitrary FiltersOverString() { - private readonly static Faker Faker = new(); - - /// - /// Generates a where the value of the filter is a string - /// - internal static Arbitrary FiltersOverString() - { #if NETCOREAPP3_1 - IEnumerable operators = EnumExtensions.GetValues(); + IEnumerable operators = EnumExtensions.GetValues(); #else - IEnumerable operators = Enum.GetValues(); + IEnumerable operators = Enum.GetValues(); #endif - Gen genOperator = Gen.OneOf(operators.Select(op => Gen.Constant(op))); + Gen genOperator = Gen.OneOf(operators.Select(op => Gen.Constant(op))); - return genOperator.Zip(GetArbitraryFor().Generator) - .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) - .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) - .ToArbitrary(); - } + return genOperator.Zip(GetArbitraryFor().Generator) + .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) + .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) + .ToArbitrary(); + } - /// - /// Generates a where the value of the filter is a string - /// - internal static Arbitrary FiltersOverNumericValues() - { - Gen genOperator = Gen.OneOf(Gen.Constant(FilterOperator.LessThan), - Gen.Constant(FilterOperator.LessThanOrEqualTo), - Gen.Constant(FilterOperator.EqualTo), - Gen.Constant(FilterOperator.GreaterThan), - Gen.Constant(FilterOperator.GreaterThanOrEqual), - Gen.Constant(FilterOperator.NotEqualTo)); - - return Gen.OneOf(genOperator.Zip(GetArbitraryFor().Generator) - .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) - .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)), - genOperator.Zip(GetArbitraryFor().Generator) - .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) - .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)), - genOperator.Zip(GetArbitraryFor().Generator) - .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) - .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) - ) - .ToArbitrary() - ; - } + /// + /// Generates a where the value of the filter is a string + /// + internal static Arbitrary FiltersOverNumericValues() + { + Gen genOperator = Gen.OneOf(Gen.Constant(FilterOperator.LessThan), + Gen.Constant(FilterOperator.LessThanOrEqualTo), + Gen.Constant(FilterOperator.EqualTo), + Gen.Constant(FilterOperator.GreaterThan), + Gen.Constant(FilterOperator.GreaterThanOrEqual), + Gen.Constant(FilterOperator.NotEqualTo)); + + return Gen.OneOf(genOperator.Zip(GetArbitraryFor().Generator) + .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) + .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)), + genOperator.Zip(GetArbitraryFor().Generator) + .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) + .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)), + genOperator.Zip(GetArbitraryFor().Generator) + .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) + .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) + ) + .ToArbitrary() + ; + } - /// - /// Generates a where the value of the filter is a string - /// - internal static Arbitrary FiltersOverGenericValues() - { + /// + /// Generates a where the value of the filter is a string + /// + internal static Arbitrary FiltersOverGenericValues() + { #if NETCOREAPP3_1 - IEnumerable operators = EnumExtensions.GetValues(); + IEnumerable operators = EnumExtensions.GetValues(); #else - IEnumerable operators = Enum.GetValues(); + IEnumerable operators = Enum.GetValues(); #endif - Gen genOperator = Gen.OneOf(operators.Select(op => Gen.Constant(op))); + Gen genOperator = Gen.OneOf(operators.Select(op => Gen.Constant(op))); - return genOperator.Zip(GetArbitraryFor().Generator) - .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) - .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) - .ToArbitrary(); - } + return genOperator.Zip(GetArbitraryFor().Generator) + .Select(tuple => (Op: tuple.Item1, Value: tuple.Item2)) + .Select(tuple => new Filter(Faker.Hacker.Noun(), tuple.Op, tuple.Value)) + .ToArbitrary(); + } - /// - /// Generates an arbitrary random . - /// - public static Arbitrary GenerateFilters() - { - Gen[] generators = - [ - EndsWithFilter().Generator.Select(filter => (IFilter) filter), - StartsWithFilter().Generator.Select(filter => (IFilter) filter), - ContainsFilter().Generator.Select(filter => (IFilter) filter), - EqualsWithFilter().Generator.Select(filter => (IFilter) filter), - Gen.Sized(SafeFilterGenerator).Select(filter => (IFilter) filter), - GreaterThanFilter(), - GreaterThanOrEqualFilter(), - LessThanFilter(), - LessThanOrEqualFilter(), - FiltersOverNumericValues().Generator.Select(filter => (IFilter)filter) - ]; - - return Gen.OneOf(generators).ToArbitrary(); - } + /// + /// Generates an arbitrary random . + /// + public static Arbitrary GenerateFilters() + { + Gen[] generators = + [ + EndsWithFilter().Generator.Select(filter => (IFilter) filter), + StartsWithFilter().Generator.Select(filter => (IFilter) filter), + ContainsFilter().Generator.Select(filter => (IFilter) filter), + EqualsWithFilter().Generator.Select(filter => (IFilter) filter), + Gen.Sized(SafeFilterGenerator).Select(filter => (IFilter) filter), + GreaterThanFilter(), + GreaterThanOrEqualFilter(), + LessThanFilter(), + LessThanOrEqualFilter(), + FiltersOverNumericValues().Generator.Select(filter => (IFilter)filter) + ]; + + return Gen.OneOf(generators).ToArbitrary(); + } - internal static Arbitrary EndsWithFilter() => GenerateFilterOverStrings(FilterOperator.EndsWith); + internal static Arbitrary EndsWithFilter() => GenerateFilterOverStrings(FilterOperator.EndsWith); - internal static Arbitrary StartsWithFilter() => GenerateFilterOverStrings(FilterOperator.StartsWith); + internal static Arbitrary StartsWithFilter() => GenerateFilterOverStrings(FilterOperator.StartsWith); - internal static Arbitrary ContainsFilter() => GenerateFilterOverStrings(FilterOperator.Contains); + internal static Arbitrary ContainsFilter() => GenerateFilterOverStrings(FilterOperator.Contains); - internal static Arbitrary EqualsWithFilter() => GenerateFilterOverStrings(FilterOperator.EqualTo); + internal static Arbitrary EqualsWithFilter() => GenerateFilterOverStrings(FilterOperator.EqualTo); - internal static Arbitrary GenerateFilterOverStrings(FilterOperator op) - => GetArbitraryFor().Generator - .Select(value => new Filter(field: Faker.Hacker.Noun(), op, value)) - .ToArbitrary(); + internal static Arbitrary GenerateFilterOverStrings(FilterOperator op) + => GetArbitraryFor().Generator + .Select(value => new Filter(field: Faker.Hacker.Noun(), op, value)) + .ToArbitrary(); - private static Gen SafeFilterGenerator(int size) + private static Gen SafeFilterGenerator(int size) + { + Gen gen; + Gen generateLogic = Gen.OneOf(Gen.Constant(FilterLogic.And), + Gen.Constant(FilterLogic.Or)); + switch (size) { - Gen gen; - Gen generateLogic = Gen.OneOf(Gen.Constant(FilterLogic.And), - Gen.Constant(FilterLogic.Or)); - switch (size) - { - case 0: - { - gen = GenerateFilters().Generator.Two() - .Select(tuple => new[] { tuple.Item1, tuple.Item2 }) - .Zip(generateLogic) - .Select(tuple => new MultiFilter { Logic = tuple.Item2, Filters = tuple.Item1 }); - break; - } - - default: - { - Gen subtree = SafeFilterGenerator(size / 2); - - gen = Gen.OneOf(GenerateFilters().Generator.Two() - .Select(tuple => new[] { tuple.Item1, tuple.Item2 }) - .Zip(generateLogic) - .Select(tuple => new MultiFilter { Logic = tuple.Item2, Filters = tuple.Item1 }), - subtree); - break; - } - } - - return gen; + case 0: + { + gen = GenerateFilters().Generator.Two() + .Select(tuple => new[] { tuple.Item1, tuple.Item2 }) + .Zip(generateLogic) + .Select(tuple => new MultiFilter { Logic = tuple.Item2, Filters = tuple.Item1 }); + break; + } + + default: + { + Gen subtree = SafeFilterGenerator(size / 2); + + gen = Gen.OneOf(GenerateFilters().Generator.Two() + .Select(tuple => new[] { tuple.Item1, tuple.Item2 }) + .Zip(generateLogic) + .Select(tuple => new MultiFilter { Logic = tuple.Item2, Filters = tuple.Item1 }), + subtree); + break; + } } - private static Gen GreaterThanFilter() - => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.GreaterThan); + return gen; + } - private static Gen GreaterThanOrEqualFilter() - => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.GreaterThanOrEqual); + private static Gen GreaterThanFilter() + => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.GreaterThan); - private static Gen LessThanOrEqualFilter() - => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.LessThanOrEqualTo); + private static Gen GreaterThanOrEqualFilter() + => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.GreaterThanOrEqual); - private static Gen LessThanFilter() - => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.LessThanOrEqualTo); + private static Gen LessThanOrEqualFilter() + => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.LessThanOrEqualTo); - private static Gen GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator op) - => GetArbitraryFor().Generator - .Select(value => (IFilter)new Filter(Faker.Hacker.Noun(), op, value)); - } + private static Gen LessThanFilter() + => GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator.LessThanOrEqualTo); + + private static Gen GenerateFilterWithSpecifiedOperatorAndValue(FilterOperator op) + => GetArbitraryFor().Generator + .Select(value => (IFilter)new Filter(Faker.Hacker.Noun(), op, value)); } diff --git a/test/DataFilters.UnitTests/MultiFilterTests.cs b/test/DataFilters.UnitTests/MultiFilterTests.cs index 9bd5ce41..b06163c2 100644 --- a/test/DataFilters.UnitTests/MultiFilterTests.cs +++ b/test/DataFilters.UnitTests/MultiFilterTests.cs @@ -3,341 +3,340 @@ #else #endif -namespace DataFilters.UnitTests +namespace DataFilters.UnitTests; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using DataFilters.UnitTests.Helpers; +using FluentAssertions; +using FsCheck; +using FsCheck.Xunit; +using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Schema; +using Xunit; +using Xunit.Abstractions; +using Xunit.Categories; +using static DataFilters.FilterLogic; +using static DataFilters.FilterOperator; + +[UnitTest] +public class MultiFilterTests(ITestOutputHelper output) { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Linq.Expressions; - using DataFilters.UnitTests.Helpers; - using FluentAssertions; - using FsCheck; - using FsCheck.Xunit; - using Newtonsoft.Json.Linq; - using Newtonsoft.Json.Schema; - using Xunit; - using Xunit.Abstractions; - using Xunit.Categories; - using static DataFilters.FilterLogic; - using static DataFilters.FilterOperator; - - [UnitTest] - public class MultiFilterTests(ITestOutputHelper output) + public class Person { - public class Person - { - public string Firstname { get; set; } + public string Firstname { get; set; } - public string Lastname { get; set; } + public string Lastname { get; set; } - public DateTime BirthDate { get; set; } - } + public DateTime BirthDate { get; set; } + } - public static IEnumerable MultiFilterToJsonCases + public static IEnumerable MultiFilterToJsonCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new MultiFilter { - Logic = Or, - Filters = new [] { - new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), - new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") - } - }, - (Expression>)(json => - JObject.Parse(json).IsValid(MultiFilter.Schema) - && JObject.Parse(json).Properties().Exactly(2) - - && "or".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) - - && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) - && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) - && - "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) - && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) + new MultiFilter { + Logic = Or, + Filters = new [] { + new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), + new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") + } + }, + (Expression>)(json => + JObject.Parse(json).IsValid(MultiFilter.Schema) + && JObject.Parse(json).Properties().Exactly(2) + + && "or".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) + + && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) + && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) + && + "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) + && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) + + ) + }; + + yield return new object[] + { + new MultiFilter { + Filters = new [] { + new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), + new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") + } + }, + (Expression>)(json => + JObject.Parse(json).IsValid(MultiFilter.Schema) + && JObject.Parse(json).Properties().Count() == 2 + + && "and".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) + + && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) + && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) + && + "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) + && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) + && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) + + ) + }; + } + } - ) - }; + public static IEnumerable CompositeFilterSchemaTestCases + { + get + { + yield return new object[] + { + "{" + + $"{MultiFilter.LogicJsonPropertyName} : 'or'," + + $"{MultiFilter.FiltersJsonPropertyName}: [" + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'batman' }}," + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + + "]" + + "}", + true + }; + + yield return new object[] + { + "{" + + $"{MultiFilter.LogicJsonPropertyName} : 'and'," + + $"{MultiFilter.FiltersJsonPropertyName}: [" + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'batman' }}," + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + + "]" + + "}", + true + }; + + yield return new object[] + { + "{" + + $"{MultiFilter.FiltersJsonPropertyName}: [" + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'batman' }}," + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + + "]" + + "}", + true + }; + + yield return new object[] + { + "{" + + $"{MultiFilter.FiltersJsonPropertyName}: [" + + $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + + "]" + + "}", + false + }; + } + } - yield return new object[] - { - new MultiFilter { - Filters = new [] { - new Filter (field : "Nickname", @operator : EqualTo, value : "Batman"), - new Filter (field : "Nickname", @operator : EqualTo, value : "Robin") - } - }, - (Expression>)(json => - JObject.Parse(json).IsValid(MultiFilter.Schema) - && JObject.Parse(json).Properties().Count() == 2 + [Theory] + [MemberData(nameof(MultiFilterToJsonCases))] + public void MultiFilterToJson(MultiFilter filter, Expression> jsonMatcher) + { + output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); - && "and".Equals((string) JObject.Parse(json)[MultiFilter.LogicJsonPropertyName]) + // Act + string json = filter.ToJson(); - && "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.OperatorJsonPropertyName]) - && "Batman".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][0][Filter.ValueJsonPropertyName]) - && - "Nickname".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.FieldJsonPropertyName]) - && "eq".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.OperatorJsonPropertyName]) - && "Robin".Equals((string)JObject.Parse(json)[MultiFilter.FiltersJsonPropertyName][1][Filter.ValueJsonPropertyName]) + output.WriteLine($"{nameof(json)} : {json}"); - ) - }; - } - } + // Assert + json.Should().Match(jsonMatcher); + } - public static IEnumerable CompositeFilterSchemaTestCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] + new MultiFilter { - "{" + - $"{MultiFilter.LogicJsonPropertyName} : 'or'," + - $"{MultiFilter.FiltersJsonPropertyName}: [" + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'batman' }}," + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + - "]" + - "}", - true - }; - - yield return new object[] + Logic = And, + Filters = new IFilter[]{ + new Filter("property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + } + }, + new MultiFilter { - "{" + - $"{MultiFilter.LogicJsonPropertyName} : 'and'," + - $"{MultiFilter.FiltersJsonPropertyName}: [" + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'batman' }}," + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + - "]" + - "}", - true - }; - - yield return new object[] + Logic = And, + Filters = new IFilter[]{ + new Filter("property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + } + }, + true, + $"Two instances of {nameof(MultiFilter)} contains same ${nameof(MultiFilter.Filters)} in same order" + }; + + yield return new object[] + { + new MultiFilter { - "{" + - $"{MultiFilter.FiltersJsonPropertyName}: [" + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'batman' }}," + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + - "]" + - "}", - true - }; - - yield return new object[] + Logic = And, + Filters = new IFilter[]{ + new Filter("property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + } + }, + new MultiFilter { - "{" + - $"{MultiFilter.FiltersJsonPropertyName}: [" + - $"{{ {Filter.FieldJsonPropertyName} : 'nickname', {Filter.OperatorJsonPropertyName} : 'eq', {Filter.ValueJsonPropertyName} : 'robin' }}" + - "]" + - "}", - false - }; - } - } - - [Theory] - [MemberData(nameof(MultiFilterToJsonCases))] - public void MultiFilterToJson(MultiFilter filter, Expression> jsonMatcher) - { - output.WriteLine($"Testing : {filter}{Environment.NewLine} against {Environment.NewLine} {jsonMatcher} "); - - // Act - string json = filter.ToJson(); - - output.WriteLine($"{nameof(json)} : {json}"); - - // Assert - json.Should().Match(jsonMatcher); - } + Logic = And, + Filters = new IFilter[]{ + new Filter("property", NotEqualTo, "value"), + new Filter("property", EqualTo, "value"), + } + }, + false, + "the second instance contains one filter that has a different operator" + }; + + yield return new object[] + { + new MultiFilter + { + Logic = And, + Filters = new IFilter[]{ + new Filter("property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + } + }, + null, + false, + "comparing to null" + }; - public static IEnumerable EqualsCases - { - get { - yield return new object[] + MultiFilter filter = new() { - new MultiFilter - { - Logic = And, - Filters = new IFilter[]{ - new Filter("property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - } - }, - new MultiFilter - { - Logic = And, - Filters = new IFilter[]{ - new Filter("property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - } - }, - true, - $"Two instances of {nameof(MultiFilter)} contains same ${nameof(MultiFilter.Filters)} in same order" + Logic = And, + Filters = new IFilter[]{ + new Filter("property", EqualTo, "value"), + new Filter("property", EqualTo, "value"), + } }; - yield return new object[] { - new MultiFilter - { - Logic = And, - Filters = new IFilter[]{ - new Filter("property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - } - }, - new MultiFilter - { - Logic = And, - Filters = new IFilter[]{ - new Filter("property", NotEqualTo, "value"), - new Filter("property", EqualTo, "value"), - } - }, - false, - "the second instance contains one filter that has a different operator" + filter, + filter, + true, + "comparing a too itself must always returns true" }; + } + { yield return new object[] { new MultiFilter { Logic = And, - Filters = new IFilter[]{ - new Filter("property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - } - }, - null, - false, - "comparing to null" - }; - - { - MultiFilter filter = new() - { - Logic = And, - Filters = new IFilter[]{ - new Filter("property", EqualTo, "value"), - new Filter("property", EqualTo, "value"), - } - }; - yield return new object[] - { - filter, - filter, - true, - "comparing a too itself must always returns true" - }; - } - - { - yield return new object[] - { - new MultiFilter + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] + new Filter("HasSuperpower", EqualTo, false), + new MultiFilter { - new Filter("HasSuperpower", EqualTo, false), - new MultiFilter + Logic = Or, + Filters = new IFilter[] { - Logic = Or, - Filters = new IFilter[] - { - new Filter("Lastname", EqualTo, "Wayne"), - new Filter("Lastname", EqualTo, "Kent") - } + new Filter("Lastname", EqualTo, "Wayne"), + new Filter("Lastname", EqualTo, "Kent") } } - }, - new MultiFilter + } + }, + new MultiFilter + { + Logic = And, + Filters = new IFilter[] { - Logic = And, - Filters = new IFilter[] + new Filter("HasSuperpower", EqualTo, false), + new MultiFilter { - new Filter("HasSuperpower", EqualTo, false), - new MultiFilter + Logic = Or, + Filters = new [] { - Logic = Or, - Filters = new [] - { - new Filter("Lastname", EqualTo, "Wayne"), - new Filter("Lastname", EqualTo, "Kent") - } + new Filter("Lastname", EqualTo, "Wayne"), + new Filter("Lastname", EqualTo, "Kent") } } - }, - true, - "Two distinct instances of multifilters that holds same data in same order" - }; - } + } + }, + true, + "Two distinct instances of multifilters that holds same data in same order" + }; } } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void CompositeFilterImplementsEquatableProperly(MultiFilter first, object second, bool expectedResult, string reason) - { - output.WriteLine($"first : {first}"); - output.WriteLine($"second : {second}"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void CompositeFilterImplementsEquatableProperly(MultiFilter first, object second, bool expectedResult, string reason) + { + output.WriteLine($"first : {first}"); + output.WriteLine($"second : {second}"); - // Act - bool result = first.Equals(second); + // Act + bool result = first.Equals(second); - // Assert - result.Should() - .Be(expectedResult, reason); - } + // Assert + result.Should() + .Be(expectedResult, reason); + } - [Theory] - [MemberData(nameof(CompositeFilterSchemaTestCases))] - public void CompositeFilterSchema(string json, bool expectedValidity) - { - output.WriteLine($"{nameof(json)} : {json}"); + [Theory] + [MemberData(nameof(CompositeFilterSchemaTestCases))] + public void CompositeFilterSchema(string json, bool expectedValidity) + { + output.WriteLine($"{nameof(json)} : {json}"); - // Arrange - JSchema schema = MultiFilter.Schema; + // Arrange + JSchema schema = MultiFilter.Schema; - // Act - bool isValid = JObject.Parse(json).IsValid(schema); + // Act + bool isValid = JObject.Parse(json).IsValid(schema); - // Assert - isValid.Should().Be(expectedValidity); - } + // Assert + isValid.Should().Be(expectedValidity); + } - [Property(Arbitrary = [typeof(FilterGenerators)])] - public void Given_filter_instance_Negate_should_work_as_expected(FilterLogic logic, NonEmptyArray source) - { - // Arrange - MultiFilter original = new() { Logic = logic, Filters = source.Item }; + [Property(Arbitrary = [typeof(FilterGenerators)])] + public void Given_filter_instance_Negate_should_work_as_expected(FilterLogic logic, NonEmptyArray source) + { + // Arrange + MultiFilter original = new() { Logic = logic, Filters = source.Item }; - // Act - IFilter oppositeFilter = original.Negate(); + // Act + IFilter oppositeFilter = original.Negate(); - // Assert - MultiFilter result = oppositeFilter.Should() - .BeOfType() - .Which; + // Assert + MultiFilter result = oppositeFilter.Should() + .BeOfType() + .Which; - result.Logic.Should() - .Be(original.Logic switch - { - And => Or, - Or => And, - _ => throw new NotSupportedException($"The original {original.Logic} logic is not supported"), - }, "the logic of the resulting filter should be the opposite of the original filter's logic"); - - IEnumerable expected = source.Item.Select(filter => filter.Negate()); - result.Filters.Should() - .HaveSameCount(original.Filters).And - .ContainInOrder(expected); - } + result.Logic.Should() + .Be(original.Logic switch + { + And => Or, + Or => And, + _ => throw new NotSupportedException($"The original {original.Logic} logic is not supported"), + }, "the logic of the resulting filter should be the opposite of the original filter's logic"); + + IEnumerable expected = source.Item.Select(filter => filter.Negate()); + result.Filters.Should() + .HaveSameCount(original.Filters).And + .ContainInOrder(expected); } } diff --git a/test/DataFilters.UnitTests/MultiOrderTests.cs b/test/DataFilters.UnitTests/MultiOrderTests.cs index f871d131..9f914b47 100644 --- a/test/DataFilters.UnitTests/MultiOrderTests.cs +++ b/test/DataFilters.UnitTests/MultiOrderTests.cs @@ -1,102 +1,100 @@ -namespace DataFilters.UnitTests -{ - using System.Collections.Generic; - using DataFilters.TestObjects; - using FluentAssertions; - using Xunit; - using Xunit.Abstractions; - using static DataFilters.OrderDirection; +namespace DataFilters.UnitTests; + +using System.Collections.Generic; +using DataFilters.TestObjects; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using static DataFilters.OrderDirection; - public class MultiOrderTests(ITestOutputHelper outputHelper) +public class MultiOrderTests(ITestOutputHelper outputHelper) +{ + public static IEnumerable EqualsCases { - [System.Diagnostics.CodeAnalysis.SuppressMessage("Minor Code Smell", "S1199:Nested code blocks should not be used")] - public static IEnumerable EqualsCases + get { - get { - { - MultiOrder first = new( - new Order(expression: nameof(SuperHero.Nickname)), - new Order(expression: nameof(SuperHero.Age), direction: Descending) - ); + MultiOrder first = new( + new Order(expression: nameof(SuperHero.Nickname)), + new Order(expression: nameof(SuperHero.Age), direction: Descending) + ); - yield return new object[] - { - first, - first, - true, - $"A {nameof(MultiOrder)} instance is equal to itself" - }; - } + yield return new object[] { - MultiOrder first = new( - new Order(expression: nameof(SuperHero.Nickname)), - new Order(expression: nameof(SuperHero.Age), direction: Descending) - ); - - MultiOrder second = new - ( - new Order(expression: nameof(SuperHero.Nickname)), - new Order(expression: nameof(SuperHero.Age), direction: Descending) - ); + first, + first, + true, + $"A {nameof(MultiOrder)} instance is equal to itself" + }; + } + { + MultiOrder first = new( + new Order(expression: nameof(SuperHero.Nickname)), + new Order(expression: nameof(SuperHero.Age), direction: Descending) + ); - yield return new object[] - { - first, - second, - true, - $"Two distinct {nameof(MultiOrder)} instances holding same data in same order" - }; - } + MultiOrder second = new + ( + new Order(expression: nameof(SuperHero.Nickname)), + new Order(expression: nameof(SuperHero.Age), direction: Descending) + ); + yield return new object[] { - MultiOrder first = new - ( - new Order(expression: nameof(SuperHero.Nickname)), - new Order(expression: nameof(SuperHero.Age), direction: Descending) - ); + first, + second, + true, + $"Two distinct {nameof(MultiOrder)} instances holding same data in same order" + }; + } - MultiOrder second = new - ( - new Order(expression: nameof(SuperHero.Age), direction: Descending), - new Order(expression: nameof(SuperHero.Nickname)) - ); + { + MultiOrder first = new + ( + new Order(expression: nameof(SuperHero.Nickname)), + new Order(expression: nameof(SuperHero.Age), direction: Descending) + ); - yield return new object[] - { - first, - second, - false, - $"Two distinct {nameof(MultiOrder)} instances holding same data but not same order" - }; - } + MultiOrder second = new + ( + new Order(expression: nameof(SuperHero.Age), direction: Descending), + new Order(expression: nameof(SuperHero.Nickname)) + ); + + yield return new object[] + { + first, + second, + false, + $"Two distinct {nameof(MultiOrder)} instances holding same data but not same order" + }; } } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void EqualsTests(IOrder first, object second, bool expected, string reason) - { - outputHelper.WriteLine($"{nameof(first)} : '{first}'"); - outputHelper.WriteLine($"{nameof(second)} : '{second}'"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void EqualsTests(IOrder first, object second, bool expected, string reason) + { + outputHelper.WriteLine($"{nameof(first)} : '{first}'"); + outputHelper.WriteLine($"{nameof(second)} : '{second}'"); - // Act - bool actual = first.Equals(second); - int actualHashCode = first.GetHashCode(); + // Act + bool actual = first.Equals(second); + int actualHashCode = first.GetHashCode(); - // Assert - actual.Should() - .Be(expected, reason); - if (actual) - { - actualHashCode.Should() - .Be(second?.GetHashCode(), reason); - } - else - { - actualHashCode.Should() - .NotBe(second?.GetHashCode(), reason); - } + // Assert + actual.Should() + .Be(expected, reason); + if (actual) + { + actualHashCode.Should() + .Be(second?.GetHashCode(), reason); + } + else + { + actualHashCode.Should() + .NotBe(second?.GetHashCode(), reason); } } } diff --git a/test/DataFilters.UnitTests/OrderTests.cs b/test/DataFilters.UnitTests/OrderTests.cs index 3fb7a1f5..e526c5c8 100644 --- a/test/DataFilters.UnitTests/OrderTests.cs +++ b/test/DataFilters.UnitTests/OrderTests.cs @@ -1,87 +1,86 @@ -namespace DataFilters.UnitTests -{ - using System; - using System.Collections.Generic; - using DataFilters.TestObjects; - using FluentAssertions; - using Xunit; - using Xunit.Abstractions; - using static DataFilters.OrderDirection; +namespace DataFilters.UnitTests; + +using System; +using System.Collections.Generic; +using DataFilters.TestObjects; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using static DataFilters.OrderDirection; - public class OrderTests(ITestOutputHelper outputHelper) +public class OrderTests(ITestOutputHelper outputHelper) +{ + [Theory] + [InlineData("", "Expression is empty")] + [InlineData(" ", "Expression is whitespace only")] + [InlineData(null, "Expression is null")] + public void Ctor_Throws_ArgumentException_If_Expression_Is_Null(string expression, string reason) { - [Theory] - [InlineData("", "Expression is empty")] - [InlineData(" ", "Expression is whitespace only")] - [InlineData(null, "Expression is null")] - public void Ctor_Throws_ArgumentException_If_Expression_Is_Null(string expression, string reason) - { - // Act - Action action = () => _ = new Order(expression); + // Act + Action action = () => _ = new Order(expression); - // Assert - action.Should() - .Throw(reason) - .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName), "Param name cannot be null") - .Where(ex => !string.IsNullOrWhiteSpace(ex.Message), "Message cannot be null"); - } + // Assert + action.Should() + .Throw(reason) + .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName), "Param name cannot be null") + .Where(ex => !string.IsNullOrWhiteSpace(ex.Message), "Message cannot be null"); + } - public static IEnumerable EqualsCases + public static IEnumerable EqualsCases + { + get { - get + yield return new object[] { - yield return new object[] - { - new Order("Name"), - new Order("Name"), - true, - $"Two distinct {nameof(Order)} instances with same properties must be equal" - }; + new Order("Name"), + new Order("Name"), + true, + $"Two distinct {nameof(Order)} instances with same properties must be equal" + }; - Order sort = new("Name"); - yield return new object[] - { - sort, - sort, - true, - $"A {nameof(Order)} instance is equal to itself" - }; + Order sort = new("Name"); + yield return new object[] + { + sort, + sort, + true, + $"A {nameof(Order)} instance is equal to itself" + }; - yield return new object[] - { - new Order("Name", Descending), - new Order("Name"), - false, - $"Two distinct {nameof(Order)} instances with same {nameof(Order.Expression)} but different {nameof(Order.Direction)} must not be equal" - }; - } + yield return new object[] + { + new Order("Name", Descending), + new Order("Name"), + false, + $"Two distinct {nameof(Order)} instances with same {nameof(Order.Expression)} but different {nameof(Order.Direction)} must not be equal" + }; } + } - [Theory] - [MemberData(nameof(EqualsCases))] - public void EqualsTests(Order first, object second, bool expected, string reason) - { - outputHelper.WriteLine($"{nameof(first)} : '{first}'"); - outputHelper.WriteLine($"{nameof(second)} : '{second}'"); + [Theory] + [MemberData(nameof(EqualsCases))] + public void EqualsTests(Order first, object second, bool expected, string reason) + { + outputHelper.WriteLine($"{nameof(first)} : '{first}'"); + outputHelper.WriteLine($"{nameof(second)} : '{second}'"); - // Act - bool actual = first.Equals(second); - int actualHashCode = first.GetHashCode(); + // Act + bool actual = first.Equals(second); + int actualHashCode = first.GetHashCode(); - // Assert - actual.Should() - .Be(expected, reason); + // Assert + actual.Should() + .Be(expected, reason); - if (expected) - { - actualHashCode.Should() - .Be(second?.GetHashCode(), reason); - } - else - { - actualHashCode.Should() - .NotBe(second?.GetHashCode(), reason); - } + if (expected) + { + actualHashCode.Should() + .Be(second?.GetHashCode(), reason); + } + else + { + actualHashCode.Should() + .NotBe(second?.GetHashCode(), reason); } } } diff --git a/test/DataFilters.UnitTests/OrderValidatorTests.cs b/test/DataFilters.UnitTests/OrderValidatorTests.cs index 99cf30d4..b427ec7f 100644 --- a/test/DataFilters.UnitTests/OrderValidatorTests.cs +++ b/test/DataFilters.UnitTests/OrderValidatorTests.cs @@ -1,37 +1,36 @@ -namespace DataFilters.UnitTests -{ - using FluentAssertions; +namespace DataFilters.UnitTests; - using FluentValidation.Results; +using FluentAssertions; - using Xunit; - using Xunit.Categories; +using FluentValidation.Results; - [UnitTest] - [Feature("Ordering")] - public class OrderValidatorTests - { - private readonly OrderValidator _sut; +using Xunit; +using Xunit.Categories; - public OrderValidatorTests() - { - _sut = new(); - } +[UnitTest] +[Feature("Ordering")] +public class OrderValidatorTests +{ + private readonly OrderValidator _sut; - [Theory] - [InlineData("", false, "Empty order")] - [InlineData(" ", false, "Whitespace order")] - [InlineData(@"prop[""subprop""]", true, "order by a sub-property")] - [InlineData(@"prop[""subprop1""][""subprop2""]", true, "order by second level sub-property")] - [InlineData(@"+prop[""subprop1""][""subprop2""], -prop[""subprop1""][""subprop3""]", true, "order by a second level sub-property")] - public void Validate(string sort, bool expected, string reason) - { - // Act - ValidationResult validationResult = _sut.Validate(sort); + public OrderValidatorTests() + { + _sut = new(); + } + + [Theory] + [InlineData("", false, "Empty order")] + [InlineData(" ", false, "Whitespace order")] + [InlineData(@"prop[""subprop""]", true, "order by a sub-property")] + [InlineData(@"prop[""subprop1""][""subprop2""]", true, "order by second level sub-property")] + [InlineData(@"+prop[""subprop1""][""subprop2""], -prop[""subprop1""][""subprop3""]", true, "order by a second level sub-property")] + public void Validate(string sort, bool expected, string reason) + { + // Act + ValidationResult validationResult = _sut.Validate(sort); - // Assert - validationResult.IsValid.Should() - .Be(expected, reason); - } + // Assert + validationResult.IsValid.Should() + .Be(expected, reason); } } \ No newline at end of file diff --git a/test/DataFilters.UnitTests/StringExtensionsTests.cs b/test/DataFilters.UnitTests/StringExtensionsTests.cs index bf96364f..c51574b9 100644 --- a/test/DataFilters.UnitTests/StringExtensionsTests.cs +++ b/test/DataFilters.UnitTests/StringExtensionsTests.cs @@ -1,171 +1,170 @@ -namespace DataFilters.UnitTests +namespace DataFilters.UnitTests; + +using System; +using System.Collections.Generic; +using DataFilters.Casing; +using DataFilters.TestObjects; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using static DataFilters.OrderDirection; + +public class StringExtensionsTests(ITestOutputHelper outputHelper) { - using System; - using System.Collections.Generic; - using DataFilters.Casing; - using DataFilters.TestObjects; - using FluentAssertions; - using Xunit; - using Xunit.Abstractions; - using static DataFilters.OrderDirection; - - public class StringExtensionsTests(ITestOutputHelper outputHelper) + [Theory] + [InlineData(null, "sort expression cannot be null")] + [InlineData(" ", "sort expression cannot be whitespace only")] + public void Throws_ArgumentNullException_When_Parameter_IsNull(string source, string reason) { - [Theory] - [InlineData(null, "sort expression cannot be null")] - [InlineData(" ", "sort expression cannot be whitespace only")] - public void Throws_ArgumentNullException_When_Parameter_IsNull(string source, string reason) - { - // Act - Action action = () => source.ToSort(); - - // Assert - action.Should() - .ThrowExactly(reason) - .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName), $"{nameof(ArgumentOutOfRangeException.ParamName)} must not be null") - .Where(ex => !string.IsNullOrWhiteSpace(ex.Message), $"{nameof(ArgumentOutOfRangeException.Message)} must not be null"); - } + // Act + Action action = () => source.ToSort(); + + // Assert + action.Should() + .ThrowExactly(reason) + .Where(ex => !string.IsNullOrWhiteSpace(ex.ParamName), $"{nameof(ArgumentOutOfRangeException.ParamName)} must not be null") + .Where(ex => !string.IsNullOrWhiteSpace(ex.Message), $"{nameof(ArgumentOutOfRangeException.Message)} must not be null"); + } - [Theory] - [InlineData("prop1 prop2", "sort expression contains two properties that are not separated by a comma")] - [InlineData("prop1,prop2,", "sort expression cannot ends with a comma")] - [InlineData(",prop1,prop2", "sort expression cannot starts with a comma")] - [InlineData("--prop", "sort expression can start with only one hyphen")] - public void Given_invalid_expression_ToSort_throws_InvalidSortExpression(string invalidExpression, string reason) - { - // Act - Action action = () => invalidExpression.ToSort(); + [Theory] + [InlineData("prop1 prop2", "sort expression contains two properties that are not separated by a comma")] + [InlineData("prop1,prop2,", "sort expression cannot ends with a comma")] + [InlineData(",prop1,prop2", "sort expression cannot starts with a comma")] + [InlineData("--prop", "sort expression can start with only one hyphen")] + public void Given_invalid_expression_ToSort_throws_InvalidSortExpression(string invalidExpression, string reason) + { + // Act + Action action = () => invalidExpression.ToSort(); - // Assert - action.Should() - .ThrowExactly(reason) - .Where(ex => !string.IsNullOrWhiteSpace(ex.Message), $"{nameof(ArgumentOutOfRangeException.Message)} must not be null"); - } + // Assert + action.Should() + .ThrowExactly(reason) + .Where(ex => !string.IsNullOrWhiteSpace(ex.Message), $"{nameof(ArgumentOutOfRangeException.Message)} must not be null"); + } - public static IEnumerable ToSortCases + public static IEnumerable ToSortCases + { + get { - get + yield return new object[] { + nameof(SuperHero.Nickname), + new Order(expression : nameof(SuperHero.Nickname), direction : Ascending) + }; + yield return new object[] + { + $"+{nameof(SuperHero.Nickname)}", + new Order(expression : nameof(SuperHero.Nickname), direction : Ascending) + }; + + yield return new object[] + { + $"-{nameof(SuperHero.Nickname)}", + new Order(expression : nameof(SuperHero.Nickname), direction : Descending) + }; + + { + MultiOrder multiSort = new + ( + new Order(expression: nameof(SuperHero.Nickname)), + new Order(expression: nameof(SuperHero.Age)) + ); + yield return new object[] { - nameof(SuperHero.Nickname), - new Order(expression : nameof(SuperHero.Nickname), direction : Ascending) - }; - yield return new object[] - { - $"+{nameof(SuperHero.Nickname)}", - new Order(expression : nameof(SuperHero.Nickname), direction : Ascending) + $"{nameof(SuperHero.Nickname)},{nameof(SuperHero.Age)}", + multiSort }; + } + { + MultiOrder multiSort = new + ( + new Order(expression: nameof(SuperHero.Nickname)), + new Order(expression: nameof(SuperHero.Age), direction: Descending) + ); yield return new object[] { - $"-{nameof(SuperHero.Nickname)}", - new Order(expression : nameof(SuperHero.Nickname), direction : Descending) + $"+{nameof(SuperHero.Nickname)},-{nameof(SuperHero.Age)}", + multiSort }; - - { - MultiOrder multiSort = new - ( - new Order(expression: nameof(SuperHero.Nickname)), - new Order(expression: nameof(SuperHero.Age)) - ); - - yield return new object[] - { - $"{nameof(SuperHero.Nickname)},{nameof(SuperHero.Age)}", - multiSort - }; - } - { - MultiOrder multiSort = new - ( - new Order(expression: nameof(SuperHero.Nickname)), - new Order(expression: nameof(SuperHero.Age), direction: Descending) - ); - - yield return new object[] - { - $"+{nameof(SuperHero.Nickname)},-{nameof(SuperHero.Age)}", - multiSort - }; - } } } + } - [Theory] - [MemberData(nameof(ToSortCases))] - public void ToSortTests(string sort, IOrder expected) - { - outputHelper.WriteLine($"{nameof(sort)} : '{sort}'"); + [Theory] + [MemberData(nameof(ToSortCases))] + public void ToSortTests(string sort, IOrder expected) + { + outputHelper.WriteLine($"{nameof(sort)} : '{sort}'"); - // Act - IOrder actual = sort.ToSort(); + // Act + IOrder actual = sort.ToSort(); - outputHelper.WriteLine($"actual sort : '{actual}'"); + outputHelper.WriteLine($"actual sort : '{actual}'"); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); + } - public static IEnumerable ToSortWithPropertyNameResolutionStrategyCases + public static IEnumerable ToSortWithPropertyNameResolutionStrategyCases + { + get { - get + yield return new object[] { - yield return new object[] - { - "-SnakeCaseProperty", - PropertyNameResolutionStrategy.SnakeCase, - new Order("snake_case_property", Descending) - }; + "-SnakeCaseProperty", + PropertyNameResolutionStrategy.SnakeCase, + new Order("snake_case_property", Descending) + }; - yield return new object[] - { - "SnakeCaseProperty", - PropertyNameResolutionStrategy.SnakeCase, - new Order("snake_case_property", Ascending) - }; + yield return new object[] + { + "SnakeCaseProperty", + PropertyNameResolutionStrategy.SnakeCase, + new Order("snake_case_property", Ascending) + }; - yield return new object[] - { - "-SnakeCaseProperty", - PropertyNameResolutionStrategy.SnakeCase, - new Order("snake_case_property", Descending) - }; + yield return new object[] + { + "-SnakeCaseProperty", + PropertyNameResolutionStrategy.SnakeCase, + new Order("snake_case_property", Descending) + }; - yield return new object[] - { - "pascal_case_property", - PropertyNameResolutionStrategy.PascalCase, - new Order("PascalCaseProperty", Ascending) - }; + yield return new object[] + { + "pascal_case_property", + PropertyNameResolutionStrategy.PascalCase, + new Order("PascalCaseProperty", Ascending) + }; - yield return new object[] - { - "+pascal_case_property", - PropertyNameResolutionStrategy.PascalCase, - new Order("PascalCaseProperty", Ascending) - }; + yield return new object[] + { + "+pascal_case_property", + PropertyNameResolutionStrategy.PascalCase, + new Order("PascalCaseProperty", Ascending) + }; - yield return new object[] - { - "-pascal_case_property", - PropertyNameResolutionStrategy.PascalCase, - new Order("PascalCaseProperty", Descending) - }; - } + yield return new object[] + { + "-pascal_case_property", + PropertyNameResolutionStrategy.PascalCase, + new Order("PascalCaseProperty", Descending) + }; } + } - [Theory] - [MemberData(nameof(ToSortWithPropertyNameResolutionStrategyCases))] - public void Given_input_and_PropertyNameResolutionStrategy_ToSort_should_return_appropriate_ISort_instance(string sort, PropertyNameResolutionStrategy propertyNameResolutionStrategy, IOrder expected) - { - // Act - IOrder actual = sort.ToOrder(propertyNameResolutionStrategy); + [Theory] + [MemberData(nameof(ToSortWithPropertyNameResolutionStrategyCases))] + public void Given_input_and_PropertyNameResolutionStrategy_ToSort_should_return_appropriate_ISort_instance(string sort, PropertyNameResolutionStrategy propertyNameResolutionStrategy, IOrder expected) + { + // Act + IOrder actual = sort.ToOrder(propertyNameResolutionStrategy); - // Assert - actual.Should() - .Be(expected); - } + // Assert + actual.Should() + .Be(expected); } } diff --git a/test/DataFilters.UnitTests/Validators/OrderValidatorTests.cs b/test/DataFilters.UnitTests/Validators/OrderValidatorTests.cs index 3c987a4b..0b649f0d 100644 --- a/test/DataFilters.UnitTests/Validators/OrderValidatorTests.cs +++ b/test/DataFilters.UnitTests/Validators/OrderValidatorTests.cs @@ -1,40 +1,39 @@ -namespace DataFilters.UnitTests.Validators -{ - using FluentAssertions; +namespace DataFilters.UnitTests.Validators; - using FluentValidation.Results; +using FluentAssertions; - using Xunit; +using FluentValidation.Results; - public class OrderValidatorTests - { - private readonly OrderValidator _sut; +using Xunit; - public OrderValidatorTests() => _sut = new OrderValidator(); +public class OrderValidatorTests +{ + private readonly OrderValidator _sut; - [Theory] - [InlineData("prop", true, "'prop' is a valid sort expression")] - [InlineData("+prop", true, "'+prop' is a valid sort expression")] - [InlineData("-prop", true, "'-prop' is a valid sort expression")] - [InlineData("--prop", false, "'--prop' is not valid because it starts with two consecutive hyphens")] - [InlineData("++prop", false, "'++prop' is not valid because it starts with two consecutive '+'")] - [InlineData("+ + prop", false, "'+ + prop' is not valid because it starts with two consecutive '+'")] - [InlineData(" ", false, "' ' is not valid because it contains only whitespace")] - [InlineData("+ ", false, "'+ ' is not valid because it contains only a '+' followed by whitespaces")] - [InlineData("- ", false, "'- ' is not valid because it contains only a '-' followed by whitespaces")] - [InlineData("prop1,prop2", true, "'prop1,prop2' is not valid because it contains only a '-' followed by whitespaces")] - [InlineData("prop1, prop2", true, "'prop1, prop2' is valid because it contains two properties separated by a comma")] - [InlineData("prop1 prop2", false, "'prop1 prop2' is not valid because it contains two properties separated by a whitespace")] - [InlineData(",prop1,prop2", false, "',prop1,prop2' is not valid because it starts with a ','")] - [InlineData("prop1,prop2,", false, "'prop1,prop2,' is not valid because it ends with a ','")] - public void IsValid(string sort, bool expected, string reason) - { - // Act - ValidationResult actual = _sut.Validate(sort); + public OrderValidatorTests() => _sut = new OrderValidator(); + + [Theory] + [InlineData("prop", true, "'prop' is a valid sort expression")] + [InlineData("+prop", true, "'+prop' is a valid sort expression")] + [InlineData("-prop", true, "'-prop' is a valid sort expression")] + [InlineData("--prop", false, "'--prop' is not valid because it starts with two consecutive hyphens")] + [InlineData("++prop", false, "'++prop' is not valid because it starts with two consecutive '+'")] + [InlineData("+ + prop", false, "'+ + prop' is not valid because it starts with two consecutive '+'")] + [InlineData(" ", false, "' ' is not valid because it contains only whitespace")] + [InlineData("+ ", false, "'+ ' is not valid because it contains only a '+' followed by whitespaces")] + [InlineData("- ", false, "'- ' is not valid because it contains only a '-' followed by whitespaces")] + [InlineData("prop1,prop2", true, "'prop1,prop2' is not valid because it contains only a '-' followed by whitespaces")] + [InlineData("prop1, prop2", true, "'prop1, prop2' is valid because it contains two properties separated by a comma")] + [InlineData("prop1 prop2", false, "'prop1 prop2' is not valid because it contains two properties separated by a whitespace")] + [InlineData(",prop1,prop2", false, "',prop1,prop2' is not valid because it starts with a ','")] + [InlineData("prop1,prop2,", false, "'prop1,prop2,' is not valid because it ends with a ','")] + public void IsValid(string sort, bool expected, string reason) + { + // Act + ValidationResult actual = _sut.Validate(sort); - // Assert - actual.IsValid.Should() - .Be(expected, reason); - } + // Assert + actual.IsValid.Should() + .Be(expected, reason); } }