From 6d52b103b09d2694ea71ee1f547780850a2c17e4 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Tue, 29 Apr 2025 15:42:18 +0100 Subject: [PATCH 1/6] Initial Code Migration --- .dockerignore | 25 +++ .github/dependabot.yml | 6 + .github/workflows/dotnet.yml | 67 ++++++++ .idea/.gitignore | 13 ++ .../.idea/.gitignore | 13 ++ .../.idea/.name | 1 + .../.idea/indexLayout.xml | 8 + .../.idea/vcs.xml | 6 + .idea/indexLayout.xml | 8 + .idea/vcs.xml | 6 + AStar.Dev.Functional.Extensions.sln | 36 +++++ AStar.ico | Bin 0 -> 11562 bytes AStar.png | Bin 0 -> 12513 bytes build-and-test.ps1 | 3 + .../AStar.Dev.Functional.Extensions.csproj | 51 ++++++ .../AStar.Dev.Functional.Extensions.xml | 148 ++++++++++++++++++ src/AStar.Dev.Functional.Extensions/AStar.ico | Bin 0 -> 11562 bytes src/AStar.Dev.Functional.Extensions/AStar.png | Bin 0 -> 12513 bytes .../EnumerableExtensions.cs | 20 +++ src/AStar.Dev.Functional.Extensions/LICENSE | 21 +++ src/AStar.Dev.Functional.Extensions/None.cs | 22 +++ .../None{T}.cs | 17 ++ src/AStar.Dev.Functional.Extensions/Option.cs | 20 +++ .../OptionExtensions.cs | 53 +++++++ .../Option{T}.cs | 25 +++ src/AStar.Dev.Functional.Extensions/Readme.md | 1 + src/AStar.Dev.Functional.Extensions/Some.cs | 28 ++++ ...ev.Functional.Extensions.Tests.Unit.csproj | 51 ++++++ .../EnumerableExtensionsShould.cs | 23 +++ .../GenericNoneShould.cs | 13 ++ .../MockClass.cs | 16 ++ .../NoneShould.cs | 19 +++ .../OptionExtensionsShould.cs | 87 ++++++++++ .../OptionShould.cs | 19 +++ .../SomeShould.cs | 39 +++++ 35 files changed, 865 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/dependabot.yml create mode 100644 .github/workflows/dotnet.yml create mode 100644 .idea/.gitignore create mode 100644 .idea/.idea.AStar.Dev.Functional.Extensions/.idea/.gitignore create mode 100644 .idea/.idea.AStar.Dev.Functional.Extensions/.idea/.name create mode 100644 .idea/.idea.AStar.Dev.Functional.Extensions/.idea/indexLayout.xml create mode 100644 .idea/.idea.AStar.Dev.Functional.Extensions/.idea/vcs.xml create mode 100644 .idea/indexLayout.xml create mode 100644 .idea/vcs.xml create mode 100644 AStar.Dev.Functional.Extensions.sln create mode 100644 AStar.ico create mode 100644 AStar.png create mode 100644 build-and-test.ps1 create mode 100644 src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj create mode 100644 src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.xml create mode 100644 src/AStar.Dev.Functional.Extensions/AStar.ico create mode 100644 src/AStar.Dev.Functional.Extensions/AStar.png create mode 100644 src/AStar.Dev.Functional.Extensions/EnumerableExtensions.cs create mode 100644 src/AStar.Dev.Functional.Extensions/LICENSE create mode 100644 src/AStar.Dev.Functional.Extensions/None.cs create mode 100644 src/AStar.Dev.Functional.Extensions/None{T}.cs create mode 100644 src/AStar.Dev.Functional.Extensions/Option.cs create mode 100644 src/AStar.Dev.Functional.Extensions/OptionExtensions.cs create mode 100644 src/AStar.Dev.Functional.Extensions/Option{T}.cs create mode 100644 src/AStar.Dev.Functional.Extensions/Readme.md create mode 100644 src/AStar.Dev.Functional.Extensions/Some.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/EnumerableExtensionsShould.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/GenericNoneShould.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/MockClass.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/NoneShould.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionExtensionsShould.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionShould.cs create mode 100644 test/AStar.Dev.Functional.Extensions.Tests.Unit/SomeShould.cs diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..cd967fc --- /dev/null +++ b/.dockerignore @@ -0,0 +1,25 @@ +**/.dockerignore +**/.env +**/.git +**/.gitignore +**/.project +**/.settings +**/.toolstarget +**/.vs +**/.vscode +**/.idea +**/*.*proj.user +**/*.dbmdl +**/*.jfm +**/azds.yaml +**/bin +**/charts +**/docker-compose* +**/Dockerfile* +**/node_modules +**/npm-debug.log +**/obj +**/secrets.dev.yaml +**/values.dev.yaml +LICENSE +README.md \ No newline at end of file diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..446b951 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "nuget" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml new file mode 100644 index 0000000..c88980a --- /dev/null +++ b/.github/workflows/dotnet.yml @@ -0,0 +1,67 @@ +name: .NET + +on: + workflow_dispatch: + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + +jobs: + build: + name: Build and analyze + runs-on: windows-latest + steps: + - name: Set up JDK + uses: actions/setup-java@v4.4.0 + with: + java-version: 17 + distribution: 'zulu' + + - name: Checkout + uses: actions/checkout@v4.2.1 + with: + fetch-depth: 0 + + - name: Cache SonarCloud packages + uses: actions/cache@v4.2.3 + with: + path: ~\sonar\cache + key: ${{ runner.os }}-sonar + restore-keys: ${{ runner.os }}-sonar + + - name: Cache SonarCloud scanner + id: cache-sonar-scanner + uses: actions/cache@v4.2.3 + with: + path: .\.sonar\scanner + key: ${{ runner.os }}-sonar-scanner + restore-keys: ${{ runner.os }}-sonar-scanner + + - name: Install SonarCloud scanner + if: steps.cache-sonar-scanner.outputs.cache-hit != 'true' + shell: powershell + run: | + New-Item -Path .\.sonar\scanner -ItemType Directory + dotnet tool update dotnet-sonarscanner --tool-path .\.sonar\scanner + + - name: Build and analyze + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} + shell: powershell + run: | + dotnet tool install --global dotnet-coverage + .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_astar-dev-utilities" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml + dotnet build --configuration Release + dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Acceptance.Tests"' -f xml -o 'coverage.xml' + .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" + + - name: Pack NuGet package + if: github.ref == 'refs/heads/main' + run: dotnet pack .\src\AStar.Dev.Utilities\AStar.Dev.Utilities.csproj + + - name: Push to NuGet + if: github.ref == 'refs/heads/main' + run: dotnet nuget push "**\AStar.Dev.Utilities.*.nupkg" --api-key ${{secrets.nuget_api_key}} --skip-duplicate --source https://api.nuget.org/v3/index.json + diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..2182218 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/projectSettingsUpdater.xml +/.idea.astar-dev-functional-extensions.iml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/.gitignore b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/.gitignore new file mode 100644 index 0000000..de8bd97 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/.gitignore @@ -0,0 +1,13 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Rider ignored files +/contentModel.xml +/.idea.AStar.Dev.Functional.Extensions.iml +/projectSettingsUpdater.xml +/modules.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/.name b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/.name new file mode 100644 index 0000000..be440be --- /dev/null +++ b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/.name @@ -0,0 +1 @@ +AStar.Dev.Functional.Extensions \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/indexLayout.xml b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/vcs.xml b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/.idea.AStar.Dev.Functional.Extensions/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/indexLayout.xml b/.idea/indexLayout.xml new file mode 100644 index 0000000..7b08163 --- /dev/null +++ b/.idea/indexLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/AStar.Dev.Functional.Extensions.sln b/AStar.Dev.Functional.Extensions.sln new file mode 100644 index 0000000..60a1881 --- /dev/null +++ b/AStar.Dev.Functional.Extensions.sln @@ -0,0 +1,36 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.31903.59 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8456212E-F453-483A-8E27-7494722AE10F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{3C168EA0-2CFB-490A-8E3A-BE08F456DD3F}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions", "src\AStar.Dev.Functional.Extensions\AStar.Dev.Functional.Extensions.csproj", "{3FC5E2DB-6C2C-4D72-8498-39A121722334}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions.Tests.Unit", "test\AStar.Dev.Functional.Extensions.Tests.Unit\AStar.Dev.Functional.Extensions.Tests.Unit.csproj", "{F61660A7-160E-4654-94A5-F8D5D75FFBD6}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {3FC5E2DB-6C2C-4D72-8498-39A121722334} = {8456212E-F453-483A-8E27-7494722AE10F} + {F61660A7-160E-4654-94A5-F8D5D75FFBD6} = {3C168EA0-2CFB-490A-8E3A-BE08F456DD3F} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3FC5E2DB-6C2C-4D72-8498-39A121722334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FC5E2DB-6C2C-4D72-8498-39A121722334}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FC5E2DB-6C2C-4D72-8498-39A121722334}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FC5E2DB-6C2C-4D72-8498-39A121722334}.Release|Any CPU.Build.0 = Release|Any CPU + {F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F61660A7-160E-4654-94A5-F8D5D75FFBD6}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection +EndGlobal diff --git a/AStar.ico b/AStar.ico new file mode 100644 index 0000000000000000000000000000000000000000..0b9aea493d206b4ce2c3a43915458e568ca18ec4 GIT binary patch literal 11562 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 literal 0 HcmV?d00001 diff --git a/AStar.png b/AStar.png new file mode 100644 index 0000000000000000000000000000000000000000..8cac0a1d8136c82d5b582bdea30f24bf2e085e0f GIT binary patch literal 12513 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 MOs&qBpL2=*KT7L~lmGw# literal 0 HcmV?d00001 diff --git a/build-and-test.ps1 b/build-and-test.ps1 new file mode 100644 index 0000000..aad68a7 --- /dev/null +++ b/build-and-test.ps1 @@ -0,0 +1,3 @@ +dotnet build +dotnet test +dotnet stryker --open-report --break-at 99 \ No newline at end of file diff --git a/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj b/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj new file mode 100644 index 0000000..1ea161b --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.csproj @@ -0,0 +1,51 @@ + + + + latest-recommended + AStar Developement, Jason Barden + AStar Development + AStar Developement, 2025 + AStar.Dev.Functional.Extensions package contains some basic implementations of Option, Some and None. This list may expand over time. + $(AssemblyName).xml + True + True + true + enable + True + true + enable + AStar.png + LICENSE + https://github.com/astar-development/astar-dev-functional-extensions/ + Readme.md + Update. + True + git + https://github.com/astar-development/astar-dev-functional-extensions.git + snupkg + net9.0 + AStar.Dev.Functional.Extensions + 0.1.0 + + + + + + + + + + + + + + True + 1701;1702; + + + + True + 1701;1702; + + + diff --git a/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.xml b/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.xml new file mode 100644 index 0000000..034a31a --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/AStar.Dev.Functional.Extensions.xml @@ -0,0 +1,148 @@ + + + + AStar.Dev.Functional.Extensions + + + + + + + + + + + + + + + + + The class that exists for when we have no object + + + + + The Value will always return an instance of , it can never return an actual value + + + + + The Of{T} method can be used to return an instance of the generic when there is no actual value to return + + The type of the original object that this instance of is replacing + The appropriate type of + + + + The class replaces the object when there is no object available + + The type of the original object + + + + The ToString method is overridden to always return "None" + + "None" no matter what the original type was + + + + The class that contains the original object + + + + + The Optional method will convert the 'raw' object to an + + The type of the original object + The object to return as an option + The original object as an option (and implemented as an instance of + + + + The class contains a basic set of extension methods to help map, filter, etc. the original object + + + + + The Map{T,TResult} method will either map the Some{T} to a new Some or return a new None of the specified result type + + The type of the source object + The type of object expected from the map + The object to map + The Map function + Either a new Some or a new None of the specified type + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + The generic class contains the result for a successful method + + The type of the return object + + + The content (actual result) object + + + + The generic class contains the result for a successful method + + The type of the return object + + + The content (actual result) object + + + + The content (actual result) object + + + + + Overrides the default ToString to return the object type or <null> + + + Once the ToJson method (in AStar.Dev.Utilities) respects the 'Mask', 'Ignore' etc. attributes, we can reconsider whether this method returns the actual object + + The object type name or null. + + + diff --git a/src/AStar.Dev.Functional.Extensions/AStar.ico b/src/AStar.Dev.Functional.Extensions/AStar.ico new file mode 100644 index 0000000000000000000000000000000000000000..0b9aea493d206b4ce2c3a43915458e568ca18ec4 GIT binary patch literal 11562 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 literal 0 HcmV?d00001 diff --git a/src/AStar.Dev.Functional.Extensions/AStar.png b/src/AStar.Dev.Functional.Extensions/AStar.png new file mode 100644 index 0000000000000000000000000000000000000000..8cac0a1d8136c82d5b582bdea30f24bf2e085e0f GIT binary patch literal 12513 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 MOs&qBpL2=*KT7L~lmGw# literal 0 HcmV?d00001 diff --git a/src/AStar.Dev.Functional.Extensions/EnumerableExtensions.cs b/src/AStar.Dev.Functional.Extensions/EnumerableExtensions.cs new file mode 100644 index 0000000..9c97003 --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/EnumerableExtensions.cs @@ -0,0 +1,20 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// +public static class EnumerableExtensions +{ + /// + /// + /// + /// + /// + /// + public static Option FirstOrNone(this IEnumerable sequence, Func predicate) + { + return sequence.Where(predicate) + .Select>(x => x) + .DefaultIfEmpty(None.Value) + .First(); + } +} diff --git a/src/AStar.Dev.Functional.Extensions/LICENSE b/src/AStar.Dev.Functional.Extensions/LICENSE new file mode 100644 index 0000000..0b1b024 --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 AStar Development, Jason Barden + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/AStar.Dev.Functional.Extensions/None.cs b/src/AStar.Dev.Functional.Extensions/None.cs new file mode 100644 index 0000000..ae933a4 --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/None.cs @@ -0,0 +1,22 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// The class that exists for when we have no object +/// +public sealed class None +{ + /// + /// The Value will always return an instance of , it can never return an actual value + /// + public static None Value { get; } = new(); + + /// + /// The Of{T} method can be used to return an instance of the generic when there is no actual value to return + /// + /// The type of the original object that this instance of is replacing + /// The appropriate type of + public static None Of() + { + return new(); + } +} diff --git a/src/AStar.Dev.Functional.Extensions/None{T}.cs b/src/AStar.Dev.Functional.Extensions/None{T}.cs new file mode 100644 index 0000000..120e54d --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/None{T}.cs @@ -0,0 +1,17 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// The class replaces the object when there is no object available +/// +/// The type of the original object +public sealed class None : Option +{ + /// + /// The ToString method is overridden to always return "None" + /// + /// "None" no matter what the original type was + public override string ToString() + { + return "None"; + } +} diff --git a/src/AStar.Dev.Functional.Extensions/Option.cs b/src/AStar.Dev.Functional.Extensions/Option.cs new file mode 100644 index 0000000..b3cbb4b --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/Option.cs @@ -0,0 +1,20 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// The class that contains the original object +/// +#pragma warning disable CA1716 // Rename type Option so that it no longer conflicts with the reserved language keyword 'Option' +public static class Option +#pragma warning restore CA1716 +{ + /// + /// The Optional method will convert the 'raw' object to an + /// + /// The type of the original object + /// The object to return as an option + /// The original object as an option (and implemented as an instance of + public static Option Optional(this T obj) + { + return new Some(obj); + } +} diff --git a/src/AStar.Dev.Functional.Extensions/OptionExtensions.cs b/src/AStar.Dev.Functional.Extensions/OptionExtensions.cs new file mode 100644 index 0000000..5c3b401 --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/OptionExtensions.cs @@ -0,0 +1,53 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// The class contains a basic set of extension methods to help map, filter, etc. the original object +/// +public static class OptionExtensions +{ + /// + /// The Map{T,TResult} method will either map the Some{T} to a new Some or return a new None of the specified result type + /// + /// The type of the source object + /// The type of object expected from the map + /// The object to map + /// The Map function + /// Either a new Some or a new None of the specified type + public static Option Map(this Option obj, Func map) + { + return obj is Some some ? new Some(map(some.Content)) : new None(); + } + + /// + /// + /// + /// + /// + /// + public static Option Filter(this Option obj, Func predicate) + { + return obj is Some some && !predicate(some.Content) ? new None() : obj; + } + + /// + /// + /// + /// + /// + /// + public static T Reduce(this Option obj, T substitute) + { + return obj is Some some ? some.Content : substitute; + } + + /// + /// + /// + /// + /// + /// + public static T Reduce(this Option obj, Func substitute) + { + return obj is Some some ? some.Content : substitute(); + } +} diff --git a/src/AStar.Dev.Functional.Extensions/Option{T}.cs b/src/AStar.Dev.Functional.Extensions/Option{T}.cs new file mode 100644 index 0000000..794828d --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/Option{T}.cs @@ -0,0 +1,25 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// +/// +#pragma warning disable CA1716 // Rename type Option so that it no longer conflicts with the reserved language keyword 'Option' +public abstract class Option +#pragma warning restore CA1716 +{ + /// + /// + /// + public static implicit operator Option(None _) + { + return new None(); + } + + /// + /// + /// + public static implicit operator Option(T value) + { + return new Some(value); + } +} diff --git a/src/AStar.Dev.Functional.Extensions/Readme.md b/src/AStar.Dev.Functional.Extensions/Readme.md new file mode 100644 index 0000000..16dacd1 --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/Readme.md @@ -0,0 +1 @@ +Update \ No newline at end of file diff --git a/src/AStar.Dev.Functional.Extensions/Some.cs b/src/AStar.Dev.Functional.Extensions/Some.cs new file mode 100644 index 0000000..53540f6 --- /dev/null +++ b/src/AStar.Dev.Functional.Extensions/Some.cs @@ -0,0 +1,28 @@ +namespace AStar.Dev.Functional.Extensions; + +/// +/// The generic class contains the result for a successful method +/// +/// The type of the return object +/// +/// +/// The content (actual result) object +public sealed class Some(T content) : Option +{ + /// + /// The content (actual result) object + /// + public T Content { get; } = content; + + /// + /// Overrides the default ToString to return the object type or <null> + /// + /// + /// Once the ToJson method (in AStar.Dev.Utilities) respects the 'Mask', 'Ignore' etc. attributes, we can reconsider whether this method returns the actual object + /// + /// The object type name or null. + public override string ToString() + { + return Content?.ToString() ?? ""; + } +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj b/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj new file mode 100644 index 0000000..5556a7d --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/AStar.Dev.Functional.Extensions.Tests.Unit.csproj @@ -0,0 +1,51 @@ + + + + net9.0 + enable + enable + false + AStar.Dev.Functional.Extensions + True + latest-recommended + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + True + 1701;1702;IDE0058; + + + + True + 1701;1702;IDE0058; + + + diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/EnumerableExtensionsShould.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/EnumerableExtensionsShould.cs new file mode 100644 index 0000000..162c82b --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/EnumerableExtensionsShould.cs @@ -0,0 +1,23 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Functional.Extensions; + +[TestSubject(typeof(EnumerableExtensions))] +public class EnumerableExtensionsShould +{ + [Fact] + public void ReturnTheExpectedFirstObject() + { + var list = new List { 1, 2, 3 }; + + list.FirstOrNone(i => i == 2).ShouldBeAssignableTo>(); + } + + [Fact] + public void ReturnTheExpectedNoneObject() + { + var list = new List { 1, 2, 3 }; + + list.FirstOrNone(i => i > 3).ShouldBeAssignableTo>(); + } +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/GenericNoneShould.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/GenericNoneShould.cs new file mode 100644 index 0000000..484b63c --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/GenericNoneShould.cs @@ -0,0 +1,13 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Functional.Extensions; + +[TestSubject(typeof(None<>))] +public class GenericNoneShould +{ + [Fact] + public void OverrideToStringAsExpected() + { + None.Of().ToString().ShouldBe("None"); + } +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/MockClass.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/MockClass.cs new file mode 100644 index 0000000..0ed5353 --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/MockClass.cs @@ -0,0 +1,16 @@ +namespace AStar.Dev.Functional.Extensions; + +internal sealed class MockClass +{ + public string SomeText { get; set; } = "SomeText"; + + public static MockClass Create() + { + return new() { SomeText = "MockClassCreate" }; + } +} + +internal sealed class MockClass2 +{ + public string SomeText { get; set; } = "SomeText"; +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/NoneShould.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/NoneShould.cs new file mode 100644 index 0000000..0e97bd1 --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/NoneShould.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Functional.Extensions; + +[TestSubject(typeof(None))] +public class NoneShould +{ + [Fact] + public void ReturnNone() + { + None.Value.ShouldBeAssignableTo(); + } + + [Fact] + public void ReturnNoneOf() + { + None.Of().ShouldBeAssignableTo>(); + } +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionExtensionsShould.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionExtensionsShould.cs new file mode 100644 index 0000000..fd0a3a9 --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionExtensionsShould.cs @@ -0,0 +1,87 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Functional.Extensions; + +[TestSubject(typeof(OptionExtensions))] +public class OptionExtensionsShould +{ + [Fact] + public void MapTheSomeObjectToTheNewSomeObject() + { + var obj = new Some(new ()); + + var result = obj.Map(mockClass => new MockClass2 { SomeText = mockClass.SomeText }); + + result.ShouldBeAssignableTo>(); + } + + [Fact] + public void MapTheNoneObjectToTheNewNone() + { + var obj = new None(); + + var result = obj.Map(mockClass => new MockClass2 { SomeText = mockClass.SomeText }); + + result.ShouldBeAssignableTo>(); + } + + [Fact] + public void FilterTheSomeOptionBasedOnThePredicate() + { + var list = new List { 1, 2, 3, 4, 5 }.Optional(); + + var result = list.Filter(item => item.Contains(1)); + + result.ShouldBeAssignableTo>>(); + } + + [Fact] + public void FilterTheSomeOptionBasedOnThePredicateToNone() + { + var list = new List { 1, 2, 3, 4, 5 }.Optional(); + + var result = list.Filter(item => item.Contains(1234)); + + result.ShouldBeAssignableTo>>(); + } + + [Fact] + public void ReduceTheSomeContentToContainingType() + { + var obj = new Some(new () { SomeText = "SomeText" }); + + var result = obj.Reduce(new MockClass { SomeText = "SubstituteSomeText" }); + + result.SomeText.ShouldBe("SomeText"); + } + + [Fact] + public void ReduceTheSomeContentToContainingTypeUsingTheSubstituteObject() + { + var obj = new None(); + + var result = obj.Reduce(new MockClass { SomeText = "SubstituteSomeText" }); + + result.SomeText.ShouldBe("SubstituteSomeText"); + } + + [Fact] + public void ReduceTheSomeContentToTheContainingType() + { + var obj = new Some(new () { SomeText = "SomeText" }); + + var result = obj.Reduce(MockClass.Create); + + result.SomeText.ShouldBe("SomeText"); + } + + [Fact] + public void ReduceTheSomeContentToContainingTypeUsingTheSubstituteFunction() + { + var obj = new None(); + + var result = obj.Reduce(MockClass.Create); + + result.SomeText.ShouldBe("MockClassCreate"); + } +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionShould.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionShould.cs new file mode 100644 index 0000000..5d79499 --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/OptionShould.cs @@ -0,0 +1,19 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Functional.Extensions; + +[TestSubject(typeof(Option))] +public class OptionShould +{ + [Fact] + public void ReturnTheSpecifiedObjectAsAnOptionalObject() + { + new MockClass().Optional().ShouldBeAssignableTo>(); + } + + [Fact] + public void ReturnTheSpecifiedObjectAsAnInstanceOfTheSomeClass() + { + new MockClass().Optional().ShouldBeAssignableTo>(); + } +} diff --git a/test/AStar.Dev.Functional.Extensions.Tests.Unit/SomeShould.cs b/test/AStar.Dev.Functional.Extensions.Tests.Unit/SomeShould.cs new file mode 100644 index 0000000..435edb8 --- /dev/null +++ b/test/AStar.Dev.Functional.Extensions.Tests.Unit/SomeShould.cs @@ -0,0 +1,39 @@ +using JetBrains.Annotations; + +namespace AStar.Dev.Functional.Extensions; + +[TestSubject(typeof(Some<>))] +public class SomeShould +{ + [Fact] + public void ReturnTheExpectedStringContent() + { + var sut = new Some("Test"); + + sut.Content.ShouldBe("Test"); + } + + [Fact] + public void ReturnTheExpectedMockClassContent() + { + var sut = new Some(new ()); + + sut.Content.ShouldBeEquivalentTo(new MockClass()); + } + + [Fact] + public void ReturnTheExpectedToStringForTheObjectType() + { + var sut = new Some(new ()); + + sut.ToString().ShouldBe("AStar.Dev.Functional.Extensions.MockClass"); + } + + [Fact] + public void ReturnTheExpectedToStringForTheNull() + { + var sut = new Some(null!); + + sut.ToString().ShouldBe(""); + } +} From cc68930008d95d45995a9cbc9220b65b6e29ee25 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Tue, 29 Apr 2025 15:49:37 +0100 Subject: [PATCH 2/6] Add /d:sonar.scanner.scanAll=false The above is from the GitHub log --- .github/workflows/dotnet.yml | 10 +++++++--- AStar.Dev.Functional.Extensions.sln | 11 +++++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index c88980a..da27bfa 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -11,6 +11,9 @@ jobs: build: name: Build and analyze runs-on: windows-latest + env: + ProjectName: 'AStar.Dev.Functional.Extensions' + RepositoryName: 'astar-dev-functional-extensions' steps: - name: Set up JDK uses: actions/setup-java@v4.4.0 @@ -52,16 +55,17 @@ jobs: shell: powershell run: | dotnet tool install --global dotnet-coverage - .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_astar-dev-utilities" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml + .\.sonar\scanner\dotnet-sonarscanner begin /d:sonar.scanner.scanAll=false /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host + .url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml dotnet build --configuration Release dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Acceptance.Tests"' -f xml -o 'coverage.xml' .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" - name: Pack NuGet package if: github.ref == 'refs/heads/main' - run: dotnet pack .\src\AStar.Dev.Utilities\AStar.Dev.Utilities.csproj + run: dotnet pack .\src\${{ env.ProjectName }}\${{ env.ProjectName }}.csproj - name: Push to NuGet if: github.ref == 'refs/heads/main' - run: dotnet nuget push "**\AStar.Dev.Utilities.*.nupkg" --api-key ${{secrets.nuget_api_key}} --skip-duplicate --source https://api.nuget.org/v3/index.json + run: dotnet nuget push "**\${{ env.ProjectName }}.*.nupkg" --api-key ${{secrets.nuget_api_key}} --skip-duplicate --source https://api.nuget.org/v3/index.json diff --git a/AStar.Dev.Functional.Extensions.sln b/AStar.Dev.Functional.Extensions.sln index 60a1881..2d11f8b 100644 --- a/AStar.Dev.Functional.Extensions.sln +++ b/AStar.Dev.Functional.Extensions.sln @@ -11,6 +11,16 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extens EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AStar.Dev.Functional.Extensions.Tests.Unit", "test\AStar.Dev.Functional.Extensions.Tests.Unit\AStar.Dev.Functional.Extensions.Tests.Unit.csproj", "{F61660A7-160E-4654-94A5-F8D5D75FFBD6}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{419B9C55-6F41-4ED8-B87C-855B23E01C41}" + ProjectSection(SolutionItems) = preProject + .github\dependabot.yml = .github\dependabot.yml + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{2BB3AD42-1BED-4792-8E37-E87783AE949C}" + ProjectSection(SolutionItems) = preProject + .github\workflows\dotnet.yml = .github\workflows\dotnet.yml + EndProjectSection +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -22,6 +32,7 @@ Global GlobalSection(NestedProjects) = preSolution {3FC5E2DB-6C2C-4D72-8498-39A121722334} = {8456212E-F453-483A-8E27-7494722AE10F} {F61660A7-160E-4654-94A5-F8D5D75FFBD6} = {3C168EA0-2CFB-490A-8E3A-BE08F456DD3F} + {2BB3AD42-1BED-4792-8E37-E87783AE949C} = {419B9C55-6F41-4ED8-B87C-855B23E01C41} EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {3FC5E2DB-6C2C-4D72-8498-39A121722334}.Debug|Any CPU.ActiveCfg = Debug|Any CPU From 2514f5e00de93afed1ea8bd2d34d21b6c3707434 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Tue, 29 Apr 2025 15:56:09 +0100 Subject: [PATCH 3/6] Change the way the scallAll is turned off https://david.gardiner.net.au/2024/12/sonarcloud says the log message was incorrect so trying the version in the link --- .github/workflows/dotnet.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index da27bfa..f6baf6a 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,7 +55,8 @@ jobs: shell: powershell run: | dotnet tool install --global dotnet-coverage - .\.sonar\scanner\dotnet-sonarscanner begin /d:sonar.scanner.scanAll=false /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host + sonar.scanner.scanAll=false + .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host .url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml dotnet build --configuration Release dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Acceptance.Tests"' -f xml -o 'coverage.xml' From dd094c062e75b90afbeba5fdb56b8ba6cc2abd6c Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Tue, 29 Apr 2025 15:58:18 +0100 Subject: [PATCH 4/6] Removing the scanAll suppression until another time --- .github/workflows/dotnet.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index f6baf6a..ae0b3ce 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,7 +55,6 @@ jobs: shell: powershell run: | dotnet tool install --global dotnet-coverage - sonar.scanner.scanAll=false .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host .url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml dotnet build --configuration Release From 232ef1e03ed16aa4774adca73555d29c40140ee4 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Tue, 29 Apr 2025 16:02:28 +0100 Subject: [PATCH 5/6] OK, as it failed again, build error spotted so have returned the original d:sonar.scanner.scanAll=false --- .github/workflows/dotnet.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index ae0b3ce..3f90170 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,8 +55,7 @@ jobs: shell: powershell run: | dotnet tool install --global dotnet-coverage - .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host - .url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml + .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host .url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.scanner.scanAll=false dotnet build --configuration Release dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Acceptance.Tests"' -f xml -o 'coverage.xml' .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" From 7a5774f72ebc11e38663fb50a61d46f652c12354 Mon Sep 17 00:00:00 2001 From: Jason Barden Date: Tue, 29 Apr 2025 16:02:59 +0100 Subject: [PATCH 6/6] Fix the weird spacing --- .github/workflows/dotnet.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index 3f90170..74f048e 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -55,7 +55,7 @@ jobs: shell: powershell run: | dotnet tool install --global dotnet-coverage - .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host .url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.scanner.scanAll=false + .\.sonar\scanner\dotnet-sonarscanner begin /k:"astar-development_${{ env.RepositoryName }}" /o:"astar-development" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.host.url="https://sonarcloud.io" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml /d:sonar.scanner.scanAll=false dotnet build --configuration Release dotnet-coverage collect 'dotnet test --filter "FullyQualifiedName!~Acceptance.Tests"' -f xml -o 'coverage.xml' .\.sonar\scanner\dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}"