mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-24 13:28:43 +00:00
Compare commits
61 Commits
515ea306a2
...
WoofWare.M
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b98080690d | ||
|
|
81b7e5361d | ||
|
|
94b88a4143 | ||
|
|
ed3ffecb52 | ||
|
|
c696dcf31f | ||
|
|
d5bb2726d3 | ||
|
|
f17290d0f1 | ||
|
|
35cd94cba1 | ||
|
|
1b3eb03380 | ||
|
|
b846ce08a3 | ||
|
|
4b9f63d374 | ||
|
|
b9ba07a8a7 | ||
|
|
e80ed51498 | ||
|
|
61b07ad802 | ||
|
|
59369bcb94 | ||
|
|
072169e4e3 | ||
|
|
91136a25ab | ||
|
|
c51038448a | ||
|
|
09780efb07 | ||
|
|
f562271c12 | ||
|
|
e3081c3136 | ||
|
|
232d2ba5ec | ||
|
|
f7458f521e | ||
|
|
bfc25a672b | ||
|
|
af7fcb3028 | ||
|
|
91853b1fff | ||
|
|
1144e93c1c | ||
|
|
d899d77ae2 | ||
|
|
a2ad430b2f | ||
|
|
9e36986bc7 | ||
|
|
679c66885d | ||
|
|
246da41672 | ||
|
|
d07541c2c2 | ||
|
|
7b49505064 | ||
|
|
3209372b5b | ||
|
|
1bbbf4bd06 | ||
|
|
3ea1c7ab79 | ||
|
|
f55a810608 | ||
|
|
afc952241d | ||
|
|
c3af52596f | ||
|
|
8bd13c0bb4 | ||
|
|
ebd6f980de | ||
|
|
690a47488d | ||
|
|
82b40ee559 | ||
|
|
5a0a7e0d17 | ||
|
|
7ef393a28d | ||
|
|
4e18e8b1bf | ||
|
|
a0fb7ee43a | ||
|
|
3d5cd7374f | ||
|
|
1215834795 | ||
|
|
e453a6f07c | ||
|
|
3dfb89d086 | ||
|
|
626f6ef137 | ||
|
|
f803b44311 | ||
|
|
5c1841c3d2 | ||
|
|
bea584e3cc | ||
|
|
f8fdcb805e | ||
|
|
0f7724903b | ||
|
|
f83ac24a73 | ||
|
|
ae3840d537 | ||
|
|
aafee9495a |
@@ -3,13 +3,13 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"fantomas": {
|
"fantomas": {
|
||||||
"version": "6.3.0-alpha-005",
|
"version": "6.3.4",
|
||||||
"commands": [
|
"commands": [
|
||||||
"fantomas"
|
"fantomas"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"fsharp-analyzers": {
|
"fsharp-analyzers": {
|
||||||
"version": "0.23.0",
|
"version": "0.26.0",
|
||||||
"commands": [
|
"commands": [
|
||||||
"fsharp-analyzers"
|
"fsharp-analyzers"
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ root=true
|
|||||||
|
|
||||||
[*]
|
[*]
|
||||||
charset=utf-8
|
charset=utf-8
|
||||||
end_of_line=crlf
|
|
||||||
trim_trailing_whitespace=true
|
trim_trailing_whitespace=true
|
||||||
insert_final_newline=true
|
insert_final_newline=true
|
||||||
indent_style=space
|
indent_style=space
|
||||||
|
|||||||
125
.github/workflows/dotnet.yaml
vendored
125
.github/workflows/dotnet.yaml
vendored
@@ -1,3 +1,4 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
|
||||||
name: .NET
|
name: .NET
|
||||||
|
|
||||||
on:
|
on:
|
||||||
@@ -28,7 +29,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -49,7 +50,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -58,7 +59,7 @@ jobs:
|
|||||||
- name: Build project
|
- name: Build project
|
||||||
run: nix develop --command dotnet build ./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
|
run: nix develop --command dotnet build ./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj
|
||||||
- name: Run analyzers
|
- name: Run analyzers
|
||||||
run: nix run .#fsharp-analyzers -- --project ./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj --analyzers-path ./.analyzerpackages/g-research.fsharp.analyzers/0.6.0/ --verbosity detailed --report ./analysis.sarif --treat-as-error GRA-STRING-001 GRA-STRING-002 GRA-STRING-003 GRA-UNIONCASE-001 GRA-INTERPOLATED-001 GRA-TYPE-ANNOTATE-001 GRA-VIRTUALCALL-001 GRA-IMMUTABLECOLLECTIONEQUALITY-001 GRA-JSONOPTS-001 GRA-LOGARGFUNCFULLAPP-001
|
run: nix run .#fsharp-analyzers -- --project ./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj --analyzers-path ./.analyzerpackages/g-research.fsharp.analyzers/*/ --verbosity detailed --report ./analysis.sarif --treat-as-error GRA-STRING-001 GRA-STRING-002 GRA-STRING-003 GRA-UNIONCASE-001 GRA-INTERPOLATED-001 GRA-TYPE-ANNOTATE-001 GRA-VIRTUALCALL-001 GRA-IMMUTABLECOLLECTIONEQUALITY-001 GRA-JSONOPTS-001 GRA-LOGARGFUNCFULLAPP-001 GRA-DISPBEFOREASYNC-001 --exclude-analyzers PartialAppAnalyzer
|
||||||
|
|
||||||
build-nix:
|
build-nix:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -66,7 +67,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -79,20 +80,41 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Run Fantomas
|
- name: Run Fantomas
|
||||||
run: nix run .#fantomas -- --check .
|
run: nix run .#fantomas -- --check .
|
||||||
|
|
||||||
|
check-accurate-generations:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@V27
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Whitespace change
|
||||||
|
run: "echo ' ' >> ConsumePlugin/List.fs"
|
||||||
|
- name: Generate code
|
||||||
|
run: nix develop --command dotnet build
|
||||||
|
- name: Run Fantomas
|
||||||
|
run: nix run .#fantomas -- .
|
||||||
|
- name: Verify there is no diff
|
||||||
|
run: git diff --name-only --no-color --exit-code
|
||||||
|
|
||||||
check-nix-format:
|
check-nix-format:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -105,7 +127,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -118,7 +140,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -132,7 +154,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -142,26 +164,59 @@ jobs:
|
|||||||
run: nix develop --command dotnet build --no-restore --configuration Release
|
run: nix develop --command dotnet build --no-restore --configuration Release
|
||||||
- name: Pack
|
- name: Pack
|
||||||
run: nix develop --command dotnet pack --configuration Release
|
run: nix develop --command dotnet pack --configuration Release
|
||||||
- name: Upload NuGet artifact
|
- name: Upload NuGet artifact (plugin)
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nuget-package
|
name: nuget-package-plugin
|
||||||
path: WoofWare.Myriad.Plugins/bin/Release/WoofWare.Myriad.Plugins.*.nupkg
|
path: WoofWare.Myriad.Plugins/bin/Release/WoofWare.Myriad.Plugins.*.nupkg
|
||||||
|
- name: Upload NuGet artifact (attributes)
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-attribute
|
||||||
|
path: WoofWare.Myriad.Plugins.Attributes/bin/Release/WoofWare.Myriad.Plugins.Attributes.*.nupkg
|
||||||
|
|
||||||
expected-pack:
|
expected-pack:
|
||||||
needs: [nuget-pack]
|
needs: [nuget-pack]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- name: Download NuGet artifact
|
- name: Download NuGet artifact (plugin)
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nuget-package
|
name: nuget-package-plugin
|
||||||
|
path: packed-plugin
|
||||||
- name: Check NuGet contents
|
- name: Check NuGet contents
|
||||||
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
||||||
run: if [[ $(find . -maxdepth 1 -name 'WoofWare.Myriad.Plugins.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
run: if [[ $(find packed-plugin -maxdepth 1 -name 'WoofWare.Myriad.Plugins.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
||||||
|
- name: Download NuGet artifact (attributes)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-attribute
|
||||||
|
path: packed-attribute
|
||||||
|
- name: Check NuGet contents
|
||||||
|
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
||||||
|
run: if [[ $(find packed-attribute -maxdepth 1 -name 'WoofWare.Myriad.Plugins.Attributes.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
||||||
|
|
||||||
|
github-release-plugin-dry-run:
|
||||||
|
needs: [nuget-pack]
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Download NuGet artifact (plugin)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-plugin
|
||||||
|
- name: Download NuGet artifact (attribute)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-attribute
|
||||||
|
- name: Tag and release plugin
|
||||||
|
env:
|
||||||
|
DRY_RUN: 1
|
||||||
|
GITHUB_TOKEN: mock-token
|
||||||
|
run: sh .github/workflows/tag.sh
|
||||||
|
|
||||||
all-required-checks-complete:
|
all-required-checks-complete:
|
||||||
needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack]
|
needs: [check-dotnet-format, check-nix-format, check-accurate-generations, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack, github-release-plugin-dry-run]
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- run: echo "All required checks complete."
|
- run: echo "All required checks complete."
|
||||||
@@ -174,13 +229,43 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@v25
|
uses: cachix/install-nix-action@V27
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Download NuGet artifact
|
- name: Download NuGet artifact (plugin)
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nuget-package
|
name: nuget-package-plugin
|
||||||
- name: Publish to NuGet
|
path: packed-plugin
|
||||||
run: nix develop --command dotnet nuget push "WoofWare.Myriad.Plugins.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json
|
- name: Publish to NuGet (plugin)
|
||||||
|
run: nix develop --command dotnet nuget push "packed-plugin/WoofWare.Myriad.Plugins.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
|
||||||
|
- name: Download NuGet artifact (attribute)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-attribute
|
||||||
|
path: packed-attribute
|
||||||
|
- name: Publish to NuGet (attribute)
|
||||||
|
run: nix develop --command dotnet nuget push "packed-attribute/WoofWare.Myriad.Plugins.Attributes.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
|
||||||
|
|
||||||
|
github-release-plugin:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
||||||
|
needs: [all-required-checks-complete]
|
||||||
|
environment: main-deploy
|
||||||
|
permissions:
|
||||||
|
contents: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Download NuGet artifact (plugin)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-plugin
|
||||||
|
- name: Download NuGet artifact (attribute)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-attribute
|
||||||
|
- name: Tag and release plugin
|
||||||
|
env:
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: sh .github/workflows/tag.sh
|
||||||
|
|||||||
120
.github/workflows/tag.sh
vendored
Normal file
120
.github/workflows/tag.sh
vendored
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
echo "Dry-run? $DRY_RUN!"
|
||||||
|
|
||||||
|
find . -maxdepth 1 -type f ! -name "$(printf "*\n*")" -name '*.nupkg' | while IFS= read -r file
|
||||||
|
do
|
||||||
|
tag=$(basename "$file" .nupkg)
|
||||||
|
git tag "$tag"
|
||||||
|
${DRY_RUN:+echo} git push origin "$tag"
|
||||||
|
done
|
||||||
|
|
||||||
|
export TAG
|
||||||
|
TAG=$(find . -maxdepth 1 -type f -name 'WoofWare.Myriad.Plugins.*.nupkg' -exec sh -c 'basename "$1" .nupkg' shell {} \; | grep -v Attributes)
|
||||||
|
|
||||||
|
case "$TAG" in
|
||||||
|
*"
|
||||||
|
"*)
|
||||||
|
echo "Error: TAG contains a newline; multiple plugins found."
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# target_commitish empty indicates the repo default branch
|
||||||
|
curl_body='{"tag_name":"'"$TAG"'","target_commitish":"","name":"'"$TAG"'","draft":false,"prerelease":false,"generate_release_notes":false}'
|
||||||
|
|
||||||
|
echo "cURL body: $curl_body"
|
||||||
|
|
||||||
|
failed_output=$(cat <<'EOF'
|
||||||
|
{
|
||||||
|
"message": "Validation Failed",
|
||||||
|
"errors": [
|
||||||
|
{
|
||||||
|
"resource": "Release",
|
||||||
|
"code": "already_exists",
|
||||||
|
"field": "tag_name"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"documentation_url": "https://docs.github.com/rest/releases/releases#create-a-release"
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
success_output=$(cat <<'EOF'
|
||||||
|
{
|
||||||
|
"url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases/158152116",
|
||||||
|
"assets_url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases/158152116/assets",
|
||||||
|
"upload_url": "https://uploads.github.com/repos/Smaug123/WoofWare.Myriad/releases/158152116/assets{?name,label}",
|
||||||
|
"html_url": "https://github.com/Smaug123/WoofWare.Myriad/releases/tag/WoofWare.Myriad.Plugins.2.1.30",
|
||||||
|
"id": 158152116,
|
||||||
|
"author": {
|
||||||
|
"login": "github-actions[bot]",
|
||||||
|
"id": 41898282,
|
||||||
|
"node_id": "MDM6Qm90NDE4OTgyODI=",
|
||||||
|
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
|
||||||
|
"gravatar_id": "",
|
||||||
|
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
|
||||||
|
"html_url": "https://github.com/apps/github-actions",
|
||||||
|
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
|
||||||
|
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
|
||||||
|
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
|
||||||
|
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
|
||||||
|
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
|
||||||
|
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
|
||||||
|
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
|
||||||
|
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
|
||||||
|
"type": "Bot",
|
||||||
|
"site_admin": false
|
||||||
|
},
|
||||||
|
"node_id": "RE_kwDOJfksgc4JbTW0",
|
||||||
|
"tag_name": "WoofWare.Myriad.Plugins.2.1.30",
|
||||||
|
"target_commitish": "main",
|
||||||
|
"name": "WoofWare.Myriad.Plugins.2.1.30",
|
||||||
|
"draft": false,
|
||||||
|
"prerelease": false,
|
||||||
|
"created_at": "2024-05-30T11:00:55Z",
|
||||||
|
"published_at": "2024-05-30T11:03:02Z",
|
||||||
|
"assets": [
|
||||||
|
|
||||||
|
],
|
||||||
|
"tarball_url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/tarball/WoofWare.Myriad.Plugins.2.1.30",
|
||||||
|
"zipball_url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/zipball/WoofWare.Myriad.Plugins.2.1.30",
|
||||||
|
"body": null
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
)
|
||||||
|
|
||||||
|
HANDLE_OUTPUT=''
|
||||||
|
handle_error() {
|
||||||
|
ERROR_OUTPUT="$1"
|
||||||
|
exit_message=$(echo "$ERROR_OUTPUT" | jq -r --exit-status 'if .errors | length == 1 then .errors[0].code else null end')
|
||||||
|
if [ "$exit_message" = "already_exists" ] ; then
|
||||||
|
HANDLE_OUTPUT="Did not create GitHub release because it already exists at this version."
|
||||||
|
else
|
||||||
|
echo "Unexpected error output from curl: $(cat curl_output.json)"
|
||||||
|
echo "JQ output: $(exit_message)"
|
||||||
|
exit 2
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tests() {
|
||||||
|
handle_error "$failed_output"
|
||||||
|
if [ "$HANDLE_OUTPUT" != "Did not create GitHub release because it already exists at this version." ]; then
|
||||||
|
echo "Bad output from handler: $HANDLE_OUTPUT"
|
||||||
|
exit 3
|
||||||
|
fi
|
||||||
|
HANDLE_OUTPUT=''
|
||||||
|
echo "Tests passed."
|
||||||
|
}
|
||||||
|
|
||||||
|
run_tests
|
||||||
|
|
||||||
|
if [ "$DRY_RUN" != 1 ] ; then
|
||||||
|
if curl --fail-with-body -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases -d "$curl_body" > curl_output.json; then
|
||||||
|
echo "Curl succeeded."
|
||||||
|
else
|
||||||
|
handle_error "$(cat curl_output.json)"
|
||||||
|
echo "$HANDLE_OUTPUT"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
@@ -9,3 +9,4 @@ riderModule.iml
|
|||||||
result
|
result
|
||||||
.analyzerpackages/
|
.analyzerpackages/
|
||||||
analysis.sarif
|
analysis.sarif
|
||||||
|
.direnv/
|
||||||
|
|||||||
18
CHANGELOG.md
Normal file
18
CHANGELOG.md
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
Notable changes are recorded here.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.20, WoofWare.Myriad.Plugins.Attributes 3.0.1
|
||||||
|
|
||||||
|
We now bundle copies of the RestEase attributes in `WoofWare.Myriad.Plugins.Attributes`, in case you don't want to take a dependency on RestEase.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.15
|
||||||
|
|
||||||
|
The `GenerateMock` generator now permits a limited amount of inheritance in the record we're mocking out (specifically, `IDisposable`).
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.8
|
||||||
|
|
||||||
|
No change to the packages, but this is when we started creating and tagging GitHub releases, which are a better source of truth than this file.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.0
|
||||||
|
|
||||||
|
This transition split the attributes (e.g. `[<JsonParseAttribute>]`) into their own assembly, WoofWare.Myriad.Plugins.Attributes.
|
||||||
|
The new assembly has minimal dependencies, so you may safely use it from your own code.
|
||||||
22
ConsumePlugin/Catamorphism.fs
Normal file
22
ConsumePlugin/Catamorphism.fs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
type Const<'a> =
|
||||||
|
| Verbatim of 'a
|
||||||
|
| String of string
|
||||||
|
|
||||||
|
type PairOpKind =
|
||||||
|
| NormalSeq
|
||||||
|
| ThenDoSeq
|
||||||
|
|
||||||
|
[<CreateCatamorphism "TreeCata">]
|
||||||
|
type Tree<'a, 'b> =
|
||||||
|
| Const of Const<'a> * 'b
|
||||||
|
| Pair of Tree<'a, 'b> * Tree<'a, 'b> * PairOpKind
|
||||||
|
| Sequential of Tree<'a, 'b> list
|
||||||
|
| Builder of Tree<'a, 'b> * TreeBuilder<'b, 'a>
|
||||||
|
|
||||||
|
and TreeBuilder<'b, 'a> =
|
||||||
|
| Child of TreeBuilder<'b, 'a>
|
||||||
|
| Parent of Tree<'a, 'b>
|
||||||
@@ -35,10 +35,27 @@
|
|||||||
<Compile Include="GeneratedVault.fs">
|
<Compile Include="GeneratedVault.fs">
|
||||||
<MyriadFile>Vault.fs</MyriadFile>
|
<MyriadFile>Vault.fs</MyriadFile>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="SerializationAndDeserialization.fs" />
|
||||||
|
<Compile Include="GeneratedSerde.fs">
|
||||||
|
<MyriadFile>SerializationAndDeserialization.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Catamorphism.fs" />
|
||||||
|
<Compile Include="GeneratedCatamorphism.fs">
|
||||||
|
<MyriadFile>Catamorphism.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="FSharpForFunAndProfitCata.fs" />
|
||||||
|
<Compile Include="GeneratedFileSystem.fs">
|
||||||
|
<MyriadFile>FSharpForFunAndProfitCata.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="List.fs" />
|
||||||
|
<Compile Include="ListCata.fs">
|
||||||
|
<MyriadFile>List.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="RestEase" Version="1.6.4"/>
|
<PackageReference Include="RestEase" Version="1.6.4"/>
|
||||||
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj" />
|
||||||
<ProjectReference Include="..\WoofWare.Myriad.Plugins\WoofWare.Myriad.Plugins.fsproj"/>
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins\WoofWare.Myriad.Plugins.fsproj"/>
|
||||||
<PackageReference Include="Myriad.Sdk" Version="0.8.3"/>
|
<PackageReference Include="Myriad.Sdk" Version="0.8.3"/>
|
||||||
<PackageReference Include="Myriad.Core" Version="0.8.3"/>
|
<PackageReference Include="Myriad.Core" Version="0.8.3"/>
|
||||||
|
|||||||
52
ConsumePlugin/FSharpForFunAndProfitCata.fs
Normal file
52
ConsumePlugin/FSharpForFunAndProfitCata.fs
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
type File =
|
||||||
|
{
|
||||||
|
Name : string
|
||||||
|
FileSize : int
|
||||||
|
}
|
||||||
|
|
||||||
|
type Directory =
|
||||||
|
{
|
||||||
|
Name : string
|
||||||
|
DirSize : int
|
||||||
|
Contents : FileSystemItem list
|
||||||
|
}
|
||||||
|
|
||||||
|
and [<CreateCatamorphism "FileSystemCata">] FileSystemItem =
|
||||||
|
| Directory of Directory
|
||||||
|
| File of File
|
||||||
|
|
||||||
|
type Book =
|
||||||
|
{
|
||||||
|
title : string
|
||||||
|
price : decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChocolateType =
|
||||||
|
| Dark
|
||||||
|
| Milk
|
||||||
|
| SeventyPercent
|
||||||
|
|
||||||
|
type Chocolate =
|
||||||
|
{
|
||||||
|
chocType : ChocolateType
|
||||||
|
price : decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
override this.ToString () = this.chocType.ToString ()
|
||||||
|
|
||||||
|
type WrappingPaperStyle =
|
||||||
|
| HappyBirthday
|
||||||
|
| HappyHolidays
|
||||||
|
| SolidColor
|
||||||
|
|
||||||
|
[<CreateCatamorphism "GiftCata">]
|
||||||
|
type Gift =
|
||||||
|
| Book of Book
|
||||||
|
| Chocolate of Chocolate
|
||||||
|
| Wrapped of Gift * WrappingPaperStyle
|
||||||
|
| Boxed of Gift
|
||||||
|
| WithACard of Gift * message : string
|
||||||
138
ConsumePlugin/GeneratedCatamorphism.fs
Normal file
138
ConsumePlugin/GeneratedCatamorphism.fs
Normal file
@@ -0,0 +1,138 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Description of how to combine cases during a fold
|
||||||
|
type TreeBuilderCataCase<'b, 'a, 'TreeBuilder, 'Tree> =
|
||||||
|
/// How to operate on the Child case
|
||||||
|
abstract Child : 'TreeBuilder -> 'TreeBuilder
|
||||||
|
/// How to operate on the Parent case
|
||||||
|
abstract Parent : 'Tree -> 'TreeBuilder
|
||||||
|
|
||||||
|
/// Description of how to combine cases during a fold
|
||||||
|
type TreeCataCase<'a, 'b, 'TreeBuilder, 'Tree> =
|
||||||
|
/// How to operate on the Const case
|
||||||
|
abstract Const : Const<'a> -> 'b -> 'Tree
|
||||||
|
/// How to operate on the Pair case
|
||||||
|
abstract Pair : 'Tree -> 'Tree -> PairOpKind -> 'Tree
|
||||||
|
/// How to operate on the Sequential case
|
||||||
|
abstract Sequential : 'Tree list -> 'Tree
|
||||||
|
/// How to operate on the Builder case
|
||||||
|
abstract Builder : 'Tree -> 'TreeBuilder -> 'Tree
|
||||||
|
|
||||||
|
/// Specifies how to perform a fold (catamorphism) over the type Tree and its friends.
|
||||||
|
type TreeCata<'b, 'a, 'TreeBuilder, 'Tree> =
|
||||||
|
{
|
||||||
|
/// How to perform a fold (catamorphism) over the type TreeBuilder
|
||||||
|
TreeBuilder : TreeBuilderCataCase<'b, 'a, 'TreeBuilder, 'Tree>
|
||||||
|
/// How to perform a fold (catamorphism) over the type Tree
|
||||||
|
Tree : TreeCataCase<'a, 'b, 'TreeBuilder, 'Tree>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods to perform a catamorphism over the type Tree
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module TreeCata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction<'b, 'a> =
|
||||||
|
| Process__TreeBuilder of TreeBuilder<'b, 'a>
|
||||||
|
| Process__Tree of Tree<'a, 'b>
|
||||||
|
| TreeBuilder_Child
|
||||||
|
| TreeBuilder_Parent
|
||||||
|
| Tree_Pair of PairOpKind
|
||||||
|
| Tree_Sequential of int
|
||||||
|
| Tree_Builder
|
||||||
|
|
||||||
|
let private loop (cata : TreeCata<'b, 'a, 'TreeBuilder, 'Tree>) (instructions : ResizeArray<Instruction<'b, 'a>>) =
|
||||||
|
let treeStack = ResizeArray<'Tree> ()
|
||||||
|
let treeBuilderStack = ResizeArray<'TreeBuilder> ()
|
||||||
|
|
||||||
|
while instructions.Count > 0 do
|
||||||
|
let currentInstruction = instructions.[instructions.Count - 1]
|
||||||
|
instructions.RemoveAt (instructions.Count - 1)
|
||||||
|
|
||||||
|
match currentInstruction with
|
||||||
|
| Instruction.Process__TreeBuilder (x) ->
|
||||||
|
match x with
|
||||||
|
| TreeBuilder.Child (arg0_0) ->
|
||||||
|
instructions.Add Instruction.TreeBuilder_Child
|
||||||
|
instructions.Add (Instruction.Process__TreeBuilder arg0_0)
|
||||||
|
| TreeBuilder.Parent (arg0_0) ->
|
||||||
|
instructions.Add Instruction.TreeBuilder_Parent
|
||||||
|
instructions.Add (Instruction.Process__Tree arg0_0)
|
||||||
|
| Instruction.Process__Tree (x) ->
|
||||||
|
match x with
|
||||||
|
| Tree.Const (arg0_0, arg1_0) -> cata.Tree.Const arg0_0 arg1_0 |> treeStack.Add
|
||||||
|
| Tree.Pair (arg0_0, arg1_0, arg2_0) ->
|
||||||
|
instructions.Add (Instruction.Tree_Pair (arg2_0))
|
||||||
|
instructions.Add (Instruction.Process__Tree arg0_0)
|
||||||
|
instructions.Add (Instruction.Process__Tree arg1_0)
|
||||||
|
| Tree.Sequential (arg0_0) ->
|
||||||
|
instructions.Add (Instruction.Tree_Sequential ((List.length arg0_0)))
|
||||||
|
|
||||||
|
for elt in arg0_0 do
|
||||||
|
instructions.Add (Instruction.Process__Tree elt)
|
||||||
|
| Tree.Builder (arg0_0, arg1_0) ->
|
||||||
|
instructions.Add Instruction.Tree_Builder
|
||||||
|
instructions.Add (Instruction.Process__Tree arg0_0)
|
||||||
|
instructions.Add (Instruction.Process__TreeBuilder arg1_0)
|
||||||
|
| Instruction.TreeBuilder_Child ->
|
||||||
|
let arg0_0 = treeBuilderStack.[treeBuilderStack.Count - 1]
|
||||||
|
treeBuilderStack.RemoveAt (treeBuilderStack.Count - 1)
|
||||||
|
cata.TreeBuilder.Child arg0_0 |> treeBuilderStack.Add
|
||||||
|
| Instruction.TreeBuilder_Parent ->
|
||||||
|
let arg0_0 = treeStack.[treeStack.Count - 1]
|
||||||
|
treeStack.RemoveAt (treeStack.Count - 1)
|
||||||
|
cata.TreeBuilder.Parent arg0_0 |> treeBuilderStack.Add
|
||||||
|
| Instruction.Tree_Pair (arg2_0) ->
|
||||||
|
let arg0_0 = treeStack.[treeStack.Count - 1]
|
||||||
|
treeStack.RemoveAt (treeStack.Count - 1)
|
||||||
|
let arg1_0 = treeStack.[treeStack.Count - 1]
|
||||||
|
treeStack.RemoveAt (treeStack.Count - 1)
|
||||||
|
cata.Tree.Pair arg0_0 arg1_0 arg2_0 |> treeStack.Add
|
||||||
|
| Instruction.Tree_Sequential (arg0_0) ->
|
||||||
|
let arg0_0_len = arg0_0
|
||||||
|
|
||||||
|
let arg0_0 =
|
||||||
|
seq {
|
||||||
|
for i = treeStack.Count - 1 downto treeStack.Count - arg0_0 do
|
||||||
|
yield treeStack.[i]
|
||||||
|
}
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
treeStack.RemoveRange (treeStack.Count - arg0_0_len, arg0_0_len)
|
||||||
|
cata.Tree.Sequential arg0_0 |> treeStack.Add
|
||||||
|
| Instruction.Tree_Builder ->
|
||||||
|
let arg0_0 = treeStack.[treeStack.Count - 1]
|
||||||
|
treeStack.RemoveAt (treeStack.Count - 1)
|
||||||
|
let arg1_0 = treeBuilderStack.[treeBuilderStack.Count - 1]
|
||||||
|
treeBuilderStack.RemoveAt (treeBuilderStack.Count - 1)
|
||||||
|
cata.Tree.Builder arg0_0 arg1_0 |> treeStack.Add
|
||||||
|
|
||||||
|
treeBuilderStack, treeStack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runTreeBuilder
|
||||||
|
(cata : TreeCata<'b, 'a, 'TreeBuilderRet, 'TreeRet>)
|
||||||
|
(x : TreeBuilder<'b, 'a>)
|
||||||
|
: 'TreeBuilderRet
|
||||||
|
=
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.Process__TreeBuilder x)
|
||||||
|
let treeBuilderRetStack, treeRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne treeBuilderRetStack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runTree (cata : TreeCata<'b, 'a, 'TreeBuilderRet, 'TreeRet>) (x : Tree<'a, 'b>) : 'TreeRet =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.Process__Tree x)
|
||||||
|
let treeBuilderRetStack, treeRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne treeRetStack
|
||||||
152
ConsumePlugin/GeneratedFileSystem.fs
Normal file
152
ConsumePlugin/GeneratedFileSystem.fs
Normal file
@@ -0,0 +1,152 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Description of how to combine cases during a fold
|
||||||
|
type FileSystemItemCataCase<'FileSystemItem> =
|
||||||
|
/// How to operate on the Directory case
|
||||||
|
abstract Directory : name : string -> dirSize : int -> contents : 'FileSystemItem list -> 'FileSystemItem
|
||||||
|
/// How to operate on the File case
|
||||||
|
abstract File : File -> 'FileSystemItem
|
||||||
|
|
||||||
|
/// Specifies how to perform a fold (catamorphism) over the type FileSystemItem and its friends.
|
||||||
|
type FileSystemCata<'FileSystemItem> =
|
||||||
|
{
|
||||||
|
/// How to perform a fold (catamorphism) over the type FileSystemItem
|
||||||
|
FileSystemItem : FileSystemItemCataCase<'FileSystemItem>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods to perform a catamorphism over the type FileSystemItem
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module FileSystemItemCata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction =
|
||||||
|
| Process__FileSystemItem of FileSystemItem
|
||||||
|
| FileSystemItem_Directory of string * int * int
|
||||||
|
|
||||||
|
let private loop (cata : FileSystemCata<'FileSystemItem>) (instructions : ResizeArray<Instruction>) =
|
||||||
|
let fileSystemItemStack = ResizeArray<'FileSystemItem> ()
|
||||||
|
|
||||||
|
while instructions.Count > 0 do
|
||||||
|
let currentInstruction = instructions.[instructions.Count - 1]
|
||||||
|
instructions.RemoveAt (instructions.Count - 1)
|
||||||
|
|
||||||
|
match currentInstruction with
|
||||||
|
| Instruction.Process__FileSystemItem (x) ->
|
||||||
|
match x with
|
||||||
|
| FileSystemItem.Directory ({
|
||||||
|
Name = name
|
||||||
|
DirSize = dirSize
|
||||||
|
Contents = contents
|
||||||
|
}) ->
|
||||||
|
instructions.Add (Instruction.FileSystemItem_Directory (name, dirSize, (List.length contents)))
|
||||||
|
|
||||||
|
for elt in contents do
|
||||||
|
instructions.Add (Instruction.Process__FileSystemItem elt)
|
||||||
|
| FileSystemItem.File (arg0_0) -> cata.FileSystemItem.File arg0_0 |> fileSystemItemStack.Add
|
||||||
|
| Instruction.FileSystemItem_Directory (name, dirSize, contents) ->
|
||||||
|
let contents_len = contents
|
||||||
|
|
||||||
|
let contents =
|
||||||
|
seq {
|
||||||
|
for i = fileSystemItemStack.Count - 1 downto fileSystemItemStack.Count - contents do
|
||||||
|
yield fileSystemItemStack.[i]
|
||||||
|
}
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
fileSystemItemStack.RemoveRange (fileSystemItemStack.Count - contents_len, contents_len)
|
||||||
|
cata.FileSystemItem.Directory name dirSize contents |> fileSystemItemStack.Add
|
||||||
|
|
||||||
|
fileSystemItemStack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runFileSystemItem (cata : FileSystemCata<'FileSystemItemRet>) (x : FileSystemItem) : 'FileSystemItemRet =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.Process__FileSystemItem x)
|
||||||
|
let fileSystemItemRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne fileSystemItemRetStack
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Description of how to combine cases during a fold
|
||||||
|
type GiftCataCase<'Gift> =
|
||||||
|
/// How to operate on the Book case
|
||||||
|
abstract Book : Book -> 'Gift
|
||||||
|
/// How to operate on the Chocolate case
|
||||||
|
abstract Chocolate : Chocolate -> 'Gift
|
||||||
|
/// How to operate on the Wrapped case
|
||||||
|
abstract Wrapped : 'Gift -> WrappingPaperStyle -> 'Gift
|
||||||
|
/// How to operate on the Boxed case
|
||||||
|
abstract Boxed : 'Gift -> 'Gift
|
||||||
|
/// How to operate on the WithACard case
|
||||||
|
abstract WithACard : 'Gift -> message : string -> 'Gift
|
||||||
|
|
||||||
|
/// Specifies how to perform a fold (catamorphism) over the type Gift and its friends.
|
||||||
|
type GiftCata<'Gift> =
|
||||||
|
{
|
||||||
|
/// How to perform a fold (catamorphism) over the type Gift
|
||||||
|
Gift : GiftCataCase<'Gift>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods to perform a catamorphism over the type Gift
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module GiftCata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction =
|
||||||
|
| Process__Gift of Gift
|
||||||
|
| Gift_Wrapped of WrappingPaperStyle
|
||||||
|
| Gift_Boxed
|
||||||
|
| Gift_WithACard of string
|
||||||
|
|
||||||
|
let private loop (cata : GiftCata<'Gift>) (instructions : ResizeArray<Instruction>) =
|
||||||
|
let giftStack = ResizeArray<'Gift> ()
|
||||||
|
|
||||||
|
while instructions.Count > 0 do
|
||||||
|
let currentInstruction = instructions.[instructions.Count - 1]
|
||||||
|
instructions.RemoveAt (instructions.Count - 1)
|
||||||
|
|
||||||
|
match currentInstruction with
|
||||||
|
| Instruction.Process__Gift (x) ->
|
||||||
|
match x with
|
||||||
|
| Gift.Book (arg0_0) -> cata.Gift.Book arg0_0 |> giftStack.Add
|
||||||
|
| Gift.Chocolate (arg0_0) -> cata.Gift.Chocolate arg0_0 |> giftStack.Add
|
||||||
|
| Gift.Wrapped (arg0_0, arg1_0) ->
|
||||||
|
instructions.Add (Instruction.Gift_Wrapped (arg1_0))
|
||||||
|
instructions.Add (Instruction.Process__Gift arg0_0)
|
||||||
|
| Gift.Boxed (arg0_0) ->
|
||||||
|
instructions.Add Instruction.Gift_Boxed
|
||||||
|
instructions.Add (Instruction.Process__Gift arg0_0)
|
||||||
|
| Gift.WithACard (arg0_0, message) ->
|
||||||
|
instructions.Add (Instruction.Gift_WithACard (message))
|
||||||
|
instructions.Add (Instruction.Process__Gift arg0_0)
|
||||||
|
| Instruction.Gift_Wrapped (arg1_0) ->
|
||||||
|
let arg0_0 = giftStack.[giftStack.Count - 1]
|
||||||
|
giftStack.RemoveAt (giftStack.Count - 1)
|
||||||
|
cata.Gift.Wrapped arg0_0 arg1_0 |> giftStack.Add
|
||||||
|
| Instruction.Gift_Boxed ->
|
||||||
|
let arg0_0 = giftStack.[giftStack.Count - 1]
|
||||||
|
giftStack.RemoveAt (giftStack.Count - 1)
|
||||||
|
cata.Gift.Boxed arg0_0 |> giftStack.Add
|
||||||
|
| Instruction.Gift_WithACard (message) ->
|
||||||
|
let arg0_0 = giftStack.[giftStack.Count - 1]
|
||||||
|
giftStack.RemoveAt (giftStack.Count - 1)
|
||||||
|
cata.Gift.WithACard arg0_0 message |> giftStack.Add
|
||||||
|
|
||||||
|
giftStack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runGift (cata : GiftCata<'GiftRet>) (x : Gift) : 'GiftRet =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.Process__Gift x)
|
||||||
|
let giftRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne giftRetStack
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the InnerType type
|
/// Module containing JSON parsing methods for the InnerType type
|
||||||
@@ -12,7 +13,7 @@ namespace ConsumePlugin
|
|||||||
module InnerType =
|
module InnerType =
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerType =
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerType =
|
||||||
let Thing =
|
let arg_0 =
|
||||||
(match node.[(Literals.something)] with
|
(match node.[(Literals.something)] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -25,7 +26,7 @@ module InnerType =
|
|||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
Thing = Thing
|
Thing = arg_0
|
||||||
}
|
}
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
@@ -35,7 +36,7 @@ namespace ConsumePlugin
|
|||||||
module JsonRecordType =
|
module JsonRecordType =
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordType =
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordType =
|
||||||
let F =
|
let arg_5 =
|
||||||
(match node.["f"] with
|
(match node.["f"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -48,7 +49,7 @@ module JsonRecordType =
|
|||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
||||||
|> Array.ofSeq
|
|> Array.ofSeq
|
||||||
|
|
||||||
let E =
|
let arg_4 =
|
||||||
(match node.["e"] with
|
(match node.["e"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -61,7 +62,7 @@ module JsonRecordType =
|
|||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
||||||
|> Array.ofSeq
|
|> Array.ofSeq
|
||||||
|
|
||||||
let D =
|
let arg_3 =
|
||||||
InnerType.jsonParse (
|
InnerType.jsonParse (
|
||||||
match node.["d"] with
|
match node.["d"] with
|
||||||
| null ->
|
| null ->
|
||||||
@@ -73,7 +74,7 @@ module JsonRecordType =
|
|||||||
| v -> v
|
| v -> v
|
||||||
)
|
)
|
||||||
|
|
||||||
let C =
|
let arg_2 =
|
||||||
(match node.["hi"] with
|
(match node.["hi"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -86,7 +87,7 @@ module JsonRecordType =
|
|||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let B =
|
let arg_1 =
|
||||||
(match node.["another-thing"] with
|
(match node.["another-thing"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -98,7 +99,7 @@ module JsonRecordType =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
let A =
|
let arg_0 =
|
||||||
(match node.["a"] with
|
(match node.["a"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -111,41 +112,247 @@ module JsonRecordType =
|
|||||||
.GetValue<int> ()
|
.GetValue<int> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
A = A
|
A = arg_0
|
||||||
B = B
|
B = arg_1
|
||||||
C = C
|
C = arg_2
|
||||||
D = D
|
D = arg_3
|
||||||
E = E
|
E = arg_4
|
||||||
F = F
|
F = arg_5
|
||||||
}
|
}
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing extension members for the ToGetExtensionMethod type
|
/// Module containing JSON parsing extension members for the ToGetExtensionMethod type
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module ToGetExtensionMethodJsonParseExtension =
|
module ToGetExtensionMethodJsonParseExtension =
|
||||||
///Extension methods for JSON parsing
|
/// Extension methods for JSON parsing
|
||||||
type ToGetExtensionMethod with
|
type ToGetExtensionMethod with
|
||||||
|
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : ToGetExtensionMethod =
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : ToGetExtensionMethod =
|
||||||
let Sailor =
|
let arg_20 = System.Numerics.BigInteger.Parse (node.["whiskey"].ToJsonString ())
|
||||||
(match node.["sailor"] with
|
|
||||||
|
let arg_19 =
|
||||||
|
(match node.["victor"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
System.Collections.Generic.KeyNotFoundException (
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
sprintf "Required key '%s' not found on JSON object" ("sailor")
|
sprintf "Required key '%s' not found on JSON object" ("victor")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Char> ()
|
||||||
|
|
||||||
|
let arg_18 =
|
||||||
|
(match node.["uniform"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("uniform")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Decimal> ()
|
||||||
|
|
||||||
|
let arg_17 =
|
||||||
|
(match node.["tango"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("tango")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.SByte> ()
|
||||||
|
|
||||||
|
let arg_16 =
|
||||||
|
(match node.["quebec"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("quebec")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Byte> ()
|
||||||
|
|
||||||
|
let arg_15 =
|
||||||
|
(match node.["papa"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("papa")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Byte> ()
|
||||||
|
|
||||||
|
let arg_14 =
|
||||||
|
(match node.["oscar"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("oscar")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.SByte> ()
|
||||||
|
|
||||||
|
let arg_13 =
|
||||||
|
(match node.["november"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("november")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt16> ()
|
||||||
|
|
||||||
|
let arg_12 =
|
||||||
|
(match node.["mike"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("mike")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int16> ()
|
||||||
|
|
||||||
|
let arg_11 =
|
||||||
|
(match node.["lima"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("lima")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt32> ()
|
||||||
|
|
||||||
|
let arg_10 =
|
||||||
|
(match node.["kilo"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("kilo")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
|
let arg_9 =
|
||||||
|
(match node.["juliette"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("juliette")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt32> ()
|
||||||
|
|
||||||
|
let arg_8 =
|
||||||
|
(match node.["india"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("india")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<int> ()
|
||||||
|
|
||||||
|
let arg_7 =
|
||||||
|
(match node.["hotel"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("hotel")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt64> ()
|
||||||
|
|
||||||
|
let arg_6 =
|
||||||
|
(match node.["golf"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("golf")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int64> ()
|
||||||
|
|
||||||
|
let arg_5 =
|
||||||
|
(match node.["foxtrot"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("foxtrot")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Double> ()
|
||||||
|
|
||||||
|
let arg_4 =
|
||||||
|
(match node.["echo"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("echo")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Single> ()
|
||||||
|
|
||||||
|
let arg_3 =
|
||||||
|
(match node.["delta"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("delta")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Single> ()
|
||||||
|
|
||||||
|
let arg_2 =
|
||||||
|
(match node.["charlie"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("charlie")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<float> ()
|
.GetValue<float> ()
|
||||||
|
|
||||||
let Soldier =
|
let arg_1 =
|
||||||
(match node.["soldier"] with
|
(match node.["bravo"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
System.Collections.Generic.KeyNotFoundException (
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
sprintf "Required key '%s' not found on JSON object" ("soldier")
|
sprintf "Required key '%s' not found on JSON object" ("bravo")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
@@ -153,24 +360,12 @@ module ToGetExtensionMethodJsonParseExtension =
|
|||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|> System.Uri
|
|> System.Uri
|
||||||
|
|
||||||
let Tailor =
|
let arg_0 =
|
||||||
(match node.["tailor"] with
|
(match node.["alpha"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
System.Collections.Generic.KeyNotFoundException (
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
sprintf "Required key '%s' not found on JSON object" ("tailor")
|
sprintf "Required key '%s' not found on JSON object" ("alpha")
|
||||||
)
|
|
||||||
)
|
|
||||||
| v -> v)
|
|
||||||
.AsValue()
|
|
||||||
.GetValue<int> ()
|
|
||||||
|
|
||||||
let Tinker =
|
|
||||||
(match node.["tinker"] with
|
|
||||||
| null ->
|
|
||||||
raise (
|
|
||||||
System.Collections.Generic.KeyNotFoundException (
|
|
||||||
sprintf "Required key '%s' not found on JSON object" ("tinker")
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
@@ -178,8 +373,25 @@ module ToGetExtensionMethodJsonParseExtension =
|
|||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
Tinker = Tinker
|
Alpha = arg_0
|
||||||
Tailor = Tailor
|
Bravo = arg_1
|
||||||
Soldier = Soldier
|
Charlie = arg_2
|
||||||
Sailor = Sailor
|
Delta = arg_3
|
||||||
|
Echo = arg_4
|
||||||
|
Foxtrot = arg_5
|
||||||
|
Golf = arg_6
|
||||||
|
Hotel = arg_7
|
||||||
|
India = arg_8
|
||||||
|
Juliette = arg_9
|
||||||
|
Kilo = arg_10
|
||||||
|
Lima = arg_11
|
||||||
|
Mike = arg_12
|
||||||
|
November = arg_13
|
||||||
|
Oscar = arg_14
|
||||||
|
Papa = arg_15
|
||||||
|
Quebec = arg_16
|
||||||
|
Tango = arg_17
|
||||||
|
Uniform = arg_18
|
||||||
|
Victor = arg_19
|
||||||
|
Whiskey = arg_20
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
|
|
||||||
namespace SomeNamespace
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
/// Mock record type for an interface
|
/// Mock record type for an interface
|
||||||
type internal PublicTypeMock =
|
type internal PublicTypeMock =
|
||||||
{
|
{
|
||||||
@@ -13,19 +16,48 @@ type internal PublicTypeMock =
|
|||||||
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
static member Empty : PublicTypeMock =
|
static member Empty : PublicTypeMock =
|
||||||
{
|
{
|
||||||
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IPublicType with
|
interface IPublicType with
|
||||||
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
member this.Mem2 (arg_0_0) = this.Mem2 (arg_0_0)
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
namespace SomeNamespace
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type public PublicTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeInternalFalse with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
/// Mock record type for an interface
|
/// Mock record type for an interface
|
||||||
type internal InternalTypeMock =
|
type internal InternalTypeMock =
|
||||||
{
|
{
|
||||||
@@ -33,17 +65,21 @@ type internal InternalTypeMock =
|
|||||||
Mem2 : string -> int
|
Mem2 : string -> int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
static member Empty : InternalTypeMock =
|
static member Empty : InternalTypeMock =
|
||||||
{
|
{
|
||||||
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface InternalType with
|
interface InternalType with
|
||||||
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
member this.Mem2 (arg_0_0) = this.Mem2 (arg_0_0)
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
namespace SomeNamespace
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
/// Mock record type for an interface
|
/// Mock record type for an interface
|
||||||
type private PrivateTypeMock =
|
type private PrivateTypeMock =
|
||||||
{
|
{
|
||||||
@@ -51,32 +87,62 @@ type private PrivateTypeMock =
|
|||||||
Mem2 : string -> int
|
Mem2 : string -> int
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
static member Empty : PrivateTypeMock =
|
static member Empty : PrivateTypeMock =
|
||||||
{
|
{
|
||||||
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface PrivateType with
|
interface PrivateType with
|
||||||
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
member this.Mem2 (arg_0_0) = this.Mem2 (arg_0_0)
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
namespace SomeNamespace
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeInternalFalseMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeInternalFalse with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
/// Mock record type for an interface
|
/// Mock record type for an interface
|
||||||
type internal VeryPublicTypeMock<'a, 'b> =
|
type internal VeryPublicTypeMock<'a, 'b> =
|
||||||
{
|
{
|
||||||
Mem1 : 'a -> 'b
|
Mem1 : 'a -> 'b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
static member Empty () : VeryPublicTypeMock<'a, 'b> =
|
static member Empty () : VeryPublicTypeMock<'a, 'b> =
|
||||||
{
|
{
|
||||||
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface VeryPublicType<'a, 'b> with
|
interface VeryPublicType<'a, 'b> with
|
||||||
member this.Mem1 (arg_0_0) = this.Mem1 (arg_0_0)
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
namespace SomeNamespace
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
/// Mock record type for an interface
|
/// Mock record type for an interface
|
||||||
type internal CurriedMock<'a> =
|
type internal CurriedMock<'a> =
|
||||||
{
|
{
|
||||||
@@ -88,20 +154,21 @@ type internal CurriedMock<'a> =
|
|||||||
Mem6 : int * string -> 'a * int -> string
|
Mem6 : int * string -> 'a * int -> string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
static member Empty () : CurriedMock<'a> =
|
static member Empty () : CurriedMock<'a> =
|
||||||
{
|
{
|
||||||
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem4 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem5 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
Mem6 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Curried<'a> with
|
interface Curried<'a> with
|
||||||
member this.Mem1 (arg_0_0) (arg_1_0) = this.Mem1 (arg_0_0) (arg_1_0)
|
member this.Mem1 arg_0_0 arg_1_0 = this.Mem1 (arg_0_0) (arg_1_0)
|
||||||
member this.Mem2 (arg_0_0, arg_0_1) (arg_1_0) = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0)
|
member this.Mem2 (arg_0_0, arg_0_1) arg_1_0 = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
member this.Mem3 ((arg_0_0, arg_0_1)) (arg_1_0) = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0)
|
member this.Mem3 ((arg_0_0, arg_0_1)) arg_1_0 = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
|
||||||
member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) =
|
member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) =
|
||||||
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
@@ -111,3 +178,31 @@ type internal CurriedMock<'a> =
|
|||||||
|
|
||||||
member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) =
|
member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) =
|
||||||
this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal TypeWithInterfaceMock =
|
||||||
|
{
|
||||||
|
/// Implementation of IDisposable.Dispose
|
||||||
|
Dispose : unit -> unit
|
||||||
|
Mem1 : string option -> string[] Async
|
||||||
|
Mem2 : unit -> string[] Async
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : TypeWithInterfaceMock =
|
||||||
|
{
|
||||||
|
Dispose = (fun _ -> ())
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeWithInterface with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
member this.Mem2 () = this.Mem2 (())
|
||||||
|
|
||||||
|
interface System.IDisposable with
|
||||||
|
member this.Dispose () : unit = this.Dispose ()
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace PureGym
|
namespace PureGym
|
||||||
|
|
||||||
open System
|
open System
|
||||||
@@ -31,7 +32,7 @@ module PureGymApi =
|
|||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("v1/gyms/", System.UriKind.Relative)
|
System.Uri (("v1/gyms/"), System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
|
|
||||||
let httpMessage =
|
let httpMessage =
|
||||||
@@ -42,13 +43,13 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return node.AsArray () |> Seq.map (fun elt -> Gym.jsonParse elt) |> List.ofSeq
|
return jsonNode.AsArray () |> Seq.map (fun elt -> Gym.jsonParse elt) |> List.ofSeq
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -76,13 +77,47 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return GymAttendance.jsonParse node
|
return GymAttendance.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetGymAttendance' (gymId : int, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri (
|
||||||
|
"v1/gyms/{gym_id}/attendance"
|
||||||
|
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return GymAttendance.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -106,17 +141,17 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return Member.jsonParse node
|
return Member.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
member _.GetGym (gymId : int, ct : CancellationToken option) =
|
member _.GetGym (gym : int, ct : CancellationToken option) =
|
||||||
async {
|
async {
|
||||||
let! ct = Async.CancellationToken
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
@@ -126,8 +161,8 @@ module PureGymApi =
|
|||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/gyms/{gym_id}"
|
"v1/gyms/{gym}"
|
||||||
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{gym}", gym.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -140,13 +175,13 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return Gym.jsonParse node
|
return Gym.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -170,13 +205,13 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return MemberActivityDto.jsonParse node
|
return MemberActivityDto.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -200,13 +235,79 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return UriThing.jsonParse node
|
return UriThing.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.PostStringToString (foo : Map<string, string> option, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("some/url", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Post,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let queryParams =
|
||||||
|
new System.Net.Http.StringContent (
|
||||||
|
foo
|
||||||
|
|> (fun field ->
|
||||||
|
match field with
|
||||||
|
| None -> null :> System.Text.Json.Nodes.JsonNode
|
||||||
|
| Some field ->
|
||||||
|
((fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (
|
||||||
|
key.ToString (),
|
||||||
|
System.Text.Json.Nodes.JsonValue.Create<string> value
|
||||||
|
)
|
||||||
|
|
||||||
|
ret
|
||||||
|
)
|
||||||
|
field)
|
||||||
|
:> System.Text.Json.Nodes.JsonNode
|
||||||
|
)
|
||||||
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Content <- queryParams
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return
|
||||||
|
match jsonNode with
|
||||||
|
| null -> None
|
||||||
|
| v ->
|
||||||
|
v.AsObject ()
|
||||||
|
|> Seq.map (fun kvp ->
|
||||||
|
let key = (kvp.Key)
|
||||||
|
let value = (kvp.Value).AsValue().GetValue<string> ()
|
||||||
|
key, value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|> Some
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -221,7 +322,11 @@ module PureGymApi =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
("/v2/gymSessions/member"
|
("/v2/gymSessions/member"
|
||||||
+ "?fromDate="
|
+ (if "/v2/gymSessions/member".IndexOf (char 63) >= 0 then
|
||||||
|
"&"
|
||||||
|
else
|
||||||
|
"?")
|
||||||
|
+ "fromDate="
|
||||||
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
|
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
|
||||||
+ "&toDate="
|
+ "&toDate="
|
||||||
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
|
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
|
||||||
@@ -237,13 +342,54 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return Sessions.jsonParse node
|
return Sessions.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetSessionsWithQuery (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri (
|
||||||
|
("/v2/gymSessions/member?foo=1"
|
||||||
|
+ (if "/v2/gymSessions/member?foo=1".IndexOf (char 63) >= 0 then
|
||||||
|
"&"
|
||||||
|
else
|
||||||
|
"?")
|
||||||
|
+ "fromDate="
|
||||||
|
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
|
||||||
|
+ "&toDate="
|
||||||
|
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return Sessions.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -269,8 +415,8 @@ module PureGymApi =
|
|||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -296,8 +442,8 @@ module PureGymApi =
|
|||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -323,8 +469,8 @@ module PureGymApi =
|
|||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -350,8 +496,8 @@ module PureGymApi =
|
|||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -377,8 +523,107 @@ module PureGymApi =
|
|||||||
do httpMessage.Content <- queryParams
|
do httpMessage.Content <- queryParams
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.CreateUserSerialisedBody (user : PureGym.Member, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Post,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let queryParams =
|
||||||
|
new System.Net.Http.StringContent (
|
||||||
|
user
|
||||||
|
|> PureGym.Member.toJsonNode
|
||||||
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Content <- queryParams
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.CreateUserSerialisedUrlBody (user : Uri, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Post,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let queryParams =
|
||||||
|
new System.Net.Http.StringContent (
|
||||||
|
user
|
||||||
|
|> System.Text.Json.Nodes.JsonValue.Create<Uri>
|
||||||
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Content <- queryParams
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.CreateUserSerialisedIntBody (user : int, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Post,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let queryParams =
|
||||||
|
new System.Net.Http.StringContent (
|
||||||
|
user
|
||||||
|
|> System.Text.Json.Nodes.JsonValue.Create<int>
|
||||||
|
|> (fun node -> if isNull node then "null" else node.ToJsonString ())
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Content <- queryParams
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -403,8 +648,8 @@ module PureGymApi =
|
|||||||
do httpMessage.Content <- user
|
do httpMessage.Content <- user
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -432,8 +677,8 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -457,8 +702,8 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -482,8 +727,8 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -507,8 +752,8 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseStream
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -532,8 +777,7 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let node = response
|
return response
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -557,8 +801,7 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let node = response
|
return response
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -582,8 +825,7 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let node = response
|
return response
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -607,8 +849,151 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let node = response
|
return response
|
||||||
return node
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetResponse (ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return
|
||||||
|
new RestEase.Response<_> (
|
||||||
|
responseString,
|
||||||
|
response,
|
||||||
|
(fun () -> (MemberActivityDto.jsonParse jsonNode))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetResponse' (ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return
|
||||||
|
new RestEase.Response<_> (
|
||||||
|
responseString,
|
||||||
|
response,
|
||||||
|
(fun () -> (MemberActivityDto.jsonParse jsonNode))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetResponse'' (ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return
|
||||||
|
new RestEase.Response<_> (
|
||||||
|
responseString,
|
||||||
|
response,
|
||||||
|
(fun () -> (MemberActivityDto.jsonParse jsonNode))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetResponse''' (ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return
|
||||||
|
new RestEase.Response<_> (
|
||||||
|
responseString,
|
||||||
|
response,
|
||||||
|
(fun () -> (MemberActivityDto.jsonParse jsonNode))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -631,8 +1016,7 @@ module PureGymApi =
|
|||||||
)
|
)
|
||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let node = response
|
return response
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -656,8 +1040,7 @@ module PureGymApi =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let node = response
|
return response
|
||||||
return node
|
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
}
|
}
|
||||||
@@ -708,8 +1091,8 @@ module internal ApiWithoutBaseAddress =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
}
|
}
|
||||||
@@ -730,7 +1113,7 @@ module ApiWithBasePath =
|
|||||||
/// Create a REST client.
|
/// Create a REST client.
|
||||||
let make (client : System.Net.Http.HttpClient) : IApiWithBasePath =
|
let make (client : System.Net.Http.HttpClient) : IApiWithBasePath =
|
||||||
{ new IApiWithBasePath with
|
{ new IApiWithBasePath with
|
||||||
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
|
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
|
||||||
async {
|
async {
|
||||||
let! ct = Async.CancellationToken
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
@@ -760,10 +1143,10 @@ module ApiWithBasePath =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
|
||||||
}
|
}
|
||||||
namespace PureGym
|
namespace PureGym
|
||||||
|
|
||||||
@@ -806,8 +1189,134 @@ module ApiWithBasePathAndAddress =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! node = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
return node
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module ApiWithHeaders =
|
||||||
|
/// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties.
|
||||||
|
let make
|
||||||
|
(someHeader : unit -> string)
|
||||||
|
(someOtherHeader : unit -> int)
|
||||||
|
(client : System.Net.Http.HttpClient)
|
||||||
|
: IApiWithHeaders
|
||||||
|
=
|
||||||
|
{ new IApiWithHeaders with
|
||||||
|
member _.SomeHeader : string = someHeader ()
|
||||||
|
member _.SomeOtherHeader : int = someOtherHeader ()
|
||||||
|
|
||||||
|
member this.GetPathParam (parameter : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri (
|
||||||
|
"endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Headers.Add ("X-Foo", this.SomeHeader.ToString ())
|
||||||
|
do httpMessage.Headers.Add ("Authorization", this.SomeOtherHeader.ToString ())
|
||||||
|
do httpMessage.Headers.Add ("Header-Name", "Header-Value")
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module ApiWithHeaders2 =
|
||||||
|
/// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties.
|
||||||
|
let make
|
||||||
|
(someHeader : unit -> string)
|
||||||
|
(someOtherHeader : unit -> int)
|
||||||
|
(client : System.Net.Http.HttpClient)
|
||||||
|
: IApiWithHeaders2
|
||||||
|
=
|
||||||
|
{ new IApiWithHeaders2 with
|
||||||
|
member _.SomeHeader : string = someHeader ()
|
||||||
|
member _.SomeOtherHeader : int = someOtherHeader ()
|
||||||
|
|
||||||
|
member this.GetPathParam (parameter : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri (
|
||||||
|
"endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
do httpMessage.Headers.Add ("X-Foo", this.SomeHeader.ToString ())
|
||||||
|
do httpMessage.Headers.Add ("Authorization", this.SomeOtherHeader.ToString ())
|
||||||
|
do httpMessage.Headers.Add ("Header-Name", "Header-Value")
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
}
|
}
|
||||||
|
|||||||
460
ConsumePlugin/GeneratedSerde.fs
Normal file
460
ConsumePlugin/GeneratedSerde.fs
Normal file
@@ -0,0 +1,460 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the InnerTypeWithBoth type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module InnerTypeWithBothJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type InnerTypeWithBoth with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : InnerTypeWithBoth) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
node.Add (("it's-a-me"), System.Text.Json.Nodes.JsonValue.Create<Guid> input.Thing)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"map",
|
||||||
|
(fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value)
|
||||||
|
|
||||||
|
ret
|
||||||
|
)
|
||||||
|
input.Map
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"readOnlyDict",
|
||||||
|
(fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (
|
||||||
|
key.ToString (),
|
||||||
|
(fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
|
for mem in field do
|
||||||
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<char> mem)
|
||||||
|
|
||||||
|
arr
|
||||||
|
)
|
||||||
|
value
|
||||||
|
)
|
||||||
|
|
||||||
|
ret
|
||||||
|
)
|
||||||
|
input.ReadOnlyDict
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"dict",
|
||||||
|
(fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<bool> value)
|
||||||
|
|
||||||
|
ret
|
||||||
|
)
|
||||||
|
input.Dict
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"concreteDict",
|
||||||
|
(fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (key.ToString (), InnerTypeWithBoth.toJsonNode value)
|
||||||
|
|
||||||
|
ret
|
||||||
|
)
|
||||||
|
input.ConcreteDict
|
||||||
|
)
|
||||||
|
|
||||||
|
node :> _
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the JsonRecordTypeWithBoth type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module JsonRecordTypeWithBothJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type JsonRecordTypeWithBoth with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : JsonRecordTypeWithBoth) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
node.Add ("a", System.Text.Json.Nodes.JsonValue.Create<int> input.A)
|
||||||
|
node.Add ("b", System.Text.Json.Nodes.JsonValue.Create<string> input.B)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"c",
|
||||||
|
(fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
|
for mem in field do
|
||||||
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
|
||||||
|
|
||||||
|
arr
|
||||||
|
)
|
||||||
|
input.C
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add ("d", InnerTypeWithBoth.toJsonNode input.D)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"e",
|
||||||
|
(fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
|
for mem in field do
|
||||||
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem)
|
||||||
|
|
||||||
|
arr
|
||||||
|
)
|
||||||
|
input.E
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"f",
|
||||||
|
(fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
|
for mem in field do
|
||||||
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
|
||||||
|
|
||||||
|
arr
|
||||||
|
)
|
||||||
|
input.F
|
||||||
|
)
|
||||||
|
|
||||||
|
node :> _
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the FirstDu type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module FirstDuJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type FirstDu with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : FirstDu) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
match input with
|
||||||
|
| FirstDu.EmptyCase -> node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "emptyCase")
|
||||||
|
| FirstDu.Case1 (arg0) ->
|
||||||
|
node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "case1")
|
||||||
|
let dataNode = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
dataNode.Add ("data", System.Text.Json.Nodes.JsonValue.Create<string> arg0)
|
||||||
|
node.Add ("data", dataNode)
|
||||||
|
| FirstDu.Case2 (arg0, arg1) ->
|
||||||
|
node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "case2")
|
||||||
|
let dataNode = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
dataNode.Add ("record", JsonRecordTypeWithBoth.toJsonNode arg0)
|
||||||
|
dataNode.Add ("i", System.Text.Json.Nodes.JsonValue.Create<int> arg1)
|
||||||
|
node.Add ("data", dataNode)
|
||||||
|
|
||||||
|
node :> _
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the InnerTypeWithBoth type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module InnerTypeWithBothJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type InnerTypeWithBoth with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerTypeWithBoth =
|
||||||
|
let arg_4 =
|
||||||
|
(match node.["concreteDict"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("concreteDict")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsObject ()
|
||||||
|
|> Seq.map (fun kvp ->
|
||||||
|
let key = (kvp.Key)
|
||||||
|
let value = InnerTypeWithBoth.jsonParse (kvp.Value)
|
||||||
|
key, value
|
||||||
|
)
|
||||||
|
|> Seq.map System.Collections.Generic.KeyValuePair
|
||||||
|
|> System.Collections.Generic.Dictionary
|
||||||
|
|
||||||
|
let arg_3 =
|
||||||
|
(match node.["dict"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("dict")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsObject ()
|
||||||
|
|> Seq.map (fun kvp ->
|
||||||
|
let key = (kvp.Key) |> System.Uri
|
||||||
|
let value = (kvp.Value).AsValue().GetValue<bool> ()
|
||||||
|
key, value
|
||||||
|
)
|
||||||
|
|> dict
|
||||||
|
|
||||||
|
let arg_2 =
|
||||||
|
(match node.["readOnlyDict"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("readOnlyDict")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsObject ()
|
||||||
|
|> Seq.map (fun kvp ->
|
||||||
|
let key = (kvp.Key)
|
||||||
|
|
||||||
|
let value =
|
||||||
|
(kvp.Value).AsArray ()
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.Char> ())
|
||||||
|
|> List.ofSeq
|
||||||
|
|
||||||
|
key, value
|
||||||
|
)
|
||||||
|
|> readOnlyDict
|
||||||
|
|
||||||
|
let arg_1 =
|
||||||
|
(match node.["map"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("map")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsObject ()
|
||||||
|
|> Seq.map (fun kvp ->
|
||||||
|
let key = (kvp.Key)
|
||||||
|
let value = (kvp.Value).AsValue().GetValue<string> () |> System.Uri
|
||||||
|
key, value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let arg_0 =
|
||||||
|
(match node.[("it's-a-me")] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" (("it's-a-me"))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<string> ()
|
||||||
|
|> System.Guid.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Thing = arg_0
|
||||||
|
Map = arg_1
|
||||||
|
ReadOnlyDict = arg_2
|
||||||
|
Dict = arg_3
|
||||||
|
ConcreteDict = arg_4
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the JsonRecordTypeWithBoth type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module JsonRecordTypeWithBothJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type JsonRecordTypeWithBoth with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordTypeWithBoth =
|
||||||
|
let arg_5 =
|
||||||
|
(match node.["f"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("f")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsArray ()
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
||||||
|
|> Array.ofSeq
|
||||||
|
|
||||||
|
let arg_4 =
|
||||||
|
(match node.["e"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("e")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsArray ()
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
||||||
|
|> Array.ofSeq
|
||||||
|
|
||||||
|
let arg_3 =
|
||||||
|
InnerTypeWithBoth.jsonParse (
|
||||||
|
match node.["d"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("d")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v
|
||||||
|
)
|
||||||
|
|
||||||
|
let arg_2 =
|
||||||
|
(match node.["c"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("c")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsArray ()
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
||||||
|
|> List.ofSeq
|
||||||
|
|
||||||
|
let arg_1 =
|
||||||
|
(match node.["b"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("b")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<string> ()
|
||||||
|
|
||||||
|
let arg_0 =
|
||||||
|
(match node.["a"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("a")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<int> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
A = arg_0
|
||||||
|
B = arg_1
|
||||||
|
C = arg_2
|
||||||
|
D = arg_3
|
||||||
|
E = arg_4
|
||||||
|
F = arg_5
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the FirstDu type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module FirstDuJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type FirstDu with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : FirstDu =
|
||||||
|
let ty =
|
||||||
|
(match node.["type"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("type")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
|> (fun v -> v.GetValue<string> ())
|
||||||
|
|
||||||
|
match ty with
|
||||||
|
| "emptyCase" -> FirstDu.EmptyCase
|
||||||
|
| "case1" ->
|
||||||
|
let node =
|
||||||
|
(match node.["data"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("data")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
|
||||||
|
FirstDu.Case1 (
|
||||||
|
(match node.["data"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("data")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<string> ()
|
||||||
|
)
|
||||||
|
| "case2" ->
|
||||||
|
let node =
|
||||||
|
(match node.["data"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("data")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
|
||||||
|
FirstDu.Case2 (
|
||||||
|
JsonRecordTypeWithBoth.jsonParse (
|
||||||
|
match node.["record"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("record")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v
|
||||||
|
),
|
||||||
|
(match node.["i"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("i")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<int> ()
|
||||||
|
)
|
||||||
|
| v -> failwith ("Unrecognised 'type' field value: " + v)
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the JwtVaultAuthResponse type
|
/// Module containing JSON parsing methods for the JwtVaultAuthResponse type
|
||||||
@@ -12,7 +13,7 @@ namespace ConsumePlugin
|
|||||||
module JwtVaultAuthResponse =
|
module JwtVaultAuthResponse =
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultAuthResponse =
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultAuthResponse =
|
||||||
let NumUses =
|
let arg_10 =
|
||||||
(match node.["num_uses"] with
|
(match node.["num_uses"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -24,7 +25,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<int> ()
|
||||||
|
|
||||||
let Orphan =
|
let arg_9 =
|
||||||
(match node.["orphan"] with
|
(match node.["orphan"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -36,7 +37,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<bool> ()
|
||||||
|
|
||||||
let EntityId =
|
let arg_8 =
|
||||||
(match node.["entity_id"] with
|
(match node.["entity_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -48,7 +49,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
let TokenType =
|
let arg_7 =
|
||||||
(match node.["token_type"] with
|
(match node.["token_type"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -60,7 +61,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
let Renewable =
|
let arg_6 =
|
||||||
(match node.["renewable"] with
|
(match node.["renewable"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -72,7 +73,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<bool> ()
|
||||||
|
|
||||||
let LeaseDuration =
|
let arg_5 =
|
||||||
(match node.["lease_duration"] with
|
(match node.["lease_duration"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -84,7 +85,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<int> ()
|
||||||
|
|
||||||
let IdentityPolicies =
|
let arg_4 =
|
||||||
(match node.["identity_policies"] with
|
(match node.["identity_policies"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -97,7 +98,7 @@ module JwtVaultAuthResponse =
|
|||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let TokenPolicies =
|
let arg_3 =
|
||||||
(match node.["token_policies"] with
|
(match node.["token_policies"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -110,7 +111,7 @@ module JwtVaultAuthResponse =
|
|||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let Policies =
|
let arg_2 =
|
||||||
(match node.["policies"] with
|
(match node.["policies"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -123,7 +124,7 @@ module JwtVaultAuthResponse =
|
|||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let Accessor =
|
let arg_1 =
|
||||||
(match node.["accessor"] with
|
(match node.["accessor"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -135,7 +136,7 @@ module JwtVaultAuthResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
let ClientToken =
|
let arg_0 =
|
||||||
(match node.["client_token"] with
|
(match node.["client_token"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -148,17 +149,17 @@ module JwtVaultAuthResponse =
|
|||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
ClientToken = ClientToken
|
ClientToken = arg_0
|
||||||
Accessor = Accessor
|
Accessor = arg_1
|
||||||
Policies = Policies
|
Policies = arg_2
|
||||||
TokenPolicies = TokenPolicies
|
TokenPolicies = arg_3
|
||||||
IdentityPolicies = IdentityPolicies
|
IdentityPolicies = arg_4
|
||||||
LeaseDuration = LeaseDuration
|
LeaseDuration = arg_5
|
||||||
Renewable = Renewable
|
Renewable = arg_6
|
||||||
TokenType = TokenType
|
TokenType = arg_7
|
||||||
EntityId = EntityId
|
EntityId = arg_8
|
||||||
Orphan = Orphan
|
Orphan = arg_9
|
||||||
NumUses = NumUses
|
NumUses = arg_10
|
||||||
}
|
}
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
@@ -168,7 +169,7 @@ namespace ConsumePlugin
|
|||||||
module JwtVaultResponse =
|
module JwtVaultResponse =
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultResponse =
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultResponse =
|
||||||
let Auth =
|
let arg_4 =
|
||||||
JwtVaultAuthResponse.jsonParse (
|
JwtVaultAuthResponse.jsonParse (
|
||||||
match node.["auth"] with
|
match node.["auth"] with
|
||||||
| null ->
|
| null ->
|
||||||
@@ -180,7 +181,7 @@ module JwtVaultResponse =
|
|||||||
| v -> v
|
| v -> v
|
||||||
)
|
)
|
||||||
|
|
||||||
let LeaseDuration =
|
let arg_3 =
|
||||||
(match node.["lease_duration"] with
|
(match node.["lease_duration"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -192,7 +193,7 @@ module JwtVaultResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<int> ()
|
||||||
|
|
||||||
let Renewable =
|
let arg_2 =
|
||||||
(match node.["renewable"] with
|
(match node.["renewable"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -204,7 +205,7 @@ module JwtVaultResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<bool> ()
|
||||||
|
|
||||||
let LeaseId =
|
let arg_1 =
|
||||||
(match node.["lease_id"] with
|
(match node.["lease_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -216,7 +217,7 @@ module JwtVaultResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
let RequestId =
|
let arg_0 =
|
||||||
(match node.["request_id"] with
|
(match node.["request_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -229,11 +230,11 @@ module JwtVaultResponse =
|
|||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
RequestId = RequestId
|
RequestId = arg_0
|
||||||
LeaseId = LeaseId
|
LeaseId = arg_1
|
||||||
Renewable = Renewable
|
Renewable = arg_2
|
||||||
LeaseDuration = LeaseDuration
|
LeaseDuration = arg_3
|
||||||
Auth = Auth
|
Auth = arg_4
|
||||||
}
|
}
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
@@ -243,7 +244,7 @@ namespace ConsumePlugin
|
|||||||
module JwtSecretResponse =
|
module JwtSecretResponse =
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtSecretResponse =
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtSecretResponse =
|
||||||
let Data8 =
|
let arg_11 =
|
||||||
(match node.["data8"] with
|
(match node.["data8"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -261,7 +262,7 @@ module JwtSecretResponse =
|
|||||||
|> Seq.map System.Collections.Generic.KeyValuePair
|
|> Seq.map System.Collections.Generic.KeyValuePair
|
||||||
|> System.Collections.Generic.Dictionary
|
|> System.Collections.Generic.Dictionary
|
||||||
|
|
||||||
let Data7 =
|
let arg_10 =
|
||||||
(match node.["data7"] with
|
(match node.["data7"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -278,7 +279,7 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
|> Map.ofSeq
|
|> Map.ofSeq
|
||||||
|
|
||||||
let Data6 =
|
let arg_9 =
|
||||||
(match node.["data6"] with
|
(match node.["data6"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -295,7 +296,7 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
|> dict
|
|> dict
|
||||||
|
|
||||||
let Data5 =
|
let arg_8 =
|
||||||
(match node.["data5"] with
|
(match node.["data5"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -312,7 +313,7 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
|> readOnlyDict
|
|> readOnlyDict
|
||||||
|
|
||||||
let Data4 =
|
let arg_7 =
|
||||||
(match node.["data4"] with
|
(match node.["data4"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -329,7 +330,7 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
|> Map.ofSeq
|
|> Map.ofSeq
|
||||||
|
|
||||||
let Data3 =
|
let arg_6 =
|
||||||
(match node.["data3"] with
|
(match node.["data3"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -347,7 +348,7 @@ module JwtSecretResponse =
|
|||||||
|> Seq.map System.Collections.Generic.KeyValuePair
|
|> Seq.map System.Collections.Generic.KeyValuePair
|
||||||
|> System.Collections.Generic.Dictionary
|
|> System.Collections.Generic.Dictionary
|
||||||
|
|
||||||
let Data2 =
|
let arg_5 =
|
||||||
(match node.["data2"] with
|
(match node.["data2"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -364,7 +365,7 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
|> dict
|
|> dict
|
||||||
|
|
||||||
let Data =
|
let arg_4 =
|
||||||
(match node.["data"] with
|
(match node.["data"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -381,7 +382,7 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
|> readOnlyDict
|
|> readOnlyDict
|
||||||
|
|
||||||
let LeaseDuration =
|
let arg_3 =
|
||||||
(match node.["lease_duration"] with
|
(match node.["lease_duration"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -393,7 +394,7 @@ module JwtSecretResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<int> ()
|
||||||
|
|
||||||
let Renewable =
|
let arg_2 =
|
||||||
(match node.["renewable"] with
|
(match node.["renewable"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -405,7 +406,7 @@ module JwtSecretResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<bool> ()
|
||||||
|
|
||||||
let LeaseId =
|
let arg_1 =
|
||||||
(match node.["lease_id"] with
|
(match node.["lease_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -417,7 +418,7 @@ module JwtSecretResponse =
|
|||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
let RequestId =
|
let arg_0 =
|
||||||
(match node.["request_id"] with
|
(match node.["request_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -430,18 +431,18 @@ module JwtSecretResponse =
|
|||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
RequestId = RequestId
|
RequestId = arg_0
|
||||||
LeaseId = LeaseId
|
LeaseId = arg_1
|
||||||
Renewable = Renewable
|
Renewable = arg_2
|
||||||
LeaseDuration = LeaseDuration
|
LeaseDuration = arg_3
|
||||||
Data = Data
|
Data = arg_4
|
||||||
Data2 = Data2
|
Data2 = arg_5
|
||||||
Data3 = Data3
|
Data3 = arg_6
|
||||||
Data4 = Data4
|
Data4 = arg_7
|
||||||
Data5 = Data5
|
Data5 = arg_8
|
||||||
Data6 = Data6
|
Data6 = arg_9
|
||||||
Data7 = Data7
|
Data7 = arg_10
|
||||||
Data8 = Data8
|
Data8 = arg_11
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
@@ -461,12 +462,7 @@ module VaultClient =
|
|||||||
let make (client : System.Net.Http.HttpClient) : IVaultClient =
|
let make (client : System.Net.Http.HttpClient) : IVaultClient =
|
||||||
{ new IVaultClient with
|
{ new IVaultClient with
|
||||||
member _.GetSecret
|
member _.GetSecret
|
||||||
(
|
(jwt : JwtVaultResponse, path : string, mountPoint : string, ct : CancellationToken option)
|
||||||
jwt : JwtVaultResponse,
|
|
||||||
path : string,
|
|
||||||
mountPoint : string,
|
|
||||||
ct : CancellationToken option
|
|
||||||
)
|
|
||||||
=
|
=
|
||||||
async {
|
async {
|
||||||
let! ct = Async.CancellationToken
|
let! ct = Async.CancellationToken
|
||||||
@@ -501,13 +497,13 @@ module VaultClient =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return JwtSecretResponse.jsonParse node
|
return JwtSecretResponse.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
@@ -537,13 +533,211 @@ module VaultClient =
|
|||||||
|
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! stream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
let! node =
|
let! jsonNode =
|
||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (stream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
return JwtVaultResponse.jsonParse node
|
return JwtVaultResponse.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
}
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module VaultClientNonExtensionMethod =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IVaultClientNonExtensionMethod =
|
||||||
|
{ new IVaultClientNonExtensionMethod with
|
||||||
|
member _.GetSecret
|
||||||
|
(jwt : JwtVaultResponse, path : string, mountPoint : string, ct : CancellationToken option)
|
||||||
|
=
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri (
|
||||||
|
"v1/{mountPoint}/{path}"
|
||||||
|
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
|
||||||
|
.Replace (
|
||||||
|
"{mountPoint}",
|
||||||
|
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
|
||||||
|
),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return JwtSecretResponse.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetJwt (role : string, jwt : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("v1/auth/jwt/login", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return JwtVaultResponse.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Extension methods for constructing a REST client.
|
||||||
|
[<AutoOpen>]
|
||||||
|
module VaultClientExtensionMethodHttpClientExtension =
|
||||||
|
/// Extension methods for HTTP clients
|
||||||
|
type VaultClientExtensionMethod with
|
||||||
|
|
||||||
|
/// Create a REST client.
|
||||||
|
static member make (client : System.Net.Http.HttpClient) : IVaultClientExtensionMethod =
|
||||||
|
{ new IVaultClientExtensionMethod with
|
||||||
|
member _.GetSecret
|
||||||
|
(jwt : JwtVaultResponse, path : string, mountPoint : string, ct : CancellationToken option)
|
||||||
|
=
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri (
|
||||||
|
"v1/{mountPoint}/{path}"
|
||||||
|
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
|
||||||
|
.Replace (
|
||||||
|
"{mountPoint}",
|
||||||
|
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
|
||||||
|
),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return JwtSecretResponse.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
|
||||||
|
member _.GetJwt (role : string, jwt : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("v1/auth/jwt/login", System.UriKind.Relative)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
|
||||||
|
|
||||||
|
let! jsonNode =
|
||||||
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
return JwtVaultResponse.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,10 +32,27 @@ type JsonRecordType =
|
|||||||
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
type ToGetExtensionMethod =
|
type ToGetExtensionMethod =
|
||||||
{
|
{
|
||||||
Tinker : string
|
Alpha : string
|
||||||
Tailor : int
|
Bravo : System.Uri
|
||||||
Soldier : System.Uri
|
Charlie : float
|
||||||
Sailor : float
|
Delta : float32
|
||||||
|
Echo : single
|
||||||
|
Foxtrot : double
|
||||||
|
Golf : int64
|
||||||
|
Hotel : uint64
|
||||||
|
India : int
|
||||||
|
Juliette : uint
|
||||||
|
Kilo : int32
|
||||||
|
Lima : uint32
|
||||||
|
Mike : int16
|
||||||
|
November : uint16
|
||||||
|
Oscar : int8
|
||||||
|
Papa : uint8
|
||||||
|
Quebec : byte
|
||||||
|
Tango : sbyte
|
||||||
|
Uniform : decimal
|
||||||
|
Victor : char
|
||||||
|
Whiskey : bigint
|
||||||
}
|
}
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
|
|||||||
19
ConsumePlugin/List.fs
Normal file
19
ConsumePlugin/List.fs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
[<CreateCatamorphism "MyListCata">]
|
||||||
|
type MyList<'a> =
|
||||||
|
| Nil
|
||||||
|
| Cons of ConsCase<'a>
|
||||||
|
|
||||||
|
and ConsCase<'a> =
|
||||||
|
{
|
||||||
|
Head : 'a
|
||||||
|
Tail : MyList<'a>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<CreateCatamorphism "MyList2Cata">]
|
||||||
|
type MyList2<'a> =
|
||||||
|
| Nil
|
||||||
|
| Cons of 'a * MyList2<'a>
|
||||||
118
ConsumePlugin/ListCata.fs
Normal file
118
ConsumePlugin/ListCata.fs
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Description of how to combine cases during a fold
|
||||||
|
type MyListCataCase<'a, 'MyList> =
|
||||||
|
/// How to operate on the Nil case
|
||||||
|
abstract Nil : 'MyList
|
||||||
|
/// How to operate on the Cons case
|
||||||
|
abstract Cons : head : 'a -> tail : 'MyList -> 'MyList
|
||||||
|
|
||||||
|
/// Specifies how to perform a fold (catamorphism) over the type MyList and its friends.
|
||||||
|
type MyListCata<'a, 'MyList> =
|
||||||
|
{
|
||||||
|
/// How to perform a fold (catamorphism) over the type MyList
|
||||||
|
MyList : MyListCataCase<'a, 'MyList>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods to perform a catamorphism over the type MyList
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module MyListCata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction<'a> =
|
||||||
|
| Process__MyList of MyList<'a>
|
||||||
|
| MyList_Cons of 'a
|
||||||
|
|
||||||
|
let private loop (cata : MyListCata<'a, 'MyList>) (instructions : ResizeArray<Instruction<'a>>) =
|
||||||
|
let myListStack = ResizeArray<'MyList> ()
|
||||||
|
|
||||||
|
while instructions.Count > 0 do
|
||||||
|
let currentInstruction = instructions.[instructions.Count - 1]
|
||||||
|
instructions.RemoveAt (instructions.Count - 1)
|
||||||
|
|
||||||
|
match currentInstruction with
|
||||||
|
| Instruction.Process__MyList (x) ->
|
||||||
|
match x with
|
||||||
|
| MyList.Nil -> cata.MyList.Nil |> myListStack.Add
|
||||||
|
| MyList.Cons ({
|
||||||
|
Head = head
|
||||||
|
Tail = tail
|
||||||
|
}) ->
|
||||||
|
instructions.Add (Instruction.MyList_Cons (head))
|
||||||
|
instructions.Add (Instruction.Process__MyList tail)
|
||||||
|
| Instruction.MyList_Cons (head) ->
|
||||||
|
let tail = myListStack.[myListStack.Count - 1]
|
||||||
|
myListStack.RemoveAt (myListStack.Count - 1)
|
||||||
|
cata.MyList.Cons head tail |> myListStack.Add
|
||||||
|
|
||||||
|
myListStack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runMyList (cata : MyListCata<'a, 'MyListRet>) (x : MyList<'a>) : 'MyListRet =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.Process__MyList x)
|
||||||
|
let myListRetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne myListRetStack
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Description of how to combine cases during a fold
|
||||||
|
type MyList2CataCase<'a, 'MyList2> =
|
||||||
|
/// How to operate on the Nil case
|
||||||
|
abstract Nil : 'MyList2
|
||||||
|
/// How to operate on the Cons case
|
||||||
|
abstract Cons : 'a -> 'MyList2 -> 'MyList2
|
||||||
|
|
||||||
|
/// Specifies how to perform a fold (catamorphism) over the type MyList2 and its friends.
|
||||||
|
type MyList2Cata<'a, 'MyList2> =
|
||||||
|
{
|
||||||
|
/// How to perform a fold (catamorphism) over the type MyList2
|
||||||
|
MyList2 : MyList2CataCase<'a, 'MyList2>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Methods to perform a catamorphism over the type MyList2
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module MyList2Cata =
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private Instruction<'a> =
|
||||||
|
| Process__MyList2 of MyList2<'a>
|
||||||
|
| MyList2_Cons of 'a
|
||||||
|
|
||||||
|
let private loop (cata : MyList2Cata<'a, 'MyList2>) (instructions : ResizeArray<Instruction<'a>>) =
|
||||||
|
let myList2Stack = ResizeArray<'MyList2> ()
|
||||||
|
|
||||||
|
while instructions.Count > 0 do
|
||||||
|
let currentInstruction = instructions.[instructions.Count - 1]
|
||||||
|
instructions.RemoveAt (instructions.Count - 1)
|
||||||
|
|
||||||
|
match currentInstruction with
|
||||||
|
| Instruction.Process__MyList2 (x) ->
|
||||||
|
match x with
|
||||||
|
| MyList2.Nil -> cata.MyList2.Nil |> myList2Stack.Add
|
||||||
|
| MyList2.Cons (arg0_0, arg1_0) ->
|
||||||
|
instructions.Add (Instruction.MyList2_Cons (arg0_0))
|
||||||
|
instructions.Add (Instruction.Process__MyList2 arg1_0)
|
||||||
|
| Instruction.MyList2_Cons (arg0_0) ->
|
||||||
|
let arg1_0 = myList2Stack.[myList2Stack.Count - 1]
|
||||||
|
myList2Stack.RemoveAt (myList2Stack.Count - 1)
|
||||||
|
cata.MyList2.Cons arg0_0 arg1_0 |> myList2Stack.Add
|
||||||
|
|
||||||
|
myList2Stack
|
||||||
|
|
||||||
|
/// Execute the catamorphism.
|
||||||
|
let runMyList2 (cata : MyList2Cata<'a, 'MyList2Ret>) (x : MyList2<'a>) : 'MyList2Ret =
|
||||||
|
let instructions = ResizeArray ()
|
||||||
|
instructions.Add (Instruction.Process__MyList2 x)
|
||||||
|
let myList2RetStack = loop cata instructions
|
||||||
|
Seq.exactlyOne myList2RetStack
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
namespace SomeNamespace
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
open WoofWare.Myriad.Plugins
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
[<GenerateMock>]
|
[<GenerateMock>]
|
||||||
@@ -8,6 +9,12 @@ type IPublicType =
|
|||||||
abstract Mem2 : string -> int
|
abstract Mem2 : string -> int
|
||||||
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
[<GenerateMock false>]
|
||||||
|
type IPublicTypeInternalFalse =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
[<GenerateMock>]
|
[<GenerateMock>]
|
||||||
type internal InternalType =
|
type internal InternalType =
|
||||||
abstract Mem1 : string * int -> unit
|
abstract Mem1 : string * int -> unit
|
||||||
@@ -18,6 +25,11 @@ type private PrivateType =
|
|||||||
abstract Mem1 : string * int -> unit
|
abstract Mem1 : string * int -> unit
|
||||||
abstract Mem2 : string -> int
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
[<GenerateMock false>]
|
||||||
|
type private PrivateTypeInternalFalse =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
[<GenerateMock>]
|
[<GenerateMock>]
|
||||||
type VeryPublicType<'a, 'b> =
|
type VeryPublicType<'a, 'b> =
|
||||||
abstract Mem1 : 'a -> 'b
|
abstract Mem1 : 'a -> 'b
|
||||||
@@ -30,3 +42,9 @@ type Curried<'a> =
|
|||||||
abstract Mem4 : (int * string) -> ('a * int) -> string
|
abstract Mem4 : (int * string) -> ('a * int) -> string
|
||||||
abstract Mem5 : x : int * string -> ('a * int) -> string
|
abstract Mem5 : x : int * string -> ('a * int) -> string
|
||||||
abstract Mem6 : int * string -> y : 'a * int -> string
|
abstract Mem6 : int * string -> y : 'a * int -> string
|
||||||
|
|
||||||
|
[<GenerateMock>]
|
||||||
|
type TypeWithInterface =
|
||||||
|
inherit IDisposable
|
||||||
|
abstract Mem1 : string option -> string[] Async
|
||||||
|
abstract Mem2 : unit -> string[] Async
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ type Gym =
|
|||||||
ReopenDate : string
|
ReopenDate : string
|
||||||
}
|
}
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
type Member =
|
type Member =
|
||||||
{
|
{
|
||||||
Id : int
|
Id : int
|
||||||
|
|||||||
@@ -11,17 +11,20 @@ open RestEase
|
|||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
[<BaseAddress "https://whatnot.com">]
|
[<BaseAddress "https://whatnot.com">]
|
||||||
type IPureGymApi =
|
type IPureGymApi =
|
||||||
[<Get "v1/gyms/">]
|
[<Get("v1/gyms/")>]
|
||||||
abstract GetGyms : ?ct : CancellationToken -> Task<Gym list>
|
abstract GetGyms : ?ct : CancellationToken -> Task<Gym list>
|
||||||
|
|
||||||
[<Get "v1/gyms/{gym_id}/attendance">]
|
[<Get "v1/gyms/{gym_id}/attendance">]
|
||||||
abstract GetGymAttendance : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<GymAttendance>
|
abstract GetGymAttendance : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<GymAttendance>
|
||||||
|
|
||||||
[<RestEase.GetAttribute "v1/member">]
|
[<Get "v1/gyms/{gym_id}/attendance">]
|
||||||
abstract GetMember : ?ct : CancellationToken -> Task<Member>
|
abstract GetGymAttendance' : [<Path("gym_id")>] gymId : int * ?ct : CancellationToken -> Task<GymAttendance>
|
||||||
|
|
||||||
[<RestEase.Get "v1/gyms/{gym_id}">]
|
[<RestEase.GetAttribute "v1/member">]
|
||||||
abstract GetGym : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<Gym>
|
abstract GetMember : ?ct : CancellationToken -> Member Task
|
||||||
|
|
||||||
|
[<RestEase.Get "v1/gyms/{gym}">]
|
||||||
|
abstract GetGym : [<Path>] gym : int * ?ct : CancellationToken -> Task<Gym>
|
||||||
|
|
||||||
[<GetAttribute "v1/member/activity">]
|
[<GetAttribute "v1/member/activity">]
|
||||||
abstract GetMemberActivity : ?ct : CancellationToken -> Task<MemberActivityDto>
|
abstract GetMemberActivity : ?ct : CancellationToken -> Task<MemberActivityDto>
|
||||||
@@ -29,11 +32,19 @@ type IPureGymApi =
|
|||||||
[<Get "some/url">]
|
[<Get "some/url">]
|
||||||
abstract GetUrl : ?ct : CancellationToken -> Task<UriThing>
|
abstract GetUrl : ?ct : CancellationToken -> Task<UriThing>
|
||||||
|
|
||||||
|
[<Post "some/url">]
|
||||||
|
abstract PostStringToString :
|
||||||
|
[<Body>] foo : Map<string, string> option * ?ct : CancellationToken -> Task<Map<string, string> option>
|
||||||
|
|
||||||
// We'll use this one to check handling of absolute URIs too
|
// We'll use this one to check handling of absolute URIs too
|
||||||
[<Get "/v2/gymSessions/member">]
|
[<Get "/v2/gymSessions/member">]
|
||||||
abstract GetSessions :
|
abstract GetSessions :
|
||||||
[<Query>] fromDate : DateOnly * [<Query>] toDate : DateOnly * ?ct : CancellationToken -> Task<Sessions>
|
[<Query>] fromDate : DateOnly * [<Query>] toDate : DateOnly * ?ct : CancellationToken -> Task<Sessions>
|
||||||
|
|
||||||
|
[<Get "/v2/gymSessions/member?foo=1">]
|
||||||
|
abstract GetSessionsWithQuery :
|
||||||
|
[<Query>] fromDate : DateOnly * [<Query>] toDate : DateOnly * ?ct : CancellationToken -> Task<Sessions>
|
||||||
|
|
||||||
// An example from RestEase's own docs
|
// An example from RestEase's own docs
|
||||||
[<Post "users/new">]
|
[<Post "users/new">]
|
||||||
abstract CreateUserString : [<Body>] user : string * ?ct : CancellationToken -> Task<string>
|
abstract CreateUserString : [<Body>] user : string * ?ct : CancellationToken -> Task<string>
|
||||||
@@ -50,6 +61,15 @@ type IPureGymApi =
|
|||||||
[<Post "users/new">]
|
[<Post "users/new">]
|
||||||
abstract CreateUserByteArr'' : [<Body>] user : byte array * ?ct : CancellationToken -> Task<Stream>
|
abstract CreateUserByteArr'' : [<Body>] user : byte array * ?ct : CancellationToken -> Task<Stream>
|
||||||
|
|
||||||
|
[<Post "users/new">]
|
||||||
|
abstract CreateUserSerialisedBody : [<Body>] user : PureGym.Member * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<Post "users/new">]
|
||||||
|
abstract CreateUserSerialisedUrlBody : [<Body>] user : Uri * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<Post "users/new">]
|
||||||
|
abstract CreateUserSerialisedIntBody : [<Body>] user : int * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
[<Post "users/new">]
|
[<Post "users/new">]
|
||||||
abstract CreateUserHttpContent :
|
abstract CreateUserHttpContent :
|
||||||
[<Body>] user : System.Net.Http.HttpContent * ?ct : CancellationToken -> Task<string>
|
[<Body>] user : System.Net.Http.HttpContent * ?ct : CancellationToken -> Task<string>
|
||||||
@@ -78,6 +98,18 @@ type IPureGymApi =
|
|||||||
[<Get "endpoint">]
|
[<Get "endpoint">]
|
||||||
abstract GetResponseMessage''' : ?ct : CancellationToken -> Task<HttpResponseMessage>
|
abstract GetResponseMessage''' : ?ct : CancellationToken -> Task<HttpResponseMessage>
|
||||||
|
|
||||||
|
[<Get "endpoint">]
|
||||||
|
abstract GetResponse : ?ct : CancellationToken -> Task<Response<MemberActivityDto>>
|
||||||
|
|
||||||
|
[<Get "endpoint">]
|
||||||
|
abstract GetResponse' : ?ct : CancellationToken -> Task<RestEase.Response<MemberActivityDto>>
|
||||||
|
|
||||||
|
[<Get "endpoint">]
|
||||||
|
abstract GetResponse'' : ?ct : CancellationToken -> Task<MemberActivityDto Response>
|
||||||
|
|
||||||
|
[<Get "endpoint">]
|
||||||
|
abstract GetResponse''' : ?ct : CancellationToken -> Task<MemberActivityDto RestEase.Response>
|
||||||
|
|
||||||
[<Get "endpoint">]
|
[<Get "endpoint">]
|
||||||
[<AllowAnyStatusCode>]
|
[<AllowAnyStatusCode>]
|
||||||
abstract GetWithAnyReturnCode : ?ct : CancellationToken -> Task<HttpResponseMessage>
|
abstract GetWithAnyReturnCode : ?ct : CancellationToken -> Task<HttpResponseMessage>
|
||||||
@@ -95,8 +127,9 @@ type internal IApiWithoutBaseAddress =
|
|||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
[<BasePath "foo">]
|
[<BasePath "foo">]
|
||||||
type IApiWithBasePath =
|
type IApiWithBasePath =
|
||||||
[<Get "endpoint/{param}">]
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
[<WoofWare.Myriad.Plugins.RestEase.Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
[<BaseAddress "https://whatnot.com">]
|
[<BaseAddress "https://whatnot.com">]
|
||||||
@@ -104,3 +137,28 @@ type IApiWithBasePath =
|
|||||||
type IApiWithBasePathAndAddress =
|
type IApiWithBasePathAndAddress =
|
||||||
[<Get "endpoint/{param}">]
|
[<Get "endpoint/{param}">]
|
||||||
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<Header("Header-Name", "Header-Value")>]
|
||||||
|
type IApiWithHeaders =
|
||||||
|
[<Header "X-Foo">]
|
||||||
|
abstract SomeHeader : string
|
||||||
|
|
||||||
|
[<Header "Authorization">]
|
||||||
|
abstract SomeOtherHeader : int
|
||||||
|
|
||||||
|
[<Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Header("Header-Name", "Header-Value")>]
|
||||||
|
type IApiWithHeaders2 =
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Header "X-Foo">]
|
||||||
|
abstract SomeHeader : string
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Header "Authorization">]
|
||||||
|
abstract SomeOtherHeader : int
|
||||||
|
|
||||||
|
[<Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam :
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|||||||
36
ConsumePlugin/SerializationAndDeserialization.fs
Normal file
36
ConsumePlugin/SerializationAndDeserialization.fs
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
type InnerTypeWithBoth =
|
||||||
|
{
|
||||||
|
[<JsonPropertyName("it's-a-me")>]
|
||||||
|
Thing : Guid
|
||||||
|
Map : Map<string, Uri>
|
||||||
|
ReadOnlyDict : IReadOnlyDictionary<string, char list>
|
||||||
|
Dict : IDictionary<Uri, bool>
|
||||||
|
ConcreteDict : Dictionary<string, InnerTypeWithBoth>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
type JsonRecordTypeWithBoth =
|
||||||
|
{
|
||||||
|
A : int
|
||||||
|
B : string
|
||||||
|
C : int list
|
||||||
|
D : InnerTypeWithBoth
|
||||||
|
E : string array
|
||||||
|
F : int[]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
type FirstDu =
|
||||||
|
| EmptyCase
|
||||||
|
| Case1 of data : string
|
||||||
|
| Case2 of record : JsonRecordTypeWithBoth * i : int
|
||||||
@@ -76,3 +76,33 @@ type IVaultClient =
|
|||||||
|
|
||||||
[<Get "v1/auth/jwt/login">]
|
[<Get "v1/auth/jwt/login">]
|
||||||
abstract GetJwt : role : string * jwt : string * ?ct : CancellationToken -> Task<JwtVaultResponse>
|
abstract GetJwt : role : string * jwt : string * ?ct : CancellationToken -> Task<JwtVaultResponse>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient false>]
|
||||||
|
type IVaultClientNonExtensionMethod =
|
||||||
|
[<Get "v1/{mountPoint}/{path}">]
|
||||||
|
abstract GetSecret :
|
||||||
|
jwt : JwtVaultResponse *
|
||||||
|
[<Path "path">] path : string *
|
||||||
|
[<Path "mountPoint">] mountPoint : string *
|
||||||
|
?ct : CancellationToken ->
|
||||||
|
Task<JwtSecretResponse>
|
||||||
|
|
||||||
|
[<Get "v1/auth/jwt/login">]
|
||||||
|
abstract GetJwt : role : string * jwt : string * ?ct : CancellationToken -> Task<JwtVaultResponse>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient(true)>]
|
||||||
|
type IVaultClientExtensionMethod =
|
||||||
|
[<Get "v1/{mountPoint}/{path}">]
|
||||||
|
abstract GetSecret :
|
||||||
|
jwt : JwtVaultResponse *
|
||||||
|
[<Path "path">] path : string *
|
||||||
|
[<Path "mountPoint">] mountPoint : string *
|
||||||
|
?ct : CancellationToken ->
|
||||||
|
Task<JwtSecretResponse>
|
||||||
|
|
||||||
|
[<Get "v1/auth/jwt/login">]
|
||||||
|
abstract GetJwt : role : string * jwt : string * ?ct : CancellationToken -> Task<JwtVaultResponse>
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type VaultClientExtensionMethod =
|
||||||
|
static member thisClashes = 99
|
||||||
|
|||||||
166
README.md
166
README.md
@@ -11,12 +11,19 @@ Some helpers in [Myriad](https://github.com/MoiraeSoftware/myriad/) which might
|
|||||||
These are currently somewhat experimental, and I personally am their primary customer.
|
These are currently somewhat experimental, and I personally am their primary customer.
|
||||||
The `RemoveOptions` generator in particular is extremely half-baked.
|
The `RemoveOptions` generator in particular is extremely half-baked.
|
||||||
|
|
||||||
|
If you would like to ensure that your particular use-case remains unbroken, please do contribute tests to this repository.
|
||||||
|
The `ConsumePlugin` assembly contains a number of invocations of these source generators,
|
||||||
|
so you just need to add copies of your types to that assembly to ensure that I will at least notice if I break the build;
|
||||||
|
and if you add tests to `WoofWare.Myriad.Plugins.Test` then I will also notice if I break the runtime semantics of the generated code.
|
||||||
|
|
||||||
Currently implemented:
|
Currently implemented:
|
||||||
|
|
||||||
* `JsonParse` (to stamp out `jsonParse : JsonNode -> 'T` methods);
|
* `JsonParse` (to stamp out `jsonParse : JsonNode -> 'T` methods);
|
||||||
|
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods);
|
||||||
* `RemoveOptions` (to strip `option` modifiers from a type).
|
* `RemoveOptions` (to strip `option` modifiers from a type).
|
||||||
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
|
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
|
||||||
* `GenerateMock` (to stamp out a record type corresponding to an interface).
|
* `GenerateMock` (to stamp out a record type corresponding to an interface).
|
||||||
|
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
|
||||||
|
|
||||||
## `JsonParse`
|
## `JsonParse`
|
||||||
|
|
||||||
@@ -74,6 +81,11 @@ module JsonRecordType =
|
|||||||
{ A = A; B = B; C = C; D = D }
|
{ A = A; B = B; C = C; D = D }
|
||||||
```
|
```
|
||||||
|
|
||||||
|
You can optionally supply the boolean `true` to the attribute,
|
||||||
|
which will cause Myriad to stamp out an extension method rather than a module with the same name as the type.
|
||||||
|
This is useful if you want to reuse the type name as a module name yourself,
|
||||||
|
or if you want to apply multiple source generators which each want to use the module name.
|
||||||
|
|
||||||
### What's the point?
|
### What's the point?
|
||||||
|
|
||||||
`System.Text.Json`, in a `PublishAot` context, relies on C# source generators.
|
`System.Text.Json`, in a `PublishAot` context, relies on C# source generators.
|
||||||
@@ -92,6 +104,55 @@ However, there is *far* more that could be done.
|
|||||||
* Make it possible to reject parsing if extra fields are present.
|
* Make it possible to reject parsing if extra fields are present.
|
||||||
* Generally support all the `System.Text.Json` attributes.
|
* Generally support all the `System.Text.Json` attributes.
|
||||||
|
|
||||||
|
For an example of using both `JsonParse` and `JsonSerialize` together with complex types, see [the type definitions](./ConsumePlugin/SerializationAndDeserialization.fs) and [tests](./WoofWare.Myriad.Plugins.Test/TestJsonSerialize/TestJsonSerde.fs).
|
||||||
|
|
||||||
|
## `JsonSerialize`
|
||||||
|
|
||||||
|
Takes records like this:
|
||||||
|
```fsharp
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
type InnerTypeWithBoth =
|
||||||
|
{
|
||||||
|
[<JsonPropertyName("it's-a-me")>]
|
||||||
|
Thing : string
|
||||||
|
ReadOnlyDict : IReadOnlyDictionary<string, Uri list>
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
and stamps out modules like this:
|
||||||
|
```fsharp
|
||||||
|
module InnerTypeWithBoth =
|
||||||
|
let toJsonNode (input : InnerTypeWithBoth) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
node.Add (("it's-a-me"), System.Text.Json.Nodes.JsonValue.Create<string> input.Thing)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"ReadOnlyDict",
|
||||||
|
(fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
for (KeyValue (key, value)) in field do
|
||||||
|
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value)
|
||||||
|
|
||||||
|
ret
|
||||||
|
) input.Map
|
||||||
|
)
|
||||||
|
|
||||||
|
node
|
||||||
|
```
|
||||||
|
|
||||||
|
Also includes an *opinionated* serializer for discriminated unions.
|
||||||
|
(Any such serializer must be opinionated, because JSON does not natively model DUs.)
|
||||||
|
|
||||||
|
As in `JsonParse`, you can optionally supply the boolean `true` to the attribute,
|
||||||
|
which will cause Myriad to stamp out an extension method rather than a module with the same name as the type.
|
||||||
|
|
||||||
|
The same limitations generally apply to `JsonSerialize` as do to `JsonParse`.
|
||||||
|
|
||||||
|
For an example of using both `JsonParse` and `JsonSerialize` together with complex types, see [the type definitions](./ConsumePlugin/SerializationAndDeserialization.fs) and [tests](./WoofWare.Myriad.Plugins.Test/TestJsonSerialize/TestJsonSerde.fs).
|
||||||
|
|
||||||
## `RemoveOptions`
|
## `RemoveOptions`
|
||||||
|
|
||||||
Takes a record like this:
|
Takes a record like this:
|
||||||
@@ -201,6 +262,11 @@ module PureGymApi =
|
|||||||
|
|
||||||
The motivating example is again ahead-of-time compilation: we wish to avoid the reflection which RestEase does.
|
The motivating example is again ahead-of-time compilation: we wish to avoid the reflection which RestEase does.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Variable and constant header values are supported:
|
||||||
|
see [the definition of `IApiWithHeaders`](./ConsumePlugin/RestApiExample.fs).
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
|
||||||
RestEase is complex, and handles a lot of different stuff.
|
RestEase is complex, and handles a lot of different stuff.
|
||||||
@@ -208,17 +274,18 @@ RestEase is complex, and handles a lot of different stuff.
|
|||||||
* If you set the `BaseAddress` on your input `HttpClient`, make sure to end with a trailing slash
|
* If you set the `BaseAddress` on your input `HttpClient`, make sure to end with a trailing slash
|
||||||
on any trailing directories (so `"blah/foo/"` rather than `"blah/foo"`).
|
on any trailing directories (so `"blah/foo/"` rather than `"blah/foo"`).
|
||||||
We combine URIs using `UriKind.Relative`, so without a trailing slash, the last component may be chopped off.
|
We combine URIs using `UriKind.Relative`, so without a trailing slash, the last component may be chopped off.
|
||||||
* Parameters are serialised solely with `ToString`, and there's no control over this;
|
* Parameters are serialised naively with `toJsonNode` as though the `JsonSerialize` generator were applied,
|
||||||
nor is there control over encoding in any sense.
|
and you can't control the serialisation. You can't yet serialise e.g. a primitive type this way (other than `String`);
|
||||||
|
all body parameters must be types which have a suitable `toJsonNode : 'a -> JsonNode` method.
|
||||||
* Deserialisation follows the same logic as the `JsonParse` generator,
|
* Deserialisation follows the same logic as the `JsonParse` generator,
|
||||||
and it generally assumes you're using types which `JsonParse` is applied to.
|
and it generally assumes you're using types which `JsonParse` is applied to.
|
||||||
* Headers are not yet supported.
|
|
||||||
* Anonymous parameters are currently forbidden.
|
* Anonymous parameters are currently forbidden.
|
||||||
|
|
||||||
There are also some design decisions:
|
There are also some design decisions:
|
||||||
|
|
||||||
* Every function must take an optional `CancellationToken` (which is good practice anyway);
|
* Every function must take an optional `CancellationToken` (which is good practice anyway);
|
||||||
so arguments are forced to be tupled.
|
so arguments are forced to be tupled.
|
||||||
|
* The `[<Optional>]` attribute is not supported and will probably not be supported, because I consider it to be cursed.
|
||||||
|
|
||||||
## `GenerateMock`
|
## `GenerateMock`
|
||||||
|
|
||||||
@@ -259,11 +326,85 @@ The [Grug-brained developer](https://grugbrain.dev/) would prefer to do this wit
|
|||||||
But since F# does not let you partially update an interface definition, we instead stamp out a record,
|
But since F# does not let you partially update an interface definition, we instead stamp out a record,
|
||||||
thereby allowing the programmer to use F#'s record-update syntax.
|
thereby allowing the programmer to use F#'s record-update syntax.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* You may supply an `isInternal : bool` argument to the attribute. By default, we make the resulting record type at most internal (never public), since this is intended only to be used in tests; but you can instead make it public with `[<GenerateMock false>]`.
|
||||||
|
|
||||||
|
## `CreateCatamorphism`
|
||||||
|
|
||||||
|
Takes a collection of mutually recursive discriminated unions:
|
||||||
|
|
||||||
|
```fsharp
|
||||||
|
[<CreateCatamorphism "MyCata">]
|
||||||
|
type Expr =
|
||||||
|
| Const of Const
|
||||||
|
| Pair of Expr * Expr * PairOpKind
|
||||||
|
| Sequential of Expr list
|
||||||
|
| Builder of Expr * ExprBuilder
|
||||||
|
|
||||||
|
and ExprBuilder =
|
||||||
|
| Child of ExprBuilder
|
||||||
|
| Parent of Expr
|
||||||
|
```
|
||||||
|
|
||||||
|
and stamps out a type like this:
|
||||||
|
```fsharp
|
||||||
|
type ExprCata<'Expr, 'ExprBuilder> =
|
||||||
|
abstract Const : Const -> 'Expr
|
||||||
|
abstract Pair : 'Expr -> 'Expr -> PairOpKind -> 'Expr
|
||||||
|
abstract Sequential : 'Expr list -> 'Expr
|
||||||
|
abstract Builder : 'Expr -> 'ExprBuilder -> 'Expr
|
||||||
|
|
||||||
|
type ExprBuilderCata<'Expr, 'ExprBuilder> =
|
||||||
|
abstract Child : 'ExprBuilder -> 'ExprBuilder
|
||||||
|
abstract Parent : 'Expr -> 'ExprBuilder
|
||||||
|
|
||||||
|
type MyCata<'Expr, 'ExprBuilder> =
|
||||||
|
{
|
||||||
|
Expr : ExprCata<'Expr, 'ExprBuilder>
|
||||||
|
ExprBuilder : ExprBuilderCata<'Expr, 'ExprBuilder>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module ExprCata =
|
||||||
|
let runExpr (cata : MyCata<'ExprRet, 'ExprBuilderRet>) (x : Expr) : 'ExprRet =
|
||||||
|
failwith "this is implemented"
|
||||||
|
|
||||||
|
let runExprBuilder (cata : MyCata<'ExprRet, 'ExprBuilderRet>) (x : ExprBuilder) : 'ExprBuilderRet =
|
||||||
|
failwith "this is implemented"
|
||||||
|
```
|
||||||
|
|
||||||
|
### What's the point?
|
||||||
|
Recursing over a tree is not easy to get right, especially if you want to avoid stack overflows.
|
||||||
|
Instead of writing the recursion many times, it's better to do it once,
|
||||||
|
and then each time you only plug in what you want to do.
|
||||||
|
|
||||||
|
### Features
|
||||||
|
|
||||||
|
* Mutually recursive DUs are supported (as in the example above).
|
||||||
|
Every DU in a recursive `type Foo... and Bar...` knot will be given an appropriate cata, as long as any one of those DUs has the `[<CreateCatamorphism>]` attribute.
|
||||||
|
* There is *limited* support for records and for lists.
|
||||||
|
* There is *extremely brittle* support for generics in the DUs you are cata'ing over.
|
||||||
|
It is based on the names of the generic parameters, so you must ensure that generic parameters with the same name have the same meaning across the various cases in your recursive knot of DUs.
|
||||||
|
(If you overstep the bounds of what this generator can do, you will get compile-time errors, e.g. with generics being constrained to each other's values.)
|
||||||
|
See the [List tests](./WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList2.fs) for an example, where we re-implement `FSharpList<'a>`.
|
||||||
|
|
||||||
### Limitations
|
### Limitations
|
||||||
|
|
||||||
* We currently only support interfaces with tupled arguments.
|
**I am not at all convinced of the correctness of this generator**, and I know it is very incomplete (in the sense that there are many possible DUs you could write for which the generator will bail out).
|
||||||
* We make the resulting record type at most internal (never public), since this is intended only to be used in tests.
|
I *strongly* recommend implementing the identity catamorphism for your type and using property-based tests ([as I do](./WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestDirectory.fs)) to assert that the correct thing happens.
|
||||||
You will therefore need an `AssemblyInfo.fs` file [like the one in WoofWare.Myriad's own tests](./ConsumePlugin/AssemblyInfo.fs).
|
Feel free to raise GitHub issues with code I can copy-paste to reproduce a case where the wrong thing happens (though I can't promise to look at them).
|
||||||
|
|
||||||
|
* This is a particularly half-baked generator which has so far seen no real-world use.
|
||||||
|
It likely has a bunch of [80/20](https://en.wikipedia.org/wiki/Pareto_principle) low-hanging fruit remaining, but it also likely has impossible problems to solve which I don't know about yet.
|
||||||
|
* Only a very few kinds of DU field are currently implemented.
|
||||||
|
For example, this generator can't see through an interface (e.g. the kind of interface one would use to implement the [crate pattern](https://www.patrickstevens.co.uk/posts/2021-10-19-crates/) to represent a [GADT](https://en.wikipedia.org/wiki/Generalized_algebraic_data_type)),
|
||||||
|
so the generated cata will simply grant you access to the interface (rather than attempting to descend into it to discover recursive references).
|
||||||
|
You can't nest lists deeply. All sorts of other cases are unaddressed.
|
||||||
|
* This generator does not try to solve the "exponential diamond dependency" problem.
|
||||||
|
If you have a case of the form `type Expr = | Branch of Expr * Expr`, the cata will walk into both `Expr`s separately.
|
||||||
|
If the `Expr`s happen to be equal, the cata will nevertheless traverse them individually (that is, it will traverse the same `Expr` twice).
|
||||||
|
Your type may represent a [DAG](https://en.wikipedia.org/wiki/Directed_acyclic_graph), but we will always effectively expand it into a tree of paths and operate on each of the exponentially-many paths.
|
||||||
|
|
||||||
# Detailed examples
|
# Detailed examples
|
||||||
|
|
||||||
@@ -275,13 +416,20 @@ For example, [PureGymDto.fs](./ConsumePlugin/PureGymDto.fs) is a real-world set
|
|||||||
* In your `.fsproj` file, define a helper variable so that subsequent steps don't all have to be kept in sync:
|
* In your `.fsproj` file, define a helper variable so that subsequent steps don't all have to be kept in sync:
|
||||||
```xml
|
```xml
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<WoofWareMyriadPluginVersion>1.1.5</WoofWareMyriadPluginVersion>
|
<WoofWareMyriadPluginVersion>2.0.1</WoofWareMyriadPluginVersion>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
```
|
```
|
||||||
* Take a reference on `WoofWare.Myriad.Plugins`:
|
* Take a reference on `WoofWare.Myriad.Plugins.Attributes` (which has no other dependencies), to obtain access to the attributes which the generator will recognise:
|
||||||
```xml
|
```xml
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="WoofWare.Myriad.Plugins" Version="$(WoofWareMyriadPluginVersion)" />
|
<PackageReference Include="WoofWare.Myriad.Plugins.Attributes" Version="2.0.2" />
|
||||||
|
</ItemGroup>
|
||||||
|
```
|
||||||
|
* Take a reference (with private assets, to prevent these from propagating to your own assembly) on `WoofWare.Myriad.Plugins`, to obtain the plugins which Myriad will run, and on `Myriad.Sdk`, to obtain the Myriad binary itself:
|
||||||
|
```xml
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="WoofWare.Myriad.Plugins" Version="$(WoofWareMyriadPluginVersion)" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Myriad.Sdk" Version="0.8.3" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
```
|
```
|
||||||
* Point Myriad to the DLL within the NuGet package which is the source of the plugins:
|
* Point Myriad to the DLL within the NuGet package which is the source of the plugins:
|
||||||
|
|||||||
81
WoofWare.Myriad.Plugins.Attributes/Attributes.fs
Normal file
81
WoofWare.Myriad.Plugins.Attributes/Attributes.fs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Attribute indicating a record type to which the "Remove Options" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
/// The purpose of this generator is to strip the `option` modifier from types.
|
||||||
|
type RemoveOptionsAttribute () =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute indicating an interface type for which the "Generate Mock" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
/// This generator creates a record which implements the interface,
|
||||||
|
/// but where each method is represented as a record field, so you can use
|
||||||
|
/// record update syntax to easily specify partially-implemented mock objects.
|
||||||
|
/// You may optionally specify `isInternal = false` to get a mock with the public visibility modifier.
|
||||||
|
type GenerateMockAttribute (isInternal : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
/// The default value of `isInternal`, the optional argument to the GenerateMockAttribute constructor.
|
||||||
|
static member DefaultIsInternal = true
|
||||||
|
|
||||||
|
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
||||||
|
new () = GenerateMockAttribute GenerateMockAttribute.DefaultIsInternal
|
||||||
|
|
||||||
|
/// Attribute indicating a record type to which the "Add JSON serializer" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
/// The purpose of this generator is to create methods (possibly extension methods) of the form
|
||||||
|
/// `{TypeName}.toJsonNode : {TypeName} -> System.Text.Json.Nodes.JsonNode`.
|
||||||
|
///
|
||||||
|
/// If you supply isExtensionMethod = true, you will get extension methods.
|
||||||
|
/// These can only be consumed from F#, but the benefit is that they don't use up the module name
|
||||||
|
/// (since by default we create a module called "{TypeName}").
|
||||||
|
type JsonSerializeAttribute (isExtensionMethod : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// The default value of `isExtensionMethod`, the optional argument to the JsonSerializeAttribute constructor.
|
||||||
|
static member DefaultIsExtensionMethod = false
|
||||||
|
|
||||||
|
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
||||||
|
new () = JsonSerializeAttribute JsonSerializeAttribute.DefaultIsExtensionMethod
|
||||||
|
|
||||||
|
/// Attribute indicating a record type to which the "Add JSON parse" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
/// The purpose of this generator is to create methods (possibly extension methods) of the form
|
||||||
|
/// `{TypeName}.jsonParse : System.Text.Json.Nodes.JsonNode -> {TypeName}`.
|
||||||
|
///
|
||||||
|
/// If you supply isExtensionMethod = true, you will get extension methods.
|
||||||
|
/// These can only be consumed from F#, but the benefit is that they don't use up the module name
|
||||||
|
/// (since by default we create a module called "{TypeName}").
|
||||||
|
type JsonParseAttribute (isExtensionMethod : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// The default value of `isExtensionMethod`, the optional argument to the JsonParseAttribute constructor.
|
||||||
|
static member DefaultIsExtensionMethod = false
|
||||||
|
|
||||||
|
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
||||||
|
new () = JsonParseAttribute JsonParseAttribute.DefaultIsExtensionMethod
|
||||||
|
|
||||||
|
/// Attribute indicating a record type to which the "create HTTP client" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
/// This generator is intended to replicate much of the functionality of RestEase,
|
||||||
|
/// i.e. to stamp out HTTP REST clients from interfaces defining the API.
|
||||||
|
///
|
||||||
|
/// If you supply isExtensionMethod = true, you will get extension methods.
|
||||||
|
/// These can only be consumed from F#, but the benefit is that they don't use up the module name
|
||||||
|
/// (since by default we create a module called "{TypeName}").
|
||||||
|
type HttpClientAttribute (isExtensionMethod : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
/// The default value of `isExtensionMethod`, the optional argument to the HttpClientAttribute constructor.
|
||||||
|
static member DefaultIsExtensionMethod = false
|
||||||
|
|
||||||
|
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
||||||
|
new () = HttpClientAttribute HttpClientAttribute.DefaultIsExtensionMethod
|
||||||
|
|
||||||
|
/// Attribute indicating a DU type to which the "create catamorphism" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
/// Supply the `typeName` for the name of the record type we will generate, which contains
|
||||||
|
/// all the catas required; for example, "MyThing" would generate:
|
||||||
|
/// type MyThing<'a, 'b> = { Du1 : Du1Cata<'a, 'b> ; Du2 : Du2Cata<'a, 'b> }.
|
||||||
|
type CreateCatamorphismAttribute (typeName : string) =
|
||||||
|
inherit Attribute ()
|
||||||
63
WoofWare.Myriad.Plugins.Attributes/RestEase.fs
Normal file
63
WoofWare.Myriad.Plugins.Attributes/RestEase.fs
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Module containing duplicates of the supported RestEase attributes, in case you don't want
|
||||||
|
/// to take a dependency on RestEase.
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module RestEase =
|
||||||
|
/// Indicates that a method represents an HTTP Get query to the specified endpoint.
|
||||||
|
type GetAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Post query to the specified endpoint.
|
||||||
|
type PostAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Delete query to the specified endpoint.
|
||||||
|
type DeleteAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Head query to the specified endpoint.
|
||||||
|
type HeadAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Options query to the specified endpoint.
|
||||||
|
type OptionsAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Put query to the specified endpoint.
|
||||||
|
type PutAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Patch query to the specified endpoint.
|
||||||
|
type PatchAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that a method represents an HTTP Trace query to the specified endpoint.
|
||||||
|
type TraceAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that this argument to a method is interpolated into the HTTP request at runtime
|
||||||
|
/// by setting a query parameter (with the given name) to the value of the annotated argument.
|
||||||
|
type QueryAttribute (paramName : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that this interface represents a REST client which accesses an API whose paths are
|
||||||
|
/// all relative to the given address.
|
||||||
|
type BaseAddressAttribute (addr : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Indicates that this interface member causes the interface to set a header with the given name,
|
||||||
|
/// whose value is obtained whenever required by a fresh call to the interface member.
|
||||||
|
type HeaderAttribute (header : string, value : string option) =
|
||||||
|
inherit Attribute ()
|
||||||
|
new (header : string) = HeaderAttribute (header, None)
|
||||||
|
new (header : string, value : string) = HeaderAttribute (header, Some value)
|
||||||
|
|
||||||
|
/// Indicates that this argument to a method is interpolated into the request path at runtime
|
||||||
|
/// by writing it into the templated string that specifies the HTTP query e.g. in the `[<Get "/foo/{template}">]`.
|
||||||
|
type PathAttribute (path : string option) =
|
||||||
|
inherit Attribute ()
|
||||||
|
new (path : string) = PathAttribute (Some path)
|
||||||
|
new () = PathAttribute None
|
||||||
53
WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt
Normal file
53
WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
WoofWare.Myriad.Plugins.CreateCatamorphismAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.CreateCatamorphismAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.GenerateMockAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.GenerateMockAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.GenerateMockAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.GenerateMockAttribute.DefaultIsInternal [static property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.GenerateMockAttribute.get_DefaultIsInternal [static method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpClientAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.HttpClientAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpClientAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.JsonParseAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.JsonParseAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.JsonParseAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.JsonSerializeAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.JsonSerializeAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.JsonSerializeAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.RestEase inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+GetAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+GetAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+HeadAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+HeadAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: (string, string option)
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: (string, string)
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+OptionsAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+OptionsAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PatchAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PatchAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PathAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: string option
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PostAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PostAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PutAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+PutAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+QueryAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+QueryAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+TraceAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+TraceAttribute..ctor [constructor]: string
|
||||||
24
WoofWare.Myriad.Plugins.Attributes/Test/TestSurface.fs
Normal file
24
WoofWare.Myriad.Plugins.Attributes/Test/TestSurface.fs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Attributes.Test
|
||||||
|
|
||||||
|
open NUnit.Framework
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
open ApiSurface
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestSurface =
|
||||||
|
let assembly = typeof<RemoveOptionsAttribute>.Assembly
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Check version against remote`` () =
|
||||||
|
MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins.Attributes"
|
||||||
|
|
||||||
|
[<Test ; Explicit>]
|
||||||
|
let ``Update API surface`` () =
|
||||||
|
ApiSurface.writeAssemblyBaseline assembly
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Ensure public API is fully documented`` () =
|
||||||
|
DocCoverage.assertFullyDocumented assembly
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net8.0</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="TestSurface.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ApiSurface" Version="4.0.40" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
|
||||||
|
<PackageReference Include="NUnit" Version="4.1.0"/>
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes.fsproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||||
|
<Authors>Patrick Stevens</Authors>
|
||||||
|
<Copyright>Copyright (c) Patrick Stevens 2024</Copyright>
|
||||||
|
<Description>Attributes to accompany the WoofWare.Myriad.Plugins source generator, so that you need take no runtime dependencies to use them.</Description>
|
||||||
|
<RepositoryType>git</RepositoryType>
|
||||||
|
<RepositoryUrl>https://github.com/Smaug123/WoofWare.Myriad</RepositoryUrl>
|
||||||
|
<PackageLicenseExpression>MIT</PackageLicenseExpression>
|
||||||
|
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||||
|
<PackageTags>myriad;fsharp;source-generator;source-gen;json</PackageTags>
|
||||||
|
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||||
|
<WarnOn>FS3559</WarnOn>
|
||||||
|
<PackageId>WoofWare.Myriad.Plugins.Attributes</PackageId>
|
||||||
|
<PackageIcon>logo.png</PackageIcon>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Attributes.fs"/>
|
||||||
|
<Compile Include="RestEase.fs" />
|
||||||
|
<EmbeddedResource Include="version.json"/>
|
||||||
|
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
||||||
|
<None Include="..\README.md">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
<None Include="../WoofWare.Myriad.Plugins/logo.png">
|
||||||
|
<Pack>True</Pack>
|
||||||
|
<PackagePath>\</PackagePath>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Update="FSharp.Core" Version="4.3.4"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
15
WoofWare.Myriad.Plugins.Attributes/version.json
Normal file
15
WoofWare.Myriad.Plugins.Attributes/version.json
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"version": "3.1",
|
||||||
|
"publicReleaseRefSpec": [
|
||||||
|
"^refs/heads/main$"
|
||||||
|
],
|
||||||
|
"pathFilters": [
|
||||||
|
":/README.md",
|
||||||
|
":/LICENSE",
|
||||||
|
":/WoofWare.Myriad.Plugins/logo.png",
|
||||||
|
":/Directory.Build.props",
|
||||||
|
":/global.json",
|
||||||
|
"./",
|
||||||
|
"^./Test"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System.Threading
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
open ConsumePlugin
|
||||||
|
open FsCheck
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestCataGenerator =
|
||||||
|
let idCata<'a, 'b> : TreeCata<'a, 'b, _, _> =
|
||||||
|
{
|
||||||
|
Tree =
|
||||||
|
{ new TreeCataCase<_, _, _, _> with
|
||||||
|
member _.Const x y = Const (x, y)
|
||||||
|
member _.Pair x y z = Pair (x, y, z)
|
||||||
|
member _.Sequential xs = Sequential xs
|
||||||
|
member _.Builder x b = Builder (x, b)
|
||||||
|
}
|
||||||
|
TreeBuilder =
|
||||||
|
{ new TreeBuilderCataCase<_, _, _, _> with
|
||||||
|
member _.Child x = Child x
|
||||||
|
member _.Parent x = Parent x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example`` () =
|
||||||
|
let x =
|
||||||
|
Tree.Pair (Tree.Const (Const.Verbatim 0, "hi"), Tree.Const (Const.String "", "bye"), PairOpKind.ThenDoSeq)
|
||||||
|
|
||||||
|
TreeCata.runTree idCata x |> shouldEqual x
|
||||||
|
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Cata works`` () =
|
||||||
|
let builderCases = ref 0
|
||||||
|
|
||||||
|
let property (x : Tree<int, string>) =
|
||||||
|
match x with
|
||||||
|
| Tree.Builder _ -> Interlocked.Increment builderCases |> ignore
|
||||||
|
| _ -> ()
|
||||||
|
|
||||||
|
TreeCata.runTree idCata x = x
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
|
builderCases.Value |> shouldBeGreaterThan 10
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open NUnit.Framework
|
||||||
|
open ConsumePlugin
|
||||||
|
open FsCheck
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestDirectory =
|
||||||
|
let idCata : FileSystemCata<_> =
|
||||||
|
{
|
||||||
|
FileSystemItem =
|
||||||
|
{ new FileSystemItemCataCase<_> with
|
||||||
|
member _.File file = FileSystemItem.File file
|
||||||
|
|
||||||
|
member _.Directory name dirSize results =
|
||||||
|
FileSystemItem.Directory
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
DirSize = dirSize
|
||||||
|
Contents = results
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: this file is preserved as an example of writing an identity cata.
|
||||||
|
// Don't add anything else to this file, because that will muddy the example.
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Cata works`` () =
|
||||||
|
let property (x : FileSystemItem) =
|
||||||
|
FileSystemItemCata.runFileSystemItem idCata x = x
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
|
|
||||||
|
// Note: this file is preserved as an example of writing an identity cata.
|
||||||
|
// Don't add anything else to this file, because that will muddy the example.
|
||||||
99
WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestGift.fs
Normal file
99
WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestGift.fs
Normal file
@@ -0,0 +1,99 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open NUnit.Framework
|
||||||
|
open ConsumePlugin
|
||||||
|
open FsCheck
|
||||||
|
open FsUnitTyped
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestGift =
|
||||||
|
|
||||||
|
let idCata : GiftCata<_> =
|
||||||
|
{
|
||||||
|
Gift =
|
||||||
|
{ new GiftCataCase<_> with
|
||||||
|
member _.Book b = Gift.Book b
|
||||||
|
member _.Boxed g = Gift.Boxed g
|
||||||
|
member _.Chocolate g = Gift.Chocolate g
|
||||||
|
member _.WithACard g message = Gift.WithACard (g, message)
|
||||||
|
member _.Wrapped g paper = Gift.Wrapped (g, paper)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let totalCostCata : GiftCata<_> =
|
||||||
|
{
|
||||||
|
Gift =
|
||||||
|
{ new GiftCataCase<_> with
|
||||||
|
member _.Book b = b.price
|
||||||
|
member _.Boxed g = g + 1.0m
|
||||||
|
member _.Chocolate c = c.price
|
||||||
|
member _.WithACard g message = g + 2.0m
|
||||||
|
member _.Wrapped g paper = g + 0.5m
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let descriptionCata : GiftCata<_> =
|
||||||
|
{
|
||||||
|
Gift =
|
||||||
|
{ new GiftCataCase<_> with
|
||||||
|
member _.Book b = b.title
|
||||||
|
member _.Boxed g = $"%s{g} in a box"
|
||||||
|
member _.Chocolate c = $"%O{c} chocolate"
|
||||||
|
|
||||||
|
member _.WithACard g message =
|
||||||
|
$"%s{g} with a card saying '%s{message}'"
|
||||||
|
|
||||||
|
member _.Wrapped g paper = $"%s{g} wrapped in %A{paper} paper"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Cata works`` () =
|
||||||
|
let property (x : Gift) = GiftCata.runGift idCata x = x
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example from docs`` () =
|
||||||
|
let wolfHall =
|
||||||
|
{
|
||||||
|
title = "Wolf Hall"
|
||||||
|
price = 20m
|
||||||
|
}
|
||||||
|
|
||||||
|
let yummyChoc =
|
||||||
|
{
|
||||||
|
chocType = SeventyPercent
|
||||||
|
price = 5m
|
||||||
|
}
|
||||||
|
|
||||||
|
let birthdayPresent =
|
||||||
|
WithACard (Wrapped (Book wolfHall, HappyBirthday), "Happy Birthday")
|
||||||
|
|
||||||
|
let christmasPresent = Wrapped (Boxed (Chocolate yummyChoc), HappyHolidays)
|
||||||
|
|
||||||
|
GiftCata.runGift totalCostCata birthdayPresent |> shouldEqual 22.5m
|
||||||
|
|
||||||
|
GiftCata.runGift descriptionCata christmasPresent
|
||||||
|
|> shouldEqual "SeventyPercent chocolate in a box wrapped in HappyHolidays paper"
|
||||||
|
|
||||||
|
let deeplyNestedBox depth =
|
||||||
|
let rec loop depth boxSoFar =
|
||||||
|
match depth with
|
||||||
|
| 0 -> boxSoFar
|
||||||
|
| n -> loop (n - 1) (Boxed boxSoFar)
|
||||||
|
|
||||||
|
loop depth (Book wolfHall)
|
||||||
|
|
||||||
|
deeplyNestedBox 10 |> GiftCata.runGift totalCostCata |> shouldEqual 30.0M
|
||||||
|
deeplyNestedBox 100 |> GiftCata.runGift totalCostCata |> shouldEqual 120.0M
|
||||||
|
deeplyNestedBox 1000 |> GiftCata.runGift totalCostCata |> shouldEqual 1020.0M
|
||||||
|
deeplyNestedBox 10000 |> GiftCata.runGift totalCostCata |> shouldEqual 10020.0M
|
||||||
|
|
||||||
|
deeplyNestedBox 100000
|
||||||
|
|> GiftCata.runGift totalCostCata
|
||||||
|
|> shouldEqual 100020.0M
|
||||||
|
|
||||||
|
deeplyNestedBox 1000000
|
||||||
|
|> GiftCata.runGift totalCostCata
|
||||||
|
|> shouldEqual 1000020.0M
|
||||||
77
WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList.fs
Normal file
77
WoofWare.Myriad.Plugins.Test/TestCataGenerator/TestMyList.fs
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsCheck
|
||||||
|
open FsUnitTyped
|
||||||
|
open ConsumePlugin
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestMyList =
|
||||||
|
|
||||||
|
let idCata<'a> : MyListCata<'a, _> =
|
||||||
|
{
|
||||||
|
MyList =
|
||||||
|
{ new MyListCataCase<'a, _> with
|
||||||
|
member _.Nil = MyList.Nil
|
||||||
|
|
||||||
|
member _.Cons head tail =
|
||||||
|
MyList.Cons
|
||||||
|
{
|
||||||
|
Head = head
|
||||||
|
Tail = tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Cata works`` () =
|
||||||
|
let property (x : MyList<int>) = MyListCata.runMyList idCata x = x
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
|
|
||||||
|
let toListCata<'a> =
|
||||||
|
{
|
||||||
|
MyList =
|
||||||
|
{ new MyListCataCase<'a, 'a list> with
|
||||||
|
member _.Nil = []
|
||||||
|
member _.Cons (head : 'a) (tail : 'a list) = head :: tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let toListViaCata<'a> (l : MyList<'a>) : 'a list = MyListCata.runMyList toListCata l
|
||||||
|
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of a fold converting to a new data structure`` () =
|
||||||
|
let rec toListNaive (l : MyList<int>) : int list =
|
||||||
|
match l with
|
||||||
|
| MyList.Nil -> []
|
||||||
|
| MyList.Cons consCell -> consCell.Head :: toListNaive consCell.Tail
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure (fun l -> toListNaive l = toListViaCata l)
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of equivalence with FoldBack`` () =
|
||||||
|
let baseCase = 0L
|
||||||
|
let atLeaf (head : int) (tail : int64) : int64 = int64 head + tail
|
||||||
|
|
||||||
|
let sumCata =
|
||||||
|
{
|
||||||
|
MyList =
|
||||||
|
{ new MyListCataCase<int, int64> with
|
||||||
|
member _.Nil = baseCase
|
||||||
|
member _.Cons (head : int) (tail : int64) = atLeaf head tail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let viaCata (l : MyList<int>) : int64 = MyListCata.runMyList sumCata l
|
||||||
|
|
||||||
|
let viaFold (l : MyList<int>) : int64 =
|
||||||
|
// choose your favourite "to list" method - here I use the cata
|
||||||
|
// but that could have been done naively
|
||||||
|
(toListViaCata l, baseCase)
|
||||||
|
||> List.foldBack (fun elt state -> atLeaf elt state)
|
||||||
|
|
||||||
|
let property (l : MyList<int>) = viaCata l = viaFold l
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsCheck
|
||||||
|
open FsUnitTyped
|
||||||
|
open ConsumePlugin
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestMyList2 =
|
||||||
|
|
||||||
|
let idCata<'a> : MyList2Cata<'a, _> =
|
||||||
|
{
|
||||||
|
MyList2 =
|
||||||
|
{ new MyList2CataCase<'a, _> with
|
||||||
|
member _.Nil = MyList2.Nil
|
||||||
|
|
||||||
|
member _.Cons (head : 'a) (tail : MyList2<'a>) = MyList2.Cons (head, tail)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Cata works`` () =
|
||||||
|
let property (x : MyList2<int>) = MyList2Cata.runMyList2 idCata x = x
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
@@ -4,7 +4,6 @@ open System
|
|||||||
open System.IO
|
open System.IO
|
||||||
open System.Net
|
open System.Net
|
||||||
open System.Net.Http
|
open System.Net.Http
|
||||||
open System.Text.Json.Nodes
|
|
||||||
open NUnit.Framework
|
open NUnit.Framework
|
||||||
open PureGym
|
open PureGym
|
||||||
open FsUnitTyped
|
open FsUnitTyped
|
||||||
@@ -103,3 +102,87 @@ module TestBodyParam =
|
|||||||
let buf = Array.zeroCreate 10
|
let buf = Array.zeroCreate 10
|
||||||
let written = observedContent.ReadAtLeast (buf.AsSpan (), 5, false)
|
let written = observedContent.ReadAtLeast (buf.AsSpan (), 5, false)
|
||||||
buf |> Array.take written |> shouldEqual contents
|
buf |> Array.take written |> shouldEqual contents
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Body param of serialised thing`` () =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Post
|
||||||
|
let! content = message.Content.ReadAsStringAsync () |> Async.AwaitTask
|
||||||
|
let content = new StringContent ("Done! " + content)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://example.com") proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
{
|
||||||
|
Id = 3
|
||||||
|
CompoundMemberId = "compound!"
|
||||||
|
FirstName = "Patrick"
|
||||||
|
LastName = "Stevens"
|
||||||
|
HomeGymId = 100
|
||||||
|
HomeGymName = "Big Boy Gym"
|
||||||
|
EmailAddress = "woof@ware"
|
||||||
|
GymAccessPin = "l3tm31n"
|
||||||
|
// To the reader: what's the significance of this date?
|
||||||
|
// answer rot13: ghevatpbzchgnovyvglragfpurvqhatfceboyrzcncre
|
||||||
|
DateOfBirth = DateOnly (1936, 05, 28)
|
||||||
|
MobileNumber = "+44-GHOST-BUSTERS"
|
||||||
|
Postcode = "W1A 111"
|
||||||
|
MembershipName = "mario"
|
||||||
|
MembershipLevel = 4
|
||||||
|
SuspendedReason = 1090
|
||||||
|
MemberStatus = -3
|
||||||
|
}
|
||||||
|
|
||||||
|
let result = api.CreateUserSerialisedBody(expected).Result
|
||||||
|
|
||||||
|
result.StartsWith ("Done! ", StringComparison.Ordinal) |> shouldEqual true
|
||||||
|
let result = result.[6..]
|
||||||
|
|
||||||
|
result
|
||||||
|
|> System.Text.Json.Nodes.JsonNode.Parse
|
||||||
|
|> PureGym.Member.jsonParse
|
||||||
|
|> shouldEqual expected
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Body param of primitive: int`` () =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Post
|
||||||
|
let! content = message.Content.ReadAsStringAsync () |> Async.AwaitTask
|
||||||
|
let content = new StringContent ("Done! " + content)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://example.com") proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
let result = api.CreateUserSerialisedIntBody(3).Result
|
||||||
|
|
||||||
|
result |> shouldEqual "Done! 3"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Body param of primitive: Uri`` () =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Post
|
||||||
|
let! content = message.Content.ReadAsStringAsync () |> Async.AwaitTask
|
||||||
|
let content = new StringContent ("Done! " + content)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://example.com") proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
let result = api.CreateUserSerialisedUrlBody(Uri "https://mything.com/blah").Result
|
||||||
|
|
||||||
|
result |> shouldEqual "Done! \"https://mything.com/blah\""
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ module TestPureGymRestApi =
|
|||||||
let api = PureGymApi.make client
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
api.GetGymAttendance(requestedGym).Result |> shouldEqual expected
|
api.GetGymAttendance(requestedGym).Result |> shouldEqual expected
|
||||||
|
api.GetGymAttendance'(requestedGym).Result |> shouldEqual expected
|
||||||
|
|
||||||
let memberCases =
|
let memberCases =
|
||||||
PureGymDtos.memberCases |> List.allPairs baseUris |> List.map TestCaseData
|
PureGymDtos.memberCases |> List.allPairs baseUris |> List.map TestCaseData
|
||||||
@@ -209,10 +210,7 @@ module TestPureGymRestApi =
|
|||||||
|
|
||||||
[<TestCaseSource(nameof sessionsCases)>]
|
[<TestCaseSource(nameof sessionsCases)>]
|
||||||
let ``Test GetSessions``
|
let ``Test GetSessions``
|
||||||
(
|
(baseUri : Uri, (startDate : DateOnly, (endDate : DateOnly, (json : string, expected : Sessions))))
|
||||||
baseUri : Uri,
|
|
||||||
(startDate : DateOnly, (endDate : DateOnly, (json : string, expected : Sessions)))
|
|
||||||
)
|
|
||||||
=
|
=
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
async {
|
async {
|
||||||
@@ -237,6 +235,33 @@ module TestPureGymRestApi =
|
|||||||
|
|
||||||
api.GetSessions(startDate, endDate).Result |> shouldEqual expected
|
api.GetSessions(startDate, endDate).Result |> shouldEqual expected
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof sessionsCases)>]
|
||||||
|
let ``Test GetSessionsWithQuery``
|
||||||
|
(baseUri : Uri, (startDate : DateOnly, (endDate : DateOnly, (json : string, expected : Sessions))))
|
||||||
|
=
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
|
|
||||||
|
// This one is specified as being absolute, in its attribute on the IPureGymApi type
|
||||||
|
let expectedUri =
|
||||||
|
let fromDate = dateOnlyToString startDate
|
||||||
|
let toDate = dateOnlyToString endDate
|
||||||
|
$"https://example.com/v2/gymSessions/member?foo=1&fromDate=%s{fromDate}&toDate=%s{toDate}"
|
||||||
|
|
||||||
|
message.RequestUri.ToString () |> shouldEqual expectedUri
|
||||||
|
|
||||||
|
let content = new StringContent (json)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make baseUri proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
api.GetSessionsWithQuery(startDate, endDate).Result |> shouldEqual expected
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``URI example`` () =
|
let ``URI example`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
@@ -260,3 +285,37 @@ module TestPureGymRestApi =
|
|||||||
uri.ToString () |> shouldEqual "https://patrick@en.wikipedia.org/wiki/foo"
|
uri.ToString () |> shouldEqual "https://patrick@en.wikipedia.org/wiki/foo"
|
||||||
uri.UserInfo |> shouldEqual "patrick"
|
uri.UserInfo |> shouldEqual "patrick"
|
||||||
uri.Host |> shouldEqual "en.wikipedia.org"
|
uri.Host |> shouldEqual "en.wikipedia.org"
|
||||||
|
|
||||||
|
[<TestCase false>]
|
||||||
|
[<TestCase true>]
|
||||||
|
let ``Map<string, string> option example`` (isSome : bool) =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Post
|
||||||
|
|
||||||
|
message.RequestUri.ToString () |> shouldEqual "https://whatnot.com/some/url"
|
||||||
|
let! content = message.Content.ReadAsStringAsync () |> Async.AwaitTask
|
||||||
|
|
||||||
|
if isSome then
|
||||||
|
content |> shouldEqual """{"hi":"bye"}"""
|
||||||
|
else
|
||||||
|
content |> shouldEqual "null"
|
||||||
|
|
||||||
|
let content = new StringContent (content)
|
||||||
|
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.makeNoUri proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
if isSome then
|
||||||
|
[ "hi", "bye" ] |> Map.ofList |> Some
|
||||||
|
else
|
||||||
|
None
|
||||||
|
|
||||||
|
let actual = api.PostStringToString(expected).Result
|
||||||
|
actual |> shouldEqual expected
|
||||||
|
|||||||
@@ -86,3 +86,36 @@ module TestReturnTypes =
|
|||||||
| _ -> failwith $"unrecognised case: %s{case}"
|
| _ -> failwith $"unrecognised case: %s{case}"
|
||||||
|
|
||||||
Object.ReferenceEquals (message, Option.get responseMessage) |> shouldEqual true
|
Object.ReferenceEquals (message, Option.get responseMessage) |> shouldEqual true
|
||||||
|
|
||||||
|
[<TestCase "Task<Response>">]
|
||||||
|
[<TestCase "Task<RestEase.Response>">]
|
||||||
|
[<TestCase "RestEase.Response Task">]
|
||||||
|
[<TestCase "RestEase.Response Task">]
|
||||||
|
let ``Response return`` (case : string) =
|
||||||
|
for json, memberDto in PureGymDtos.memberActivityDtoCases do
|
||||||
|
let mutable responseMessage = None
|
||||||
|
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
|
let content = new StringContent (json)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
responseMessage <- Some resp
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://example.com") proc
|
||||||
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
|
let response =
|
||||||
|
match case with
|
||||||
|
| "Task<Response>" -> api.GetResponse().Result
|
||||||
|
| "Task<RestEase.Response>" -> api.GetResponse'().Result
|
||||||
|
| "Response Task" -> api.GetResponse''().Result
|
||||||
|
| "RestEase.Response Task" -> api.GetResponse'''().Result
|
||||||
|
| _ -> failwith $"unrecognised case: %s{case}"
|
||||||
|
|
||||||
|
response.ResponseMessage |> shouldEqual (Option.get responseMessage)
|
||||||
|
response.StringContent |> shouldEqual json
|
||||||
|
response.GetContent () |> shouldEqual memberDto
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open System.Threading
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
open PureGym
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestVariableHeader =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Headers are set`` () : unit =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
|
|
||||||
|
message.RequestUri.ToString ()
|
||||||
|
|> shouldEqual "https://example.com/endpoint/param"
|
||||||
|
|
||||||
|
let headers =
|
||||||
|
[
|
||||||
|
for h in message.Headers do
|
||||||
|
yield $"%s{h.Key}: %s{Seq.exactlyOne h.Value}"
|
||||||
|
]
|
||||||
|
|> String.concat "\n"
|
||||||
|
|
||||||
|
let content = new StringContent (headers)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://example.com") proc
|
||||||
|
|
||||||
|
let someHeaderCount = ref 10
|
||||||
|
|
||||||
|
let someHeader () =
|
||||||
|
(Interlocked.Increment someHeaderCount : int).ToString ()
|
||||||
|
|
||||||
|
let someOtherHeaderCount = ref -100
|
||||||
|
|
||||||
|
let someOtherHeader () =
|
||||||
|
Interlocked.Increment someOtherHeaderCount
|
||||||
|
|
||||||
|
let api = ApiWithHeaders.make someHeader someOtherHeader client
|
||||||
|
|
||||||
|
someHeaderCount.Value |> shouldEqual 10
|
||||||
|
someOtherHeaderCount.Value |> shouldEqual -100
|
||||||
|
|
||||||
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|
|> Array.sort
|
||||||
|
|> shouldEqual [| "Authorization: -99" ; "Header-Name: Header-Value" ; "X-Foo: 11" |]
|
||||||
|
|
||||||
|
someHeaderCount.Value |> shouldEqual 11
|
||||||
|
someOtherHeaderCount.Value |> shouldEqual -99
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Headers get re-evaluated every time`` () : unit =
|
||||||
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
|
|
||||||
|
message.RequestUri.ToString ()
|
||||||
|
|> shouldEqual "https://example.com/endpoint/param"
|
||||||
|
|
||||||
|
let headers =
|
||||||
|
[
|
||||||
|
for h in message.Headers do
|
||||||
|
yield $"%s{h.Key}: %s{Seq.exactlyOne h.Value}"
|
||||||
|
]
|
||||||
|
|> String.concat "\n"
|
||||||
|
|
||||||
|
let content = new StringContent (headers)
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://example.com") proc
|
||||||
|
|
||||||
|
let someHeaderCount = ref 10
|
||||||
|
|
||||||
|
let someHeader () =
|
||||||
|
(Interlocked.Increment someHeaderCount : int).ToString ()
|
||||||
|
|
||||||
|
let someOtherHeaderCount = ref -100
|
||||||
|
|
||||||
|
let someOtherHeader () =
|
||||||
|
Interlocked.Increment someOtherHeaderCount
|
||||||
|
|
||||||
|
let api = ApiWithHeaders.make someHeader someOtherHeader client
|
||||||
|
|
||||||
|
someHeaderCount.Value |> shouldEqual 10
|
||||||
|
someOtherHeaderCount.Value |> shouldEqual -100
|
||||||
|
|
||||||
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|
|> Array.sort
|
||||||
|
|> shouldEqual [| "Authorization: -99" ; "Header-Name: Header-Value" ; "X-Foo: 11" |]
|
||||||
|
|
||||||
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|
|> Array.sort
|
||||||
|
|> shouldEqual [| "Authorization: -98" ; "Header-Name: Header-Value" ; "X-Foo: 12" |]
|
||||||
|
|
||||||
|
someHeaderCount.Value |> shouldEqual 12
|
||||||
|
someOtherHeaderCount.Value |> shouldEqual -98
|
||||||
@@ -87,8 +87,10 @@ module TestVaultClient =
|
|||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
[<Test>]
|
[<TestCase 1>]
|
||||||
let ``URI example`` () =
|
[<TestCase 2>]
|
||||||
|
[<TestCase 3>]
|
||||||
|
let ``URI example`` (vaultClientId : int) =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
async {
|
async {
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
@@ -112,10 +114,25 @@ module TestVaultClient =
|
|||||||
}
|
}
|
||||||
|
|
||||||
use client = HttpClientMock.make (Uri "https://my-vault.com") proc
|
use client = HttpClientMock.make (Uri "https://my-vault.com") proc
|
||||||
let api = VaultClient.make client
|
|
||||||
|
|
||||||
let vaultResponse = api.GetJwt("role", "jwt").Result
|
let value =
|
||||||
let value = api.GetSecret(vaultResponse, "path", "mount").Result
|
match vaultClientId with
|
||||||
|
| 1 ->
|
||||||
|
let api = VaultClient.make client
|
||||||
|
let vaultResponse = api.GetJwt("role", "jwt").Result
|
||||||
|
let value = api.GetSecret(vaultResponse, "path", "mount").Result
|
||||||
|
value
|
||||||
|
| 2 ->
|
||||||
|
let api = VaultClientNonExtensionMethod.make client
|
||||||
|
let vaultResponse = api.GetJwt("role", "jwt").Result
|
||||||
|
let value = api.GetSecret(vaultResponse, "path", "mount").Result
|
||||||
|
value
|
||||||
|
| 3 ->
|
||||||
|
let api = VaultClientExtensionMethod.make client
|
||||||
|
let vaultResponse = api.GetJwt("role", "jwt").Result
|
||||||
|
let value = api.GetSecret(vaultResponse, "path", "mount").Result
|
||||||
|
value
|
||||||
|
| _ -> failwith $"Unrecognised ID: %i{vaultClientId}"
|
||||||
|
|
||||||
value.Data
|
value.Data
|
||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
@@ -168,3 +185,5 @@ module TestVaultClient =
|
|||||||
"key8_1", "https://example.com/data8/1"
|
"key8_1", "https://example.com/data8/1"
|
||||||
"key8_2", "https://example.com/data8/2"
|
"key8_2", "https://example.com/data8/2"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
let _canSeePastExtensionMethod = VaultClientExtensionMethod.thisClashes
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
namespace WoofWare.Myriad.Plugins.Test
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
open System
|
open System
|
||||||
|
open System.Numerics
|
||||||
open System.Text.Json.Nodes
|
open System.Text.Json.Nodes
|
||||||
open ConsumePlugin
|
open ConsumePlugin
|
||||||
open NUnit.Framework
|
open NUnit.Framework
|
||||||
@@ -12,15 +13,62 @@ module TestExtensionMethod =
|
|||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Parse via extension method`` () =
|
let ``Parse via extension method`` () =
|
||||||
let json =
|
let json =
|
||||||
"""{"tinker": "job", "tailor": 3, "soldier": "https://example.com", "sailor": 3.1}"""
|
"""{
|
||||||
|
"alpha": "hello!",
|
||||||
|
"bravo": "https://example.com",
|
||||||
|
"charlie": 0.3341,
|
||||||
|
"delta": 110033.4,
|
||||||
|
"echo": -0.000993,
|
||||||
|
"foxtrot": -999999999999,
|
||||||
|
"golf": -123456789101112,
|
||||||
|
"hotel": 18446744073709551615,
|
||||||
|
"india": 99884,
|
||||||
|
"juliette": 12223334,
|
||||||
|
"kilo": -2147483642,
|
||||||
|
"lima": 4294967293,
|
||||||
|
"mike": -32767,
|
||||||
|
"november": 65533,
|
||||||
|
"oscar": -125,
|
||||||
|
"papa": 253,
|
||||||
|
"quebec": 254,
|
||||||
|
"tango": -3,
|
||||||
|
"uniform": 1004443.300988393349583009,
|
||||||
|
"victor": "x",
|
||||||
|
"whiskey": 123456123456123456123456123456123456123456
|
||||||
|
}"""
|
||||||
|> JsonNode.Parse
|
|> JsonNode.Parse
|
||||||
|
|
||||||
let expected =
|
let expected =
|
||||||
{
|
{
|
||||||
Tinker = "job"
|
Alpha = "hello!"
|
||||||
Tailor = 3
|
Bravo = Uri "https://example.com"
|
||||||
Soldier = Uri "https://example.com"
|
Charlie = 0.3341
|
||||||
Sailor = 3.1
|
Delta = 110033.4f
|
||||||
|
Echo = -0.000993f
|
||||||
|
Foxtrot = -999999999999.0
|
||||||
|
Golf = -123456789101112L
|
||||||
|
Hotel = 18446744073709551615UL
|
||||||
|
India = 99884
|
||||||
|
Juliette = 12223334u
|
||||||
|
Kilo = -2147483642
|
||||||
|
Lima = 4294967293u
|
||||||
|
Mike = -32767s
|
||||||
|
November = 65533us
|
||||||
|
Oscar = -125y
|
||||||
|
Papa = 253uy
|
||||||
|
Quebec = 254uy
|
||||||
|
Tango = -3y
|
||||||
|
Uniform = 1004443.300988393349583009m
|
||||||
|
Victor = 'x'
|
||||||
|
Whiskey =
|
||||||
|
let mutable i = BigInteger 0
|
||||||
|
|
||||||
|
for _ = 0 to 6 do
|
||||||
|
i <- i * BigInteger 1000000 + BigInteger 123456
|
||||||
|
|
||||||
|
i
|
||||||
}
|
}
|
||||||
|
|
||||||
ToGetExtensionMethod.jsonParse json |> shouldEqual expected
|
let actual = ToGetExtensionMethod.jsonParse json
|
||||||
|
|
||||||
|
actual |> shouldEqual expected
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ open FsUnitTyped
|
|||||||
|
|
||||||
[<TestFixture>]
|
[<TestFixture>]
|
||||||
module TestJsonParse =
|
module TestJsonParse =
|
||||||
|
let _canSeePastExtensionMethod = ToGetExtensionMethod.thisModuleWouldClash
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Single example`` () =
|
let ``Single example`` () =
|
||||||
let s =
|
let s =
|
||||||
|
|||||||
204
WoofWare.Myriad.Plugins.Test/TestJsonSerialize/TestJsonSerde.fs
Normal file
204
WoofWare.Myriad.Plugins.Test/TestJsonSerialize/TestJsonSerde.fs
Normal file
@@ -0,0 +1,204 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
open FsCheck.Random
|
||||||
|
open Microsoft.FSharp.Reflection
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsCheck
|
||||||
|
open FsUnitTyped
|
||||||
|
open ConsumePlugin
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestJsonSerde =
|
||||||
|
|
||||||
|
let uriGen : Gen<Uri> =
|
||||||
|
gen {
|
||||||
|
let! suffix = Arb.generate<int>
|
||||||
|
return Uri $"https://example.com/%i{suffix}"
|
||||||
|
}
|
||||||
|
|
||||||
|
let rec innerGen (count : int) : Gen<InnerTypeWithBoth> =
|
||||||
|
gen {
|
||||||
|
let! guid = Arb.generate<Guid>
|
||||||
|
let! mapKeys = Gen.listOf Arb.generate<NonNull<string>>
|
||||||
|
let mapKeys = mapKeys |> List.map _.Get |> List.distinct
|
||||||
|
let! mapValues = Gen.listOfLength mapKeys.Length uriGen
|
||||||
|
let map = List.zip mapKeys mapValues |> Map.ofList
|
||||||
|
|
||||||
|
let! concreteDictKeys =
|
||||||
|
if count > 0 then
|
||||||
|
Gen.listOf Arb.generate<NonNull<string>>
|
||||||
|
else
|
||||||
|
Gen.constant []
|
||||||
|
|
||||||
|
let concreteDictKeys =
|
||||||
|
concreteDictKeys
|
||||||
|
|> List.map _.Get
|
||||||
|
|> List.distinct
|
||||||
|
|> fun x -> List.take (min 3 x.Length) x
|
||||||
|
|
||||||
|
let! concreteDictValues =
|
||||||
|
if count > 0 then
|
||||||
|
Gen.listOfLength concreteDictKeys.Length (innerGen (count - 1))
|
||||||
|
else
|
||||||
|
Gen.constant []
|
||||||
|
|
||||||
|
let concreteDict =
|
||||||
|
List.zip concreteDictKeys concreteDictValues
|
||||||
|
|> List.map KeyValuePair
|
||||||
|
|> Dictionary
|
||||||
|
|
||||||
|
let! readOnlyDictKeys = Gen.listOf Arb.generate<NonNull<string>>
|
||||||
|
let readOnlyDictKeys = readOnlyDictKeys |> List.map _.Get |> List.distinct
|
||||||
|
let! readOnlyDictValues = Gen.listOfLength readOnlyDictKeys.Length (Gen.listOf Arb.generate<char>)
|
||||||
|
let readOnlyDict = List.zip readOnlyDictKeys readOnlyDictValues |> readOnlyDict
|
||||||
|
|
||||||
|
let! dictKeys = Gen.listOf uriGen
|
||||||
|
let! dictValues = Gen.listOfLength dictKeys.Length Arb.generate<bool>
|
||||||
|
let dict = List.zip dictKeys dictValues |> dict
|
||||||
|
|
||||||
|
return
|
||||||
|
{
|
||||||
|
Thing = guid
|
||||||
|
Map = map
|
||||||
|
ReadOnlyDict = readOnlyDict
|
||||||
|
Dict = dict
|
||||||
|
ConcreteDict = concreteDict
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let outerGen : Gen<JsonRecordTypeWithBoth> =
|
||||||
|
gen {
|
||||||
|
let! a = Arb.generate<int>
|
||||||
|
let! b = Arb.generate<NonNull<string>>
|
||||||
|
let! c = Gen.listOf Arb.generate<int>
|
||||||
|
let! depth = Gen.choose (0, 2)
|
||||||
|
let! d = innerGen depth
|
||||||
|
let! e = Gen.arrayOf Arb.generate<NonNull<string>>
|
||||||
|
let! f = Gen.arrayOf Arb.generate<int>
|
||||||
|
|
||||||
|
return
|
||||||
|
{
|
||||||
|
A = a
|
||||||
|
B = b.Get
|
||||||
|
C = c
|
||||||
|
D = d
|
||||||
|
E = e |> Array.map _.Get
|
||||||
|
F = f
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``It just works`` () =
|
||||||
|
let property (o : JsonRecordTypeWithBoth) : bool =
|
||||||
|
o
|
||||||
|
|> JsonRecordTypeWithBoth.toJsonNode
|
||||||
|
|> fun s -> s.ToJsonString ()
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|> JsonRecordTypeWithBoth.jsonParse
|
||||||
|
|> shouldEqual o
|
||||||
|
|
||||||
|
true
|
||||||
|
|
||||||
|
property |> Prop.forAll (Arb.fromGen outerGen) |> Check.QuickThrowOnFailure
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Guids are treated just like strings`` () =
|
||||||
|
let guidStr = "b1e7496e-6e79-4158-8579-a01de355d3b2"
|
||||||
|
let guid = Guid.Parse guidStr
|
||||||
|
|
||||||
|
let node =
|
||||||
|
{
|
||||||
|
Thing = guid
|
||||||
|
Map = Map.empty
|
||||||
|
ReadOnlyDict = readOnlyDict []
|
||||||
|
Dict = dict []
|
||||||
|
ConcreteDict = Dictionary ()
|
||||||
|
}
|
||||||
|
|> InnerTypeWithBoth.toJsonNode
|
||||||
|
|
||||||
|
node.ToJsonString ()
|
||||||
|
|> shouldEqual (
|
||||||
|
sprintf """{"it\u0027s-a-me":"%s","map":{},"readOnlyDict":{},"dict":{},"concreteDict":{}}""" guidStr
|
||||||
|
)
|
||||||
|
|
||||||
|
type Generators =
|
||||||
|
static member TestCase () =
|
||||||
|
{ new Arbitrary<InnerTypeWithBoth>() with
|
||||||
|
override x.Generator = innerGen 5
|
||||||
|
}
|
||||||
|
|
||||||
|
let sanitiseInner (r : InnerTypeWithBoth) : InnerTypeWithBoth =
|
||||||
|
{
|
||||||
|
Thing = r.Thing
|
||||||
|
Map = r.Map
|
||||||
|
ReadOnlyDict = r.ReadOnlyDict
|
||||||
|
Dict = r.Dict
|
||||||
|
ConcreteDict = r.ConcreteDict
|
||||||
|
}
|
||||||
|
|
||||||
|
let sanitiseRec (r : JsonRecordTypeWithBoth) : JsonRecordTypeWithBoth =
|
||||||
|
{
|
||||||
|
A = r.A
|
||||||
|
B = if isNull r.B then "<null>" else r.B
|
||||||
|
C =
|
||||||
|
if Object.ReferenceEquals (r.C, (null : obj)) then
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
r.C
|
||||||
|
D = sanitiseInner r.D
|
||||||
|
E = if isNull r.E then [||] else r.E
|
||||||
|
F =
|
||||||
|
if Object.ReferenceEquals (r.F, (null : obj)) then
|
||||||
|
[||]
|
||||||
|
else
|
||||||
|
r.F
|
||||||
|
}
|
||||||
|
|
||||||
|
let duGen =
|
||||||
|
gen {
|
||||||
|
let! case = Gen.choose (0, 2)
|
||||||
|
|
||||||
|
match case with
|
||||||
|
| 0 -> return FirstDu.EmptyCase
|
||||||
|
| 1 ->
|
||||||
|
let! s = Arb.generate<NonNull<string>>
|
||||||
|
return FirstDu.Case1 s.Get
|
||||||
|
| 2 ->
|
||||||
|
let! i = Arb.generate<int>
|
||||||
|
let! record = outerGen
|
||||||
|
return FirstDu.Case2 (record, i)
|
||||||
|
| _ -> return failwith $"unexpected: %i{case}"
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Discriminated union works`` () =
|
||||||
|
let property (du : FirstDu) : unit =
|
||||||
|
du
|
||||||
|
|> FirstDu.toJsonNode
|
||||||
|
|> fun s -> s.ToJsonString ()
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|> FirstDu.jsonParse
|
||||||
|
|> shouldEqual du
|
||||||
|
|
||||||
|
property |> Prop.forAll (Arb.fromGen duGen) |> Check.QuickThrowOnFailure
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``DU generator covers all cases`` () =
|
||||||
|
let rand = Random ()
|
||||||
|
let cases = FSharpType.GetUnionCases typeof<FirstDu>
|
||||||
|
let counts = Array.zeroCreate<int> cases.Length
|
||||||
|
|
||||||
|
let decompose = FSharpValue.PreComputeUnionTagReader typeof<FirstDu>
|
||||||
|
|
||||||
|
Gen.listOf duGen
|
||||||
|
|> Gen.eval 100 (StdGen.StdGen (rand.Next (), rand.Next ()))
|
||||||
|
|> List.iter (fun du ->
|
||||||
|
let tag = decompose du
|
||||||
|
counts.[tag] <- counts.[tag] + 1
|
||||||
|
)
|
||||||
|
|
||||||
|
for i in counts do
|
||||||
|
i |> shouldBeGreaterThan 0
|
||||||
@@ -6,7 +6,7 @@ open ApiSurface
|
|||||||
|
|
||||||
[<TestFixture>]
|
[<TestFixture>]
|
||||||
module TestSurface =
|
module TestSurface =
|
||||||
let assembly = typeof<RemoveOptionsAttribute>.Assembly
|
let assembly = typeof<RemoveOptionsGenerator>.Assembly
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
|
let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
|
||||||
|
|||||||
@@ -19,20 +19,26 @@
|
|||||||
<Compile Include="TestHttpClient\TestBasePath.fs" />
|
<Compile Include="TestHttpClient\TestBasePath.fs" />
|
||||||
<Compile Include="TestHttpClient\TestBodyParam.fs" />
|
<Compile Include="TestHttpClient\TestBodyParam.fs" />
|
||||||
<Compile Include="TestHttpClient\TestVaultClient.fs" />
|
<Compile Include="TestHttpClient\TestVaultClient.fs" />
|
||||||
|
<Compile Include="TestHttpClient\TestVariableHeader.fs" />
|
||||||
<Compile Include="TestMockGenerator\TestMockGenerator.fs" />
|
<Compile Include="TestMockGenerator\TestMockGenerator.fs" />
|
||||||
|
<Compile Include="TestJsonSerialize\TestJsonSerde.fs" />
|
||||||
|
<Compile Include="TestCataGenerator\TestCataGenerator.fs" />
|
||||||
|
<Compile Include="TestCataGenerator\TestDirectory.fs" />
|
||||||
|
<Compile Include="TestCataGenerator\TestGift.fs" />
|
||||||
|
<Compile Include="TestCataGenerator\TestMyList.fs" />
|
||||||
|
<Compile Include="TestCataGenerator\TestMyList2.fs" />
|
||||||
<Compile Include="TestRemoveOptions.fs"/>
|
<Compile Include="TestRemoveOptions.fs"/>
|
||||||
<Compile Include="TestSurface.fs"/>
|
<Compile Include="TestSurface.fs"/>
|
||||||
|
<None Include="../.github/workflows/dotnet.yaml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ApiSurface" Version="4.0.25"/>
|
<PackageReference Include="ApiSurface" Version="4.0.40"/>
|
||||||
<PackageReference Include="FsCheck" Version="2.16.6"/>
|
<PackageReference Include="FsCheck" Version="2.16.6"/>
|
||||||
<PackageReference Include="FsUnit" Version="6.0.0"/>
|
<PackageReference Include="FsUnit" Version="6.0.0"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
|
||||||
<PackageReference Include="NUnit" Version="4.0.1"/>
|
<PackageReference Include="NUnit" Version="4.1.0"/>
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
||||||
<PackageReference Include="NUnit.Analyzers" Version="3.10.0"/>
|
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|||||||
@@ -33,11 +33,30 @@ type internal MemberInfo =
|
|||||||
IsMutable : bool
|
IsMutable : bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type internal PropertyAccessors =
|
||||||
|
| Get
|
||||||
|
| Set
|
||||||
|
| GetSet
|
||||||
|
|
||||||
|
type internal PropertyInfo =
|
||||||
|
{
|
||||||
|
Type : SynType
|
||||||
|
Accessibility : SynAccess option
|
||||||
|
Attributes : SynAttribute list
|
||||||
|
XmlDoc : PreXmlDoc option
|
||||||
|
Accessors : PropertyAccessors
|
||||||
|
IsInline : bool
|
||||||
|
Identifier : Ident
|
||||||
|
}
|
||||||
|
|
||||||
type internal InterfaceType =
|
type internal InterfaceType =
|
||||||
{
|
{
|
||||||
Attributes : SynAttribute list
|
Attributes : SynAttribute list
|
||||||
Name : LongIdent
|
Name : LongIdent
|
||||||
|
Inherits : SynType list
|
||||||
Members : MemberInfo list
|
Members : MemberInfo list
|
||||||
|
Properties : PropertyInfo list
|
||||||
Generics : SynTyparDecls option
|
Generics : SynTyparDecls option
|
||||||
Accessibility : SynAccess option
|
Accessibility : SynAccess option
|
||||||
}
|
}
|
||||||
@@ -52,9 +71,57 @@ type internal RecordType =
|
|||||||
Accessibility : SynAccess option
|
Accessibility : SynAccess option
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Anything that is part of an ADT.
|
||||||
|
/// A record is a product of stuff; this type represents one of those stuffs.
|
||||||
|
type internal AdtNode =
|
||||||
|
{
|
||||||
|
Type : SynType
|
||||||
|
Name : Ident option
|
||||||
|
/// An ordered list, so you can look up any given generic within `this.Type`
|
||||||
|
/// to discover what its index is in the parent DU which defined it.
|
||||||
|
GenericsOfParent : SynTyparDecl list
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A DU is a sum of products (e.g. `type Thing = Foo of a * b`);
|
||||||
|
/// similarly a record is a product.
|
||||||
|
/// This type represents a product in that sense.
|
||||||
|
type internal AdtProduct =
|
||||||
|
{
|
||||||
|
Name : SynIdent
|
||||||
|
Fields : AdtNode list
|
||||||
|
/// This AdtProduct represents a product in which there might be
|
||||||
|
/// some bound type parameters. This field lists the bound
|
||||||
|
/// type parameters in the order they appeared on the parent type.
|
||||||
|
Generics : SynTyparDecl list
|
||||||
|
}
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal AstHelper =
|
module internal AstHelper =
|
||||||
|
|
||||||
|
/// Given e.g. "byte", returns "System.Byte".
|
||||||
|
let qualifyPrimitiveType (typeName : string) : LongIdent option =
|
||||||
|
match typeName with
|
||||||
|
| "float32"
|
||||||
|
| "single" -> [ "System" ; "Single" ] |> Some
|
||||||
|
| "float"
|
||||||
|
| "double" -> [ "System" ; "Double" ] |> Some
|
||||||
|
| "byte"
|
||||||
|
| "uint8" -> [ "System" ; "Byte" ] |> Some
|
||||||
|
| "sbyte"
|
||||||
|
| "int8" -> [ "System" ; "SByte" ] |> Some
|
||||||
|
| "int16" -> [ "System" ; "Int16" ] |> Some
|
||||||
|
| "int"
|
||||||
|
| "int32" -> [ "System" ; "Int32" ] |> Some
|
||||||
|
| "int64" -> [ "System" ; "Int64" ] |> Some
|
||||||
|
| "uint16" -> [ "System" ; "UInt16" ] |> Some
|
||||||
|
| "uint"
|
||||||
|
| "uint32" -> [ "System" ; "UInt32" ] |> Some
|
||||||
|
| "uint64" -> [ "System" ; "UInt64" ] |> Some
|
||||||
|
| "char" -> [ "System" ; "Char" ] |> Some
|
||||||
|
| "decimal" -> [ "System" ; "Decimal" ] |> Some
|
||||||
|
| _ -> None
|
||||||
|
|> Option.map (List.map Ident.Create)
|
||||||
|
|
||||||
let instantiateRecord (fields : (RecordFieldName * SynExpr option) list) : SynExpr =
|
let instantiateRecord (fields : (RecordFieldName * SynExpr option) list) : SynExpr =
|
||||||
let fields =
|
let fields =
|
||||||
fields
|
fields
|
||||||
@@ -89,6 +156,11 @@ module internal AstHelper =
|
|||||||
// TODO: consider Microsoft.FSharp.Option or whatever it is
|
// TODO: consider Microsoft.FSharp.Option or whatever it is
|
||||||
| _ -> false
|
| _ -> false
|
||||||
|
|
||||||
|
let isUnitIdent (ident : SynLongIdent) : bool =
|
||||||
|
match ident.LongIdent with
|
||||||
|
| [ i ] when System.String.Equals (i.idText, "unit", System.StringComparison.OrdinalIgnoreCase) -> true
|
||||||
|
| _ -> false
|
||||||
|
|
||||||
let isListIdent (ident : SynLongIdent) : bool =
|
let isListIdent (ident : SynLongIdent) : bool =
|
||||||
match ident.LongIdent with
|
match ident.LongIdent with
|
||||||
| [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true
|
| [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true
|
||||||
@@ -104,6 +176,12 @@ module internal AstHelper =
|
|||||||
true
|
true
|
||||||
| _ -> false
|
| _ -> false
|
||||||
|
|
||||||
|
let isResponseIdent (ident : SynLongIdent) : bool =
|
||||||
|
match ident.LongIdent |> List.map _.idText with
|
||||||
|
| [ "Response" ]
|
||||||
|
| [ "RestEase" ; "Response" ] -> true
|
||||||
|
| _ -> false
|
||||||
|
|
||||||
let isMapIdent (ident : SynLongIdent) : bool =
|
let isMapIdent (ident : SynLongIdent) : bool =
|
||||||
match ident.LongIdent |> List.map _.idText with
|
match ident.LongIdent |> List.map _.idText with
|
||||||
| [ "Map" ] -> true
|
| [ "Map" ] -> true
|
||||||
@@ -224,6 +302,119 @@ module internal AstHelper =
|
|||||||
((toFun (List.map fst inputArgs) inputRet), hasParen) :: args, ret
|
((toFun (List.map fst inputArgs) inputRet), hasParen) :: args, ret
|
||||||
| _ -> [], ty
|
| _ -> [], ty
|
||||||
|
|
||||||
|
let private parseMember (slotSig : SynValSig) (flags : SynMemberFlags) : Choice<MemberInfo, PropertyInfo> =
|
||||||
|
if not flags.IsInstance then
|
||||||
|
failwith "member was not an instance member"
|
||||||
|
|
||||||
|
let propertyAccessors =
|
||||||
|
match flags.MemberKind with
|
||||||
|
| SynMemberKind.Member -> None
|
||||||
|
| SynMemberKind.PropertyGet -> Some PropertyAccessors.Get
|
||||||
|
| SynMemberKind.PropertySet -> Some PropertyAccessors.Set
|
||||||
|
| SynMemberKind.PropertyGetSet -> Some PropertyAccessors.GetSet
|
||||||
|
| kind -> failwithf "Unrecognised member kind: %+A" kind
|
||||||
|
|
||||||
|
match slotSig with
|
||||||
|
| SynValSig (attrs,
|
||||||
|
SynIdent.SynIdent (ident, _),
|
||||||
|
_typeParams,
|
||||||
|
synType,
|
||||||
|
_arity,
|
||||||
|
isInline,
|
||||||
|
isMutable,
|
||||||
|
xmlDoc,
|
||||||
|
accessibility,
|
||||||
|
synExpr,
|
||||||
|
_,
|
||||||
|
_) ->
|
||||||
|
|
||||||
|
match synExpr with
|
||||||
|
| Some _ -> failwith "literal members are not supported"
|
||||||
|
| None -> ()
|
||||||
|
|
||||||
|
let attrs = attrs |> List.collect _.Attributes
|
||||||
|
|
||||||
|
let args, ret = getType synType
|
||||||
|
|
||||||
|
let args =
|
||||||
|
args
|
||||||
|
|> List.map (fun (args, hasParen) ->
|
||||||
|
match args with
|
||||||
|
| SynType.Tuple (false, path, _) -> extractTupledTypes path
|
||||||
|
| SynType.SignatureParameter _ ->
|
||||||
|
let arg, hasParen = convertSigParam args
|
||||||
|
|
||||||
|
{
|
||||||
|
HasParen = hasParen
|
||||||
|
Args = [ arg ]
|
||||||
|
}
|
||||||
|
| SynType.LongIdent (SynLongIdent (ident, _, _)) ->
|
||||||
|
{
|
||||||
|
HasParen = false
|
||||||
|
Args =
|
||||||
|
{
|
||||||
|
Attributes = []
|
||||||
|
IsOptional = false
|
||||||
|
Id = None
|
||||||
|
Type = SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent ident)
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
}
|
||||||
|
| SynType.Var (typar, _) ->
|
||||||
|
{
|
||||||
|
HasParen = false
|
||||||
|
Args =
|
||||||
|
{
|
||||||
|
Attributes = []
|
||||||
|
IsOptional = false
|
||||||
|
Id = None
|
||||||
|
Type = SynType.Var (typar, range0)
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
}
|
||||||
|
| arg ->
|
||||||
|
{
|
||||||
|
HasParen = false
|
||||||
|
Args =
|
||||||
|
{
|
||||||
|
Attributes = []
|
||||||
|
IsOptional = false
|
||||||
|
Id = None
|
||||||
|
Type = arg
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
}
|
||||||
|
|> fun ty ->
|
||||||
|
{ ty with
|
||||||
|
HasParen = ty.HasParen || hasParen
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
match propertyAccessors with
|
||||||
|
| None ->
|
||||||
|
{
|
||||||
|
ReturnType = ret
|
||||||
|
Args = args
|
||||||
|
Identifier = ident
|
||||||
|
Attributes = attrs
|
||||||
|
XmlDoc = Some xmlDoc
|
||||||
|
Accessibility = accessibility
|
||||||
|
IsInline = isInline
|
||||||
|
IsMutable = isMutable
|
||||||
|
}
|
||||||
|
|> Choice1Of2
|
||||||
|
| Some accessors ->
|
||||||
|
{
|
||||||
|
Type = ret
|
||||||
|
Accessibility = accessibility
|
||||||
|
Attributes = attrs
|
||||||
|
XmlDoc = Some xmlDoc
|
||||||
|
Accessors = accessors
|
||||||
|
IsInline = isInline
|
||||||
|
Identifier = ident
|
||||||
|
}
|
||||||
|
|> Choice2Of2
|
||||||
|
|
||||||
/// Assumes that the input type is an ObjectModel, i.e. a `type Foo = member ...`
|
/// Assumes that the input type is an ObjectModel, i.e. a `type Foo = member ...`
|
||||||
let parseInterface (interfaceType : SynTypeDefn) : InterfaceType =
|
let parseInterface (interfaceType : SynTypeDefn) : InterfaceType =
|
||||||
let (SynTypeDefn (SynComponentInfo (attrs, typars, _, interfaceName, _, _, accessibility, _),
|
let (SynTypeDefn (SynComponentInfo (attrs, typars, _, interfaceName, _, _, accessibility, _),
|
||||||
@@ -236,110 +427,101 @@ module internal AstHelper =
|
|||||||
|
|
||||||
let attrs = attrs |> List.collect (fun s -> s.Attributes)
|
let attrs = attrs |> List.collect (fun s -> s.Attributes)
|
||||||
|
|
||||||
let members =
|
let members, inherits =
|
||||||
match synTypeDefnRepr with
|
match synTypeDefnRepr with
|
||||||
| SynTypeDefnRepr.ObjectModel (_kind, members, _) ->
|
| SynTypeDefnRepr.ObjectModel (_kind, members, _) ->
|
||||||
members
|
members
|
||||||
|> List.map (fun defn ->
|
|> List.map (fun defn ->
|
||||||
match defn with
|
match defn with
|
||||||
| SynMemberDefn.AbstractSlot (slotSig, flags, _, _) ->
|
| SynMemberDefn.AbstractSlot (slotSig, flags, _, _) -> Choice1Of2 (parseMember slotSig flags)
|
||||||
match flags.MemberKind with
|
| SynMemberDefn.Inherit (baseType, _asIdent, _) -> Choice2Of2 baseType
|
||||||
| SynMemberKind.Member -> ()
|
|
||||||
| kind -> failwithf "Unrecognised member kind: %+A" kind
|
|
||||||
|
|
||||||
if not flags.IsInstance then
|
|
||||||
failwith "member was not an instance member"
|
|
||||||
|
|
||||||
match slotSig with
|
|
||||||
| SynValSig (attrs,
|
|
||||||
SynIdent.SynIdent (ident, _),
|
|
||||||
_typeParams,
|
|
||||||
synType,
|
|
||||||
arity,
|
|
||||||
isInline,
|
|
||||||
isMutable,
|
|
||||||
xmlDoc,
|
|
||||||
accessibility,
|
|
||||||
synExpr,
|
|
||||||
_,
|
|
||||||
_) ->
|
|
||||||
|
|
||||||
match synExpr with
|
|
||||||
| Some _ -> failwith "literal members are not supported"
|
|
||||||
| None -> ()
|
|
||||||
|
|
||||||
let attrs = attrs |> List.collect (fun attr -> attr.Attributes)
|
|
||||||
|
|
||||||
let args, ret = getType synType
|
|
||||||
|
|
||||||
let args =
|
|
||||||
args
|
|
||||||
|> List.map (fun (args, hasParen) ->
|
|
||||||
match args with
|
|
||||||
| SynType.Tuple (false, path, _) -> extractTupledTypes path
|
|
||||||
| SynType.SignatureParameter _ ->
|
|
||||||
let arg, hasParen = convertSigParam args
|
|
||||||
|
|
||||||
{
|
|
||||||
HasParen = hasParen
|
|
||||||
Args = [ arg ]
|
|
||||||
}
|
|
||||||
| SynType.LongIdent (SynLongIdent (ident, _, _)) ->
|
|
||||||
{
|
|
||||||
HasParen = false
|
|
||||||
Args =
|
|
||||||
{
|
|
||||||
Attributes = []
|
|
||||||
IsOptional = false
|
|
||||||
Id = None
|
|
||||||
Type =
|
|
||||||
SynType.CreateLongIdent (
|
|
||||||
SynLongIdent.CreateFromLongIdent ident
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|> List.singleton
|
|
||||||
}
|
|
||||||
| SynType.Var (typar, _) ->
|
|
||||||
{
|
|
||||||
HasParen = false
|
|
||||||
Args =
|
|
||||||
{
|
|
||||||
Attributes = []
|
|
||||||
IsOptional = false
|
|
||||||
Id = None
|
|
||||||
Type = SynType.Var (typar, range0)
|
|
||||||
}
|
|
||||||
|> List.singleton
|
|
||||||
}
|
|
||||||
| _ -> failwith $"Unrecognised args in interface method declaration: %+A{args}"
|
|
||||||
|> fun ty ->
|
|
||||||
{ ty with
|
|
||||||
HasParen = ty.HasParen || hasParen
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
{
|
|
||||||
ReturnType = ret
|
|
||||||
Args = args
|
|
||||||
Identifier = ident
|
|
||||||
Attributes = attrs
|
|
||||||
XmlDoc = Some xmlDoc
|
|
||||||
Accessibility = accessibility
|
|
||||||
IsInline = isInline
|
|
||||||
IsMutable = isMutable
|
|
||||||
}
|
|
||||||
| _ -> failwith $"Unrecognised member definition: %+A{defn}"
|
| _ -> failwith $"Unrecognised member definition: %+A{defn}"
|
||||||
)
|
)
|
||||||
| _ -> failwith $"Unrecognised SynTypeDefnRepr for an interface type: %+A{synTypeDefnRepr}"
|
| _ -> failwith $"Unrecognised SynTypeDefnRepr for an interface type: %+A{synTypeDefnRepr}"
|
||||||
|
|> List.partitionChoice
|
||||||
|
|
||||||
|
let members, properties = members |> List.partitionChoice
|
||||||
|
|
||||||
{
|
{
|
||||||
Members = members
|
Members = members
|
||||||
|
Properties = properties
|
||||||
Name = interfaceName
|
Name = interfaceName
|
||||||
|
Inherits = inherits
|
||||||
Attributes = attrs
|
Attributes = attrs
|
||||||
Generics = typars
|
Generics = typars
|
||||||
Accessibility = accessibility
|
Accessibility = accessibility
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let getUnionCases
|
||||||
|
(SynTypeDefn.SynTypeDefn (info, repr, _, _, _, _))
|
||||||
|
: AdtProduct list * SynTyparDecl list * SynAccess option
|
||||||
|
=
|
||||||
|
let typars, access =
|
||||||
|
match info with
|
||||||
|
| SynComponentInfo (_, typars, _, _, _, _, access, _) -> typars, access
|
||||||
|
|
||||||
|
let typars =
|
||||||
|
match typars with
|
||||||
|
| None -> []
|
||||||
|
| Some (SynTyparDecls.PrefixList (decls, _)) -> decls
|
||||||
|
| Some (SynTyparDecls.SinglePrefix (l, _)) -> [ l ]
|
||||||
|
| Some (SynTyparDecls.PostfixList (decls, constraints, _)) ->
|
||||||
|
if not constraints.IsEmpty then
|
||||||
|
failwith "Constrained type parameters not currently supported"
|
||||||
|
|
||||||
|
decls
|
||||||
|
|
||||||
|
match repr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_, cases, _), _) ->
|
||||||
|
let cases =
|
||||||
|
cases
|
||||||
|
|> List.map (fun (SynUnionCase.SynUnionCase (_, ident, kind, _, _, _, _)) ->
|
||||||
|
match kind with
|
||||||
|
| SynUnionCaseKind.FullType _ -> failwith "FullType union cases not supported"
|
||||||
|
| SynUnionCaseKind.Fields fields ->
|
||||||
|
{
|
||||||
|
Name = ident
|
||||||
|
Fields =
|
||||||
|
fields
|
||||||
|
|> List.map (fun (SynField.SynField (_, _, id, ty, _, _, _, _, _)) ->
|
||||||
|
{
|
||||||
|
Type = ty
|
||||||
|
Name = id
|
||||||
|
GenericsOfParent = typars
|
||||||
|
}
|
||||||
|
)
|
||||||
|
Generics = typars
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
cases, typars, access
|
||||||
|
| _ -> failwithf "Failed to get union cases for type that was: %+A" repr
|
||||||
|
|
||||||
|
let getRecordFields (SynTypeDefn.SynTypeDefn (typeInfo, repr, _, _, _, _)) : AdtNode list =
|
||||||
|
let (SynComponentInfo.SynComponentInfo (typeParams = typars)) = typeInfo
|
||||||
|
|
||||||
|
let typars =
|
||||||
|
match typars with
|
||||||
|
| None -> []
|
||||||
|
| Some (SynTyparDecls.PrefixList (decls, _)) -> decls
|
||||||
|
| Some (SynTyparDecls.SinglePrefix (l, _)) -> [ l ]
|
||||||
|
| Some (SynTyparDecls.PostfixList (decls, constraints, _)) ->
|
||||||
|
if not constraints.IsEmpty then
|
||||||
|
failwith "Constrained type parameters not currently supported"
|
||||||
|
|
||||||
|
decls
|
||||||
|
|
||||||
|
match repr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_, fields, _), _) ->
|
||||||
|
fields
|
||||||
|
|> List.map (fun (SynField.SynField (_, _, ident, ty, _, _, _, _, _)) ->
|
||||||
|
{
|
||||||
|
Name = ident
|
||||||
|
Type = ty
|
||||||
|
GenericsOfParent = typars
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| _ -> failwithf "Failed to get record elements for type that was: %+A" repr
|
||||||
|
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module internal SynTypePatterns =
|
module internal SynTypePatterns =
|
||||||
@@ -349,6 +531,11 @@ module internal SynTypePatterns =
|
|||||||
Some innerType
|
Some innerType
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|UnitType|_|) (fieldType : SynType) : unit option =
|
||||||
|
match fieldType with
|
||||||
|
| SynType.LongIdent ident when AstHelper.isUnitIdent ident -> Some ()
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|ListType|_|) (fieldType : SynType) =
|
let (|ListType|_|) (fieldType : SynType) =
|
||||||
match fieldType with
|
match fieldType with
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isListIdent ident ->
|
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isListIdent ident ->
|
||||||
@@ -362,6 +549,12 @@ module internal SynTypePatterns =
|
|||||||
| SynType.Array (1, innerType, _) -> Some innerType
|
| SynType.Array (1, innerType, _) -> Some innerType
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|RestEaseResponseType|_|) (fieldType : SynType) =
|
||||||
|
match fieldType with
|
||||||
|
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isResponseIdent ident ->
|
||||||
|
Some innerType
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|DictionaryType|_|) (fieldType : SynType) =
|
let (|DictionaryType|_|) (fieldType : SynType) =
|
||||||
match fieldType with
|
match fieldType with
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isDictionaryIdent ident ->
|
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isDictionaryIdent ident ->
|
||||||
@@ -388,12 +581,23 @@ module internal SynTypePatterns =
|
|||||||
Some (key, value)
|
Some (key, value)
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
/// Returns the string name of the type.
|
let (|BigInt|_|) (fieldType : SynType) : unit option =
|
||||||
let (|PrimitiveType|_|) (fieldType : SynType) =
|
match fieldType with
|
||||||
|
| SynType.LongIdent ident ->
|
||||||
|
match ident.LongIdent |> List.map _.idText with
|
||||||
|
| [ "bigint" ]
|
||||||
|
| [ "BigInteger" ]
|
||||||
|
| [ "Numerics" ; "BigInteger" ]
|
||||||
|
| [ "System" ; "Numerics" ; "BigInteger" ] -> Some ()
|
||||||
|
| _ -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
/// Returns the type, qualified as in e.g. `System.Boolean`.
|
||||||
|
let (|PrimitiveType|_|) (fieldType : SynType) : LongIdent option =
|
||||||
match fieldType with
|
match fieldType with
|
||||||
| SynType.LongIdent ident ->
|
| SynType.LongIdent ident ->
|
||||||
match ident.LongIdent with
|
match ident.LongIdent with
|
||||||
| [ i ] -> [ "string" ; "float" ; "int" ; "bool" ] |> List.tryFind (fun s -> s = i.idText)
|
| [ i ] -> AstHelper.qualifyPrimitiveType i.idText
|
||||||
| _ -> None
|
| _ -> None
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
@@ -416,6 +620,15 @@ module internal SynTypePatterns =
|
|||||||
| _ -> None
|
| _ -> None
|
||||||
| _ -> None
|
| _ -> None
|
||||||
|
|
||||||
|
let (|Guid|_|) (fieldType : SynType) : unit option =
|
||||||
|
match fieldType with
|
||||||
|
| SynType.LongIdent ident ->
|
||||||
|
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
||||||
|
| [ "System" ; "Guid" ]
|
||||||
|
| [ "Guid" ] -> Some ()
|
||||||
|
| _ -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
|
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
|
||||||
match fieldType with
|
match fieldType with
|
||||||
| SynType.LongIdent ident ->
|
| SynType.LongIdent ident ->
|
||||||
|
|||||||
1488
WoofWare.Myriad.Plugins/CataGenerator.fs
Normal file
1488
WoofWare.Myriad.Plugins/CataGenerator.fs
Normal file
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
14
WoofWare.Myriad.Plugins/Ident.fs
Normal file
14
WoofWare.Myriad.Plugins/Ident.fs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Text
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal Ident =
|
||||||
|
let lowerFirstLetter (x : Ident) : Ident =
|
||||||
|
let result = StringBuilder x.idText.Length
|
||||||
|
result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore
|
||||||
|
result.Append x.idText.[1..] |> ignore
|
||||||
|
Ident.Create ((result : StringBuilder).ToString ())
|
||||||
@@ -6,13 +6,10 @@ open Fantomas.FCS.SyntaxTrivia
|
|||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
open Myriad.Core
|
open Myriad.Core
|
||||||
|
|
||||||
/// Attribute indicating an interface type for which the "Generate Mock" Myriad
|
type internal GenerateMockOutputSpec =
|
||||||
/// generator should apply during build.
|
{
|
||||||
/// This generator creates a record which implements the interface,
|
IsInternal : bool
|
||||||
/// but where each method is represented as a record field, so you can use
|
}
|
||||||
/// record update syntax to easily specify partially-implemented mock objects.
|
|
||||||
type GenerateMockAttribute () =
|
|
||||||
inherit Attribute ()
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal InterfaceMockGenerator =
|
module internal InterfaceMockGenerator =
|
||||||
@@ -24,101 +21,102 @@ module internal InterfaceMockGenerator =
|
|||||||
| None -> failwith "Expected record field to have a name, but it was somehow anonymous"
|
| None -> failwith "Expected record field to have a name, but it was somehow anonymous"
|
||||||
| Some id -> id
|
| Some id -> id
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private KnownInheritance = | IDisposable
|
||||||
|
|
||||||
let createType
|
let createType
|
||||||
|
(spec : GenerateMockOutputSpec)
|
||||||
(name : string)
|
(name : string)
|
||||||
(interfaceType : InterfaceType)
|
(interfaceType : InterfaceType)
|
||||||
(xmlDoc : PreXmlDoc)
|
(xmlDoc : PreXmlDoc)
|
||||||
(fields : SynField list)
|
(fields : SynField list)
|
||||||
: SynModuleDecl
|
: SynModuleDecl
|
||||||
=
|
=
|
||||||
let synValData =
|
let inherits =
|
||||||
{
|
interfaceType.Inherits
|
||||||
SynMemberFlags.IsInstance = false
|
|> Seq.map (fun ty ->
|
||||||
SynMemberFlags.IsDispatchSlot = false
|
match ty with
|
||||||
SynMemberFlags.IsOverrideOrExplicitImpl = false
|
| SynType.LongIdent (SynLongIdent.SynLongIdent (name, _, _)) ->
|
||||||
SynMemberFlags.IsFinal = false
|
match name |> List.map _.idText with
|
||||||
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
| [] -> failwith "Unexpected empty identifier in inheritance declaration"
|
||||||
SynMemberFlags.MemberKind = SynMemberKind.Member
|
| [ "IDisposable" ]
|
||||||
}
|
| [ "System" ; "IDisposable" ] -> KnownInheritance.IDisposable
|
||||||
|
| _ -> failwithf "Unrecognised inheritance identifier: %+A" name
|
||||||
|
| x -> failwithf "Unrecognised type in inheritance: %+A" x
|
||||||
|
)
|
||||||
|
|> Set.ofSeq
|
||||||
|
|
||||||
let failwithFun =
|
let failwithFun =
|
||||||
SynExpr.createLambda
|
SynExpr.createLongIdent [ "System" ; "NotImplementedException" ]
|
||||||
"x"
|
|> SynExpr.applyTo (SynExpr.CreateConstString "Unimplemented mock function")
|
||||||
(SynExpr.CreateApp (
|
|> SynExpr.CreateParen
|
||||||
SynExpr.CreateIdentString "raise",
|
|> SynExpr.applyFunction (SynExpr.CreateIdentString "raise")
|
||||||
SynExpr.CreateParen (
|
|> SynExpr.createLambda "_"
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "NotImplementedException" ]),
|
|
||||||
SynExpr.CreateConstString "Unimplemented mock function"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
))
|
|
||||||
|
|
||||||
let constructorIdent =
|
|
||||||
let generics =
|
|
||||||
interfaceType.Generics
|
|
||||||
|> Option.map (fun generics -> SynValTyparDecls (Some generics, false))
|
|
||||||
|
|
||||||
SynPat.LongIdent (
|
|
||||||
SynLongIdent.CreateString "Empty",
|
|
||||||
None,
|
|
||||||
None, // no generics on the "Empty", only on the return type
|
|
||||||
SynArgPats.Pats (
|
|
||||||
if generics.IsNone then
|
|
||||||
[]
|
|
||||||
else
|
|
||||||
[ SynPat.CreateParen (SynPat.CreateConst SynConst.Unit) ]
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
let constructorReturnType =
|
let constructorReturnType =
|
||||||
match interfaceType.Generics with
|
match interfaceType.Generics with
|
||||||
| None -> SynType.CreateLongIdent name
|
| None -> SynType.CreateLongIdent name
|
||||||
| Some generics ->
|
| Some generics ->
|
||||||
let generics =
|
|
||||||
generics.TyparDecls
|
|
||||||
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.Var (typar, range0))
|
|
||||||
|
|
||||||
SynType.App (
|
let generics =
|
||||||
SynType.CreateLongIdent name,
|
generics.TyparDecls
|
||||||
Some range0,
|
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.Var (typar, range0))
|
||||||
generics,
|
|
||||||
List.replicate (generics.Length - 1) range0,
|
|
||||||
Some range0,
|
|
||||||
false,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|> SynBindingReturnInfo.Create
|
|
||||||
|
|
||||||
let constructor =
|
SynType.App (
|
||||||
SynMemberDefn.Member (
|
SynType.CreateLongIdent name,
|
||||||
SynBinding.SynBinding (
|
Some range0,
|
||||||
None,
|
generics,
|
||||||
SynBindingKind.Normal,
|
List.replicate (generics.Length - 1) range0,
|
||||||
false,
|
Some range0,
|
||||||
false,
|
false,
|
||||||
[],
|
|
||||||
PreXmlDoc.Empty,
|
|
||||||
SynValData.SynValData (Some synValData, SynValInfo.Empty, None),
|
|
||||||
constructorIdent,
|
|
||||||
Some constructorReturnType,
|
|
||||||
AstHelper.instantiateRecord (
|
|
||||||
fields
|
|
||||||
|> List.map (fun field ->
|
|
||||||
((SynLongIdent.CreateFromLongIdent [ getName field ], true), Some failwithFun)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
range0,
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
{ SynExpr.synBindingTriviaZero true with
|
|
||||||
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
range0
|
range0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let constructorFields =
|
||||||
|
let extras =
|
||||||
|
if inherits.Contains KnownInheritance.IDisposable then
|
||||||
|
let unitFun = SynExpr.createLambda "_" SynExpr.CreateUnit
|
||||||
|
|
||||||
|
[
|
||||||
|
(SynLongIdent.CreateFromLongIdent [ Ident.Create "Dispose" ], true), Some unitFun
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
let nonExtras =
|
||||||
|
fields
|
||||||
|
|> List.map (fun field -> (SynLongIdent.CreateFromLongIdent [ getName field ], true), Some failwithFun)
|
||||||
|
|
||||||
|
extras @ nonExtras
|
||||||
|
|
||||||
|
let constructor =
|
||||||
|
SynBinding.basic
|
||||||
|
(SynLongIdent.CreateString "Empty")
|
||||||
|
(if interfaceType.Generics.IsNone then
|
||||||
|
[]
|
||||||
|
else
|
||||||
|
[ SynPat.CreateConst SynConst.Unit ])
|
||||||
|
(AstHelper.instantiateRecord constructorFields)
|
||||||
|
|> SynBinding.makeStaticMember
|
||||||
|
|> SynBinding.withXmlDoc (PreXmlDoc.Create " An implementation where every method throws.")
|
||||||
|
|> SynBinding.withReturnAnnotation constructorReturnType
|
||||||
|
|> fun m -> SynMemberDefn.Member (m, range0)
|
||||||
|
|
||||||
|
let fields =
|
||||||
|
let extras =
|
||||||
|
if inherits.Contains KnownInheritance.IDisposable then
|
||||||
|
[
|
||||||
|
SynField.Create (
|
||||||
|
SynType.CreateFun (SynType.CreateUnit, SynType.CreateUnit),
|
||||||
|
Ident.Create "Dispose",
|
||||||
|
xmldoc = PreXmlDoc.Create " Implementation of IDisposable.Dispose"
|
||||||
|
)
|
||||||
|
]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
extras @ fields
|
||||||
|
|
||||||
let interfaceMembers =
|
let interfaceMembers =
|
||||||
let members =
|
let members =
|
||||||
interfaceType.Members
|
interfaceType.Members
|
||||||
@@ -152,7 +150,9 @@ module internal InterfaceMockGenerator =
|
|||||||
|> List.mapi (fun i arg ->
|
|> List.mapi (fun i arg ->
|
||||||
arg.Args
|
arg.Args
|
||||||
|> List.mapi (fun j arg ->
|
|> List.mapi (fun j arg ->
|
||||||
SynArgInfo.CreateIdString $"arg_%i{i}_%i{j}"
|
match arg.Type with
|
||||||
|
| UnitType -> SynArgInfo.SynArgInfo ([], false, None)
|
||||||
|
| _ -> SynArgInfo.CreateIdString $"arg_%i{i}_%i{j}"
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
],
|
],
|
||||||
@@ -167,10 +167,18 @@ module internal InterfaceMockGenerator =
|
|||||||
|> List.mapi (fun i tupledArgs ->
|
|> List.mapi (fun i tupledArgs ->
|
||||||
let args =
|
let args =
|
||||||
tupledArgs.Args
|
tupledArgs.Args
|
||||||
|> List.mapi (fun j _ -> SynPat.CreateNamed (Ident.Create $"arg_%i{i}_%i{j}"))
|
|> List.mapi (fun j ty ->
|
||||||
|
match ty.Type with
|
||||||
|
| UnitType -> SynPat.Const (SynConst.Unit, range0)
|
||||||
|
| _ -> SynPat.CreateNamed (Ident.Create $"arg_%i{i}_%i{j}")
|
||||||
|
)
|
||||||
|
|
||||||
SynPat.Tuple (false, args, List.replicate (args.Length - 1) range0, range0)
|
match args with
|
||||||
|> SynPat.CreateParen
|
| [] -> failwith "somehow got no args at all"
|
||||||
|
| [ arg ] -> arg
|
||||||
|
| args ->
|
||||||
|
SynPat.Tuple (false, args, List.replicate (args.Length - 1) range0, range0)
|
||||||
|
|> SynPat.CreateParen
|
||||||
|> fun i -> if tupledArgs.HasParen then SynPat.Paren (i, range0) else i
|
|> fun i -> if tupledArgs.HasParen then SynPat.Paren (i, range0) else i
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -189,7 +197,11 @@ module internal InterfaceMockGenerator =
|
|||||||
memberInfo.Args
|
memberInfo.Args
|
||||||
|> List.mapi (fun i args ->
|
|> List.mapi (fun i args ->
|
||||||
args.Args
|
args.Args
|
||||||
|> List.mapi (fun j args -> SynExpr.CreateIdentString $"arg_%i{i}_%i{j}")
|
|> List.mapi (fun j arg ->
|
||||||
|
match arg.Type with
|
||||||
|
| UnitType -> SynExpr.CreateConst SynConst.Unit
|
||||||
|
| _ -> SynExpr.CreateIdentString $"arg_%i{i}_%i{j}"
|
||||||
|
)
|
||||||
|> SynExpr.CreateParenedTuple
|
|> SynExpr.CreateParenedTuple
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -199,13 +211,9 @@ module internal InterfaceMockGenerator =
|
|||||||
|
|
||||||
(last, rest)
|
(last, rest)
|
||||||
||> List.fold (fun trail next -> SynExpr.CreateApp (next, trail))
|
||> List.fold (fun trail next -> SynExpr.CreateApp (next, trail))
|
||||||
|> fun args ->
|
|> SynExpr.applyFunction (
|
||||||
SynExpr.CreateApp (
|
SynExpr.createLongIdent' [ Ident.Create "this" ; memberInfo.Identifier ]
|
||||||
SynExpr.CreateLongIdent (
|
)
|
||||||
SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; memberInfo.Identifier ]
|
|
||||||
),
|
|
||||||
args
|
|
||||||
)
|
|
||||||
|
|
||||||
SynMemberDefn.Member (
|
SynMemberDefn.Member (
|
||||||
SynBinding.SynBinding (
|
SynBinding.SynBinding (
|
||||||
@@ -257,19 +265,44 @@ module internal InterfaceMockGenerator =
|
|||||||
|
|
||||||
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
|
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
|
||||||
|
|
||||||
// TODO: allow an arg to the attribute, specifying a custom visibility
|
|
||||||
let access =
|
let access =
|
||||||
match interfaceType.Accessibility with
|
match interfaceType.Accessibility, spec.IsInternal with
|
||||||
| Some (SynAccess.Public _)
|
| Some (SynAccess.Public _), true
|
||||||
| Some (SynAccess.Internal _)
|
| None, true -> SynAccess.Internal range0
|
||||||
| None -> SynAccess.Internal range0
|
| Some (SynAccess.Public _), false -> SynAccess.Public range0
|
||||||
| Some (SynAccess.Private _) -> SynAccess.Private range0
|
| None, false -> SynAccess.Public range0
|
||||||
|
| Some (SynAccess.Internal _), _ -> SynAccess.Internal range0
|
||||||
|
| Some (SynAccess.Private _), _ -> SynAccess.Private range0
|
||||||
|
|
||||||
|
let extraInterfaces =
|
||||||
|
inherits
|
||||||
|
|> Seq.map (fun inheritance ->
|
||||||
|
match inheritance with
|
||||||
|
| KnownInheritance.IDisposable ->
|
||||||
|
let binding =
|
||||||
|
SynBinding.basic
|
||||||
|
(SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; Ident.Create "Dispose" ])
|
||||||
|
[ SynPat.CreateConst SynConst.Unit ]
|
||||||
|
(SynExpr.CreateApp (SynExpr.createLongIdent [ "this" ; "Dispose" ], SynExpr.CreateUnit))
|
||||||
|
|> SynBinding.withReturnAnnotation (SynType.Unit ())
|
||||||
|
|> SynBinding.makeInstanceMember
|
||||||
|
|
||||||
|
let mem = SynMemberDefn.Member (binding, range0)
|
||||||
|
|
||||||
|
SynMemberDefn.Interface (
|
||||||
|
SynType.CreateLongIdent (SynLongIdent.Create [ "System" ; "IDisposable" ]),
|
||||||
|
Some range0,
|
||||||
|
Some [ mem ],
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
let record =
|
let record =
|
||||||
{
|
{
|
||||||
Name = Ident.Create name
|
Name = Ident.Create name
|
||||||
Fields = fields
|
Fields = fields
|
||||||
Members = Some [ constructor ; interfaceMembers ]
|
Members = Some ([ constructor ; interfaceMembers ] @ extraInterfaces)
|
||||||
XmlDoc = Some xmlDoc
|
XmlDoc = Some xmlDoc
|
||||||
Generics = interfaceType.Generics
|
Generics = interfaceType.Generics
|
||||||
Accessibility = Some access
|
Accessibility = Some access
|
||||||
@@ -312,24 +345,32 @@ module internal InterfaceMockGenerator =
|
|||||||
SynFieldTrivia.Zero
|
SynFieldTrivia.Zero
|
||||||
)
|
)
|
||||||
|
|
||||||
let createRecord (namespaceId : LongIdent) (interfaceType : SynTypeDefn) : SynModuleOrNamespace =
|
let createRecord
|
||||||
|
(namespaceId : LongIdent)
|
||||||
|
(opens : SynOpenDeclTarget list)
|
||||||
|
(interfaceType : SynTypeDefn, spec : GenerateMockOutputSpec)
|
||||||
|
: SynModuleOrNamespace
|
||||||
|
=
|
||||||
let interfaceType = AstHelper.parseInterface interfaceType
|
let interfaceType = AstHelper.parseInterface interfaceType
|
||||||
let fields = interfaceType.Members |> List.map constructMember
|
let fields = interfaceType.Members |> List.map constructMember
|
||||||
let docString = PreXmlDoc.Create " Mock record type for an interface"
|
let docString = PreXmlDoc.Create " Mock record type for an interface"
|
||||||
|
|
||||||
let name =
|
let name =
|
||||||
List.last interfaceType.Name
|
List.last interfaceType.Name
|
||||||
|> fun s -> s.idText
|
|> _.idText
|
||||||
|> fun s ->
|
|> fun s ->
|
||||||
if s.StartsWith 'I' && s.Length > 1 && Char.IsUpper s.[1] then
|
if s.StartsWith 'I' && s.Length > 1 && Char.IsUpper s.[1] then
|
||||||
s.[1..]
|
s.Substring 1
|
||||||
else
|
else
|
||||||
s
|
s
|
||||||
|> fun s -> s + "Mock"
|
|> fun s -> s + "Mock"
|
||||||
|
|
||||||
let typeDecl = createType name interfaceType docString fields
|
let typeDecl = createType spec name interfaceType docString fields
|
||||||
|
|
||||||
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ typeDecl ])
|
SynModuleOrNamespace.CreateNamespace (
|
||||||
|
namespaceId,
|
||||||
|
decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ typeDecl ]
|
||||||
|
)
|
||||||
|
|
||||||
/// Myriad generator that creates a record which implements the given interface,
|
/// Myriad generator that creates a record which implements the given interface,
|
||||||
/// but with every field mocked out.
|
/// but with every field mocked out.
|
||||||
@@ -348,15 +389,37 @@ type InterfaceMockGenerator () =
|
|||||||
let namespaceAndInterfaces =
|
let namespaceAndInterfaces =
|
||||||
types
|
types
|
||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
match types |> List.filter Ast.hasAttribute<GenerateMockAttribute> with
|
types
|
||||||
| [] -> None
|
|> List.choose (fun typeDef ->
|
||||||
| types -> Some (ns, types)
|
match Ast.getAttribute<GenerateMockAttribute> typeDef with
|
||||||
|
| None -> None
|
||||||
|
| Some attr ->
|
||||||
|
let arg =
|
||||||
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
| SynExpr.Const (SynConst.Bool value, _) -> value
|
||||||
|
| SynExpr.Const (SynConst.Unit, _) -> GenerateMockAttribute.DefaultIsInternal
|
||||||
|
| arg ->
|
||||||
|
failwith
|
||||||
|
$"Unrecognised argument %+A{arg} to [<%s{nameof GenerateMockAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
||||||
|
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
IsInternal = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
)
|
||||||
|
|> function
|
||||||
|
| [] -> None
|
||||||
|
| ty -> Some (ns, ty)
|
||||||
)
|
)
|
||||||
|
|
||||||
let opens = AstHelper.extractOpens ast
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
let modules =
|
let modules =
|
||||||
namespaceAndInterfaces
|
namespaceAndInterfaces
|
||||||
|> List.collect (fun (ns, records) -> records |> List.map (InterfaceMockGenerator.createRecord ns))
|
|> List.collect (fun (ns, records) ->
|
||||||
|
records |> List.map (InterfaceMockGenerator.createRecord ns opens)
|
||||||
|
)
|
||||||
|
|
||||||
Output.Ast modules
|
Output.Ast modules
|
||||||
|
|||||||
@@ -7,23 +7,6 @@ open Fantomas.FCS.SyntaxTrivia
|
|||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
open Myriad.Core
|
open Myriad.Core
|
||||||
|
|
||||||
/// Attribute indicating a record type to which the "Add JSON parse" Myriad
|
|
||||||
/// generator should apply during build.
|
|
||||||
/// The purpose of this generator is to create methods (possibly extension methods) of the form
|
|
||||||
/// `{TypeName}.jsonParse : System.Text.Json.Nodes.JsonNode -> {TypeName}`.
|
|
||||||
///
|
|
||||||
/// If you supply isExtensionMethod = true, you will get extension methods.
|
|
||||||
/// These can only be consumed from F#, but the benefit is that they don't use up the module name
|
|
||||||
/// (since by default we create a module called "{TypeName}").
|
|
||||||
type JsonParseAttribute (isExtensionMethod : bool) =
|
|
||||||
inherit Attribute ()
|
|
||||||
|
|
||||||
/// If changing this, *adjust the documentation strings*
|
|
||||||
static member internal DefaultIsExtensionMethod = false
|
|
||||||
|
|
||||||
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
|
||||||
new () = JsonParseAttribute JsonParseAttribute.DefaultIsExtensionMethod
|
|
||||||
|
|
||||||
type internal JsonParseOutputSpec =
|
type internal JsonParseOutputSpec =
|
||||||
{
|
{
|
||||||
ExtensionMethods : bool
|
ExtensionMethods : bool
|
||||||
@@ -48,24 +31,20 @@ module internal JsonParseGenerator =
|
|||||||
let assertNotNull (propertyName : SynExpr) (indexed : SynExpr) =
|
let assertNotNull (propertyName : SynExpr) (indexed : SynExpr) =
|
||||||
let raiseExpr =
|
let raiseExpr =
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateApp (
|
||||||
SynExpr.CreateIdentString "raise",
|
SynExpr.CreateApp (
|
||||||
SynExpr.CreateParen (
|
SynExpr.CreateIdentString "sprintf",
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateConstString "Required key '%s' not found on JSON object"
|
||||||
SynExpr.CreateLongIdent (
|
),
|
||||||
SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "KeyNotFoundException" ]
|
SynExpr.CreateParen propertyName
|
||||||
),
|
)
|
||||||
SynExpr.CreateParen (
|
|> SynExpr.CreateParen
|
||||||
SynExpr.CreateApp (
|
|> SynExpr.applyFunction (
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateLongIdent (
|
||||||
SynExpr.CreateIdentString "sprintf",
|
SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "KeyNotFoundException" ]
|
||||||
SynExpr.CreateConstString "Required key '%s' not found on JSON object"
|
|
||||||
),
|
|
||||||
SynExpr.CreateParen propertyName
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|> SynExpr.CreateParen
|
||||||
|
|> SynExpr.applyFunction (SynExpr.CreateIdentString "raise")
|
||||||
|
|
||||||
SynExpr.CreateMatch (
|
SynExpr.CreateMatch (
|
||||||
indexed,
|
indexed,
|
||||||
@@ -79,6 +58,13 @@ module internal JsonParseGenerator =
|
|||||||
/// {node}.AsValue().GetValue<{typeName}> ()
|
/// {node}.AsValue().GetValue<{typeName}> ()
|
||||||
/// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`.
|
/// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`.
|
||||||
let asValueGetValue (propertyName : SynExpr option) (typeName : string) (node : SynExpr) : SynExpr =
|
let asValueGetValue (propertyName : SynExpr option) (typeName : string) (node : SynExpr) : SynExpr =
|
||||||
|
match propertyName with
|
||||||
|
| None -> node
|
||||||
|
| Some propertyName -> assertNotNull propertyName node
|
||||||
|
|> SynExpr.callMethod "AsValue"
|
||||||
|
|> SynExpr.callGenericMethod' "GetValue" typeName
|
||||||
|
|
||||||
|
let asValueGetValueIdent (propertyName : SynExpr option) (typeName : LongIdent) (node : SynExpr) : SynExpr =
|
||||||
match propertyName with
|
match propertyName with
|
||||||
| None -> node
|
| None -> node
|
||||||
| Some propertyName -> assertNotNull propertyName node
|
| Some propertyName -> assertNotNull propertyName node
|
||||||
@@ -139,42 +125,29 @@ module internal JsonParseGenerator =
|
|||||||
|
|
||||||
/// Given e.g. "float", returns "System.Double.Parse"
|
/// Given e.g. "float", returns "System.Double.Parse"
|
||||||
let parseFunction (typeName : string) : LongIdent =
|
let parseFunction (typeName : string) : LongIdent =
|
||||||
List.append (SynExpr.qualifyPrimitiveType typeName) [ Ident.Create "Parse" ]
|
let qualified =
|
||||||
|
match AstHelper.qualifyPrimitiveType typeName with
|
||||||
|
| Some x -> x
|
||||||
|
| None -> failwith $"Could not recognise type %s{typeName} as a primitive."
|
||||||
|
|
||||||
|
List.append qualified [ Ident.Create "Parse" ]
|
||||||
|
|
||||||
/// fun kvp -> let key = {key(kvp)} in let value = {value(kvp)} in (key, value))
|
/// fun kvp -> let key = {key(kvp)} in let value = {value(kvp)} in (key, value))
|
||||||
/// The inputs will be fed with appropriate SynExprs to apply them to the `kvp.Key` and `kvp.Value` args.
|
/// The inputs will be fed with appropriate SynExprs to apply them to the `kvp.Key` and `kvp.Value` args.
|
||||||
let dictionaryMapper (key : SynExpr -> SynExpr) (value : SynExpr -> SynExpr) : SynExpr =
|
let dictionaryMapper (key : SynExpr -> SynExpr) (value : SynExpr -> SynExpr) : SynExpr =
|
||||||
let keyArg =
|
let keyArg = SynExpr.createLongIdent [ "kvp" ; "Key" ] |> SynExpr.CreateParen
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "kvp" ; "Key" ])
|
|
||||||
|> SynExpr.CreateParen
|
|
||||||
|
|
||||||
let valueArg =
|
let valueArg = SynExpr.createLongIdent [ "kvp" ; "Value" ] |> SynExpr.CreateParen
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "kvp" ; "Value" ])
|
|
||||||
|> SynExpr.CreateParen
|
|
||||||
|
|
||||||
SynExpr.LetOrUse (
|
SynExpr.CreateTuple [ SynExpr.CreateIdentString "key" ; SynExpr.CreateIdentString "value" ]
|
||||||
false,
|
|> SynExpr.createLet
|
||||||
false,
|
[
|
||||||
|
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "value"), expr = value valueArg)
|
||||||
|
]
|
||||||
|
|> SynExpr.createLet
|
||||||
[
|
[
|
||||||
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "key"), expr = key keyArg)
|
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "key"), expr = key keyArg)
|
||||||
],
|
]
|
||||||
SynExpr.LetOrUse (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
[
|
|
||||||
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "value"), expr = value valueArg)
|
|
||||||
],
|
|
||||||
SynExpr.CreateTuple [ SynExpr.CreateIdentString "key" ; SynExpr.CreateIdentString "value" ],
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
||||||
),
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> SynExpr.createLambda "kvp"
|
|> SynExpr.createLambda "kvp"
|
||||||
|
|
||||||
/// A conforming JSON object has only strings as keys. But it would be reasonable to allow the user
|
/// A conforming JSON object has only strings as keys. But it would be reasonable to allow the user
|
||||||
@@ -204,19 +177,19 @@ module internal JsonParseGenerator =
|
|||||||
| DateOnly ->
|
| DateOnly ->
|
||||||
node
|
node
|
||||||
|> asValueGetValue propertyName "string"
|
|> asValueGetValue propertyName "string"
|
||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateOnly" ; "Parse" ])
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "DateOnly" ; "Parse" ])
|
|
||||||
)
|
|
||||||
| Uri ->
|
| Uri ->
|
||||||
node
|
node
|
||||||
|> asValueGetValue propertyName "string"
|
|> asValueGetValue propertyName "string"
|
||||||
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Uri" ]))
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Uri" ])
|
||||||
|
| Guid ->
|
||||||
|
node
|
||||||
|
|> asValueGetValue propertyName "string"
|
||||||
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Guid" ; "Parse" ])
|
||||||
| DateTime ->
|
| DateTime ->
|
||||||
node
|
node
|
||||||
|> asValueGetValue propertyName "string"
|
|> asValueGetValue propertyName "string"
|
||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ])
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "DateTime" ; "Parse" ])
|
|
||||||
)
|
|
||||||
| NumberType typeName ->
|
| NumberType typeName ->
|
||||||
let basic = asValueGetValue propertyName typeName node
|
let basic = asValueGetValue propertyName typeName node
|
||||||
|
|
||||||
@@ -236,9 +209,7 @@ module internal JsonParseGenerator =
|
|||||||
|
|
||||||
let handler =
|
let handler =
|
||||||
asValueGetValue propertyName "string" node
|
asValueGetValue propertyName "string" node
|
||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (parseFunction typeName))
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent (parseFunction typeName))
|
|
||||||
)
|
|
||||||
|> SynExpr.ifThenElse
|
|> SynExpr.ifThenElse
|
||||||
(SynExpr.equals
|
(SynExpr.equals
|
||||||
option
|
option
|
||||||
@@ -263,7 +234,7 @@ module internal JsonParseGenerator =
|
|||||||
range0
|
range0
|
||||||
))
|
))
|
||||||
handler
|
handler
|
||||||
| PrimitiveType typeName -> asValueGetValue propertyName typeName node
|
| PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node
|
||||||
| OptionType ty ->
|
| OptionType ty ->
|
||||||
parseNode None options ty (SynExpr.CreateIdentString "v")
|
parseNode None options ty (SynExpr.CreateIdentString "v")
|
||||||
|> createParseLineOption node
|
|> createParseLineOption node
|
||||||
@@ -323,6 +294,11 @@ module internal JsonParseGenerator =
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Map" ; "ofSeq" ]))
|
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Map" ; "ofSeq" ]))
|
||||||
|
| BigInt ->
|
||||||
|
node
|
||||||
|
|> SynExpr.callMethod "ToJsonString"
|
||||||
|
|> SynExpr.CreateParen
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Numerics" ; "BigInteger" ; "Parse" ])
|
||||||
| _ ->
|
| _ ->
|
||||||
// Let's just hope that we've also got our own type annotation!
|
// Let's just hope that we've also got our own type annotation!
|
||||||
let typeName =
|
let typeName =
|
||||||
@@ -338,9 +314,8 @@ module internal JsonParseGenerator =
|
|||||||
/// propertyName is probably a string literal, but it could be a [<Literal>] variable
|
/// propertyName is probably a string literal, but it could be a [<Literal>] variable
|
||||||
/// The result of this function is the body of a let-binding (not including the LHS of that let-binding).
|
/// The result of this function is the body of a let-binding (not including the LHS of that let-binding).
|
||||||
let createParseRhs (options : JsonParseOption) (propertyName : SynExpr) (fieldType : SynType) : SynExpr =
|
let createParseRhs (options : JsonParseOption) (propertyName : SynExpr) (fieldType : SynType) : SynExpr =
|
||||||
SynExpr.CreateIdentString "node"
|
let objectToParse = SynExpr.CreateIdentString "node" |> SynExpr.index propertyName
|
||||||
|> SynExpr.index propertyName
|
parseNode (Some propertyName) options fieldType objectToParse
|
||||||
|> parseNode (Some propertyName) options fieldType
|
|
||||||
|
|
||||||
let isJsonNumberHandling (literal : LongIdent) : bool =
|
let isJsonNumberHandling (literal : LongIdent) : bool =
|
||||||
match List.rev literal |> List.map (fun ident -> ident.idText) with
|
match List.rev literal |> List.map (fun ident -> ident.idText) with
|
||||||
@@ -351,189 +326,36 @@ module internal JsonParseGenerator =
|
|||||||
| [ _ ; "JsonNumberHandling" ; "Serialization" ; "Json" ; "Text" ; "System" ] -> true
|
| [ _ ; "JsonNumberHandling" ; "Serialization" ; "Json" ; "Text" ; "System" ] -> true
|
||||||
| _ -> false
|
| _ -> false
|
||||||
|
|
||||||
let createMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : SynField list) =
|
/// `populateNode` will be inserted before we return the `node` variable.
|
||||||
|
///
|
||||||
|
/// That is, we give you access to a `JsonNode` called `node`,
|
||||||
|
/// and you must return a `typeName`.
|
||||||
|
let scaffolding (spec : JsonParseOutputSpec) (typeName : LongIdent) (functionBody : SynExpr) : SynModuleDecl =
|
||||||
let xmlDoc = PreXmlDoc.Create " Parse from a JSON node."
|
let xmlDoc = PreXmlDoc.Create " Parse from a JSON node."
|
||||||
|
|
||||||
let returnInfo =
|
let returnInfo = SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName)
|
||||||
SynBindingReturnInfo.Create (SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName))
|
|
||||||
|
|
||||||
let inputArg = Ident.Create "node"
|
let inputArg = Ident.Create "node"
|
||||||
let functionName = Ident.Create "jsonParse"
|
let functionName = Ident.Create "jsonParse"
|
||||||
|
|
||||||
let inputVal =
|
let arg =
|
||||||
let memberFlags =
|
SynPat.CreateNamed inputArg
|
||||||
if spec.ExtensionMethods then
|
|> SynPat.annotateType (
|
||||||
{
|
SynType.LongIdent (SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|
||||||
SynMemberFlags.IsInstance = false
|
|
||||||
SynMemberFlags.IsDispatchSlot = false
|
|
||||||
SynMemberFlags.IsOverrideOrExplicitImpl = false
|
|
||||||
SynMemberFlags.IsFinal = false
|
|
||||||
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
|
||||||
SynMemberFlags.MemberKind = SynMemberKind.Member
|
|
||||||
}
|
|
||||||
|> Some
|
|
||||||
else
|
|
||||||
None
|
|
||||||
|
|
||||||
let thisIdOpt = if spec.ExtensionMethods then None else Some inputArg
|
|
||||||
|
|
||||||
SynValData.SynValData (
|
|
||||||
memberFlags,
|
|
||||||
SynValInfo.SynValInfo ([ [ SynArgInfo.CreateId functionName ] ], SynArgInfo.Empty),
|
|
||||||
thisIdOpt
|
|
||||||
)
|
|
||||||
|
|
||||||
let assignments =
|
|
||||||
fields
|
|
||||||
|> List.map (fun (SynField (attrs, _, id, fieldType, _, _, _, _, _)) ->
|
|
||||||
let id =
|
|
||||||
match id with
|
|
||||||
| None -> failwith "didn't get an ID on field"
|
|
||||||
| Some id -> id
|
|
||||||
|
|
||||||
let attrs = attrs |> List.collect (fun l -> l.Attributes)
|
|
||||||
|
|
||||||
let propertyNameAttr =
|
|
||||||
attrs
|
|
||||||
|> List.tryFind (fun attr ->
|
|
||||||
attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
|
|
||||||
)
|
|
||||||
|
|
||||||
let options =
|
|
||||||
(JsonParseOption.None, attrs)
|
|
||||||
||> List.fold (fun options attr ->
|
|
||||||
if attr.TypeName.AsString.EndsWith ("JsonNumberHandling", StringComparison.Ordinal) then
|
|
||||||
let qualifiedEnumValue =
|
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
|
||||||
| SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when
|
|
||||||
isJsonNumberHandling ident
|
|
||||||
->
|
|
||||||
// Make sure it's fully qualified
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.Create
|
|
||||||
[
|
|
||||||
"System"
|
|
||||||
"Text"
|
|
||||||
"Json"
|
|
||||||
"Serialization"
|
|
||||||
"JsonNumberHandling"
|
|
||||||
"AllowReadingFromString"
|
|
||||||
]
|
|
||||||
)
|
|
||||||
| _ -> attr.ArgExpr
|
|
||||||
|
|
||||||
{
|
|
||||||
JsonNumberHandlingArg = Some qualifiedEnumValue
|
|
||||||
}
|
|
||||||
else
|
|
||||||
options
|
|
||||||
)
|
|
||||||
|
|
||||||
let propertyName =
|
|
||||||
match propertyNameAttr with
|
|
||||||
| None ->
|
|
||||||
let sb = StringBuilder id.idText.Length
|
|
||||||
sb.Append (Char.ToLowerInvariant id.idText.[0]) |> ignore
|
|
||||||
|
|
||||||
if id.idText.Length > 1 then
|
|
||||||
sb.Append id.idText.[1..] |> ignore
|
|
||||||
|
|
||||||
sb.ToString () |> SynConst.CreateString |> SynExpr.CreateConst
|
|
||||||
| Some name -> name.ArgExpr
|
|
||||||
|
|
||||||
let pattern =
|
|
||||||
SynPat.LongIdent (
|
|
||||||
SynLongIdent.CreateFromLongIdent [ id ],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
SynArgPats.Empty,
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
SynBinding.Let (
|
|
||||||
isInline = false,
|
|
||||||
isMutable = false,
|
|
||||||
expr = createParseRhs options propertyName fieldType,
|
|
||||||
valData = inputVal,
|
|
||||||
pattern = pattern
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
let finalConstruction =
|
|
||||||
fields
|
|
||||||
|> List.map (fun (SynField (_, _, id, _, _, _, _, _, _)) ->
|
|
||||||
let id =
|
|
||||||
match id with
|
|
||||||
| None -> failwith "Expected record field to have an identifying name"
|
|
||||||
| Some id -> id
|
|
||||||
|
|
||||||
(SynLongIdent.CreateFromLongIdent [ id ], true),
|
|
||||||
Some (SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent [ id ]))
|
|
||||||
)
|
|
||||||
|> AstHelper.instantiateRecord
|
|
||||||
|
|
||||||
let assignments =
|
|
||||||
(finalConstruction, assignments)
|
|
||||||
||> List.fold (fun final assignment ->
|
|
||||||
SynExpr.LetOrUse (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
[ assignment ],
|
|
||||||
final,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
let pattern =
|
|
||||||
SynPat.LongIdent (
|
|
||||||
SynLongIdent.CreateFromLongIdent [ functionName ],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
SynArgPats.Pats
|
|
||||||
[
|
|
||||||
SynPat.CreateTyped (
|
|
||||||
SynPat.CreateNamed inputArg,
|
|
||||||
SynType.LongIdent (
|
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|> SynPat.CreateParen
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
if spec.ExtensionMethods then
|
if spec.ExtensionMethods then
|
||||||
let binding =
|
let binding =
|
||||||
SynBinding.SynBinding (
|
SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ arg ] functionBody
|
||||||
None,
|
|> SynBinding.makeStaticMember
|
||||||
SynBindingKind.Normal,
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
false,
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
false,
|
|
||||||
[],
|
|
||||||
xmlDoc,
|
|
||||||
inputVal,
|
|
||||||
pattern,
|
|
||||||
Some returnInfo,
|
|
||||||
assignments,
|
|
||||||
range0,
|
|
||||||
DebugPointAtBinding.NoneAtInvisible,
|
|
||||||
{
|
|
||||||
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0)
|
|
||||||
InlineKeyword = None
|
|
||||||
EqualsRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let mem = SynMemberDefn.Member (binding, range0)
|
let mem = SynMemberDefn.Member (binding, range0)
|
||||||
|
|
||||||
let containingType =
|
let containingType =
|
||||||
SynTypeDefn.SynTypeDefn (
|
SynTypeDefn.SynTypeDefn (
|
||||||
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create "Extension methods for JSON parsing"),
|
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create " Extension methods for JSON parsing"),
|
||||||
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0),
|
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0),
|
||||||
[ mem ],
|
[ mem ],
|
||||||
None,
|
None,
|
||||||
@@ -547,74 +369,251 @@ module internal JsonParseGenerator =
|
|||||||
|
|
||||||
SynModuleDecl.Types ([ containingType ], range0)
|
SynModuleDecl.Types ([ containingType ], range0)
|
||||||
else
|
else
|
||||||
let binding =
|
SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ arg ] functionBody
|
||||||
SynBinding.Let (
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
isInline = false,
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
isMutable = false,
|
|> List.singleton
|
||||||
xmldoc = xmlDoc,
|
|> SynModuleDecl.CreateLet
|
||||||
returnInfo = returnInfo,
|
|
||||||
expr = assignments,
|
let getParseOptions (fieldAttrs : SynAttribute list) =
|
||||||
valData = inputVal,
|
(JsonParseOption.None, fieldAttrs)
|
||||||
pattern = pattern
|
||> List.fold (fun options attr ->
|
||||||
|
if attr.TypeName.AsString.EndsWith ("JsonNumberHandling", StringComparison.Ordinal) then
|
||||||
|
let qualifiedEnumValue =
|
||||||
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
| SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when isJsonNumberHandling ident ->
|
||||||
|
// Make sure it's fully qualified
|
||||||
|
SynExpr.createLongIdent
|
||||||
|
[
|
||||||
|
"System"
|
||||||
|
"Text"
|
||||||
|
"Json"
|
||||||
|
"Serialization"
|
||||||
|
"JsonNumberHandling"
|
||||||
|
"AllowReadingFromString"
|
||||||
|
]
|
||||||
|
| _ -> attr.ArgExpr
|
||||||
|
|
||||||
|
{
|
||||||
|
JsonNumberHandlingArg = Some qualifiedEnumValue
|
||||||
|
}
|
||||||
|
else
|
||||||
|
options
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
let createRecordMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : SynFieldData<Ident> list) =
|
||||||
|
let assignments =
|
||||||
|
fields
|
||||||
|
|> List.mapi (fun i fieldData ->
|
||||||
|
let propertyNameAttr =
|
||||||
|
fieldData.Attrs
|
||||||
|
|> List.tryFind (fun attr ->
|
||||||
|
attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
|
||||||
|
)
|
||||||
|
|
||||||
|
let options = getParseOptions fieldData.Attrs
|
||||||
|
|
||||||
|
let propertyName =
|
||||||
|
match propertyNameAttr with
|
||||||
|
| None ->
|
||||||
|
let sb = StringBuilder fieldData.Ident.idText.Length
|
||||||
|
|
||||||
|
sb.Append (Char.ToLowerInvariant fieldData.Ident.idText.[0])
|
||||||
|
|> ignore<StringBuilder>
|
||||||
|
|
||||||
|
if fieldData.Ident.idText.Length > 1 then
|
||||||
|
sb.Append (fieldData.Ident.idText.Substring 1) |> ignore<StringBuilder>
|
||||||
|
|
||||||
|
sb.ToString () |> SynConst.CreateString |> SynExpr.CreateConst
|
||||||
|
| Some name -> name.ArgExpr
|
||||||
|
|
||||||
|
createParseRhs options propertyName fieldData.Type
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateString $"arg_%i{i}") []
|
||||||
|
)
|
||||||
|
|
||||||
|
let finalConstruction =
|
||||||
|
fields
|
||||||
|
|> List.mapi (fun i fieldData ->
|
||||||
|
(SynLongIdent.CreateFromLongIdent [ fieldData.Ident ], true),
|
||||||
|
Some (SynExpr.CreateLongIdent (SynLongIdent.CreateString $"arg_%i{i}"))
|
||||||
|
)
|
||||||
|
|> AstHelper.instantiateRecord
|
||||||
|
|
||||||
|
(finalConstruction, assignments)
|
||||||
|
||> List.fold (fun final assignment -> SynExpr.createLet [ assignment ] final)
|
||||||
|
|
||||||
|
let createUnionMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : UnionCase<Ident> list) =
|
||||||
|
fields
|
||||||
|
|> List.map (fun case ->
|
||||||
|
let propertyName = JsonSerializeGenerator.getPropertyName case.Ident case.Attrs
|
||||||
|
|
||||||
|
let body =
|
||||||
|
if case.Fields.IsEmpty then
|
||||||
|
SynExpr.createLongIdent' (typeName @ [ case.Ident ])
|
||||||
|
else
|
||||||
|
case.Fields
|
||||||
|
|> List.map (fun field ->
|
||||||
|
let propertyName = JsonSerializeGenerator.getPropertyName field.Ident field.Attrs
|
||||||
|
let options = getParseOptions field.Attrs
|
||||||
|
createParseRhs options propertyName field.Type
|
||||||
|
)
|
||||||
|
|> SynExpr.CreateParenedTuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent' (typeName @ [ case.Ident ]))
|
||||||
|
|> SynExpr.createLet
|
||||||
|
[
|
||||||
|
SynExpr.index (SynExpr.CreateConstString "data") (SynExpr.CreateIdentString "node")
|
||||||
|
|> assertNotNull (SynExpr.CreateConstString "data")
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateString "node") []
|
||||||
|
]
|
||||||
|
|
||||||
|
match propertyName with
|
||||||
|
| SynExpr.Const (synConst, _) ->
|
||||||
|
SynMatchClause.SynMatchClause (
|
||||||
|
SynPat.CreateConst synConst,
|
||||||
|
None,
|
||||||
|
body,
|
||||||
|
range0,
|
||||||
|
DebugPointAtTarget.Yes,
|
||||||
|
{
|
||||||
|
ArrowRange = Some range0
|
||||||
|
BarRange = Some range0
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
| _ ->
|
||||||
|
SynMatchClause.SynMatchClause (
|
||||||
|
SynPat.CreateNamed (Ident.Create "x"),
|
||||||
|
Some (SynExpr.equals (SynExpr.CreateIdentString "x") propertyName),
|
||||||
|
body,
|
||||||
|
range0,
|
||||||
|
DebugPointAtTarget.Yes,
|
||||||
|
{
|
||||||
|
ArrowRange = Some range0
|
||||||
|
BarRange = Some range0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> fun l ->
|
||||||
|
l
|
||||||
|
@ [
|
||||||
|
let fail =
|
||||||
|
SynExpr.plus
|
||||||
|
(SynExpr.CreateConstString "Unrecognised 'type' field value: ")
|
||||||
|
(SynExpr.CreateIdentString "v")
|
||||||
|
|> SynExpr.CreateParen
|
||||||
|
|> SynExpr.applyFunction (SynExpr.CreateIdentString "failwith")
|
||||||
|
|
||||||
SynModuleDecl.CreateLet [ binding ]
|
SynMatchClause.SynMatchClause (
|
||||||
|
SynPat.CreateNamed (Ident.Create "v"),
|
||||||
|
None,
|
||||||
|
fail,
|
||||||
|
range0,
|
||||||
|
DebugPointAtTarget.Yes,
|
||||||
|
{
|
||||||
|
ArrowRange = Some range0
|
||||||
|
BarRange = Some range0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|> SynExpr.createMatch (SynExpr.CreateIdentString "ty")
|
||||||
|
|> SynExpr.createLet
|
||||||
|
[
|
||||||
|
let property = SynExpr.CreateConstString "type"
|
||||||
|
|
||||||
let createRecordModule (namespaceId : LongIdent) (spec : JsonParseOutputSpec) (typeDefn : SynTypeDefn) =
|
SynExpr.CreateIdentString "node"
|
||||||
|
|> SynExpr.index property
|
||||||
|
|> assertNotNull property
|
||||||
|
|> SynExpr.pipeThroughFunction (
|
||||||
|
SynExpr.createLambda
|
||||||
|
"v"
|
||||||
|
(SynExpr.callGenericMethod "GetValue" [ Ident.Create "string" ] (SynExpr.CreateIdentString "v"))
|
||||||
|
)
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateString "ty") []
|
||||||
|
]
|
||||||
|
(*
|
||||||
|
let ty =
|
||||||
|
match node.["type"] with
|
||||||
|
| null -> raise (System.Collections.Generic.KeyNotFoundException ())
|
||||||
|
| v -> v.GetValue<string> ()
|
||||||
|
match ty with
|
||||||
|
| "emptyCase" -> FirstDu.EmptyCase
|
||||||
|
| "case1" ->
|
||||||
|
FirstDu.Case1
|
||||||
|
| "case2" -> FirstDu.Case2
|
||||||
|
| _ -> failwithf "Unrecognised case name: %s" ty
|
||||||
|
*)
|
||||||
|
|
||||||
|
|
||||||
|
let createModule (namespaceId : LongIdent) (spec : JsonParseOutputSpec) (typeDefn : SynTypeDefn) =
|
||||||
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
|
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
|
||||||
typeDefn
|
typeDefn
|
||||||
|
|
||||||
let (SynComponentInfo (_attributes, _typeParams, _constraints, recordId, _, _preferPostfix, _access, _)) =
|
let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, _access, _)) =
|
||||||
synComponentInfo
|
synComponentInfo
|
||||||
|
|
||||||
match synTypeDefnRepr with
|
let attributes =
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _recordRange), _) ->
|
if spec.ExtensionMethods then
|
||||||
|
[ SynAttributeList.Create SynAttribute.autoOpen ]
|
||||||
|
else
|
||||||
|
[
|
||||||
|
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
|
||||||
|
SynAttributeList.Create SynAttribute.compilationRepresentation
|
||||||
|
]
|
||||||
|
|
||||||
let decls = [ createMaker spec recordId recordFields ]
|
let xmlDoc =
|
||||||
|
let fullyQualified = ident |> Seq.map (fun i -> i.idText) |> String.concat "."
|
||||||
|
|
||||||
let attributes =
|
let description =
|
||||||
if spec.ExtensionMethods then
|
if spec.ExtensionMethods then
|
||||||
[ SynAttributeList.Create SynAttribute.autoOpen ]
|
"extension members"
|
||||||
else
|
else
|
||||||
[
|
"methods"
|
||||||
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
|
|
||||||
SynAttributeList.Create SynAttribute.compilationRepresentation
|
|
||||||
]
|
|
||||||
|
|
||||||
let xmlDoc =
|
$" Module containing JSON parsing %s{description} for the %s{fullyQualified} type"
|
||||||
let fullyQualified = recordId |> Seq.map (fun i -> i.idText) |> String.concat "."
|
|> PreXmlDoc.Create
|
||||||
|
|
||||||
let description =
|
let moduleName =
|
||||||
if spec.ExtensionMethods then
|
if spec.ExtensionMethods then
|
||||||
"extension members"
|
match ident with
|
||||||
else
|
| [] -> failwith "unexpectedly got an empty identifier for record name"
|
||||||
"methods"
|
| ident ->
|
||||||
|
let expanded =
|
||||||
|
List.last ident
|
||||||
|
|> fun i -> i.idText
|
||||||
|
|> fun s -> s + "JsonParseExtension"
|
||||||
|
|> Ident.Create
|
||||||
|
|
||||||
$" Module containing JSON parsing %s{description} for the %s{fullyQualified} type"
|
List.take (List.length ident - 1) ident @ [ expanded ]
|
||||||
|> PreXmlDoc.Create
|
else
|
||||||
|
ident
|
||||||
|
|
||||||
let moduleName =
|
let info =
|
||||||
if spec.ExtensionMethods then
|
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc)
|
||||||
match recordId with
|
|
||||||
| [] -> failwith "unexpectedly got an empty identifier for record name"
|
|
||||||
| recordId ->
|
|
||||||
let expanded =
|
|
||||||
List.last recordId
|
|
||||||
|> fun i -> i.idText
|
|
||||||
|> fun s -> s + "JsonParseExtension"
|
|
||||||
|> Ident.Create
|
|
||||||
|
|
||||||
List.take (List.length recordId - 1) recordId @ [ expanded ]
|
let decl =
|
||||||
else
|
match synTypeDefnRepr with
|
||||||
recordId
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, fields, _range), _) ->
|
||||||
|
let fields = fields |> List.map SynField.extractWithIdent
|
||||||
|
createRecordMaker spec ident fields
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, cases, _range), _) ->
|
||||||
|
let optionGet (i : Ident option) =
|
||||||
|
match i with
|
||||||
|
| None -> failwith "WoofWare.Myriad requires union cases to have identifiers on each field."
|
||||||
|
| Some i -> i
|
||||||
|
|
||||||
let info =
|
let cases =
|
||||||
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc)
|
cases
|
||||||
|
|> List.map SynUnionCase.extract
|
||||||
|
|> List.map (UnionCase.mapIdentFields optionGet)
|
||||||
|
|
||||||
let mdl = SynModuleDecl.CreateNestedModule (info, decls)
|
createUnionMaker spec ident cases
|
||||||
|
| _ -> failwithf "Not a record or union type"
|
||||||
|
|
||||||
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ])
|
let mdl =
|
||||||
| _ -> failwithf "Not a record type"
|
[ scaffolding spec ident decl ]
|
||||||
|
|> fun d -> SynModuleDecl.CreateNestedModule (info, d)
|
||||||
|
|
||||||
|
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ])
|
||||||
|
|
||||||
/// Myriad generator that provides a method (possibly an extension method) for a record type,
|
/// Myriad generator that provides a method (possibly an extension method) for a record type,
|
||||||
/// containing a JSON parse function.
|
/// containing a JSON parse function.
|
||||||
@@ -628,10 +627,20 @@ type JsonParseGenerator () =
|
|||||||
let ast, _ =
|
let ast, _ =
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
let records = Ast.extractRecords ast
|
let recordsAndUnions =
|
||||||
|
Ast.extractTypeDefn ast
|
||||||
|
|> List.map (fun (name, defns) ->
|
||||||
|
defns
|
||||||
|
|> List.choose (fun defn ->
|
||||||
|
if Ast.isRecord defn then Some defn
|
||||||
|
elif Ast.isDu defn then Some defn
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|> fun defns -> name, defns
|
||||||
|
)
|
||||||
|
|
||||||
let namespaceAndRecords =
|
let namespaceAndTypes =
|
||||||
records
|
recordsAndUnions
|
||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
@@ -644,7 +653,7 @@ type JsonParseGenerator () =
|
|||||||
| SynExpr.Const (SynConst.Unit, _) -> JsonParseAttribute.DefaultIsExtensionMethod
|
| SynExpr.Const (SynConst.Unit, _) -> JsonParseAttribute.DefaultIsExtensionMethod
|
||||||
| arg ->
|
| arg ->
|
||||||
failwith
|
failwith
|
||||||
$"Unrecognised argument %+A{arg} to [<JsonParseAttribute>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
$"Unrecognised argument %+A{arg} to [<%s{nameof JsonParseAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
||||||
|
|
||||||
let spec =
|
let spec =
|
||||||
{
|
{
|
||||||
@@ -659,13 +668,9 @@ type JsonParseGenerator () =
|
|||||||
)
|
)
|
||||||
|
|
||||||
let modules =
|
let modules =
|
||||||
namespaceAndRecords
|
namespaceAndTypes
|
||||||
|> List.collect (fun (ns, records) ->
|
|> List.collect (fun (ns, types) ->
|
||||||
records
|
types |> List.map (fun (ty, spec) -> JsonParseGenerator.createModule ns spec ty)
|
||||||
|> List.map (fun (record, spec) ->
|
|
||||||
let recordModule = JsonParseGenerator.createRecordModule ns spec record
|
|
||||||
recordModule
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Output.Ast modules
|
Output.Ast modules
|
||||||
|
|||||||
490
WoofWare.Myriad.Plugins/JsonSerializeGenerator.fs
Normal file
490
WoofWare.Myriad.Plugins/JsonSerializeGenerator.fs
Normal file
@@ -0,0 +1,490 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Text
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
|
type internal JsonSerializeOutputSpec =
|
||||||
|
{
|
||||||
|
ExtensionMethods : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal JsonSerializeGenerator =
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
open Myriad.Core.Ast
|
||||||
|
|
||||||
|
/// Given `input.Ident`, for example, choose how to add it to the ambient `node`.
|
||||||
|
/// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`.
|
||||||
|
let rec serializeNode (fieldType : SynType) : SynExpr =
|
||||||
|
// TODO: serialization format for DateTime etc
|
||||||
|
match fieldType with
|
||||||
|
| DateOnly
|
||||||
|
| DateTime
|
||||||
|
| NumberType _
|
||||||
|
| PrimitiveType _
|
||||||
|
| Guid
|
||||||
|
| Uri ->
|
||||||
|
// JsonValue.Create<type>
|
||||||
|
SynExpr.TypeApp (
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ],
|
||||||
|
range0,
|
||||||
|
[ fieldType ],
|
||||||
|
[],
|
||||||
|
Some range0,
|
||||||
|
range0,
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
| OptionType ty ->
|
||||||
|
// fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field
|
||||||
|
[
|
||||||
|
SynMatchClause.Create (
|
||||||
|
SynPat.CreateLongIdent (SynLongIdent.CreateString "None", []),
|
||||||
|
None,
|
||||||
|
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
|
||||||
|
// identically equal to null. We have to work around this later, but we might as well just
|
||||||
|
// be efficient here and whip up the null directly.
|
||||||
|
SynExpr.CreateNull
|
||||||
|
|> SynExpr.upcast' (
|
||||||
|
SynType.CreateLongIdent (
|
||||||
|
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
SynMatchClause.Create (
|
||||||
|
SynPat.CreateLongIdent (
|
||||||
|
SynLongIdent.CreateString "Some",
|
||||||
|
[ SynPat.CreateNamed (Ident.Create "field") ]
|
||||||
|
),
|
||||||
|
None,
|
||||||
|
SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "field")
|
||||||
|
|> SynExpr.CreateParen
|
||||||
|
|> SynExpr.upcast' (
|
||||||
|
SynType.CreateLongIdent (
|
||||||
|
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|> SynExpr.createMatch (SynExpr.CreateIdentString "field")
|
||||||
|
|> SynExpr.createLambda "field"
|
||||||
|
| ArrayType ty
|
||||||
|
| ListType ty ->
|
||||||
|
// fun field ->
|
||||||
|
// let arr = JsonArray ()
|
||||||
|
// for mem in field do arr.Add ({serializeNode} mem)
|
||||||
|
// arr
|
||||||
|
[
|
||||||
|
SynExpr.ForEach (
|
||||||
|
DebugPointAtFor.Yes range0,
|
||||||
|
DebugPointAtInOrTo.Yes range0,
|
||||||
|
SeqExprOnly.SeqExprOnly false,
|
||||||
|
true,
|
||||||
|
SynPat.CreateNamed (Ident.Create "mem"),
|
||||||
|
SynExpr.CreateIdent (Ident.Create "field"),
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateLongIdent (SynLongIdent.Create [ "arr" ; "Add" ]),
|
||||||
|
SynExpr.CreateParen (SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "mem"))
|
||||||
|
),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
SynExpr.CreateIdentString "arr"
|
||||||
|
]
|
||||||
|
|> SynExpr.CreateSequential
|
||||||
|
|> SynExpr.createLet
|
||||||
|
[
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonArray" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst SynConst.Unit)
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateString "arr") []
|
||||||
|
]
|
||||||
|
|> SynExpr.createLambda "field"
|
||||||
|
| IDictionaryType (_keyType, valueType)
|
||||||
|
| DictionaryType (_keyType, valueType)
|
||||||
|
| IReadOnlyDictionaryType (_keyType, valueType)
|
||||||
|
| MapType (_keyType, valueType) ->
|
||||||
|
// fun field ->
|
||||||
|
// let ret = JsonObject ()
|
||||||
|
// for (KeyValue(key, value)) in field do
|
||||||
|
// ret.Add (key.ToString (), {serializeNode} value)
|
||||||
|
// ret
|
||||||
|
[
|
||||||
|
SynExpr.ForEach (
|
||||||
|
DebugPointAtFor.Yes range0,
|
||||||
|
DebugPointAtInOrTo.Yes range0,
|
||||||
|
SeqExprOnly.SeqExprOnly false,
|
||||||
|
true,
|
||||||
|
SynPat.CreateParen (
|
||||||
|
SynPat.CreateLongIdent (
|
||||||
|
SynLongIdent.CreateString "KeyValue",
|
||||||
|
[
|
||||||
|
SynPat.CreateParen (
|
||||||
|
SynPat.Tuple (
|
||||||
|
false,
|
||||||
|
[
|
||||||
|
SynPat.CreateNamed (Ident.Create "key")
|
||||||
|
SynPat.CreateNamed (Ident.Create "value")
|
||||||
|
],
|
||||||
|
[ range0 ],
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
SynExpr.CreateIdent (Ident.Create "field"),
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.createLongIdent [ "ret" ; "Add" ],
|
||||||
|
SynExpr.CreateParenedTuple
|
||||||
|
[
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.createLongIdent [ "key" ; "ToString" ],
|
||||||
|
SynExpr.CreateConst SynConst.Unit
|
||||||
|
)
|
||||||
|
SynExpr.CreateApp (serializeNode valueType, SynExpr.CreateIdentString "value")
|
||||||
|
]
|
||||||
|
),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
SynExpr.CreateIdentString "ret"
|
||||||
|
]
|
||||||
|
|> SynExpr.CreateSequential
|
||||||
|
|> SynExpr.createLet
|
||||||
|
[
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst SynConst.Unit)
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateString "ret") []
|
||||||
|
]
|
||||||
|
|> SynExpr.createLambda "field"
|
||||||
|
| _ ->
|
||||||
|
// {type}.toJsonNode
|
||||||
|
let typeName =
|
||||||
|
match fieldType with
|
||||||
|
| SynType.LongIdent ident -> ident.LongIdent
|
||||||
|
| _ -> failwith $"Unrecognised type: %+A{fieldType}"
|
||||||
|
|
||||||
|
SynExpr.createLongIdent' (typeName @ [ Ident.Create "toJsonNode" ])
|
||||||
|
|
||||||
|
/// propertyName is probably a string literal, but it could be a [<Literal>] variable
|
||||||
|
/// `node.Add ({propertyName}, {toJsonNode})`
|
||||||
|
let createSerializeRhsRecord (propertyName : SynExpr) (fieldId : Ident) (fieldType : SynType) : SynExpr =
|
||||||
|
[
|
||||||
|
propertyName
|
||||||
|
SynExpr.CreateApp (serializeNode fieldType, SynExpr.createLongIdent' [ Ident.Create "input" ; fieldId ])
|
||||||
|
]
|
||||||
|
|> SynExpr.CreateParenedTuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
|
||||||
|
|
||||||
|
let getPropertyName (fieldId : Ident) (attrs : SynAttribute list) : SynExpr =
|
||||||
|
let propertyNameAttr =
|
||||||
|
attrs
|
||||||
|
|> List.tryFind (fun attr -> attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal))
|
||||||
|
|
||||||
|
match propertyNameAttr with
|
||||||
|
| None ->
|
||||||
|
let sb = StringBuilder fieldId.idText.Length
|
||||||
|
sb.Append (Char.ToLowerInvariant fieldId.idText.[0]) |> ignore
|
||||||
|
|
||||||
|
if fieldId.idText.Length > 1 then
|
||||||
|
sb.Append fieldId.idText.[1..] |> ignore
|
||||||
|
|
||||||
|
sb.ToString () |> SynConst.CreateString |> SynExpr.CreateConst
|
||||||
|
| Some name -> name.ArgExpr
|
||||||
|
|
||||||
|
/// `populateNode` will be inserted before we return the `node` variable.
|
||||||
|
///
|
||||||
|
/// That is, we give you access to a `JsonObject` called `node`,
|
||||||
|
/// and you have access to a variable `inputArgName` which is of type `typeName`.
|
||||||
|
/// Your job is to provide a `populateNode` expression which has the side effect
|
||||||
|
/// of mutating `node` to faithfully reflect the value of `inputArgName`.
|
||||||
|
let scaffolding
|
||||||
|
(spec : JsonSerializeOutputSpec)
|
||||||
|
(typeName : LongIdent)
|
||||||
|
(inputArgName : Ident)
|
||||||
|
(populateNode : SynExpr)
|
||||||
|
: SynModuleDecl
|
||||||
|
=
|
||||||
|
let xmlDoc = PreXmlDoc.Create " Serialize to a JSON node"
|
||||||
|
|
||||||
|
let returnInfo =
|
||||||
|
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
|> SynType.LongIdent
|
||||||
|
|
||||||
|
let functionName = Ident.Create "toJsonNode"
|
||||||
|
|
||||||
|
let assignments =
|
||||||
|
[
|
||||||
|
populateNode
|
||||||
|
SynExpr.Upcast (SynExpr.CreateIdentString "node", SynType.Anon range0, range0)
|
||||||
|
]
|
||||||
|
|> SynExpr.CreateSequential
|
||||||
|
|> SynExpr.createLet
|
||||||
|
[
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst SynConst.Unit)
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateString "node") []
|
||||||
|
]
|
||||||
|
|
||||||
|
let pattern =
|
||||||
|
SynPat.CreateNamed inputArgName
|
||||||
|
|> SynPat.annotateType (SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName))
|
||||||
|
|
||||||
|
if spec.ExtensionMethods then
|
||||||
|
let binding =
|
||||||
|
assignments
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ pattern ]
|
||||||
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
|
|> SynBinding.makeStaticMember
|
||||||
|
|
||||||
|
let mem = SynMemberDefn.Member (binding, range0)
|
||||||
|
|
||||||
|
let containingType =
|
||||||
|
SynTypeDefn.SynTypeDefn (
|
||||||
|
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create " Extension methods for JSON parsing"),
|
||||||
|
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0),
|
||||||
|
[ mem ],
|
||||||
|
None,
|
||||||
|
range0,
|
||||||
|
{
|
||||||
|
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
|
||||||
|
EqualsRange = None
|
||||||
|
WithKeyword = None
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
SynModuleDecl.Types ([ containingType ], range0)
|
||||||
|
else
|
||||||
|
let binding =
|
||||||
|
assignments
|
||||||
|
|> SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ pattern ]
|
||||||
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
|
|
||||||
|
SynModuleDecl.CreateLet [ binding ]
|
||||||
|
|
||||||
|
let recordModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (fields : SynField list) =
|
||||||
|
let inputArg = Ident.Create "input"
|
||||||
|
let fields = fields |> List.map SynField.extractWithIdent
|
||||||
|
|
||||||
|
fields
|
||||||
|
|> List.map (fun fieldData ->
|
||||||
|
let propertyName = getPropertyName fieldData.Ident fieldData.Attrs
|
||||||
|
createSerializeRhsRecord propertyName fieldData.Ident fieldData.Type
|
||||||
|
)
|
||||||
|
|> SynExpr.CreateSequential
|
||||||
|
|> fun expr -> SynExpr.Do (expr, range0)
|
||||||
|
|> scaffolding spec typeName inputArg
|
||||||
|
|
||||||
|
let unionModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (cases : SynUnionCase list) =
|
||||||
|
let inputArg = Ident.Create "input"
|
||||||
|
let fields = cases |> List.map SynUnionCase.extract
|
||||||
|
|
||||||
|
fields
|
||||||
|
|> List.map (fun unionCase ->
|
||||||
|
let propertyName = getPropertyName unionCase.Ident unionCase.Attrs
|
||||||
|
|
||||||
|
let caseNames = unionCase.Fields |> List.mapi (fun i _ -> Ident.Create $"arg%i{i}")
|
||||||
|
|
||||||
|
let argPats = SynArgPats.create caseNames
|
||||||
|
|
||||||
|
let pattern =
|
||||||
|
SynPat.LongIdent (
|
||||||
|
SynLongIdent.CreateFromLongIdent (typeName @ [ unionCase.Ident ]),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
argPats,
|
||||||
|
None,
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
|
||||||
|
let typeLine =
|
||||||
|
[
|
||||||
|
SynExpr.CreateConstString "type"
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ],
|
||||||
|
propertyName
|
||||||
|
)
|
||||||
|
]
|
||||||
|
|> SynExpr.CreateParenedTuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
|
||||||
|
|
||||||
|
let dataNode =
|
||||||
|
SynBinding.Let (
|
||||||
|
pattern = SynPat.CreateNamed (Ident.Create "dataNode"),
|
||||||
|
expr =
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateLongIdent (
|
||||||
|
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
|
),
|
||||||
|
SynExpr.CreateConst SynConst.Unit
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let dataBindings =
|
||||||
|
(unionCase.Fields, caseNames)
|
||||||
|
||> List.zip
|
||||||
|
|> List.map (fun (fieldData, caseName) ->
|
||||||
|
let propertyName = getPropertyName (Option.get fieldData.Ident) fieldData.Attrs
|
||||||
|
|
||||||
|
let node =
|
||||||
|
SynExpr.CreateApp (serializeNode fieldData.Type, SynExpr.CreateIdent caseName)
|
||||||
|
|
||||||
|
[ propertyName ; node ]
|
||||||
|
|> SynExpr.CreateParenedTuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "dataNode" ; "Add" ])
|
||||||
|
)
|
||||||
|
|
||||||
|
let assignToNode =
|
||||||
|
[ SynExpr.CreateConstString "data" ; SynExpr.CreateIdentString "dataNode" ]
|
||||||
|
|> SynExpr.CreateParenedTuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
|
||||||
|
|
||||||
|
let dataNode =
|
||||||
|
SynExpr.CreateSequential (dataBindings @ [ assignToNode ])
|
||||||
|
|> SynExpr.createLet [ dataNode ]
|
||||||
|
|
||||||
|
let action =
|
||||||
|
[
|
||||||
|
yield typeLine
|
||||||
|
if not dataBindings.IsEmpty then
|
||||||
|
yield dataNode
|
||||||
|
]
|
||||||
|
|> SynExpr.CreateSequential
|
||||||
|
|
||||||
|
SynMatchClause.Create (pattern, None, action)
|
||||||
|
)
|
||||||
|
|> fun clauses -> SynExpr.CreateMatch (SynExpr.CreateIdent inputArg, clauses)
|
||||||
|
|> scaffolding spec typeName inputArg
|
||||||
|
|
||||||
|
let createModule
|
||||||
|
(namespaceId : LongIdent)
|
||||||
|
(opens : SynOpenDeclTarget list)
|
||||||
|
(spec : JsonSerializeOutputSpec)
|
||||||
|
(typeDefn : SynTypeDefn)
|
||||||
|
=
|
||||||
|
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
|
||||||
|
typeDefn
|
||||||
|
|
||||||
|
let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, _access, _)) =
|
||||||
|
synComponentInfo
|
||||||
|
|
||||||
|
let attributes =
|
||||||
|
if spec.ExtensionMethods then
|
||||||
|
[ SynAttributeList.Create SynAttribute.autoOpen ]
|
||||||
|
else
|
||||||
|
[
|
||||||
|
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
|
||||||
|
SynAttributeList.Create SynAttribute.compilationRepresentation
|
||||||
|
]
|
||||||
|
|
||||||
|
let xmlDoc =
|
||||||
|
let fullyQualified = ident |> Seq.map (fun i -> i.idText) |> String.concat "."
|
||||||
|
|
||||||
|
let description =
|
||||||
|
if spec.ExtensionMethods then
|
||||||
|
"extension members"
|
||||||
|
else
|
||||||
|
"methods"
|
||||||
|
|
||||||
|
$" Module containing JSON serializing %s{description} for the %s{fullyQualified} type"
|
||||||
|
|> PreXmlDoc.Create
|
||||||
|
|
||||||
|
let moduleName =
|
||||||
|
if spec.ExtensionMethods then
|
||||||
|
match ident with
|
||||||
|
| [] -> failwith "unexpectedly got an empty identifier for type name"
|
||||||
|
| ident ->
|
||||||
|
let expanded =
|
||||||
|
List.last ident
|
||||||
|
|> fun i -> i.idText
|
||||||
|
|> fun s -> s + "JsonSerializeExtension"
|
||||||
|
|> Ident.Create
|
||||||
|
|
||||||
|
List.take (List.length ident - 1) ident @ [ expanded ]
|
||||||
|
else
|
||||||
|
ident
|
||||||
|
|
||||||
|
let info =
|
||||||
|
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc)
|
||||||
|
|
||||||
|
let decls =
|
||||||
|
match synTypeDefnRepr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _range), _) ->
|
||||||
|
[ recordModule spec ident recordFields ]
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, unionFields, _range), _) ->
|
||||||
|
[ unionModule spec ident unionFields ]
|
||||||
|
| _ -> failwithf "Only record types currently supported."
|
||||||
|
|
||||||
|
let mdl = SynModuleDecl.CreateNestedModule (info, decls)
|
||||||
|
|
||||||
|
SynModuleOrNamespace.CreateNamespace (
|
||||||
|
namespaceId,
|
||||||
|
decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ mdl ]
|
||||||
|
)
|
||||||
|
|
||||||
|
/// Myriad generator that provides a method (possibly an extension method) for a record type,
|
||||||
|
/// containing a JSON serialization function.
|
||||||
|
[<MyriadGenerator("json-serialize")>]
|
||||||
|
type JsonSerializeGenerator () =
|
||||||
|
|
||||||
|
interface IMyriadGenerator with
|
||||||
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let ast, _ =
|
||||||
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
|
let recordsAndUnions =
|
||||||
|
Ast.extractTypeDefn ast
|
||||||
|
|> List.map (fun (name, defns) ->
|
||||||
|
defns
|
||||||
|
|> List.choose (fun defn ->
|
||||||
|
if Ast.isRecord defn then Some defn
|
||||||
|
elif Ast.isDu defn then Some defn
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|> fun defns -> name, defns
|
||||||
|
)
|
||||||
|
|
||||||
|
let namespaceAndTypes =
|
||||||
|
recordsAndUnions
|
||||||
|
|> List.choose (fun (ns, types) ->
|
||||||
|
types
|
||||||
|
|> List.choose (fun typeDef ->
|
||||||
|
match Ast.getAttribute<JsonSerializeAttribute> typeDef with
|
||||||
|
| None -> None
|
||||||
|
| Some attr ->
|
||||||
|
let arg =
|
||||||
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
| SynExpr.Const (SynConst.Bool value, _) -> value
|
||||||
|
| SynExpr.Const (SynConst.Unit, _) -> JsonSerializeAttribute.DefaultIsExtensionMethod
|
||||||
|
| arg ->
|
||||||
|
failwith
|
||||||
|
$"Unrecognised argument %+A{arg} to [<%s{nameof JsonSerializeAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
||||||
|
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
ExtensionMethods = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
)
|
||||||
|
|> function
|
||||||
|
| [] -> None
|
||||||
|
| ty -> Some (ns, ty)
|
||||||
|
)
|
||||||
|
|
||||||
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
|
let modules =
|
||||||
|
namespaceAndTypes
|
||||||
|
|> List.collect (fun (ns, types) ->
|
||||||
|
types
|
||||||
|
|> List.map (fun (ty, spec) -> JsonSerializeGenerator.createModule ns opens spec ty)
|
||||||
|
)
|
||||||
|
|
||||||
|
Output.Ast modules
|
||||||
14
WoofWare.Myriad.Plugins/List.fs
Normal file
14
WoofWare.Myriad.Plugins/List.fs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module private List =
|
||||||
|
let partitionChoice<'a, 'b> (xs : Choice<'a, 'b> list) : 'a list * 'b list =
|
||||||
|
let xs, ys =
|
||||||
|
(([], []), xs)
|
||||||
|
||> List.fold (fun (xs, ys) v ->
|
||||||
|
match v with
|
||||||
|
| Choice1Of2 x -> x :: xs, ys
|
||||||
|
| Choice2Of2 y -> xs, y :: ys
|
||||||
|
)
|
||||||
|
|
||||||
|
List.rev xs, List.rev ys
|
||||||
@@ -1,17 +1,10 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
open System
|
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
open Myriad.Core
|
open Myriad.Core
|
||||||
|
|
||||||
/// Attribute indicating a record type to which the "Remove Options" Myriad
|
|
||||||
/// generator should apply during build.
|
|
||||||
/// The purpose of this generator is to strip the `option` modifier from types.
|
|
||||||
type RemoveOptionsAttribute () =
|
|
||||||
inherit Attribute ()
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal RemoveOptionsGenerator =
|
module internal RemoveOptionsGenerator =
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
@@ -70,7 +63,7 @@ module internal RemoveOptionsGenerator =
|
|||||||
|
|
||||||
SynModuleDecl.Types ([ typeDecl ], range0)
|
SynModuleDecl.Types ([ typeDecl ], range0)
|
||||||
|
|
||||||
let createMaker (withOptionsType : LongIdent) (withoutOptionsType : LongIdent) (fields : SynField list) =
|
let createMaker (withOptionsType : LongIdent) (withoutOptionsType : LongIdent) (fields : SynFieldData<Ident> list) =
|
||||||
let xmlDoc = PreXmlDoc.Create " Remove the optional members of the input."
|
let xmlDoc = PreXmlDoc.Create " Remove the optional members of the input."
|
||||||
|
|
||||||
let returnInfo =
|
let returnInfo =
|
||||||
@@ -88,17 +81,17 @@ module internal RemoveOptionsGenerator =
|
|||||||
|
|
||||||
let body =
|
let body =
|
||||||
fields
|
fields
|
||||||
|> List.map (fun (SynField (_, _, id, fieldType, _, _, _, _, _)) ->
|
|> List.map (fun fieldData ->
|
||||||
let id =
|
|
||||||
match id with
|
|
||||||
| None -> failwith "Expected record field to have an identifying name"
|
|
||||||
| Some id -> id
|
|
||||||
|
|
||||||
let accessor =
|
let accessor =
|
||||||
SynExpr.LongIdent (false, SynLongIdent ([ inputArg ; id ], [ range0 ], []), None, range0)
|
SynExpr.LongIdent (
|
||||||
|
false,
|
||||||
|
SynLongIdent ([ inputArg ; fieldData.Ident ], [ range0 ], []),
|
||||||
|
None,
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
|
||||||
let body =
|
let body =
|
||||||
match fieldType with
|
match fieldData.Type with
|
||||||
| OptionType _ ->
|
| OptionType _ ->
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateApp (
|
||||||
SynExpr.CreateAppInfix (
|
SynExpr.CreateAppInfix (
|
||||||
@@ -118,14 +111,15 @@ module internal RemoveOptionsGenerator =
|
|||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateString "Option.defaultWith"),
|
SynExpr.CreateLongIdent (SynLongIdent.CreateString "Option.defaultWith"),
|
||||||
SynExpr.CreateLongIdent (
|
SynExpr.CreateLongIdent (
|
||||||
SynLongIdent.CreateFromLongIdent (
|
SynLongIdent.CreateFromLongIdent (
|
||||||
withoutOptionsType @ [ Ident.Create (sprintf "Default%s" id.idText) ]
|
withoutOptionsType
|
||||||
|
@ [ Ident.Create (sprintf "Default%s" fieldData.Ident.idText) ]
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
| _ -> accessor
|
| _ -> accessor
|
||||||
|
|
||||||
(SynLongIdent.CreateFromLongIdent [ id ], true), Some body
|
(SynLongIdent.CreateFromLongIdent [ fieldData.Ident ], true), Some body
|
||||||
)
|
)
|
||||||
|> AstHelper.instantiateRecord
|
|> AstHelper.instantiateRecord
|
||||||
|
|
||||||
@@ -167,12 +161,13 @@ module internal RemoveOptionsGenerator =
|
|||||||
synComponentInfo
|
synComponentInfo
|
||||||
|
|
||||||
match synTypeDefnRepr with
|
match synTypeDefnRepr with
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (accessibility, recordFields, _recordRange), _) ->
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (accessibility, fields, _range), _) ->
|
||||||
|
let fieldData = fields |> List.map SynField.extractWithIdent
|
||||||
|
|
||||||
let decls =
|
let decls =
|
||||||
[
|
[
|
||||||
createType (Some doc) accessibility typeParams recordFields
|
createType (Some doc) accessibility typeParams fields
|
||||||
createMaker [ Ident.Create "Short" ] recordId recordFields
|
createMaker [ Ident.Create "Short" ] recordId fieldData
|
||||||
]
|
]
|
||||||
|
|
||||||
let attributes =
|
let attributes =
|
||||||
|
|||||||
@@ -1,17 +1,12 @@
|
|||||||
WoofWare.Myriad.Plugins.GenerateMockAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.GenerateMockAttribute..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.HttpClientAttribute inherit System.Attribute
|
|
||||||
WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: unit
|
|
||||||
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.HttpClientGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.HttpClientGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.InterfaceMockGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.InterfaceMockGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.InterfaceMockGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.InterfaceMockGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.JsonParseAttribute inherit System.Attribute
|
|
||||||
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: bool
|
|
||||||
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: unit
|
|
||||||
WoofWare.Myriad.Plugins.JsonParseGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.JsonParseGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
|
||||||
18
WoofWare.Myriad.Plugins/SynExpr/SynArgPats.fs
Normal file
18
WoofWare.Myriad.Plugins/SynExpr/SynArgPats.fs
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynArgPats =
|
||||||
|
let create (caseNames : Ident list) : SynArgPats =
|
||||||
|
if caseNames.IsEmpty then
|
||||||
|
SynArgPats.Pats []
|
||||||
|
else
|
||||||
|
|
||||||
|
caseNames
|
||||||
|
|> List.map (fun ident -> SynPat.Named (SynIdent.SynIdent (ident, None), false, None, range0))
|
||||||
|
|> fun ps -> SynPat.Tuple (false, ps, List.replicate (ps.Length - 1) range0, range0)
|
||||||
|
|> fun p -> SynPat.Paren (p, range0)
|
||||||
|
|> List.singleton
|
||||||
|
|> SynArgPats.Pats
|
||||||
173
WoofWare.Myriad.Plugins/SynExpr/SynBinding.fs
Normal file
173
WoofWare.Myriad.Plugins/SynExpr/SynBinding.fs
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynBinding =
|
||||||
|
|
||||||
|
let rec private stripParen (pat : SynPat) =
|
||||||
|
match pat with
|
||||||
|
| SynPat.Paren (p, _) -> stripParen p
|
||||||
|
| _ -> pat
|
||||||
|
|
||||||
|
let rec private getName (pat : SynPat) : Ident option =
|
||||||
|
match stripParen pat with
|
||||||
|
| SynPat.Named (SynIdent.SynIdent (name, _), _, _, _) -> Some name
|
||||||
|
| SynPat.Wild _ -> None
|
||||||
|
| SynPat.Typed (pat, _, _) -> getName pat
|
||||||
|
| SynPat.Const _ -> None
|
||||||
|
| SynPat.LongIdent (SynLongIdent.SynLongIdent (longIdent, _, _), _, _, _, _, _) ->
|
||||||
|
match longIdent with
|
||||||
|
| [ x ] -> Some x
|
||||||
|
| _ -> failwithf "got long ident %O ; can only get the name of a long ident with one component" longIdent
|
||||||
|
| _ -> failwithf "unrecognised pattern: %+A" pat
|
||||||
|
|
||||||
|
let triviaZero (isMember : bool) =
|
||||||
|
{
|
||||||
|
SynBindingTrivia.EqualsRange = Some range0
|
||||||
|
InlineKeyword = None
|
||||||
|
LeadingKeyword =
|
||||||
|
if isMember then
|
||||||
|
SynLeadingKeyword.Member range0
|
||||||
|
else
|
||||||
|
SynLeadingKeyword.Let range0
|
||||||
|
}
|
||||||
|
|
||||||
|
let basic (name : SynLongIdent) (args : SynPat list) (body : SynExpr) : SynBinding =
|
||||||
|
let valInfo : SynValInfo =
|
||||||
|
args
|
||||||
|
|> List.map (fun pat -> [ SynArgInfo.SynArgInfo (SynAttributes.Empty, false, getName pat) ])
|
||||||
|
|> fun x -> SynValInfo.SynValInfo (x, SynArgInfo.SynArgInfo ([], false, None))
|
||||||
|
|
||||||
|
SynBinding.SynBinding (
|
||||||
|
None,
|
||||||
|
SynBindingKind.Normal,
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
[],
|
||||||
|
PreXmlDoc.Empty,
|
||||||
|
SynValData.SynValData (None, valInfo, None),
|
||||||
|
SynPat.LongIdent (name, None, None, SynArgPats.Pats args, None, range0),
|
||||||
|
None,
|
||||||
|
body,
|
||||||
|
range0,
|
||||||
|
DebugPointAtBinding.Yes range0,
|
||||||
|
triviaZero false
|
||||||
|
)
|
||||||
|
|
||||||
|
let withAccessibility (acc : SynAccess option) (binding : SynBinding) : SynBinding =
|
||||||
|
match binding with
|
||||||
|
| SynBinding (_, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
|
||||||
|
let headPat =
|
||||||
|
match headPat with
|
||||||
|
| SynPat.LongIdent (ident, extra, options, argPats, _, range) ->
|
||||||
|
SynPat.LongIdent (ident, extra, options, argPats, acc, range)
|
||||||
|
| _ -> failwithf "unrecognised head pattern: %O" headPat
|
||||||
|
|
||||||
|
SynBinding (acc, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
|
||||||
|
|
||||||
|
let withXmlDoc (doc : PreXmlDoc) (binding : SynBinding) : SynBinding =
|
||||||
|
match binding with
|
||||||
|
| SynBinding (acc, kind, inl, mut, attrs, _, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
|
||||||
|
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
|
||||||
|
|
||||||
|
let withReturnAnnotation (ty : SynType) (binding : SynBinding) : SynBinding =
|
||||||
|
match binding with
|
||||||
|
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, _, expr, range, debugPoint, trivia) ->
|
||||||
|
let retInfo =
|
||||||
|
SynBindingReturnInfo.SynBindingReturnInfo (
|
||||||
|
ty,
|
||||||
|
range0,
|
||||||
|
[],
|
||||||
|
{
|
||||||
|
ColonRange = Some range0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
SynBinding (
|
||||||
|
acc,
|
||||||
|
kind,
|
||||||
|
inl,
|
||||||
|
mut,
|
||||||
|
attrs,
|
||||||
|
doc,
|
||||||
|
valData,
|
||||||
|
headPat,
|
||||||
|
Some retInfo,
|
||||||
|
expr,
|
||||||
|
range,
|
||||||
|
debugPoint,
|
||||||
|
trivia
|
||||||
|
)
|
||||||
|
|
||||||
|
let makeInline (binding : SynBinding) : SynBinding =
|
||||||
|
match binding with
|
||||||
|
| SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
||||||
|
SynBinding (
|
||||||
|
acc,
|
||||||
|
kind,
|
||||||
|
true,
|
||||||
|
mut,
|
||||||
|
attrs,
|
||||||
|
doc,
|
||||||
|
valData,
|
||||||
|
headPat,
|
||||||
|
ret,
|
||||||
|
expr,
|
||||||
|
range,
|
||||||
|
debugPoint,
|
||||||
|
{ trivia with
|
||||||
|
InlineKeyword = Some range0
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
let makeStaticMember (binding : SynBinding) : SynBinding =
|
||||||
|
let memberFlags =
|
||||||
|
{
|
||||||
|
SynMemberFlags.IsInstance = false
|
||||||
|
SynMemberFlags.IsDispatchSlot = false
|
||||||
|
SynMemberFlags.IsOverrideOrExplicitImpl = false
|
||||||
|
SynMemberFlags.IsFinal = false
|
||||||
|
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
||||||
|
SynMemberFlags.MemberKind = SynMemberKind.Member
|
||||||
|
}
|
||||||
|
|
||||||
|
match binding with
|
||||||
|
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
||||||
|
let valData =
|
||||||
|
match valData with
|
||||||
|
| SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None)
|
||||||
|
|
||||||
|
let trivia =
|
||||||
|
{ trivia with
|
||||||
|
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0)
|
||||||
|
}
|
||||||
|
|
||||||
|
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia)
|
||||||
|
|
||||||
|
let makeInstanceMember (binding : SynBinding) : SynBinding =
|
||||||
|
let memberFlags =
|
||||||
|
{
|
||||||
|
SynMemberFlags.IsInstance = true
|
||||||
|
SynMemberFlags.IsDispatchSlot = false
|
||||||
|
SynMemberFlags.IsOverrideOrExplicitImpl = true
|
||||||
|
SynMemberFlags.IsFinal = false
|
||||||
|
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
||||||
|
SynMemberFlags.MemberKind = SynMemberKind.Member
|
||||||
|
}
|
||||||
|
|
||||||
|
match binding with
|
||||||
|
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
||||||
|
let valData =
|
||||||
|
match valData with
|
||||||
|
| SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None)
|
||||||
|
|
||||||
|
let trivia =
|
||||||
|
{ trivia with
|
||||||
|
LeadingKeyword = SynLeadingKeyword.Member range0
|
||||||
|
}
|
||||||
|
|
||||||
|
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia)
|
||||||
@@ -15,21 +15,25 @@ type internal CompExprBinding =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal SynExpr =
|
module internal SynExpr =
|
||||||
|
|
||||||
|
/// {f} {x}
|
||||||
|
let applyFunction (f : SynExpr) (x : SynExpr) : SynExpr = SynExpr.CreateApp (f, x)
|
||||||
|
|
||||||
|
/// {f} {x}
|
||||||
|
let applyTo (x : SynExpr) (f : SynExpr) : SynExpr = SynExpr.CreateApp (f, x)
|
||||||
|
|
||||||
/// {expr} |> {func}
|
/// {expr} |> {func}
|
||||||
let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr =
|
let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr =
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateAppInfix (
|
||||||
SynExpr.CreateAppInfix (
|
SynExpr.CreateLongIdent (
|
||||||
SynExpr.CreateLongIdent (
|
SynLongIdent.SynLongIdent (
|
||||||
SynLongIdent.SynLongIdent (
|
[ Ident.Create "op_PipeRight" ],
|
||||||
[ Ident.Create "op_PipeRight" ],
|
[],
|
||||||
[],
|
[ Some (IdentTrivia.OriginalNotation "|>") ]
|
||||||
[ Some (IdentTrivia.OriginalNotation "|>") ]
|
)
|
||||||
)
|
|
||||||
),
|
|
||||||
expr
|
|
||||||
),
|
),
|
||||||
func
|
expr
|
||||||
)
|
)
|
||||||
|
|> applyTo func
|
||||||
|
|
||||||
/// if {cond} then {trueBranch} else {falseBranch}
|
/// if {cond} then {trueBranch} else {falseBranch}
|
||||||
/// Note that this function puts the trueBranch last, for pipelining convenience:
|
/// Note that this function puts the trueBranch last, for pipelining convenience:
|
||||||
@@ -72,89 +76,75 @@ module internal SynExpr =
|
|||||||
|
|
||||||
/// {a} = {b}
|
/// {a} = {b}
|
||||||
let equals (a : SynExpr) (b : SynExpr) =
|
let equals (a : SynExpr) (b : SynExpr) =
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateAppInfix (
|
||||||
SynExpr.CreateAppInfix (
|
SynExpr.CreateLongIdent (
|
||||||
SynExpr.CreateLongIdent (
|
SynLongIdent.SynLongIdent (
|
||||||
SynLongIdent.SynLongIdent (
|
Ident.CreateLong "op_Equality",
|
||||||
Ident.CreateLong "op_Equality",
|
[],
|
||||||
[],
|
[ Some (IdentTrivia.OriginalNotation "=") ]
|
||||||
[ Some (IdentTrivia.OriginalNotation "=") ]
|
)
|
||||||
)
|
|
||||||
),
|
|
||||||
a
|
|
||||||
),
|
),
|
||||||
b
|
a
|
||||||
)
|
)
|
||||||
|
|> applyTo b
|
||||||
|
|
||||||
/// {a} + {b}
|
/// {a} + {b}
|
||||||
let plus (a : SynExpr) (b : SynExpr) =
|
let plus (a : SynExpr) (b : SynExpr) =
|
||||||
SynExpr.CreateApp (
|
SynExpr.CreateAppInfix (
|
||||||
SynExpr.CreateAppInfix (
|
SynExpr.CreateLongIdent (
|
||||||
SynExpr.CreateLongIdent (
|
SynLongIdent.SynLongIdent (
|
||||||
SynLongIdent.SynLongIdent (
|
Ident.CreateLong "op_Addition",
|
||||||
Ident.CreateLong "op_Addition",
|
[],
|
||||||
[],
|
[ Some (IdentTrivia.OriginalNotation "+") ]
|
||||||
[ Some (IdentTrivia.OriginalNotation "+") ]
|
)
|
||||||
)
|
|
||||||
),
|
|
||||||
a
|
|
||||||
),
|
),
|
||||||
b
|
a
|
||||||
)
|
)
|
||||||
|
|> applyTo b
|
||||||
|
|
||||||
let rec stripOptionalParen (expr : SynExpr) : SynExpr =
|
let rec stripOptionalParen (expr : SynExpr) : SynExpr =
|
||||||
match expr with
|
match expr with
|
||||||
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr
|
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr
|
||||||
| expr -> expr
|
| expr -> expr
|
||||||
|
|
||||||
/// Given e.g. "byte", returns "System.Byte".
|
|
||||||
let qualifyPrimitiveType (typeName : string) : LongIdent =
|
|
||||||
match typeName with
|
|
||||||
| "float32" -> [ "System" ; "Single" ]
|
|
||||||
| "float" -> [ "System" ; "Double" ]
|
|
||||||
| "byte"
|
|
||||||
| "uint8" -> [ "System" ; "Byte" ]
|
|
||||||
| "sbyte" -> [ "System" ; "SByte" ]
|
|
||||||
| "int16" -> [ "System" ; "Int16" ]
|
|
||||||
| "int" -> [ "System" ; "Int32" ]
|
|
||||||
| "int64" -> [ "System" ; "Int64" ]
|
|
||||||
| "uint16" -> [ "System" ; "UInt16" ]
|
|
||||||
| "uint"
|
|
||||||
| "uint32" -> [ "System" ; "UInt32" ]
|
|
||||||
| "uint64" -> [ "System" ; "UInt64" ]
|
|
||||||
| _ -> failwith $"Unable to identify a parsing function `string -> %s{typeName}`"
|
|
||||||
|> List.map Ident.Create
|
|
||||||
|
|
||||||
/// {obj}.{meth} {arg}
|
/// {obj}.{meth} {arg}
|
||||||
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr =
|
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr =
|
||||||
SynExpr.CreateApp (
|
SynExpr.DotGet (
|
||||||
SynExpr.DotGet (
|
obj,
|
||||||
obj,
|
range0,
|
||||||
range0,
|
SynLongIdent.SynLongIdent (id = [ Ident.Create meth ], dotRanges = [], trivia = [ None ]),
|
||||||
SynLongIdent.SynLongIdent (id = [ Ident.Create meth ], dotRanges = [], trivia = [ None ]),
|
range0
|
||||||
range0
|
|
||||||
),
|
|
||||||
arg
|
|
||||||
)
|
)
|
||||||
|
|> applyTo arg
|
||||||
|
|
||||||
/// {obj}.{meth}()
|
/// {obj}.{meth}()
|
||||||
let callMethod (meth : string) (obj : SynExpr) : SynExpr =
|
let callMethod (meth : string) (obj : SynExpr) : SynExpr =
|
||||||
callMethodArg meth (SynExpr.CreateConst SynConst.Unit) obj
|
callMethodArg meth (SynExpr.CreateConst SynConst.Unit) obj
|
||||||
|
|
||||||
/// {obj}.{meth}<ty>()
|
let callGenericMethod (meth : string) (ty : LongIdent) (obj : SynExpr) : SynExpr =
|
||||||
let callGenericMethod (meth : string) (ty : string) (obj : SynExpr) : SynExpr =
|
SynExpr.TypeApp (
|
||||||
SynExpr.CreateApp (
|
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0),
|
||||||
SynExpr.TypeApp (
|
range0,
|
||||||
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0),
|
[ SynType.LongIdent (SynLongIdent.CreateFromLongIdent ty) ],
|
||||||
range0,
|
[],
|
||||||
[ SynType.CreateLongIdent ty ],
|
Some range0,
|
||||||
[],
|
range0,
|
||||||
Some range0,
|
range0
|
||||||
range0,
|
|
||||||
range0
|
|
||||||
),
|
|
||||||
SynExpr.CreateConst SynConst.Unit
|
|
||||||
)
|
)
|
||||||
|
|> applyTo (SynExpr.CreateConst SynConst.Unit)
|
||||||
|
|
||||||
|
/// {obj}.{meth}<ty>()
|
||||||
|
let callGenericMethod' (meth : string) (ty : string) (obj : SynExpr) : SynExpr =
|
||||||
|
SynExpr.TypeApp (
|
||||||
|
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0),
|
||||||
|
range0,
|
||||||
|
[ SynType.CreateLongIdent ty ],
|
||||||
|
[],
|
||||||
|
Some range0,
|
||||||
|
range0,
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
|> applyTo (SynExpr.CreateConst SynConst.Unit)
|
||||||
|
|
||||||
let index (property : SynExpr) (obj : SynExpr) : SynExpr =
|
let index (property : SynExpr) (obj : SynExpr) : SynExpr =
|
||||||
SynExpr.DotIndexedGet (obj, property, range0, range0)
|
SynExpr.DotIndexedGet (obj, property, range0, range0)
|
||||||
@@ -177,25 +167,37 @@ module internal SynExpr =
|
|||||||
|> SynExpr.CreateParen
|
|> SynExpr.CreateParen
|
||||||
|
|
||||||
let reraise : SynExpr =
|
let reraise : SynExpr =
|
||||||
SynExpr.CreateApp (SynExpr.CreateIdent (Ident.Create "reraise"), SynExpr.CreateConst SynConst.Unit)
|
SynExpr.CreateIdent (Ident.Create "reraise")
|
||||||
|
|> applyTo (SynExpr.CreateConst SynConst.Unit)
|
||||||
|
|
||||||
/// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct)
|
/// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct)
|
||||||
let startAsTask (body : SynExpr) =
|
let startAsTask (ct : SynLongIdent) (body : SynExpr) =
|
||||||
let lambda =
|
let lambda =
|
||||||
SynExpr.CreateApp (
|
[
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "StartAsTask" ]),
|
SynExpr.CreateLongIdent (SynLongIdent.CreateString "a")
|
||||||
SynExpr.CreateParenedTuple
|
equals
|
||||||
[
|
(SynExpr.LongIdent (true, SynLongIdent.CreateString "cancellationToken", None, range0))
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateString "a")
|
(SynExpr.CreateLongIdent ct)
|
||||||
equals
|
]
|
||||||
(SynExpr.LongIdent (true, SynLongIdent.CreateString "cancellationToken", None, range0))
|
|> SynExpr.CreateParenedTuple
|
||||||
(SynExpr.CreateLongIdent (SynLongIdent.CreateString "ct"))
|
|> applyFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "StartAsTask" ]))
|
||||||
]
|
|
||||||
)
|
|
||||||
|> createLambda "a"
|
|> createLambda "a"
|
||||||
|
|
||||||
pipeThroughFunction lambda body
|
pipeThroughFunction lambda body
|
||||||
|
|
||||||
|
let createLongIdent (ident : string list) : SynExpr =
|
||||||
|
SynExpr.CreateLongIdent (SynLongIdent.Create ident)
|
||||||
|
|
||||||
|
let createLongIdent' (ident : Ident list) : SynExpr =
|
||||||
|
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent ident)
|
||||||
|
|
||||||
|
let createLet (bindings : SynBinding list) (body : SynExpr) : SynExpr =
|
||||||
|
SynExpr.LetOrUse (false, false, bindings, body, range0, SynExprLetOrUseTrivia.empty)
|
||||||
|
|
||||||
|
let createMatch (matchOn : SynExpr) (cases : SynMatchClause list) : SynExpr = SynExpr.CreateMatch (matchOn, cases)
|
||||||
|
|
||||||
|
let typeAnnotate (ty : SynType) (expr : SynExpr) : SynExpr = SynExpr.CreateTyped (expr, ty)
|
||||||
|
|
||||||
/// {compExpr} { {lets} ; return {ret} }
|
/// {compExpr} { {lets} ; return {ret} }
|
||||||
let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr =
|
let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr =
|
||||||
let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0)
|
let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0)
|
||||||
@@ -219,16 +221,7 @@ module internal SynExpr =
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
| Let (lhs, rhs) ->
|
| Let (lhs, rhs) ->
|
||||||
SynExpr.LetOrUse (
|
createLet [ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ] state
|
||||||
false,
|
|
||||||
false,
|
|
||||||
[ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ],
|
|
||||||
state,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
SynExprLetOrUseTrivia.InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
| Use (lhs, rhs) ->
|
| Use (lhs, rhs) ->
|
||||||
SynExpr.LetOrUse (
|
SynExpr.LetOrUse (
|
||||||
false,
|
false,
|
||||||
@@ -263,13 +256,54 @@ module internal SynExpr =
|
|||||||
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
|
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
|
||||||
| _ -> callMethod "ToString" ident
|
| _ -> callMethod "ToString" ident
|
||||||
|
|
||||||
let synBindingTriviaZero (isMember : bool) =
|
let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0)
|
||||||
{
|
|
||||||
SynBindingTrivia.EqualsRange = Some range0
|
/// {ident} - {rhs}
|
||||||
InlineKeyword = None
|
let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr =
|
||||||
LeadingKeyword =
|
SynExpr.CreateApp (
|
||||||
if isMember then
|
SynExpr.CreateAppInfix (
|
||||||
SynLeadingKeyword.Member range0
|
SynExpr.CreateLongIdent (
|
||||||
else
|
SynLongIdent.SynLongIdent (
|
||||||
SynLeadingKeyword.Let range0
|
[ Ident.Create "op_Subtraction" ],
|
||||||
}
|
[],
|
||||||
|
[ Some (IdentTrivia.OriginalNotation "-") ]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
SynExpr.CreateLongIdent ident
|
||||||
|
),
|
||||||
|
rhs
|
||||||
|
)
|
||||||
|
|
||||||
|
/// {ident} - {n}
|
||||||
|
let minusN (ident : SynLongIdent) (n : int) : SynExpr =
|
||||||
|
minus ident (SynExpr.CreateConst (SynConst.Int32 n))
|
||||||
|
|
||||||
|
/// {y} > {x}
|
||||||
|
let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr =
|
||||||
|
SynExpr.CreateApp (
|
||||||
|
SynExpr.CreateAppInfix (
|
||||||
|
SynExpr.CreateLongIdent (
|
||||||
|
SynLongIdent.SynLongIdent (
|
||||||
|
[ Ident.Create "op_GreaterThan" ],
|
||||||
|
[],
|
||||||
|
[ Some (IdentTrivia.OriginalNotation ">") ]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
y
|
||||||
|
),
|
||||||
|
x
|
||||||
|
)
|
||||||
|
|
||||||
|
/// {y} >= {x}
|
||||||
|
let greaterThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr =
|
||||||
|
SynExpr.CreateAppInfix (
|
||||||
|
SynExpr.CreateLongIdent (
|
||||||
|
SynLongIdent.SynLongIdent (
|
||||||
|
[ Ident.Create "op_GreaterThanOrEqual" ],
|
||||||
|
[],
|
||||||
|
[ Some (IdentTrivia.OriginalNotation ">=") ]
|
||||||
|
)
|
||||||
|
),
|
||||||
|
y
|
||||||
|
)
|
||||||
|
|> applyTo x
|
||||||
10
WoofWare.Myriad.Plugins/SynExpr/SynExprLetOrUseTrivia.fs
Normal file
10
WoofWare.Myriad.Plugins/SynExpr/SynExprLetOrUseTrivia.fs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynExprLetOrUseTrivia =
|
||||||
|
let empty : SynExprLetOrUseTrivia =
|
||||||
|
{
|
||||||
|
InKeyword = None
|
||||||
|
}
|
||||||
39
WoofWare.Myriad.Plugins/SynExpr/SynField.fs
Normal file
39
WoofWare.Myriad.Plugins/SynExpr/SynField.fs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
|
||||||
|
type internal SynFieldData<'Ident> =
|
||||||
|
{
|
||||||
|
Attrs : SynAttribute list
|
||||||
|
Ident : 'Ident
|
||||||
|
Type : SynType
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynField =
|
||||||
|
/// Get the useful information out of a SynField.
|
||||||
|
let extract (SynField (attrs, _, id, fieldType, _, _, _, _, _)) : SynFieldData<Ident option> =
|
||||||
|
{
|
||||||
|
Attrs = attrs |> List.collect (fun l -> l.Attributes)
|
||||||
|
Ident = id
|
||||||
|
Type = fieldType
|
||||||
|
}
|
||||||
|
|
||||||
|
let mapIdent<'a, 'b> (f : 'a -> 'b) (x : SynFieldData<'a>) : SynFieldData<'b> =
|
||||||
|
let ident = f x.Ident
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = x.Attrs
|
||||||
|
Ident = ident
|
||||||
|
Type = x.Type
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Throws if the field has no identifier.
|
||||||
|
let extractWithIdent (f : SynField) : SynFieldData<Ident> =
|
||||||
|
f
|
||||||
|
|> extract
|
||||||
|
|> mapIdent (fun ident ->
|
||||||
|
match ident with
|
||||||
|
| None -> failwith "expected field identifier to have a value, but it did not"
|
||||||
|
| Some i -> i
|
||||||
|
)
|
||||||
10
WoofWare.Myriad.Plugins/SynExpr/SynPat.fs
Normal file
10
WoofWare.Myriad.Plugins/SynExpr/SynPat.fs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynPat =
|
||||||
|
|
||||||
|
let annotateType (ty : SynType) (pat : SynPat) =
|
||||||
|
SynPat.Paren (SynPat.Typed (pat, ty, range0), range0)
|
||||||
10
WoofWare.Myriad.Plugins/SynExpr/SynType.fs
Normal file
10
WoofWare.Myriad.Plugins/SynExpr/SynType.fs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynType =
|
||||||
|
let rec stripOptionalParen (ty : SynType) : SynType =
|
||||||
|
match ty with
|
||||||
|
| SynType.Paren (ty, _) -> stripOptionalParen ty
|
||||||
|
| ty -> ty
|
||||||
41
WoofWare.Myriad.Plugins/SynExpr/SynUnionCase.fs
Normal file
41
WoofWare.Myriad.Plugins/SynExpr/SynUnionCase.fs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
|
||||||
|
type internal UnionCase<'Ident> =
|
||||||
|
{
|
||||||
|
Fields : SynFieldData<'Ident> list
|
||||||
|
Attrs : SynAttribute list
|
||||||
|
Ident : Ident
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal UnionCase =
|
||||||
|
let mapIdentFields<'a, 'b> (f : 'a -> 'b) (unionCase : UnionCase<'a>) : UnionCase<'b> =
|
||||||
|
{
|
||||||
|
Fields = unionCase.Fields |> List.map (SynField.mapIdent f)
|
||||||
|
Attrs = unionCase.Attrs
|
||||||
|
Ident = unionCase.Ident
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SynUnionCase =
|
||||||
|
let extract (SynUnionCase (attrs, id, caseType, _, _, _, _)) : UnionCase<Ident option> =
|
||||||
|
match caseType with
|
||||||
|
| SynUnionCaseKind.FullType _ -> failwith "WoofWare.Myriad does not support FullType union cases."
|
||||||
|
| SynUnionCaseKind.Fields fields ->
|
||||||
|
|
||||||
|
let fields = fields |> List.map SynField.extract
|
||||||
|
|
||||||
|
let id =
|
||||||
|
match id with
|
||||||
|
| SynIdent.SynIdent (ident, _) -> ident
|
||||||
|
|
||||||
|
// As far as I can tell, there's no way to get any attributes here? :shrug:
|
||||||
|
let attrs = attrs |> List.collect (fun l -> l.Attributes)
|
||||||
|
|
||||||
|
{
|
||||||
|
Fields = fields
|
||||||
|
Attrs = attrs
|
||||||
|
Ident = id
|
||||||
|
}
|
||||||
@@ -18,19 +18,30 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Myriad.Core" Version="0.8.3"/>
|
<PackageReference Include="Myriad.Core" Version="0.8.3" PrivateAssets="all"/>
|
||||||
<!-- the lowest version allowed by Myriad.Core -->
|
<!-- the lowest version allowed by Myriad.Core -->
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.1"/>
|
<PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<Compile Include="List.fs"/>
|
||||||
|
<Compile Include="Ident.fs" />
|
||||||
<Compile Include="AstHelper.fs"/>
|
<Compile Include="AstHelper.fs"/>
|
||||||
<Compile Include="SynExpr.fs"/>
|
<Compile Include="SynExpr\SynExprLetOrUseTrivia.fs" />
|
||||||
<Compile Include="SynAttribute.fs"/>
|
<Compile Include="SynExpr\SynBinding.fs" />
|
||||||
|
<Compile Include="SynExpr\SynExpr.fs" />
|
||||||
|
<Compile Include="SynExpr\SynType.fs" />
|
||||||
|
<Compile Include="SynExpr\SynAttribute.fs" />
|
||||||
|
<Compile Include="SynExpr\SynArgPats.fs" />
|
||||||
|
<Compile Include="SynExpr\SynField.fs" />
|
||||||
|
<Compile Include="SynExpr\SynUnionCase.fs" />
|
||||||
|
<Compile Include="SynExpr\SynPat.fs" />
|
||||||
<Compile Include="RemoveOptionsGenerator.fs"/>
|
<Compile Include="RemoveOptionsGenerator.fs"/>
|
||||||
<Compile Include="InterfaceMockGenerator.fs" />
|
<Compile Include="InterfaceMockGenerator.fs"/>
|
||||||
|
<Compile Include="JsonSerializeGenerator.fs"/>
|
||||||
<Compile Include="JsonParseGenerator.fs"/>
|
<Compile Include="JsonParseGenerator.fs"/>
|
||||||
<Compile Include="HttpClientGenerator.fs"/>
|
<Compile Include="HttpClientGenerator.fs"/>
|
||||||
|
<Compile Include="CataGenerator.fs" />
|
||||||
<EmbeddedResource Include="version.json"/>
|
<EmbeddedResource Include="version.json"/>
|
||||||
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
||||||
<None Include="..\README.md">
|
<None Include="..\README.md">
|
||||||
@@ -43,4 +54,11 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj"/>
|
||||||
|
<!-- NuGet is such a clown package manager! Get the DLLs into the Nupkg artefact, I have no idea why this is needed,
|
||||||
|
but without this line, we don't get any dependency at all packaged into the resulting artefact. -->
|
||||||
|
<None Include="$(OutputPath)\WoofWare.Myriad.Plugins.Attributes.dll" Pack="true" PackagePath="lib\$(TargetFramework)"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,12 @@
|
|||||||
{
|
{
|
||||||
"version": "1.3",
|
"version": "2.1",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
"pathFilters": null
|
"pathFilters": [
|
||||||
|
":/",
|
||||||
|
":^WoofWare.Myriad.Plugins.Test/",
|
||||||
|
":^WoofWare.Myriad.Plugins.Attributes/Test/",
|
||||||
|
":^/.github/"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -6,6 +6,10 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins", "
|
|||||||
EndProject
|
EndProject
|
||||||
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Test", "WoofWare.Myriad.Plugins.Test\WoofWare.Myriad.Plugins.Test.fsproj", "{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}"
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Test", "WoofWare.Myriad.Plugins.Test\WoofWare.Myriad.Plugins.Test.fsproj", "{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Attributes", "WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj", "{17548737-9BAB-4B1E-B680-76D47C343AAC}"
|
||||||
|
EndProject
|
||||||
|
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Attributes.Test", "WoofWare.Myriad.Plugins.Attributes\Test\WoofWare.Myriad.Plugins.Attributes.Test.fsproj", "{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -24,5 +28,13 @@ Global
|
|||||||
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Release|Any CPU.Build.0 = Release|Any CPU
|
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageDownload Include="G-Research.FSharp.Analyzers" Version="[0.6.0]" />
|
<PackageDownload Include="G-Research.FSharp.Analyzers" Version="[0.10.0]" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
11
flake.lock
generated
11
flake.lock
generated
@@ -20,17 +20,18 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1703134684,
|
"lastModified": 1706367331,
|
||||||
"narHash": "sha256-SQmng1EnBFLzS7WSRyPM9HgmZP2kLJcPAz+Ug/nug6o=",
|
"narHash": "sha256-AqgkGHRrI6h/8FWuVbnkfFmXr4Bqsr4fV23aISqj/xg=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "d6863cbcbbb80e71cecfc03356db1cda38919523",
|
"rev": "160b762eda6d139ac10ae081f8f78d640dd523eb",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"id": "nixpkgs",
|
"owner": "NixOS",
|
||||||
"ref": "nixpkgs-unstable",
|
"ref": "nixpkgs-unstable",
|
||||||
"type": "indirect"
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
|
|||||||
11
flake.nix
11
flake.nix
@@ -3,11 +3,10 @@
|
|||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
flake-utils.url = "github:numtide/flake-utils";
|
flake-utils.url = "github:numtide/flake-utils";
|
||||||
nixpkgs.url = "nixpkgs/nixpkgs-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
self,
|
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
flake-utils,
|
flake-utils,
|
||||||
...
|
...
|
||||||
@@ -44,8 +43,8 @@
|
|||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
packages = {
|
packages = {
|
||||||
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version "sha256-Jmo7s8JMdQ8SxvNvPnryfE7n24mIgKi5cbgNwcQw3yU=";
|
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).sha256;
|
||||||
fsharp-analyzers = dotnetTool "FSharp.Analyzers.Cli" "fsharp-analyzers" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fsharp-analyzers.version "sha256-CWMW06ncSs8QkQvxNPNrgn3TAzMU6qCT1k2A3pnGrYQ=";
|
fsharp-analyzers = dotnetTool "FSharp.Analyzers.Cli" "fsharp-analyzers" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fsharp-analyzers.version (builtins.head (builtins.filter (elem: elem.pname == "fsharp-analyzers") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).sha256;
|
||||||
fetchDeps = let
|
fetchDeps = let
|
||||||
flags = [];
|
flags = [];
|
||||||
runtimeIds = ["win-x64"] ++ map (system: pkgs.dotnetCorePackages.systemToDotnetRid system) dotnet-sdk.meta.platforms;
|
runtimeIds = ["win-x64"] ++ map (system: pkgs.dotnetCorePackages.systemToDotnetRid system) dotnet-sdk.meta.platforms;
|
||||||
@@ -54,8 +53,8 @@
|
|||||||
src = ./nix/fetchDeps.sh;
|
src = ./nix/fetchDeps.sh;
|
||||||
pname = pname;
|
pname = pname;
|
||||||
binPath = pkgs.lib.makeBinPath [pkgs.coreutils dotnet-sdk (pkgs.nuget-to-nix.override {inherit dotnet-sdk;})];
|
binPath = pkgs.lib.makeBinPath [pkgs.coreutils dotnet-sdk (pkgs.nuget-to-nix.override {inherit dotnet-sdk;})];
|
||||||
projectFiles = toString ["./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj" "./ConsumePlugin/ConsumePlugin.fsproj"];
|
projectFiles = toString ["./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj" "./ConsumePlugin/ConsumePlugin.fsproj" "./WoofWare.Myriad.Plugins.Attributes/WoofWare.Myriad.Plugins.Attributes.fsproj"];
|
||||||
testProjectFiles = ["./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj"];
|
testProjectFiles = ["./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj" "./WoofWare.Myriad.Plugins.Attributes/Test/Woofware.Myriad.Plugins.Attributes.Test.fsproj"];
|
||||||
rids = pkgs.lib.concatStringsSep "\" \"" runtimeIds;
|
rids = pkgs.lib.concatStringsSep "\" \"" runtimeIds;
|
||||||
packages = dotnet-sdk.packages;
|
packages = dotnet-sdk.packages;
|
||||||
storeSrc = pkgs.srcOnly {
|
storeSrc = pkgs.srcOnly {
|
||||||
|
|||||||
234
nix/deps.nix
234
nix/deps.nix
@@ -3,23 +3,18 @@
|
|||||||
{fetchNuGet}: [
|
{fetchNuGet}: [
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "fsharp-analyzers";
|
pname = "fsharp-analyzers";
|
||||||
version = "0.23.0";
|
version = "0.26.0";
|
||||||
sha256 = "sha256-CWMW06ncSs8QkQvxNPNrgn3TAzMU6qCT1k2A3pnGrYQ=";
|
sha256 = "sha256-60Bl36LOb/zVNdH2SBSuQ5O41lP9dKTNZbs5vvYs+3U=";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "fantomas";
|
pname = "fantomas";
|
||||||
version = "6.3.0-alpha-005";
|
version = "6.3.4";
|
||||||
sha256 = "sha256-Jmo7s8JMdQ8SxvNvPnryfE7n24mIgKi5cbgNwcQw3yU=";
|
sha256 = "sha256-1aWqZynBkQoznenGoP0sbf1PcUXAbcHiWyECuv89xa0=";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "ApiSurface";
|
pname = "ApiSurface";
|
||||||
version = "4.0.25";
|
version = "4.0.40";
|
||||||
sha256 = "0zjq8an9cr0l7wxdmm9n9s3iyq5m0zl4x0h0wmy5cz7am8y15qc4";
|
sha256 = "1c9z0b6minlripwrjmv4yd5w8zj4lcpak4x41izh7ygx8kgmbvx0";
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "coverlet.collector";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "12j34vrkmph8lspbafnqmfnj2qvysz1jcrks2khw798s6dwv0j90";
|
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Fantomas.Core";
|
pname = "Fantomas.Core";
|
||||||
@@ -36,6 +31,11 @@
|
|||||||
version = "2.16.6";
|
version = "2.16.6";
|
||||||
sha256 = "176rwky6b5rk8dzldiz4068p7m9c5y9ygzbhadrs14jkl94pc56n";
|
sha256 = "176rwky6b5rk8dzldiz4068p7m9c5y9ygzbhadrs14jkl94pc56n";
|
||||||
})
|
})
|
||||||
|
(fetchNuGet {
|
||||||
|
pname = "FSharp.Core";
|
||||||
|
version = "4.3.4";
|
||||||
|
sha256 = "1sg6i4q5nwyzh769g76f6c16876nvdpn83adqjr2y9x6xsiv5p5j";
|
||||||
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "FSharp.Core";
|
pname = "FSharp.Core";
|
||||||
version = "6.0.1";
|
version = "6.0.1";
|
||||||
@@ -43,8 +43,8 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "FSharp.Core";
|
pname = "FSharp.Core";
|
||||||
version = "8.0.100";
|
version = "8.0.101";
|
||||||
sha256 = "06z3vg8yj7i83x6gmnzl2lka1bp4hzc07h6mrydpilxswnmy2a0l";
|
sha256 = "0prgcnki6s0rlrfbarrcv50w1bbhaalsyhhw5gsnjs2is7qrjbii";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "FsUnit";
|
pname = "FsUnit";
|
||||||
@@ -53,63 +53,63 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Ref";
|
pname = "Microsoft.AspNetCore.App.Ref";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "1vrmqn5j6ibwkqasbf7x7n4w5jdclnz3giymiwvym2wa0y5zc59q";
|
sha256 = "1d8nkz24vsm0iy2xm8y5ak2q1w1p99dxyz0y26acs6sfk2na0vm6";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Ref";
|
pname = "Microsoft.AspNetCore.App.Ref";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "0k304yhpm92c46a1fscbzlgvdbhrm9vlbpyfgwp3cafz4f7z7a5y";
|
sha256 = "0yaaiqq7mi6sclyrb1v0fyncanbx0ifmnnhv9whynqj8439jsdwh";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "0mgcs4si7mwd0f555s1vg17pf4nqfaijd1pci359l1pgrmv70rrg";
|
sha256 = "1za8lc52m4z54d68wd64c2nhzy05g3gx171k5cdlx73fbymiys9z";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "05y1xb5fw8lzvb4si77a5qwfwfz1855crqbphrwky6x9llivbhkx";
|
sha256 = "0dsdgqg7566qximmjfza4x9if3icy4kskq698ddj5apdia88h2mw";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "0wvzhqhlmlbnpa18qp8m3wcrlcgj3ckvp3iv2n7g8vb60c3238aq";
|
sha256 = "1zpbmz6z8758gwywzg0bac8kx9x39sxxc9j4a4r2jl74l9ssw4vm";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "18zdbcb2bn7wy1dp14z5jyqiiwr9rkad1lcb158r5ikjfq1rg5iw";
|
sha256 = "1gjz379y61ag9whi78qxx09bwkwcznkx2mzypgycibxk61g11da1";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "1pywgvb8ck1d5aadmijd5s3z6yclchd9pa6dsahijmm55ibplx36";
|
sha256 = "1i8ydlwjzk7j0mzvn0rpljxfp1h50zwaqalnyvfxai1fwgigzgw5";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "1nbxzmj6cnccylxis67c54c0ik38ma4rwdvgg6sxd6r04219maqm";
|
sha256 = "0w3mrs4zdl9mfanl1j81759xwwrzmicsjxn6yfxv5yrxbxzq695n";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "1zlf0w7i6r02719dv3nw4jy14sa0rs53i89an5alz5qmywdy3f1d";
|
sha256 = "02src68hd3213sd1a2ms1my7i92knfmdxclvv90il9cky2zsq8kw";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "1wqkbjd1ywv9w397l7rsb89mijc5n0hv7jq9h09xfz6wn9qsp152";
|
sha256 = "0a9aljr4fy4haq6ndz2y723liv5hbfpss1rn45s88nmgcp27m15m";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "1fbsnm4056cpd4avgpi5sq05m1yd9k4x229ckxpr4q7yc94sncwy";
|
sha256 = "1gxlmfdkfzmhw9pac5jiv674nn6i1zymcp2hj81irjwhhjk01mf5";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "08vlmswmiyp2nxlr9d77716hk7kz7h9x5bl8wh76xzbj5id1xlb2";
|
sha256 = "01kzndyqmsvcq49i2jrv7ymfp0l71yxfylv1cy3nhkdbprqz8ipx";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.Build.Tasks.Git";
|
pname = "Microsoft.Build.Tasks.Git";
|
||||||
@@ -118,123 +118,128 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.CodeCoverage";
|
pname = "Microsoft.CodeCoverage";
|
||||||
version = "17.8.0";
|
version = "17.10.0";
|
||||||
sha256 = "173wjadp3gan4x2jfjchngnc4ca4mb95h1sbb28jydfkfw0z1zvj";
|
sha256 = "0s0v7jmrq85n356xv7zixvwa4z94fszjcr5vll8x4im1a2lp00f9";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NET.Test.Sdk";
|
pname = "Microsoft.NET.Test.Sdk";
|
||||||
version = "17.8.0";
|
version = "17.10.0";
|
||||||
sha256 = "1syvl3g0hbrcgfi9rq6pld8s8hqqww4dflf1lxn59ccddyyx0gmv";
|
sha256 = "13g8fwl09li8fc71nk13dgkb7gahd4qhamyg2xby7am63nlchhdf";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "052388yjivzkfllkss0nljbzmjx787jqdjsbb6ls855sp6wh9xfd";
|
sha256 = "19y6c6v20bgf7x7rrh4rx9y7s5fy8vp5m4j9b6gi1wp4rpb5mza4";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "0bpg3v9dnalz7yh7lsgriw9rnm9jx37mqhhvf7snznb3sfk7rgwb";
|
sha256 = "0dhpdlcdz7adcfh9w01fc867051m35fqaxnvj3fqvqhgcm2n3143";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "103xy6kncjwbbchfnpqvsjpjy92x3dralcg9pw939jp0dwggwarz";
|
sha256 = "0p7hhidaa3mnyiwnsijwy8578v843x8hh99255s69qwwyld6falv";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "1c7l68bm05d94x5wk1y33mnd4v8m196vyprgrzqnh94yrqy6fkf7";
|
sha256 = "1aw6mc7zcmzs1grxz2wa9cw9kfj8pz7zpj417xnp1a9n4ix1bxgr";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "13m14pdx5xfxky07xgxf6hjd7g9l4k6k40wvp9znhvn27pa0wdxv";
|
sha256 = "1mq11xsv9g1vsasp6k80y7xlvwi9hrpk5dgm773fvy8538s01gfv";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "1hdv825s964vfcgnk94pzhgxnj948f1vdj423jjxpkppcy30fl0m";
|
sha256 = "1dzg3prng9zfdzz7gcgywjdbwzhwm85j89z0jahynxx4q2dra4b9";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "132pgjhv42mqzx4007sd59bkds0fwsv5xaz07y2yffbn3lzr228k";
|
sha256 = "1chac9b4424ihrrnlzvc7qz6j4ymfjyv4kzyazzzw19yhymdkh2s";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "0jmzf58vv45j0hqlxq8yalpjwi328vp2mjr3h0pdg0qr143iivnr";
|
sha256 = "010f8wn15s2kv7yyzgys3pv9i1mxw20hpv1ig2zhybjxs8lpj8jj";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.win-x64";
|
pname = "Microsoft.NETCore.App.Host.win-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "039433rm4w37h9qri11v3lrpddpz7zcly9kq8vmk6w1ixzlqwf01";
|
sha256 = "0i7g9fsqjnbh9rc6807m57r2idg5pkcw6xjfwhnxkcpgqm96258v";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Host.win-x64";
|
pname = "Microsoft.NETCore.App.Host.win-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "1n8yr13df2f6jhxpfazs6rxahfqm18fhjvfm16g5d60c3za1hwnk";
|
sha256 = "1ssj1cyam3nfidm8q82kvh4i3fzm2lzb3bxw6ck09hwhvwh909z4";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Ref";
|
pname = "Microsoft.NETCore.App.Ref";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "0jfhmfxpx1h4f3axgf60gc8d4cnlvbb853400kag6nk0875hr0x1";
|
sha256 = "12gb52dhg5h9hgnyqh1zgj2w46paxv2pfh33pphl9ajhrdr7hlsb";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Ref";
|
pname = "Microsoft.NETCore.App.Ref";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "0hyvbh86433764qqqhw9i7ga0ax7bbdmzh77jw58pq0ggm41cff9";
|
sha256 = "02r4jg4ha0qksix9v6s3cpmvavmz54gkawkxy9bvknw5ynxhhl1l";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "0jpcmva1l8z36r4phz055l7fz9s6z8pv8pqc4ia69mhhgvr0ks7y";
|
sha256 = "164hfrwqz5dxcbb441lridk4mzcqmarb0b7ckgvqhsvpawyjw88v";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "0gwqmkmr7jy3sjh9gha82amlry41gp8nwswy2iqfw54f28db63n7";
|
sha256 = "0353whnjgz3sqhzsfrviad3a3db4pk7hl7m4wwppv5mqdg9i9ri5";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "012jml0bqxbspahf1j4bvvd91pz85hsbcyhq00gxczcazhxpkhz4";
|
sha256 = "0islayddpnflviqpbq4djc4f3v9nhsa2y76k5x6il3csq5vdw2hq";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "042cjvnwrrjs3mw5q8q5kinh0cwkks33i3n1vyifaid2jbr3wlc0";
|
sha256 = "1g5b30f4l8a1zjjr3b8pk9mcqxkxqwa86362f84646xaj4iw3a4d";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "0wgwxpyy1n550sw7npjg69zpxknwn0ay30m2qybvqb5mj857qzxi";
|
sha256 = "1acn5zw1pxzmcg3c0pbf9hal36fbdh9mvbsiwra7simrk7hzqpdc";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "06ndp4wh1cap01dql3nixka4g56bf6ipmqys7xaxvg4xisf79x8d";
|
sha256 = "0cdrpdaq5sl3602anfx1p0z0ncx2sjjvl6mgsd6y38g47n7f95jc";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "08vr7c5bg5x3w35l54z1azif7ysfc2yiyz50ip1dl0mpqywvlswr";
|
sha256 = "00f9l9dkdz0zv5csaw8fkm6s8ckrj5n9k3ygz12daa22l3bcn6ii";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "1kh5bnaf6h9mr4swcalrp304625frjiw6mlz1052rxwzsdq98a96";
|
sha256 = "1fk1flqp6ji0l4c2gvh83ykndpx7a2nkkgrgkgql3c75j1k2v1s9";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
||||||
version = "6.0.25";
|
version = "6.0.26";
|
||||||
sha256 = "03snpmx204xvc9668riisvvdjjgdqhwj7yjp85w5lh8j8ygrqkif";
|
sha256 = "0i2p356phfc5y6qnr3vyrzjfi1mrbwfb6g85k4q37bbyxjfp7zl9";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
||||||
version = "8.0.0";
|
version = "8.0.1";
|
||||||
sha256 = "054icf5jjnwnswrnv1r05x3pfjvacbz6g3dj8caar1zp53k49rkk";
|
sha256 = "198576cdkl72xs29zznff9ls763p8pfr0zji7b74dqxd5ga0s3bd";
|
||||||
|
})
|
||||||
|
(fetchNuGet {
|
||||||
|
pname = "Microsoft.NETCore.Platforms";
|
||||||
|
version = "1.1.0";
|
||||||
|
sha256 = "08vh1r12g6ykjygq5d3vq09zylgb84l63k49jc4v8faw9g93iqqm";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.NETCore.Platforms";
|
pname = "Microsoft.NETCore.Platforms";
|
||||||
@@ -263,13 +268,13 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.TestPlatform.ObjectModel";
|
pname = "Microsoft.TestPlatform.ObjectModel";
|
||||||
version = "17.8.0";
|
version = "17.10.0";
|
||||||
sha256 = "0b0i7lmkrcfvim8i3l93gwqvkhhhfzd53fqfnygdqvkg6np0cg7m";
|
sha256 = "07j69cw8r39533w4p39mnj00kahazz38760in3jfc45kmlcdb26x";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.TestPlatform.TestHost";
|
pname = "Microsoft.TestPlatform.TestHost";
|
||||||
version = "17.8.0";
|
version = "17.10.0";
|
||||||
sha256 = "0f5jah93kjkvxwmhwb78lw11m9pkkq9fvf135hpymmmpxqbdh97q";
|
sha256 = "1bl471s7fx9jycr0cc8rylwf34mrvlg9qn1an6l86nisavfcyb7v";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Myriad.Core";
|
pname = "Myriad.Core";
|
||||||
@@ -286,6 +291,11 @@
|
|||||||
version = "3.6.133";
|
version = "3.6.133";
|
||||||
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80";
|
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80";
|
||||||
})
|
})
|
||||||
|
(fetchNuGet {
|
||||||
|
pname = "NETStandard.Library";
|
||||||
|
version = "2.0.3";
|
||||||
|
sha256 = "1fn9fxppfcg4jgypp2pmrpr6awl3qz1xmnri0cygpkwvyx27df1y";
|
||||||
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Newtonsoft.Json";
|
pname = "Newtonsoft.Json";
|
||||||
version = "13.0.1";
|
version = "13.0.1";
|
||||||
@@ -298,48 +308,38 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NuGet.Common";
|
pname = "NuGet.Common";
|
||||||
version = "6.8.0";
|
version = "6.10.0";
|
||||||
sha256 = "0l3ij8iwy7wj6s7f93lzi9168r4wz8zyin6a08iwgk7hvq44cia1";
|
sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NuGet.Configuration";
|
pname = "NuGet.Configuration";
|
||||||
version = "6.8.0";
|
version = "6.10.0";
|
||||||
sha256 = "0x03p408smkmv1gv7pmvsia4lkn0xaj4wfrkl58pjf8bbv51y0yw";
|
sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NuGet.Frameworks";
|
pname = "NuGet.Frameworks";
|
||||||
version = "6.5.0";
|
version = "6.10.0";
|
||||||
sha256 = "0s37d1p4md0k6d4cy6sq36f2dgkd9qfbzapxhkvi8awwh0vrynhj";
|
sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk";
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Frameworks";
|
|
||||||
version = "6.8.0";
|
|
||||||
sha256 = "0i2xvhgkjkjr496i3pg8hamwv6505fia45qhn7jg5m01wb3cvsjl";
|
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NuGet.Packaging";
|
pname = "NuGet.Packaging";
|
||||||
version = "6.8.0";
|
version = "6.10.0";
|
||||||
sha256 = "031z4s905bxi94h3f0qy4j1b6jxdxgqgpkzqvvpfxch07szxcbim";
|
sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NuGet.Protocol";
|
pname = "NuGet.Protocol";
|
||||||
version = "6.7.0";
|
version = "6.10.0";
|
||||||
sha256 = "1v5ibnq2mp801vw68zyj169hkj3xm7h55824i33n1jxxj2vs3vbk";
|
sha256 = "0hmv4q0ks9i34mfgpb13l01la9v3jjllfh1qd3aqv105xrqrdxac";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NuGet.Versioning";
|
pname = "NuGet.Versioning";
|
||||||
version = "6.8.0";
|
version = "6.10.0";
|
||||||
sha256 = "1sd25h46fd12ng780r02q4ijcx1imkb53kj1y2y7cwg5myh537ks";
|
sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NUnit";
|
pname = "NUnit";
|
||||||
version = "4.0.1";
|
version = "4.1.0";
|
||||||
sha256 = "0jgiq3dbwli5r70j0bw7021d69r7bhr58s8kphlpjmf7k47l5pcd";
|
sha256 = "0fj6xwgqaxq3mrai86bklclfmjkzf038mrslwfqf4ignaz9f7g5j";
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NUnit.Analyzers";
|
|
||||||
version = "3.10.0";
|
|
||||||
sha256 = "1zc6s7lmzw5avrnbbjwyzla9d6bafbpxgv62m4zlqxv14p85md0d";
|
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "NUnit3TestAdapter";
|
pname = "NUnit3TestAdapter";
|
||||||
@@ -433,12 +433,12 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "System.Text.Encodings.Web";
|
pname = "System.Text.Encodings.Web";
|
||||||
version = "6.0.0";
|
version = "7.0.0";
|
||||||
sha256 = "06n9ql3fmhpjl32g3492sj181zjml5dlcc5l76xq2h38c4f87sai";
|
sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "System.Text.Json";
|
pname = "System.Text.Json";
|
||||||
version = "6.0.0";
|
version = "7.0.3";
|
||||||
sha256 = "1si2my1g0q0qv1hiqnji4xh9wd05qavxnzj9dwgs23iqvgjky0gl";
|
sha256 = "0zjrnc9lshagm6kdb9bdh45dmlnkpwcpyssa896sda93ngbmj8k9";
|
||||||
})
|
})
|
||||||
]
|
]
|
||||||
|
|||||||
Reference in New Issue
Block a user