mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-25 22:08:40 +00:00
Compare commits
207 Commits
5c1841c3d2
...
WoofWare.M
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d275f0047 | ||
|
|
682b12fdb2 | ||
|
|
325f8634a4 | ||
|
|
3e5d663544 | ||
|
|
bb88f80c85 | ||
|
|
71f26930c6 | ||
|
|
680728a06e | ||
|
|
cdc6f2d511 | ||
|
|
3be487c328 | ||
|
|
a5f4d169ca | ||
|
|
ce634efff2 | ||
|
|
1529dd1fb2 | ||
|
|
59558b0766 | ||
|
|
8602894efc | ||
|
|
51d349b365 | ||
|
|
120df84bbf | ||
|
|
603f875a12 | ||
|
|
2df41555de | ||
|
|
49e31e52b5 | ||
|
|
277a186fda | ||
|
|
129687ec1c | ||
|
|
c7fea55e28 | ||
|
|
ded7b32771 | ||
|
|
b272f8b645 | ||
|
|
b8d60aec90 | ||
|
|
0e3510e1e5 | ||
|
|
8a1edd90d5 | ||
|
|
74fdd7c0a9 | ||
|
|
23f55814f9 | ||
|
|
15c04bb373 | ||
|
|
a860a93f9c | ||
|
|
b056af348e | ||
|
|
b44c8db6e9 | ||
|
|
7d6a2cea01 | ||
|
|
d779a602f4 | ||
|
|
23cd5272fb | ||
|
|
93538ee6b4 | ||
|
|
8a29c2f444 | ||
|
|
1367e00f34 | ||
|
|
bff08c90cd | ||
|
|
4136f7fe94 | ||
|
|
9fe2e3b1fa | ||
|
|
13a597a365 | ||
|
|
3acc492f22 | ||
|
|
eefe64f5a4 | ||
|
|
93a2d92299 | ||
|
|
33793e8cbe | ||
|
|
fa7ef1ffba | ||
|
|
a25c45dc3a | ||
|
|
af5f2abdf8 | ||
|
|
0f89816432 | ||
|
|
d571da6a22 | ||
|
|
39a9e94ca5 | ||
|
|
487f73312a | ||
|
|
b7aa564306 | ||
|
|
a34dee0a1c | ||
|
|
44506f3650 | ||
|
|
ca7134cfb8 | ||
|
|
773fd088a7 | ||
|
|
6e5c0332cd | ||
|
|
aece186424 | ||
|
|
827e9aa3ec | ||
|
|
d59ebdfccb | ||
|
|
5319a33b7b | ||
|
|
29b93b2f20 | ||
|
|
40106d8aa3 | ||
|
|
4d641b0662 | ||
|
|
16e6b91548 | ||
|
|
8488883835 | ||
|
|
0652744c57 | ||
|
|
9252979673 | ||
|
|
1120a3752d | ||
|
|
7ca6b0c0eb | ||
|
|
50efb8d9bc | ||
|
|
93a1b630c8 | ||
|
|
4482038e8a | ||
|
|
a41fa9dd37 | ||
|
|
fc5acc2f58 | ||
|
|
0a1783d6ed | ||
|
|
9a3ebbf28f | ||
|
|
e22525c200 | ||
|
|
09b7109c84 | ||
|
|
693b95106a | ||
|
|
49ecfbf5e5 | ||
|
|
5748ac3d5b | ||
|
|
913959a740 | ||
|
|
93ffc065cd | ||
|
|
d14efba7e7 | ||
|
|
f5cf0b79dd | ||
|
|
029e3746bb | ||
|
|
8ae749c529 | ||
|
|
e4cbab3209 | ||
|
|
bdce82fb7a | ||
|
|
8f9f933971 | ||
|
|
3a55ba1242 | ||
|
|
047b2eda99 | ||
|
|
2220f88053 | ||
|
|
86b938c81e | ||
|
|
1832a57bdf | ||
|
|
38f4821fa4 | ||
|
|
70aaf8c408 | ||
|
|
417ca45c37 | ||
|
|
569b3cc553 | ||
|
|
20226b9da9 | ||
|
|
f800e53bff | ||
|
|
5358f5da0e | ||
|
|
a868b8c08e | ||
|
|
a4f945a3ee | ||
|
|
8434730ba7 | ||
|
|
811026996c | ||
|
|
25b2b160bb | ||
|
|
4679474604 | ||
|
|
e16e241785 | ||
|
|
a52e4a46b0 | ||
|
|
f40a368948 | ||
|
|
adaee61fbf | ||
|
|
d388660bfe | ||
|
|
d0e9ba0efd | ||
|
|
d7d6c57910 | ||
|
|
98e52743f5 | ||
|
|
896696e002 | ||
|
|
654f760f3a | ||
|
|
31bd9e22f2 | ||
|
|
b7a240bbb9 | ||
|
|
ebbe10ad81 | ||
|
|
8f9af9af67 | ||
|
|
2c7cd91cbc | ||
|
|
ffaa373da9 | ||
|
|
9f8459a7d3 | ||
|
|
362542d5ee | ||
|
|
18309becbd | ||
|
|
e96803e303 | ||
|
|
b53b410feb | ||
|
|
398cd04a2a | ||
|
|
434c042510 | ||
|
|
c590db2a65 | ||
|
|
6a81513a93 | ||
|
|
ba31689145 | ||
|
|
85929d49d5 | ||
|
|
db4694f6e7 | ||
|
|
669eccbdef | ||
|
|
1bb87e55da | ||
|
|
4901e7cdf4 | ||
|
|
68bd4bc1fd | ||
|
|
8da0fd01fe | ||
|
|
18c7a2e920 | ||
|
|
f371ee59fe | ||
|
|
f8296e54bc | ||
|
|
adf497c5db | ||
|
|
04ecbe6002 | ||
|
|
7b14e52e9d | ||
|
|
8e47f39efc | ||
|
|
6942ba42b9 | ||
|
|
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 |
@@ -3,13 +3,13 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"fantomas": {
|
"fantomas": {
|
||||||
"version": "6.3.0-alpha-007",
|
"version": "7.0.1",
|
||||||
"commands": [
|
"commands": [
|
||||||
"fantomas"
|
"fantomas"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"fsharp-analyzers": {
|
"fsharp-analyzers": {
|
||||||
"version": "0.23.0",
|
"version": "0.30.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
|
||||||
|
|||||||
1
.fantomasignore
Normal file
1
.fantomasignore
Normal file
@@ -0,0 +1 @@
|
|||||||
|
.direnv/
|
||||||
10
.gitattributes
vendored
10
.gitattributes
vendored
@@ -1,5 +1,5 @@
|
|||||||
* eol=auto
|
* eol=auto
|
||||||
*.sh text eol=lf
|
*.sh text eol=lf
|
||||||
*.yaml text
|
*.yaml text
|
||||||
*.nix text eol=lf
|
*.nix text eol=lf
|
||||||
hooks/pre-push text eol=lf
|
hooks/pre-push text eol=lf
|
||||||
|
|||||||
235
.github/workflows/dotnet.yaml
vendored
235
.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@v31
|
||||||
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@v31
|
||||||
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,12 +67,14 @@ 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@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
- name: Build
|
- name: Build
|
||||||
run: nix build
|
run: nix build
|
||||||
|
- name: Reproducibility check
|
||||||
|
run: nix build --rebuild
|
||||||
|
|
||||||
check-dotnet-format:
|
check-dotnet-format:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
@@ -79,20 +82,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@v31
|
||||||
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@v31
|
||||||
|
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@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -105,7 +129,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@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -118,7 +142,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@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -132,7 +156,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@v31
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -142,45 +166,214 @@ 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 (plugin)
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-plugin
|
||||||
|
path: packed-plugin
|
||||||
|
- name: Check NuGet contents
|
||||||
|
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
||||||
|
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-dry-run:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
artifact:
|
||||||
|
- nuget-package-plugin
|
||||||
|
- nuget-package-attribute
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [nuget-pack]
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
- name: Download NuGet artifact
|
- name: Download NuGet artifact
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nuget-package
|
name: ${{ matrix.artifact }}
|
||||||
- name: Check NuGet contents
|
- name: Compute package path
|
||||||
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
|
id: compute-path
|
||||||
run: if [[ $(find . -maxdepth 1 -name 'WoofWare.Myriad.Plugins.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
|
run: |
|
||||||
|
find . -maxdepth 1 -type f -name 'WoofWare.Myriad.*.nupkg' -exec sh -c 'echo "output=$(basename "$1")" >> $GITHUB_OUTPUT' shell {} \;
|
||||||
|
- name: Compute tag name
|
||||||
|
id: compute-tag
|
||||||
|
env:
|
||||||
|
NUPKG_PATH: ${{ steps.compute-path.outputs.output }}
|
||||||
|
run: echo "output=$(basename "$NUPKG_PATH" .nupkg)" >> $GITHUB_OUTPUT
|
||||||
|
- name: Tag and release
|
||||||
|
uses: G-Research/common-actions/github-release@19d7281a0f9f83e13c78f99a610dbc80fc59ba3b
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
target-commitish: ${{ github.sha }}
|
||||||
|
tag: ${{ steps.compute-tag.outputs.output }}
|
||||||
|
binary-contents: ${{ steps.compute-path.outputs.output }}
|
||||||
|
dry-run: true
|
||||||
|
|
||||||
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-dry-run]
|
||||||
|
if: ${{ always() }}
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
steps:
|
steps:
|
||||||
- run: echo "All required checks complete."
|
- uses: G-Research/common-actions/check-required-lite@2b7dc49cb14f3344fbe6019c14a31165e258c059
|
||||||
|
with:
|
||||||
|
needs-context: ${{ toJSON(needs) }}
|
||||||
|
|
||||||
nuget-publish:
|
attestation-attribute:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [all-required-checks-complete]
|
||||||
|
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
attestations: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Download NuGet artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-attribute
|
||||||
|
path: packed
|
||||||
|
- name: Attest Build Provenance
|
||||||
|
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
|
||||||
|
with:
|
||||||
|
subject-path: "packed/*.nupkg"
|
||||||
|
|
||||||
|
attestation-plugin:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: [all-required-checks-complete]
|
||||||
|
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
attestations: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- name: Download NuGet artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-plugin
|
||||||
|
path: packed
|
||||||
|
- name: Attest Build Provenance
|
||||||
|
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
|
||||||
|
with:
|
||||||
|
subject-path: "packed/*.nupkg"
|
||||||
|
|
||||||
|
nuget-publish-attribute:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
||||||
needs: [all-required-checks-complete]
|
needs: [all-required-checks-complete]
|
||||||
environment: main-deploy
|
environment: main-deploy
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
attestations: write
|
||||||
|
contents: read
|
||||||
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@v31
|
||||||
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
|
||||||
uses: actions/download-artifact@v4
|
uses: actions/download-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: nuget-package
|
name: nuget-package-attribute
|
||||||
|
path: packed
|
||||||
|
- name: Identify `dotnet`
|
||||||
|
id: dotnet-identify
|
||||||
|
run: nix develop --command bash -c 'echo "dotnet=$(which dotnet)" >> $GITHUB_OUTPUT'
|
||||||
- name: Publish to NuGet
|
- name: Publish to NuGet
|
||||||
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
|
id: publish-success
|
||||||
|
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
|
||||||
|
with:
|
||||||
|
package-name: WoofWare.Myriad.Plugins.Attributes
|
||||||
|
nuget-key: ${{ secrets.NUGET_API_KEY }}
|
||||||
|
nupkg-dir: packed/
|
||||||
|
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
|
||||||
|
|
||||||
|
nuget-publish-plugin:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
|
||||||
|
needs: [all-required-checks-complete]
|
||||||
|
environment: main-deploy
|
||||||
|
permissions:
|
||||||
|
id-token: write
|
||||||
|
attestations: write
|
||||||
|
contents: read
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
- name: Install Nix
|
||||||
|
uses: cachix/install-nix-action@v31
|
||||||
|
with:
|
||||||
|
extra_nix_config: |
|
||||||
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
- name: Download NuGet artifact
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: nuget-package-plugin
|
||||||
|
path: packed
|
||||||
|
- name: Identify `dotnet`
|
||||||
|
id: dotnet-identify
|
||||||
|
run: nix develop --command bash -c 'echo "dotnet=$(which dotnet)" >> $GITHUB_OUTPUT'
|
||||||
|
- name: Publish to NuGet
|
||||||
|
id: publish-success
|
||||||
|
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
|
||||||
|
with:
|
||||||
|
package-name: WoofWare.Myriad.Plugins
|
||||||
|
nuget-key: ${{ secrets.NUGET_API_KEY }}
|
||||||
|
nupkg-dir: packed/
|
||||||
|
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
|
||||||
|
|
||||||
|
github-release:
|
||||||
|
strategy:
|
||||||
|
matrix:
|
||||||
|
artifact:
|
||||||
|
- nuget-package-attribute
|
||||||
|
- nuget-package-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
|
||||||
|
uses: actions/download-artifact@v4
|
||||||
|
with:
|
||||||
|
name: ${{ matrix.artifact }}
|
||||||
|
- name: Compute package path
|
||||||
|
id: compute-path
|
||||||
|
run: |
|
||||||
|
find . -maxdepth 1 -type f -name 'WoofWare.Myriad.*.nupkg' -exec sh -c 'echo "output=$(basename "$1")" >> $GITHUB_OUTPUT' shell {} \;
|
||||||
|
- name: Compute tag name
|
||||||
|
id: compute-tag
|
||||||
|
env:
|
||||||
|
NUPKG_PATH: ${{ steps.compute-path.outputs.output }}
|
||||||
|
run: echo "output=$(basename "$NUPKG_PATH" .nupkg)" >> $GITHUB_OUTPUT
|
||||||
|
- name: Tag and release
|
||||||
|
uses: G-Research/common-actions/github-release@19d7281a0f9f83e13c78f99a610dbc80fc59ba3b
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
target-commitish: ${{ github.sha }}
|
||||||
|
tag: ${{ steps.compute-tag.outputs.output }}
|
||||||
|
binary-contents: ${{ steps.compute-path.outputs.output }}
|
||||||
|
|||||||
56
.github/workflows/flake_update.yaml
vendored
Normal file
56
.github/workflows/flake_update.yaml
vendored
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
|
||||||
|
name: Weekly Nix Flake Update
|
||||||
|
|
||||||
|
on:
|
||||||
|
schedule:
|
||||||
|
- cron: '0 0 * * 0' # Runs at 00:00 every Sunday
|
||||||
|
workflow_dispatch: # Allows manual triggering
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
update-nix-flake:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Check out repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Nix
|
||||||
|
uses: DeterminateSystems/nix-installer-action@main
|
||||||
|
with:
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Update Nix flake
|
||||||
|
run: 'nix flake update'
|
||||||
|
|
||||||
|
- name: Build fetch-deps
|
||||||
|
run: 'nix build ".#default.fetch-deps"'
|
||||||
|
|
||||||
|
- name: Run fetch-deps
|
||||||
|
run: |
|
||||||
|
set -o pipefail
|
||||||
|
./result nix/deps.json
|
||||||
|
|
||||||
|
- name: Format
|
||||||
|
run: 'nix develop --command alejandra .'
|
||||||
|
|
||||||
|
- name: Create token
|
||||||
|
id: generate-token
|
||||||
|
uses: actions/create-github-app-token@v2
|
||||||
|
with:
|
||||||
|
# https://github.com/actions/create-github-app-token/issues/136
|
||||||
|
app-id: ${{ secrets.APP_ID }}
|
||||||
|
private-key: ${{ secrets.APP_PRIVATE_KEY }}
|
||||||
|
|
||||||
|
- name: Raise pull request
|
||||||
|
uses: Smaug123/commit-action@cc25e6d80a796c49669dda4a0aa36c54c573983d
|
||||||
|
id: cpr
|
||||||
|
with:
|
||||||
|
bearer-token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
pr-title: "Upgrade Nix flake and deps"
|
||||||
|
|
||||||
|
- name: Enable Pull Request Automerge
|
||||||
|
if: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
|
uses: peter-evans/enable-pull-request-automerge@v3
|
||||||
|
with:
|
||||||
|
token: ${{ steps.generate-token.outputs.token }}
|
||||||
|
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
|
||||||
|
merge-method: squash
|
||||||
26
.gitignore
vendored
26
.gitignore
vendored
@@ -1,12 +1,14 @@
|
|||||||
bin/
|
bin/
|
||||||
obj/
|
obj/
|
||||||
/packages/
|
/packages/
|
||||||
riderModule.iml
|
riderModule.iml
|
||||||
/_ReSharper.Caches/
|
/_ReSharper.Caches/
|
||||||
.idea/
|
.idea/
|
||||||
*.sln.DotSettings.user
|
*.sln.DotSettings.user
|
||||||
.DS_Store
|
.DS_Store
|
||||||
result
|
result
|
||||||
.analyzerpackages/
|
.analyzerpackages/
|
||||||
analysis.sarif
|
analysis.sarif
|
||||||
.direnv/
|
.direnv/
|
||||||
|
.venv/
|
||||||
|
.vs/
|
||||||
|
|||||||
45
CHANGELOG.md
Normal file
45
CHANGELOG.md
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
Notable changes are recorded here.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 3.0.1
|
||||||
|
|
||||||
|
Semantics of `HttpClient`'s URI component composition changed:
|
||||||
|
we now implicitly insert `/` characters after `[<BaseAddress>]` and `[<BasePath>]`, so that URI composition doesn't silently drop the last component if you didn't put a slash there.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.3.9
|
||||||
|
|
||||||
|
`JsonParse` and `JsonSerialize` now interpret `[<JsonExtensionData>]`, which must be on a `Dictionary<string, _>`; this collects any extra components that were present on the JSON object.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.2.1, WoofWare.Myriad.Plugins.Attributes 3.2.1
|
||||||
|
|
||||||
|
New generator: `ArgParser`, a basic reflection-free argument parser.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.45, WoofWare.Myriad.Plugins.Attributes 3.1.7
|
||||||
|
|
||||||
|
The NuGet packages are now attested to through [GitHub Attestations](https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/).
|
||||||
|
You can run `gh attestation verify ~/.nuget/packages/woofware.myriad.plugins/2.1.45/woofware.myriad.plugins.2.1.45.nupkg -o Smaug123`, for example, to verify with GitHub that the GitHub Actions pipeline on this repository produced a nupkg file with the same hash as the one you were served from NuGet.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.33
|
||||||
|
|
||||||
|
`JsonParse` can now deserialize the discriminated unions which `JsonSerialize` wrote out.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.1.32, WoofWare.Myriad.Plugins.Attributes 3.1.4
|
||||||
|
|
||||||
|
`JsonSerialize` can now serialize many discriminated unions.
|
||||||
|
(This operation is inherently opinionated, because JSON does not model discriminated unions.)
|
||||||
|
|
||||||
|
# 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.
|
||||||
237
ConsumePlugin/Args.fs
Normal file
237
ConsumePlugin/Args.fs
Normal file
@@ -0,0 +1,237 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.IO
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
[<ArgParser>]
|
||||||
|
type BasicNoPositionals =
|
||||||
|
{
|
||||||
|
Foo : int
|
||||||
|
Bar : string
|
||||||
|
Baz : bool
|
||||||
|
Rest : int list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser>]
|
||||||
|
type Basic =
|
||||||
|
{
|
||||||
|
[<ArgumentHelpText "This is a foo!">]
|
||||||
|
Foo : int
|
||||||
|
Bar : string
|
||||||
|
Baz : bool
|
||||||
|
[<ArgumentHelpText "Here's where the rest of the args go">]
|
||||||
|
[<PositionalArgs>]
|
||||||
|
Rest : string list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser>]
|
||||||
|
type BasicWithIntPositionals =
|
||||||
|
{
|
||||||
|
Foo : int
|
||||||
|
Bar : string
|
||||||
|
Baz : bool
|
||||||
|
[<PositionalArgs>]
|
||||||
|
Rest : int list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser>]
|
||||||
|
type LoadsOfTypes =
|
||||||
|
{
|
||||||
|
Foo : int
|
||||||
|
Bar : string
|
||||||
|
Baz : bool
|
||||||
|
SomeFile : FileInfo
|
||||||
|
SomeDirectory : DirectoryInfo
|
||||||
|
SomeList : DirectoryInfo list
|
||||||
|
OptionalThingWithNoDefault : int option
|
||||||
|
[<PositionalArgs>]
|
||||||
|
Positionals : int list
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
OptionalThing : Choice<bool, bool>
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
AnotherOptionalThing : Choice<int, int>
|
||||||
|
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
|
||||||
|
YetAnotherOptionalThing : Choice<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
static member DefaultOptionalThing () = true
|
||||||
|
|
||||||
|
static member DefaultAnotherOptionalThing () = 3
|
||||||
|
|
||||||
|
[<ArgParser>]
|
||||||
|
type LoadsOfTypesNoPositionals =
|
||||||
|
{
|
||||||
|
Foo : int
|
||||||
|
Bar : string
|
||||||
|
Baz : bool
|
||||||
|
SomeFile : FileInfo
|
||||||
|
SomeDirectory : DirectoryInfo
|
||||||
|
SomeList : DirectoryInfo list
|
||||||
|
OptionalThingWithNoDefault : int option
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
OptionalThing : Choice<bool, bool>
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
AnotherOptionalThing : Choice<int, int>
|
||||||
|
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
|
||||||
|
YetAnotherOptionalThing : Choice<string, string>
|
||||||
|
}
|
||||||
|
|
||||||
|
static member DefaultOptionalThing () = false
|
||||||
|
|
||||||
|
static member DefaultAnotherOptionalThing () = 3
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type DatesAndTimes =
|
||||||
|
{
|
||||||
|
Plain : TimeSpan
|
||||||
|
[<InvariantCulture>]
|
||||||
|
Invariant : TimeSpan
|
||||||
|
[<ParseExact @"hh\:mm\:ss">]
|
||||||
|
[<ArgumentHelpText "An exact time please">]
|
||||||
|
Exact : TimeSpan
|
||||||
|
[<InvariantCulture ; ParseExact @"hh\:mm\:ss">]
|
||||||
|
InvariantExact : TimeSpan
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChildRecord =
|
||||||
|
{
|
||||||
|
Thing1 : int
|
||||||
|
Thing2 : string
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ParentRecord =
|
||||||
|
{
|
||||||
|
Child : ChildRecord
|
||||||
|
AndAnother : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
type ChildRecordWithPositional =
|
||||||
|
{
|
||||||
|
Thing1 : int
|
||||||
|
[<PositionalArgs>]
|
||||||
|
Thing2 : Uri list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ParentRecordChildPos =
|
||||||
|
{
|
||||||
|
Child : ChildRecordWithPositional
|
||||||
|
AndAnother : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ParentRecordSelfPos =
|
||||||
|
{
|
||||||
|
Child : ChildRecord
|
||||||
|
[<PositionalArgs>]
|
||||||
|
AndAnother : bool list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ChoicePositionals =
|
||||||
|
{
|
||||||
|
[<PositionalArgs>]
|
||||||
|
Args : Choice<string, string> list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ContainsBoolEnvVar =
|
||||||
|
{
|
||||||
|
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
|
||||||
|
BoolVar : Choice<bool, bool>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Consts =
|
||||||
|
[<Literal>]
|
||||||
|
let FALSE = false
|
||||||
|
|
||||||
|
[<Literal>]
|
||||||
|
let TRUE = true
|
||||||
|
|
||||||
|
type DryRunMode =
|
||||||
|
| [<ArgumentFlag(Consts.FALSE)>] Wet
|
||||||
|
| [<ArgumentFlag true>] Dry
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type WithFlagDu =
|
||||||
|
{
|
||||||
|
DryRun : DryRunMode
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ContainsFlagEnvVar =
|
||||||
|
{
|
||||||
|
// This phrasing is odd, but it's for a test. Nobody's really going to have `--dry-run`
|
||||||
|
// controlled by an env var!
|
||||||
|
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
|
||||||
|
DryRun : Choice<DryRunMode, DryRunMode>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ContainsFlagDefaultValue =
|
||||||
|
{
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
DryRun : Choice<DryRunMode, DryRunMode>
|
||||||
|
}
|
||||||
|
|
||||||
|
static member DefaultDryRun () = DryRunMode.Wet
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type ManyLongForms =
|
||||||
|
{
|
||||||
|
[<ArgumentLongForm "do-something-else">]
|
||||||
|
[<ArgumentLongForm "anotherarg">]
|
||||||
|
DoTheThing : string
|
||||||
|
|
||||||
|
[<ArgumentLongForm "turn-it-on">]
|
||||||
|
[<ArgumentLongForm "dont-turn-it-off">]
|
||||||
|
SomeFlag : bool
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
type private IrrelevantDu =
|
||||||
|
| Foo
|
||||||
|
| Bar
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type FlagsIntoPositionalArgs =
|
||||||
|
{
|
||||||
|
A : string
|
||||||
|
[<PositionalArgs true>]
|
||||||
|
GrabEverything : string list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type FlagsIntoPositionalArgsChoice =
|
||||||
|
{
|
||||||
|
A : string
|
||||||
|
[<PositionalArgs true>]
|
||||||
|
GrabEverything : Choice<string, string> list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type FlagsIntoPositionalArgsInt =
|
||||||
|
{
|
||||||
|
A : string
|
||||||
|
[<PositionalArgs true>]
|
||||||
|
GrabEverything : int list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type FlagsIntoPositionalArgsIntChoice =
|
||||||
|
{
|
||||||
|
A : string
|
||||||
|
[<PositionalArgs true>]
|
||||||
|
GrabEverything : Choice<int, int> list
|
||||||
|
}
|
||||||
|
|
||||||
|
[<ArgParser true>]
|
||||||
|
type FlagsIntoPositionalArgs' =
|
||||||
|
{
|
||||||
|
A : string
|
||||||
|
[<PositionalArgs false>]
|
||||||
|
DontGrabEverything : string list
|
||||||
|
}
|
||||||
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>
|
||||||
@@ -1,8 +1,9 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
<OtherFlags>--reflectionfree $(OtherFlags)</OtherFlags>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<MyriadSdkGenerator Include="$(MSBuildThisFileDirectory)..\WoofWare.Myriad.Plugins\bin\$(Configuration)\net6.0\WoofWare.Myriad.Plugins.dll"/>
|
<MyriadSdkGenerator Include="$(MSBuildThisFileDirectory)..\WoofWare.Myriad.Plugins\bin\$(Configuration)\net6.0\WoofWare.Myriad.Plugins.dll"/>
|
||||||
@@ -10,6 +11,10 @@
|
|||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="myriad.toml"/>
|
<None Include="myriad.toml"/>
|
||||||
|
<Compile Include="JsonParseNullness.fs" />
|
||||||
|
<Compile Include="GeneratedJsonParseNullness.fs">
|
||||||
|
<MyriadFile>JsonParseNullness.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
<Compile Include="AssemblyInfo.fs" />
|
<Compile Include="AssemblyInfo.fs" />
|
||||||
<Compile Include="RecordFile.fs"/>
|
<Compile Include="RecordFile.fs"/>
|
||||||
<Compile Include="GeneratedRecord.fs">
|
<Compile Include="GeneratedRecord.fs">
|
||||||
@@ -31,6 +36,20 @@
|
|||||||
<Compile Include="GeneratedMock.fs">
|
<Compile Include="GeneratedMock.fs">
|
||||||
<MyriadFile>MockExample.fs</MyriadFile>
|
<MyriadFile>MockExample.fs</MyriadFile>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="MockExampleNoAttributes.fs" />
|
||||||
|
<Compile Include="GeneratedMockNoAttributes.fs">
|
||||||
|
<MyriadFile>MockExampleNoAttributes.fs</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<IPublicTypeNoAttr>GenerateMock</IPublicTypeNoAttr>
|
||||||
|
<IPublicTypeInternalFalseNoAttr>GenerateMock(false)</IPublicTypeInternalFalseNoAttr>
|
||||||
|
<InternalTypeNoAttr>GenerateMock</InternalTypeNoAttr>
|
||||||
|
<PrivateTypeNoAttr>GenerateMock</PrivateTypeNoAttr>
|
||||||
|
<PrivateTypeInternalFalseNoAttr>GenerateMock(false)</PrivateTypeInternalFalseNoAttr>
|
||||||
|
<VeryPublicTypeNoAttr>GenerateMock</VeryPublicTypeNoAttr>
|
||||||
|
<CurriedNoAttr>GenerateMock</CurriedNoAttr>
|
||||||
|
<TypeWithInterfaceNoAttr>GenerateMock</TypeWithInterfaceNoAttr>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Vault.fs" />
|
<Compile Include="Vault.fs" />
|
||||||
<Compile Include="GeneratedVault.fs">
|
<Compile Include="GeneratedVault.fs">
|
||||||
<MyriadFile>Vault.fs</MyriadFile>
|
<MyriadFile>Vault.fs</MyriadFile>
|
||||||
@@ -39,13 +58,40 @@
|
|||||||
<Compile Include="GeneratedSerde.fs">
|
<Compile Include="GeneratedSerde.fs">
|
||||||
<MyriadFile>SerializationAndDeserialization.fs</MyriadFile>
|
<MyriadFile>SerializationAndDeserialization.fs</MyriadFile>
|
||||||
</Compile>
|
</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>
|
||||||
|
<Compile Include="Args.fs" />
|
||||||
|
<Compile Include="GeneratedArgs.fs">
|
||||||
|
<MyriadFile>Args.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
|
<None Include="swagger-gitea.json" />
|
||||||
|
<Compile Include="GeneratedSwaggerGitea.fs">
|
||||||
|
<MyriadFile>swagger-gitea.json</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<GenerateMockInternal>true</GenerateMockInternal>
|
||||||
|
<ClassName>Gitea</ClassName>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Generated2SwaggerGitea.fs">
|
||||||
|
<MyriadFile>GeneratedSwaggerGitea.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\WoofWare.Myriad.Plugins.fsproj"/>
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj" />
|
||||||
<PackageReference Include="Myriad.Sdk" Version="0.8.3"/>
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins\WoofWare.Myriad.Plugins.fsproj" PrivateAssets="all" />
|
||||||
<PackageReference Include="Myriad.Core" Version="0.8.3"/>
|
<PackageReference Include="Myriad.Sdk" Version="0.8.3" PrivateAssets="all" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
64
ConsumePlugin/FSharpForFunAndProfitCata.fs
Normal file
64
ConsumePlugin/FSharpForFunAndProfitCata.fs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
override this.ToString () =
|
||||||
|
match this with
|
||||||
|
| ChocolateType.Dark -> "Dark"
|
||||||
|
| ChocolateType.Milk -> "Milk"
|
||||||
|
| ChocolateType.SeventyPercent -> "SeventyPercent"
|
||||||
|
|
||||||
|
type Chocolate =
|
||||||
|
{
|
||||||
|
chocType : ChocolateType
|
||||||
|
price : decimal
|
||||||
|
}
|
||||||
|
|
||||||
|
override this.ToString () = this.chocType.ToString ()
|
||||||
|
|
||||||
|
type WrappingPaperStyle =
|
||||||
|
| HappyBirthday
|
||||||
|
| HappyHolidays
|
||||||
|
| SolidColor
|
||||||
|
|
||||||
|
override this.ToString () =
|
||||||
|
match this with
|
||||||
|
| WrappingPaperStyle.HappyBirthday -> "HappyBirthday"
|
||||||
|
| WrappingPaperStyle.HappyHolidays -> "HappyHolidays"
|
||||||
|
| WrappingPaperStyle.SolidColor -> "SolidColor"
|
||||||
|
|
||||||
|
[<CreateCatamorphism "GiftCata">]
|
||||||
|
type Gift =
|
||||||
|
| Book of Book
|
||||||
|
| Chocolate of Chocolate
|
||||||
|
| Wrapped of Gift * WrappingPaperStyle
|
||||||
|
| Boxed of Gift
|
||||||
|
| WithACard of Gift * message : string
|
||||||
43825
ConsumePlugin/Generated2SwaggerGitea.fs
Normal file
43825
ConsumePlugin/Generated2SwaggerGitea.fs
Normal file
File diff suppressed because it is too large
Load Diff
4327
ConsumePlugin/GeneratedArgs.fs
Normal file
4327
ConsumePlugin/GeneratedArgs.fs
Normal file
File diff suppressed because it is too large
Load Diff
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 name : string * dirSize : int * contents : 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 message : 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,16 +4,42 @@
|
|||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing methods for the InternalTypeNotExtensionSerial type
|
||||||
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
module internal InternalTypeNotExtensionSerial =
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
let toJsonNode (input : InternalTypeNotExtensionSerial) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
do node.Add ((Literals.something), (input.InternalThing2 |> System.Text.Json.Nodes.JsonValue.Create<string>))
|
||||||
|
node :> _
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the InternalTypeExtension type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal InternalTypeExtensionJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type InternalTypeExtension with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : InternalTypeExtension) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
do node.Add ((Literals.something), (input.ExternalThing |> System.Text.Json.Nodes.JsonValue.Create<string>))
|
||||||
|
node :> _
|
||||||
|
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the InnerType type
|
/// Module containing JSON parsing methods for the InnerType type
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
|
||||||
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 (
|
||||||
@@ -23,20 +49,19 @@ module InnerType =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
Thing = Thing
|
Thing = arg_0
|
||||||
}
|
}
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the JsonRecordType type
|
/// Module containing JSON parsing methods for the JsonRecordType type
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
|
||||||
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 (
|
||||||
@@ -46,10 +71,11 @@ module JsonRecordType =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|
||||||
|> Array.ofSeq
|
|> Array.ofSeq
|
||||||
|
|
||||||
let E =
|
let arg_4 =
|
||||||
(match node.["e"] with
|
(match node.["e"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -59,10 +85,11 @@ module JsonRecordType =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|
||||||
|> Array.ofSeq
|
|> Array.ofSeq
|
||||||
|
|
||||||
let D =
|
let arg_3 =
|
||||||
InnerType.jsonParse (
|
InnerType.jsonParse (
|
||||||
match node.["d"] with
|
match node.["d"] with
|
||||||
| null ->
|
| null ->
|
||||||
@@ -74,7 +101,7 @@ module JsonRecordType =
|
|||||||
| v -> v
|
| v -> v
|
||||||
)
|
)
|
||||||
|
|
||||||
let C =
|
let arg_2 =
|
||||||
(match node.["hi"] with
|
(match node.["hi"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -84,10 +111,11 @@ module JsonRecordType =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let B =
|
let arg_1 =
|
||||||
(match node.["another-thing"] with
|
(match node.["another-thing"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -97,9 +125,9 @@ module JsonRecordType =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let A =
|
let arg_0 =
|
||||||
(match node.["a"] with
|
(match node.["a"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -109,18 +137,65 @@ module JsonRecordType =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
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 methods for the InternalTypeNotExtension type
|
||||||
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
module internal InternalTypeNotExtension =
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InternalTypeNotExtension =
|
||||||
|
let arg_0 =
|
||||||
|
(match node.[(Literals.something)] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ((Literals.something))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
InternalThing = arg_0
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the InternalTypeExtension type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal InternalTypeExtensionJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type InternalTypeExtension with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : InternalTypeExtension =
|
||||||
|
let arg_0 =
|
||||||
|
(match node.[(Literals.something)] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ((Literals.something))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
ExternalThing = arg_0
|
||||||
|
}
|
||||||
|
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 =
|
||||||
@@ -129,24 +204,238 @@ module ToGetExtensionMethodJsonParseExtension =
|
|||||||
|
|
||||||
/// 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 =
|
||||||
(match node.["sailor"] with
|
let v = node.["whiskey"]
|
||||||
|
|
||||||
|
System.Numerics.BigInteger.Parse (
|
||||||
|
(match v with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v)
|
||||||
|
.ToJsonString ()
|
||||||
|
)
|
||||||
|
|
||||||
|
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)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<float> ()
|
.GetValue<System.Char> ()
|
||||||
|
|
||||||
let Soldier =
|
let arg_18 =
|
||||||
(match node.["soldier"] with
|
(match node.["uniform"] 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" ("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<System.Int32> ()
|
||||||
|
|
||||||
|
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)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Double> ()
|
||||||
|
|
||||||
|
let arg_1 =
|
||||||
|
(match node.["bravo"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("bravo")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
@@ -154,33 +443,38 @@ 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)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let Tinker =
|
|
||||||
(match node.["tinker"] with
|
|
||||||
| null ->
|
|
||||||
raise (
|
|
||||||
System.Collections.Generic.KeyNotFoundException (
|
|
||||||
sprintf "Required key '%s' not found on JSON object" ("tinker")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
| v -> v)
|
|
||||||
.AsValue()
|
|
||||||
.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
|
||||||
}
|
}
|
||||||
|
|||||||
53
ConsumePlugin/GeneratedJsonParseNullness.fs
Normal file
53
ConsumePlugin/GeneratedJsonParseNullness.fs
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing methods for the InnerStruct type
|
||||||
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
module InnerStruct =
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerStruct =
|
||||||
|
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<System.Int32> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
A = arg_0
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing methods for the ArrayOfInnerStruct type
|
||||||
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
|
module ArrayOfInnerStruct =
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : ArrayOfInnerStruct =
|
||||||
|
let arg_0 =
|
||||||
|
(match node.["b"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("b")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsArray ()
|
||||||
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> InnerStruct.jsonParse elt)
|
||||||
|
|> Array.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
B = arg_0
|
||||||
|
}
|
||||||
@@ -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: Mem1"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
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: Mem1"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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: Mem1"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
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: Mem1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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: Mem1"))
|
||||||
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
Mem4 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem4"))
|
||||||
Mem5 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem5"))
|
||||||
Mem6 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function"))
|
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem6"))
|
||||||
}
|
}
|
||||||
|
|
||||||
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,62 @@ 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: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
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 ()
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal TypeWithPropertiesMock =
|
||||||
|
{
|
||||||
|
/// Implementation of IDisposable.Dispose
|
||||||
|
Dispose : unit -> unit
|
||||||
|
Prop1 : unit -> int
|
||||||
|
Prop2 : unit -> unit Async
|
||||||
|
Mem1 : string option -> string[] Async
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : TypeWithPropertiesMock =
|
||||||
|
{
|
||||||
|
Dispose = (fun () -> ())
|
||||||
|
Prop1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Prop1"))
|
||||||
|
Prop2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Prop2"))
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeWithProperties with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
member this.Prop1 = this.Prop1 ()
|
||||||
|
member this.Prop2 = this.Prop2 ()
|
||||||
|
|
||||||
|
interface System.IDisposable with
|
||||||
|
member this.Dispose () : unit = this.Dispose ()
|
||||||
|
|||||||
200
ConsumePlugin/GeneratedMockNoAttributes.fs
Normal file
200
ConsumePlugin/GeneratedMockNoAttributes.fs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal PublicTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeNoAttr 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
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type public PublicTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeInternalFalseNoAttr 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
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal InternalTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : InternalTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InternalTypeNoAttr 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
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeNoAttr 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
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeInternalFalseNoAttr 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
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal VeryPublicTypeNoAttrMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 : 'a -> 'b
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : VeryPublicTypeNoAttrMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VeryPublicTypeNoAttr<'a, 'b> with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal CurriedNoAttrMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 : int -> 'a -> string
|
||||||
|
Mem2 : int * string -> 'a -> string
|
||||||
|
Mem3 : (int * string) -> 'a -> string
|
||||||
|
Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
Mem5 : int * string -> ('a * int) -> string
|
||||||
|
Mem6 : int * string -> 'a * int -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : CurriedNoAttrMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem4"))
|
||||||
|
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem5"))
|
||||||
|
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem6"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedNoAttr<'a> with
|
||||||
|
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.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)) =
|
||||||
|
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem5 (arg_0_0, arg_0_1) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem5 (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)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal TypeWithInterfaceNoAttrMock =
|
||||||
|
{
|
||||||
|
/// 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 : TypeWithInterfaceNoAttrMock =
|
||||||
|
{
|
||||||
|
Dispose = (fun () -> ())
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeWithInterfaceNoAttr 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
File diff suppressed because it is too large
Load Diff
@@ -21,69 +21,69 @@ module InnerTypeWithBothJsonSerializeExtension =
|
|||||||
let node = System.Text.Json.Nodes.JsonObject ()
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
do
|
do
|
||||||
node.Add (("it's-a-me"), System.Text.Json.Nodes.JsonValue.Create<string> input.Thing)
|
node.Add (("it's-a-me"), (input.Thing |> System.Text.Json.Nodes.JsonValue.Create<Guid>))
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"map",
|
"map",
|
||||||
(fun field ->
|
(input.Map
|
||||||
let ret = System.Text.Json.Nodes.JsonObject ()
|
|> (fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
for (KeyValue (key, value)) in field do
|
for (KeyValue (key, value)) in field do
|
||||||
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value)
|
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value)
|
||||||
|
|
||||||
ret
|
ret
|
||||||
)
|
))
|
||||||
input.Map
|
|
||||||
)
|
)
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"readOnlyDict",
|
"readOnlyDict",
|
||||||
(fun field ->
|
(input.ReadOnlyDict
|
||||||
let ret = System.Text.Json.Nodes.JsonObject ()
|
|> (fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
for (KeyValue (key, value)) in field do
|
for (KeyValue (key, value)) in field do
|
||||||
ret.Add (
|
ret.Add (
|
||||||
key.ToString (),
|
key.ToString (),
|
||||||
(fun field ->
|
(fun field ->
|
||||||
let arr = System.Text.Json.Nodes.JsonArray ()
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
for mem in field do
|
for mem in field do
|
||||||
arr.Add (System.Text.Json.Nodes.JsonValue.Create<char> mem)
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<char> mem)
|
||||||
|
|
||||||
arr
|
arr
|
||||||
)
|
)
|
||||||
value
|
value
|
||||||
)
|
)
|
||||||
|
|
||||||
ret
|
ret
|
||||||
)
|
))
|
||||||
input.ReadOnlyDict
|
|
||||||
)
|
)
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"dict",
|
"dict",
|
||||||
(fun field ->
|
(input.Dict
|
||||||
let ret = System.Text.Json.Nodes.JsonObject ()
|
|> (fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
for (KeyValue (key, value)) in field do
|
for (KeyValue (key, value)) in field do
|
||||||
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<bool> value)
|
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<bool> value)
|
||||||
|
|
||||||
ret
|
ret
|
||||||
)
|
))
|
||||||
input.Dict
|
|
||||||
)
|
)
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"concreteDict",
|
"concreteDict",
|
||||||
(fun field ->
|
(input.ConcreteDict
|
||||||
let ret = System.Text.Json.Nodes.JsonObject ()
|
|> (fun field ->
|
||||||
|
let ret = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
for (KeyValue (key, value)) in field do
|
for (KeyValue (key, value)) in field do
|
||||||
ret.Add (key.ToString (), InnerTypeWithBoth.toJsonNode value)
|
ret.Add (key.ToString (), InnerTypeWithBoth.toJsonNode value)
|
||||||
|
|
||||||
ret
|
ret
|
||||||
)
|
))
|
||||||
input.ConcreteDict
|
|
||||||
)
|
)
|
||||||
|
|
||||||
node :> _
|
node :> _
|
||||||
@@ -93,6 +93,24 @@ open System
|
|||||||
open System.Collections.Generic
|
open System.Collections.Generic
|
||||||
open System.Text.Json.Serialization
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the SomeEnum type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module SomeEnumJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type SomeEnum with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : SomeEnum) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
match input with
|
||||||
|
| SomeEnum.Blah -> System.Text.Json.Nodes.JsonValue.Create 1
|
||||||
|
| SomeEnum.Thing -> System.Text.Json.Nodes.JsonValue.Create 0
|
||||||
|
| v -> failwith (sprintf "Unrecognised value for enum: %O" v)
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
/// Module containing JSON serializing extension members for the JsonRecordTypeWithBoth type
|
/// Module containing JSON serializing extension members for the JsonRecordTypeWithBoth type
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module JsonRecordTypeWithBothJsonSerializeExtension =
|
module JsonRecordTypeWithBothJsonSerializeExtension =
|
||||||
@@ -104,50 +122,230 @@ module JsonRecordTypeWithBothJsonSerializeExtension =
|
|||||||
let node = System.Text.Json.Nodes.JsonObject ()
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
do
|
do
|
||||||
node.Add ("a", System.Text.Json.Nodes.JsonValue.Create<int> input.A)
|
node.Add ("a", (input.A |> System.Text.Json.Nodes.JsonValue.Create<int>))
|
||||||
node.Add ("b", System.Text.Json.Nodes.JsonValue.Create<string> input.B)
|
node.Add ("b", (input.B |> System.Text.Json.Nodes.JsonValue.Create<string>))
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"c",
|
"c",
|
||||||
(fun field ->
|
(input.C
|
||||||
let arr = System.Text.Json.Nodes.JsonArray ()
|
|> (fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
for mem in field do
|
for mem in field do
|
||||||
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
|
||||||
|
|
||||||
arr
|
arr
|
||||||
)
|
))
|
||||||
input.C
|
|
||||||
)
|
)
|
||||||
|
|
||||||
node.Add ("d", InnerTypeWithBoth.toJsonNode input.D)
|
node.Add ("d", (input.D |> InnerTypeWithBoth.toJsonNode))
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"e",
|
"e",
|
||||||
(fun field ->
|
(input.E
|
||||||
let arr = System.Text.Json.Nodes.JsonArray ()
|
|> (fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
for mem in field do
|
for mem in field do
|
||||||
arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem)
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem)
|
||||||
|
|
||||||
arr
|
arr
|
||||||
)
|
))
|
||||||
input.E
|
|
||||||
)
|
)
|
||||||
|
|
||||||
node.Add (
|
node.Add (
|
||||||
"f",
|
"arr",
|
||||||
(fun field ->
|
(input.Arr
|
||||||
let arr = System.Text.Json.Nodes.JsonArray ()
|
|> (fun field ->
|
||||||
|
let arr = System.Text.Json.Nodes.JsonArray ()
|
||||||
|
|
||||||
for mem in field do
|
for mem in field do
|
||||||
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
|
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
|
||||||
|
|
||||||
arr
|
arr
|
||||||
)
|
))
|
||||||
input.F
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
node.Add ("byte", (input.Byte |> System.Text.Json.Nodes.JsonValue.Create<byte<measure>>))
|
||||||
|
node.Add ("sbyte", (input.Sbyte |> System.Text.Json.Nodes.JsonValue.Create<sbyte<measure>>))
|
||||||
|
node.Add ("i", (input.I |> System.Text.Json.Nodes.JsonValue.Create<int<measure>>))
|
||||||
|
node.Add ("i32", (input.I32 |> System.Text.Json.Nodes.JsonValue.Create<int32<measure>>))
|
||||||
|
node.Add ("i64", (input.I64 |> System.Text.Json.Nodes.JsonValue.Create<int64<measure>>))
|
||||||
|
node.Add ("u", (input.U |> System.Text.Json.Nodes.JsonValue.Create<uint<measure>>))
|
||||||
|
node.Add ("u32", (input.U32 |> System.Text.Json.Nodes.JsonValue.Create<uint32<measure>>))
|
||||||
|
node.Add ("u64", (input.U64 |> System.Text.Json.Nodes.JsonValue.Create<uint64<measure>>))
|
||||||
|
node.Add ("f", (input.F |> System.Text.Json.Nodes.JsonValue.Create<float<measure>>))
|
||||||
|
node.Add ("f32", (input.F32 |> System.Text.Json.Nodes.JsonValue.Create<float32<measure>>))
|
||||||
|
node.Add ("single", (input.Single |> System.Text.Json.Nodes.JsonValue.Create<single<measure>>))
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"intMeasureOption",
|
||||||
|
(input.IntMeasureOption
|
||||||
|
|> (fun field ->
|
||||||
|
match field with
|
||||||
|
| None -> null :> System.Text.Json.Nodes.JsonNode
|
||||||
|
| Some field ->
|
||||||
|
(System.Text.Json.Nodes.JsonValue.Create<int<measure>> field)
|
||||||
|
:> System.Text.Json.Nodes.JsonNode
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"intMeasureNullable",
|
||||||
|
(input.IntMeasureNullable
|
||||||
|
|> (fun field ->
|
||||||
|
if field.HasValue then
|
||||||
|
System.Text.Json.Nodes.JsonValue.Create<int<measure>> field.Value
|
||||||
|
:> System.Text.Json.Nodes.JsonNode
|
||||||
|
else
|
||||||
|
null :> System.Text.Json.Nodes.JsonNode
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add ("enum", (input.Enum |> SomeEnum.toJsonNode))
|
||||||
|
|
||||||
|
node.Add (
|
||||||
|
"timestamp",
|
||||||
|
(input.Timestamp
|
||||||
|
|> (fun field -> field.ToString "o" |> System.Text.Json.Nodes.JsonValue.Create<string>))
|
||||||
|
)
|
||||||
|
|
||||||
|
node.Add ("unit", (input.Unit |> (fun value -> System.Text.Json.Nodes.JsonObject ())))
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the HeaderAndValue type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module HeaderAndValueJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type HeaderAndValue with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : HeaderAndValue) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
node.Add ("header", (input.Header |> System.Text.Json.Nodes.JsonValue.Create<string>))
|
||||||
|
node.Add ("value", (input.Value |> System.Text.Json.Nodes.JsonValue.Create<string>))
|
||||||
|
|
||||||
|
node :> _
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the Foo type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module FooJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type Foo with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : Foo) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
node.Add (
|
||||||
|
"message",
|
||||||
|
(input.Message
|
||||||
|
|> (fun field ->
|
||||||
|
match field with
|
||||||
|
| None -> null :> System.Text.Json.Nodes.JsonNode
|
||||||
|
| Some field -> HeaderAndValue.toJsonNode field
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
node :> _
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the CollectRemaining type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module CollectRemainingJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type CollectRemaining with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : CollectRemaining) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
node.Add (
|
||||||
|
"message",
|
||||||
|
(input.Message
|
||||||
|
|> (fun field ->
|
||||||
|
match field with
|
||||||
|
| None -> null :> System.Text.Json.Nodes.JsonNode
|
||||||
|
| Some field -> HeaderAndValue.toJsonNode field
|
||||||
|
))
|
||||||
|
)
|
||||||
|
|
||||||
|
for KeyValue (key, value) in input.Rest do
|
||||||
|
node.Add (key, id value)
|
||||||
|
|
||||||
|
node :> _
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.Text.Json.Serialization
|
||||||
|
|
||||||
|
/// Module containing JSON serializing extension members for the OuterCollectRemaining type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module OuterCollectRemainingJsonSerializeExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type OuterCollectRemaining with
|
||||||
|
|
||||||
|
/// Serialize to a JSON node
|
||||||
|
static member toJsonNode (input : OuterCollectRemaining) : System.Text.Json.Nodes.JsonNode =
|
||||||
|
let node = System.Text.Json.Nodes.JsonObject ()
|
||||||
|
|
||||||
|
do
|
||||||
|
for KeyValue (key, value) in input.Others do
|
||||||
|
node.Add (key, System.Text.Json.Nodes.JsonValue.Create<int> value)
|
||||||
|
|
||||||
|
node.Add ("remaining", (input.Remaining |> CollectRemaining.toJsonNode))
|
||||||
|
|
||||||
node :> _
|
node :> _
|
||||||
|
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
@@ -160,7 +358,7 @@ module InnerTypeWithBothJsonParseExtension =
|
|||||||
|
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerTypeWithBoth =
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerTypeWithBoth =
|
||||||
let ConcreteDict =
|
let arg_4 =
|
||||||
(match node.["concreteDict"] with
|
(match node.["concreteDict"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -178,7 +376,7 @@ module InnerTypeWithBothJsonParseExtension =
|
|||||||
|> Seq.map System.Collections.Generic.KeyValuePair
|
|> Seq.map System.Collections.Generic.KeyValuePair
|
||||||
|> System.Collections.Generic.Dictionary
|
|> System.Collections.Generic.Dictionary
|
||||||
|
|
||||||
let Dict =
|
let arg_3 =
|
||||||
(match node.["dict"] with
|
(match node.["dict"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -190,12 +388,12 @@ module InnerTypeWithBothJsonParseExtension =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key) |> System.Uri
|
let key = (kvp.Key) |> System.Uri
|
||||||
let value = (kvp.Value).AsValue().GetValue<bool> ()
|
let value = (kvp.Value).AsValue().GetValue<System.Boolean> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> dict
|
|> dict
|
||||||
|
|
||||||
let ReadOnlyDict =
|
let arg_2 =
|
||||||
(match node.["readOnlyDict"] with
|
(match node.["readOnlyDict"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -210,14 +408,15 @@ module InnerTypeWithBothJsonParseExtension =
|
|||||||
|
|
||||||
let value =
|
let value =
|
||||||
(kvp.Value).AsArray ()
|
(kvp.Value).AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<char> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.Char> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> readOnlyDict
|
|> readOnlyDict
|
||||||
|
|
||||||
let Map =
|
let arg_1 =
|
||||||
(match node.["map"] with
|
(match node.["map"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -234,7 +433,7 @@ module InnerTypeWithBothJsonParseExtension =
|
|||||||
)
|
)
|
||||||
|> Map.ofSeq
|
|> Map.ofSeq
|
||||||
|
|
||||||
let Thing =
|
let arg_0 =
|
||||||
(match node.[("it's-a-me")] with
|
(match node.[("it's-a-me")] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -245,16 +444,35 @@ module InnerTypeWithBothJsonParseExtension =
|
|||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<string> ()
|
||||||
|
|> System.Guid.Parse
|
||||||
|
|
||||||
{
|
{
|
||||||
Thing = Thing
|
Thing = arg_0
|
||||||
Map = Map
|
Map = arg_1
|
||||||
ReadOnlyDict = ReadOnlyDict
|
ReadOnlyDict = arg_2
|
||||||
Dict = Dict
|
Dict = arg_3
|
||||||
ConcreteDict = ConcreteDict
|
ConcreteDict = arg_4
|
||||||
}
|
}
|
||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the SomeEnum type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module SomeEnumJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type SomeEnum with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : SomeEnum =
|
||||||
|
match node.GetValueKind () with
|
||||||
|
| System.Text.Json.JsonValueKind.Number -> node.AsValue().GetValue<int> () |> enum<SomeEnum>
|
||||||
|
| System.Text.Json.JsonValueKind.String ->
|
||||||
|
match node.AsValue().GetValue<string>().ToLowerInvariant () with
|
||||||
|
| "blah" -> SomeEnum.Blah
|
||||||
|
| "thing" -> SomeEnum.Thing
|
||||||
|
| v -> failwith ("Unrecognised value for enum: %i" + v)
|
||||||
|
| _ -> failwith ("Unrecognised kind for enum of type: " + "SomeEnum")
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing extension members for the JsonRecordTypeWithBoth type
|
/// Module containing JSON parsing extension members for the JsonRecordTypeWithBoth type
|
||||||
[<AutoOpen>]
|
[<AutoOpen>]
|
||||||
module JsonRecordTypeWithBothJsonParseExtension =
|
module JsonRecordTypeWithBothJsonParseExtension =
|
||||||
@@ -263,7 +481,76 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
|
|
||||||
/// Parse from a JSON node.
|
/// Parse from a JSON node.
|
||||||
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordTypeWithBoth =
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordTypeWithBoth =
|
||||||
let F =
|
let arg_21 = ()
|
||||||
|
|
||||||
|
let arg_20 =
|
||||||
|
(match node.["timestamp"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("timestamp")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<string> ()
|
||||||
|
|> System.DateTimeOffset.Parse
|
||||||
|
|
||||||
|
let arg_19 =
|
||||||
|
SomeEnum.jsonParse (
|
||||||
|
match node.["enum"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("enum")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v
|
||||||
|
)
|
||||||
|
|
||||||
|
let arg_18 =
|
||||||
|
match node.["intMeasureNullable"] with
|
||||||
|
| null -> System.Nullable ()
|
||||||
|
| v ->
|
||||||
|
v.AsValue().GetValue<System.Int32> ()
|
||||||
|
|> LanguagePrimitives.Int32WithMeasure
|
||||||
|
|> System.Nullable
|
||||||
|
|
||||||
|
let arg_17 =
|
||||||
|
match node.["intMeasureOption"] with
|
||||||
|
| null -> None
|
||||||
|
| v ->
|
||||||
|
v.AsValue().GetValue<System.Int32> ()
|
||||||
|
|> LanguagePrimitives.Int32WithMeasure
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
let arg_16 =
|
||||||
|
(match node.["single"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("single")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Single> ()
|
||||||
|
|> LanguagePrimitives.Float32WithMeasure
|
||||||
|
|
||||||
|
let arg_15 =
|
||||||
|
(match node.["f32"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("f32")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Single> ()
|
||||||
|
|> LanguagePrimitives.Float32WithMeasure
|
||||||
|
|
||||||
|
let arg_14 =
|
||||||
(match node.["f"] with
|
(match node.["f"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -271,12 +558,130 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
sprintf "Required key '%s' not found on JSON object" ("f")
|
sprintf "Required key '%s' not found on JSON object" ("f")
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Double> ()
|
||||||
|
|> LanguagePrimitives.FloatWithMeasure
|
||||||
|
|
||||||
|
let arg_13 =
|
||||||
|
(match node.["u64"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("u64")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt64> ()
|
||||||
|
|> LanguagePrimitives.UInt64WithMeasure
|
||||||
|
|
||||||
|
let arg_12 =
|
||||||
|
(match node.["u32"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("u32")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt32> ()
|
||||||
|
|> LanguagePrimitives.UInt32WithMeasure
|
||||||
|
|
||||||
|
let arg_11 =
|
||||||
|
(match node.["u"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("u")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.UInt32> ()
|
||||||
|
|> LanguagePrimitives.UInt32WithMeasure
|
||||||
|
|
||||||
|
let arg_10 =
|
||||||
|
(match node.["i64"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("i64")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int64> ()
|
||||||
|
|> LanguagePrimitives.Int64WithMeasure
|
||||||
|
|
||||||
|
let arg_9 =
|
||||||
|
(match node.["i32"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("i32")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int32> ()
|
||||||
|
|> LanguagePrimitives.Int32WithMeasure
|
||||||
|
|
||||||
|
let arg_8 =
|
||||||
|
(match node.["i"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("i")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int32> ()
|
||||||
|
|> LanguagePrimitives.Int32WithMeasure
|
||||||
|
|
||||||
|
let arg_7 =
|
||||||
|
(match node.["sbyte"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("sbyte")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.SByte> ()
|
||||||
|
|> LanguagePrimitives.SByteWithMeasure
|
||||||
|
|
||||||
|
let arg_6 =
|
||||||
|
(match node.["byte"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("byte")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Byte> ()
|
||||||
|
|> LanguagePrimitives.ByteWithMeasure
|
||||||
|
|
||||||
|
let arg_5 =
|
||||||
|
(match node.["arr"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("arr")
|
||||||
|
)
|
||||||
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|
||||||
|> Array.ofSeq
|
|> Array.ofSeq
|
||||||
|
|
||||||
let E =
|
let arg_4 =
|
||||||
(match node.["e"] with
|
(match node.["e"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -286,10 +691,11 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|
||||||
|> Array.ofSeq
|
|> Array.ofSeq
|
||||||
|
|
||||||
let D =
|
let arg_3 =
|
||||||
InnerTypeWithBoth.jsonParse (
|
InnerTypeWithBoth.jsonParse (
|
||||||
match node.["d"] with
|
match node.["d"] with
|
||||||
| null ->
|
| null ->
|
||||||
@@ -301,7 +707,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
| v -> v
|
| v -> v
|
||||||
)
|
)
|
||||||
|
|
||||||
let C =
|
let arg_2 =
|
||||||
(match node.["c"] with
|
(match node.["c"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -311,10 +717,11 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let B =
|
let arg_1 =
|
||||||
(match node.["b"] with
|
(match node.["b"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -324,9 +731,9 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let A =
|
let arg_0 =
|
||||||
(match node.["a"] with
|
(match node.["a"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -336,13 +743,245 @@ module JsonRecordTypeWithBothJsonParseExtension =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
{
|
{
|
||||||
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
|
Arr = arg_5
|
||||||
|
Byte = arg_6
|
||||||
|
Sbyte = arg_7
|
||||||
|
I = arg_8
|
||||||
|
I32 = arg_9
|
||||||
|
I64 = arg_10
|
||||||
|
U = arg_11
|
||||||
|
U32 = arg_12
|
||||||
|
U64 = arg_13
|
||||||
|
F = arg_14
|
||||||
|
F32 = arg_15
|
||||||
|
Single = arg_16
|
||||||
|
IntMeasureOption = arg_17
|
||||||
|
IntMeasureNullable = arg_18
|
||||||
|
Enum = arg_19
|
||||||
|
Timestamp = arg_20
|
||||||
|
Unit = arg_21
|
||||||
|
}
|
||||||
|
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<System.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<System.Int32> ()
|
||||||
|
)
|
||||||
|
| v -> failwith ("Unrecognised 'type' field value: " + v)
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the HeaderAndValue type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module HeaderAndValueJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type HeaderAndValue with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : HeaderAndValue =
|
||||||
|
let arg_1 =
|
||||||
|
(match node.["value"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("value")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
|
let arg_0 =
|
||||||
|
(match node.["header"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("header")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
Header = arg_0
|
||||||
|
Value = arg_1
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the Foo type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module FooJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type Foo with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : Foo =
|
||||||
|
let arg_0 =
|
||||||
|
match node.["message"] with
|
||||||
|
| null -> None
|
||||||
|
| v -> HeaderAndValue.jsonParse v |> Some
|
||||||
|
|
||||||
|
{
|
||||||
|
Message = arg_0
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the CollectRemaining type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module CollectRemainingJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type CollectRemaining with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : CollectRemaining =
|
||||||
|
let arg_1 =
|
||||||
|
let result =
|
||||||
|
System.Collections.Generic.Dictionary<string, System.Text.Json.Nodes.JsonNode> ()
|
||||||
|
|
||||||
|
let node = node.AsObject ()
|
||||||
|
|
||||||
|
for KeyValue (key, value) in node do
|
||||||
|
if key = "message" then () else result.Add (key, node.[key])
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
let arg_0 =
|
||||||
|
match node.["message"] with
|
||||||
|
| null -> None
|
||||||
|
| v -> HeaderAndValue.jsonParse v |> Some
|
||||||
|
|
||||||
|
{
|
||||||
|
Message = arg_0
|
||||||
|
Rest = arg_1
|
||||||
|
}
|
||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
/// Module containing JSON parsing extension members for the OuterCollectRemaining type
|
||||||
|
[<AutoOpen>]
|
||||||
|
module OuterCollectRemainingJsonParseExtension =
|
||||||
|
/// Extension methods for JSON parsing
|
||||||
|
type OuterCollectRemaining with
|
||||||
|
|
||||||
|
/// Parse from a JSON node.
|
||||||
|
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : OuterCollectRemaining =
|
||||||
|
let arg_1 =
|
||||||
|
CollectRemaining.jsonParse (
|
||||||
|
match node.["remaining"] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" ("remaining")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v
|
||||||
|
)
|
||||||
|
|
||||||
|
let arg_0 =
|
||||||
|
let result = System.Collections.Generic.Dictionary<string, int> ()
|
||||||
|
let node = node.AsObject ()
|
||||||
|
|
||||||
|
for KeyValue (key, value) in node do
|
||||||
|
if key = "remaining" then
|
||||||
|
()
|
||||||
|
else
|
||||||
|
result.Add (
|
||||||
|
key,
|
||||||
|
(match node.[key] with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.Collections.Generic.KeyNotFoundException (
|
||||||
|
sprintf "Required key '%s' not found on JSON object" (key)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v)
|
||||||
|
.AsValue()
|
||||||
|
.GetValue<System.Int32> ()
|
||||||
|
)
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
{
|
||||||
|
Others = arg_0
|
||||||
|
Remaining = arg_1
|
||||||
}
|
}
|
||||||
|
|||||||
6432
ConsumePlugin/GeneratedSwaggerGitea.fs
Normal file
6432
ConsumePlugin/GeneratedSwaggerGitea.fs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -8,12 +8,11 @@
|
|||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the JwtVaultAuthResponse type
|
/// Module containing JSON parsing methods for the JwtVaultAuthResponse type
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
|
||||||
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 (
|
||||||
@@ -23,9 +22,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
let Orphan =
|
let arg_9 =
|
||||||
(match node.["orphan"] with
|
(match node.["orphan"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -35,9 +34,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<System.Boolean> ()
|
||||||
|
|
||||||
let EntityId =
|
let arg_8 =
|
||||||
(match node.["entity_id"] with
|
(match node.["entity_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -47,9 +46,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let TokenType =
|
let arg_7 =
|
||||||
(match node.["token_type"] with
|
(match node.["token_type"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -59,9 +58,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let Renewable =
|
let arg_6 =
|
||||||
(match node.["renewable"] with
|
(match node.["renewable"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -71,9 +70,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<System.Boolean> ()
|
||||||
|
|
||||||
let LeaseDuration =
|
let arg_5 =
|
||||||
(match node.["lease_duration"] with
|
(match node.["lease_duration"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -83,9 +82,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
let IdentityPolicies =
|
let arg_4 =
|
||||||
(match node.["identity_policies"] with
|
(match node.["identity_policies"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -95,10 +94,11 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let TokenPolicies =
|
let arg_3 =
|
||||||
(match node.["token_policies"] with
|
(match node.["token_policies"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -108,10 +108,11 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let Policies =
|
let arg_2 =
|
||||||
(match node.["policies"] with
|
(match node.["policies"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -121,10 +122,11 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsArray ()
|
.AsArray ()
|
||||||
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|
|> Seq.cast<System.Text.Json.Nodes.JsonNode>
|
||||||
|
|> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|
||||||
|> List.ofSeq
|
|> List.ofSeq
|
||||||
|
|
||||||
let Accessor =
|
let arg_1 =
|
||||||
(match node.["accessor"] with
|
(match node.["accessor"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -134,9 +136,9 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let ClientToken =
|
let arg_0 =
|
||||||
(match node.["client_token"] with
|
(match node.["client_token"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -146,30 +148,29 @@ module JwtVaultAuthResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.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
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the JwtVaultResponse type
|
/// Module containing JSON parsing methods for the JwtVaultResponse type
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
|
||||||
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 ->
|
||||||
@@ -181,7 +182,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 (
|
||||||
@@ -191,9 +192,9 @@ module JwtVaultResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
let Renewable =
|
let arg_2 =
|
||||||
(match node.["renewable"] with
|
(match node.["renewable"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -203,9 +204,9 @@ module JwtVaultResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<System.Boolean> ()
|
||||||
|
|
||||||
let LeaseId =
|
let arg_1 =
|
||||||
(match node.["lease_id"] with
|
(match node.["lease_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -215,9 +216,9 @@ module JwtVaultResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let RequestId =
|
let arg_0 =
|
||||||
(match node.["request_id"] with
|
(match node.["request_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -227,24 +228,23 @@ module JwtVaultResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.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
|
||||||
|
|
||||||
/// Module containing JSON parsing methods for the JwtSecretResponse type
|
/// Module containing JSON parsing methods for the JwtSecretResponse type
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
|
||||||
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 (
|
||||||
@@ -262,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 (
|
||||||
@@ -274,12 +274,12 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key)
|
let key = (kvp.Key)
|
||||||
let value = (kvp.Value).AsValue().GetValue<int> ()
|
let value = (kvp.Value).AsValue().GetValue<System.Int32> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> Map.ofSeq
|
|> Map.ofSeq
|
||||||
|
|
||||||
let Data6 =
|
let arg_9 =
|
||||||
(match node.["data6"] with
|
(match node.["data6"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -291,12 +291,12 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key) |> System.Uri
|
let key = (kvp.Key) |> System.Uri
|
||||||
let value = (kvp.Value).AsValue().GetValue<string> ()
|
let value = (kvp.Value).AsValue().GetValue<System.String> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> dict
|
|> dict
|
||||||
|
|
||||||
let Data5 =
|
let arg_8 =
|
||||||
(match node.["data5"] with
|
(match node.["data5"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -308,12 +308,12 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key) |> System.Uri
|
let key = (kvp.Key) |> System.Uri
|
||||||
let value = (kvp.Value).AsValue().GetValue<string> ()
|
let value = (kvp.Value).AsValue().GetValue<System.String> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> readOnlyDict
|
|> readOnlyDict
|
||||||
|
|
||||||
let Data4 =
|
let arg_7 =
|
||||||
(match node.["data4"] with
|
(match node.["data4"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -325,12 +325,12 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key)
|
let key = (kvp.Key)
|
||||||
let value = (kvp.Value).AsValue().GetValue<string> ()
|
let value = (kvp.Value).AsValue().GetValue<System.String> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> Map.ofSeq
|
|> Map.ofSeq
|
||||||
|
|
||||||
let Data3 =
|
let arg_6 =
|
||||||
(match node.["data3"] with
|
(match node.["data3"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -342,13 +342,13 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key)
|
let key = (kvp.Key)
|
||||||
let value = (kvp.Value).AsValue().GetValue<string> ()
|
let value = (kvp.Value).AsValue().GetValue<System.String> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> 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 (
|
||||||
@@ -360,12 +360,12 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key)
|
let key = (kvp.Key)
|
||||||
let value = (kvp.Value).AsValue().GetValue<string> ()
|
let value = (kvp.Value).AsValue().GetValue<System.String> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> dict
|
|> dict
|
||||||
|
|
||||||
let Data =
|
let arg_4 =
|
||||||
(match node.["data"] with
|
(match node.["data"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -377,12 +377,12 @@ module JwtSecretResponse =
|
|||||||
.AsObject ()
|
.AsObject ()
|
||||||
|> Seq.map (fun kvp ->
|
|> Seq.map (fun kvp ->
|
||||||
let key = (kvp.Key)
|
let key = (kvp.Key)
|
||||||
let value = (kvp.Value).AsValue().GetValue<string> ()
|
let value = (kvp.Value).AsValue().GetValue<System.String> ()
|
||||||
key, value
|
key, value
|
||||||
)
|
)
|
||||||
|> readOnlyDict
|
|> readOnlyDict
|
||||||
|
|
||||||
let LeaseDuration =
|
let arg_3 =
|
||||||
(match node.["lease_duration"] with
|
(match node.["lease_duration"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -392,9 +392,9 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<int> ()
|
.GetValue<System.Int32> ()
|
||||||
|
|
||||||
let Renewable =
|
let arg_2 =
|
||||||
(match node.["renewable"] with
|
(match node.["renewable"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -404,9 +404,9 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<bool> ()
|
.GetValue<System.Boolean> ()
|
||||||
|
|
||||||
let LeaseId =
|
let arg_1 =
|
||||||
(match node.["lease_id"] with
|
(match node.["lease_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -416,9 +416,9 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.String> ()
|
||||||
|
|
||||||
let RequestId =
|
let arg_0 =
|
||||||
(match node.["request_id"] with
|
(match node.["request_id"] with
|
||||||
| null ->
|
| null ->
|
||||||
raise (
|
raise (
|
||||||
@@ -428,21 +428,21 @@ module JwtSecretResponse =
|
|||||||
)
|
)
|
||||||
| v -> v)
|
| v -> v)
|
||||||
.AsValue()
|
.AsValue()
|
||||||
.GetValue<string> ()
|
.GetValue<System.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
|
||||||
@@ -455,8 +455,7 @@ open System.Threading.Tasks
|
|||||||
open RestEase
|
open RestEase
|
||||||
|
|
||||||
/// Module for constructing a REST client.
|
/// Module for constructing a REST client.
|
||||||
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module VaultClient =
|
module VaultClient =
|
||||||
/// Create a REST client.
|
/// Create a REST client.
|
||||||
let make (client : System.Net.Http.HttpClient) : IVaultClient =
|
let make (client : System.Net.Http.HttpClient) : IVaultClient =
|
||||||
@@ -480,11 +479,8 @@ module VaultClient =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/{mountPoint}/{path}"
|
"v1/{mountPoint}/{path}"
|
||||||
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
|
.Replace("{path}", path.ToString () |> System.Uri.EscapeDataString)
|
||||||
.Replace (
|
.Replace ("{mountPoint}", mountPoint.ToString () |> System.Uri.EscapeDataString),
|
||||||
"{mountPoint}",
|
|
||||||
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
|
|
||||||
),
|
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -503,6 +499,11 @@ module VaultClient =
|
|||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let jsonNode =
|
||||||
|
match jsonNode with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v
|
||||||
|
|
||||||
return JwtSecretResponse.jsonParse jsonNode
|
return JwtSecretResponse.jsonParse jsonNode
|
||||||
}
|
}
|
||||||
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
@@ -539,7 +540,223 @@ module VaultClient =
|
|||||||
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|
||||||
|> Async.AwaitTask
|
|> Async.AwaitTask
|
||||||
|
|
||||||
|
let jsonNode =
|
||||||
|
match jsonNode with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v
|
||||||
|
|
||||||
return JwtVaultResponse.jsonParse jsonNode
|
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.Uri.EscapeDataString)
|
||||||
|
.Replace ("{mountPoint}", mountPoint.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
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
|
||||||
|
|
||||||
|
let jsonNode =
|
||||||
|
match jsonNode with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
let jsonNode =
|
||||||
|
match jsonNode with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v
|
||||||
|
|
||||||
|
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.Uri.EscapeDataString)
|
||||||
|
.Replace ("{mountPoint}", mountPoint.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
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
|
||||||
|
|
||||||
|
let jsonNode =
|
||||||
|
match jsonNode with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
let jsonNode =
|
||||||
|
match jsonNode with
|
||||||
|
| null -> raise (System.ArgumentNullException ())
|
||||||
|
| v -> v
|
||||||
|
|
||||||
|
return JwtVaultResponse.jsonParse jsonNode
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
|||||||
13
ConsumePlugin/JsonParseNullness.fs
Normal file
13
ConsumePlugin/JsonParseNullness.fs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
namespace ConsumePlugin
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
|
type InnerStruct =
|
||||||
|
{
|
||||||
|
A : int
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
|
type ArrayOfInnerStruct =
|
||||||
|
{
|
||||||
|
B : InnerStruct array
|
||||||
|
}
|
||||||
@@ -29,13 +29,52 @@ type JsonRecordType =
|
|||||||
F : int[]
|
F : int[]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
|
type internal InternalTypeNotExtension =
|
||||||
|
{
|
||||||
|
[<JsonPropertyName(Literals.something)>]
|
||||||
|
InternalThing : string
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize>]
|
||||||
|
type internal InternalTypeNotExtensionSerial =
|
||||||
|
{
|
||||||
|
[<JsonPropertyName(Literals.something)>]
|
||||||
|
InternalThing2 : string
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
type internal InternalTypeExtension =
|
||||||
|
{
|
||||||
|
[<JsonPropertyName(Literals.something)>]
|
||||||
|
ExternalThing : string
|
||||||
|
}
|
||||||
|
|
||||||
[<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 head : '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,16 @@ 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
|
||||||
|
|
||||||
|
[<GenerateMock>]
|
||||||
|
type TypeWithProperties =
|
||||||
|
inherit IDisposable
|
||||||
|
abstract Mem1 : string option -> string[] Async
|
||||||
|
abstract Prop1 : int
|
||||||
|
abstract Prop2 : unit Async
|
||||||
|
|||||||
41
ConsumePlugin/MockExampleNoAttributes.fs
Normal file
41
ConsumePlugin/MockExampleNoAttributes.fs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
type IPublicTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type IPublicTypeInternalFalseNoAttr =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type internal InternalTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeInternalFalseNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type VeryPublicTypeNoAttr<'a, 'b> =
|
||||||
|
abstract Mem1 : 'a -> 'b
|
||||||
|
|
||||||
|
type CurriedNoAttr<'a> =
|
||||||
|
abstract Mem1 : int -> 'a -> string
|
||||||
|
abstract Mem2 : int * string -> 'a -> string
|
||||||
|
abstract Mem3 : (int * string) -> 'a -> string
|
||||||
|
abstract Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
abstract Mem5 : x : int * string -> ('a * int) -> string
|
||||||
|
abstract Mem6 : int * string -> y : 'a * int -> string
|
||||||
|
|
||||||
|
type TypeWithInterfaceNoAttr =
|
||||||
|
inherit IDisposable
|
||||||
|
abstract Mem1 : string option -> string[] Async
|
||||||
|
abstract Mem2 : unit -> string[] Async
|
||||||
@@ -19,13 +19,16 @@ type GymAccessOptions =
|
|||||||
QrCodeAccess : bool
|
QrCodeAccess : bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[<Measure>]
|
||||||
|
type measure
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
type GymLocation =
|
type GymLocation =
|
||||||
{
|
{
|
||||||
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
||||||
Longitude : float
|
Longitude : float
|
||||||
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
|
||||||
Latitude : float
|
Latitude : float<measure>
|
||||||
}
|
}
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.JsonParse>]
|
[<WoofWare.Myriad.Plugins.JsonParse>]
|
||||||
|
|||||||
@@ -1,9 +1,5 @@
|
|||||||
namespace ConsumePlugin
|
namespace ConsumePlugin
|
||||||
|
|
||||||
type ParseState =
|
|
||||||
| AwaitingKey
|
|
||||||
| AwaitingValue of string
|
|
||||||
|
|
||||||
/// My whatnot
|
/// My whatnot
|
||||||
[<WoofWare.Myriad.Plugins.RemoveOptions>]
|
[<WoofWare.Myriad.Plugins.RemoveOptions>]
|
||||||
type RecordType =
|
type RecordType =
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|
||||||
|
[<Get "v1/gyms/{gym_id}/attendance">]
|
||||||
|
abstract GetGymAttendance' : [<Path("gym_id")>] gymId : int * ?ct : CancellationToken -> Task<GymAttendance>
|
||||||
|
|
||||||
[<RestEase.GetAttribute "v1/member">]
|
[<RestEase.GetAttribute "v1/member">]
|
||||||
abstract GetMember : ?ct : CancellationToken -> Member Task
|
abstract GetMember : ?ct : CancellationToken -> Member Task
|
||||||
|
|
||||||
[<RestEase.Get "v1/gyms/{gym_id}">]
|
[<RestEase.Get "v1/gyms/{gym}">]
|
||||||
abstract GetGym : [<Path "gym_id">] gymId : int * ?ct : CancellationToken -> Task<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>
|
||||||
@@ -111,17 +122,84 @@ type internal IApiWithoutBaseAddress =
|
|||||||
[<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>
|
||||||
|
|
||||||
// TODO: implement BasePath support
|
|
||||||
|
|
||||||
[<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/thing">]
|
||||||
[<BasePath "foo">]
|
[<BasePath "foo">]
|
||||||
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>]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePath =
|
||||||
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePathAndAddress =
|
||||||
|
[<Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BasePath "foo">]
|
||||||
|
type IApiWithBasePathAndAbsoluteEndpoint =
|
||||||
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
|
[<BasePath "foo">]
|
||||||
|
type IApiWithBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
[<Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePathAndAbsoluteEndpoint =
|
||||||
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
[<Get "/endpoint/{param}">]
|
||||||
|
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}">]
|
||||||
|
[<Header("Something-Else", "val")>]
|
||||||
|
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>
|
||||||
|
|||||||
@@ -9,13 +9,22 @@ open System.Text.Json.Serialization
|
|||||||
type InnerTypeWithBoth =
|
type InnerTypeWithBoth =
|
||||||
{
|
{
|
||||||
[<JsonPropertyName("it's-a-me")>]
|
[<JsonPropertyName("it's-a-me")>]
|
||||||
Thing : string
|
Thing : Guid
|
||||||
Map : Map<string, Uri>
|
Map : Map<string, Uri>
|
||||||
ReadOnlyDict : IReadOnlyDictionary<string, char list>
|
ReadOnlyDict : IReadOnlyDictionary<string, char list>
|
||||||
Dict : IDictionary<Uri, bool>
|
Dict : IDictionary<Uri, bool>
|
||||||
ConcreteDict : Dictionary<string, InnerTypeWithBoth>
|
ConcreteDict : Dictionary<string, InnerTypeWithBoth>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
type SomeEnum =
|
||||||
|
| Blah = 1
|
||||||
|
| Thing = 0
|
||||||
|
|
||||||
|
[<Measure>]
|
||||||
|
type measure
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
type JsonRecordTypeWithBoth =
|
type JsonRecordTypeWithBoth =
|
||||||
@@ -25,5 +34,61 @@ type JsonRecordTypeWithBoth =
|
|||||||
C : int list
|
C : int list
|
||||||
D : InnerTypeWithBoth
|
D : InnerTypeWithBoth
|
||||||
E : string array
|
E : string array
|
||||||
F : int[]
|
Arr : int[]
|
||||||
|
Byte : byte<measure>
|
||||||
|
Sbyte : sbyte<measure>
|
||||||
|
I : int<measure>
|
||||||
|
I32 : int32<measure>
|
||||||
|
I64 : int64<measure>
|
||||||
|
U : uint<measure>
|
||||||
|
U32 : uint32<measure>
|
||||||
|
U64 : uint64<measure>
|
||||||
|
F : float<measure>
|
||||||
|
F32 : float32<measure>
|
||||||
|
Single : single<measure>
|
||||||
|
IntMeasureOption : int<measure> option
|
||||||
|
IntMeasureNullable : int<measure> Nullable
|
||||||
|
Enum : SomeEnum
|
||||||
|
Timestamp : DateTimeOffset
|
||||||
|
Unit : unit
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
type FirstDu =
|
||||||
|
| EmptyCase
|
||||||
|
| Case1 of data : string
|
||||||
|
| Case2 of record : JsonRecordTypeWithBoth * i : int
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
type HeaderAndValue =
|
||||||
|
{
|
||||||
|
Header : string
|
||||||
|
Value : string
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
type Foo =
|
||||||
|
{
|
||||||
|
Message : HeaderAndValue option
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
type CollectRemaining =
|
||||||
|
{
|
||||||
|
Message : HeaderAndValue option
|
||||||
|
[<JsonExtensionData>]
|
||||||
|
Rest : Dictionary<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
|
||||||
|
[<WoofWare.Myriad.Plugins.JsonParse true>]
|
||||||
|
type OuterCollectRemaining =
|
||||||
|
{
|
||||||
|
[<JsonExtensionData>]
|
||||||
|
Others : Dictionary<string, int>
|
||||||
|
Remaining : CollectRemaining
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
21054
ConsumePlugin/swagger-gitea.json
Normal file
21054
ConsumePlugin/swagger-gitea.json
Normal file
File diff suppressed because it is too large
Load Diff
@@ -10,19 +10,10 @@
|
|||||||
<WarnOn>FS3388,FS3559</WarnOn>
|
<WarnOn>FS3388,FS3559</WarnOn>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all"/>
|
<PackageReference Include="Nerdbank.GitVersioning" Version="3.8.38-alpha" PrivateAssets="all"/>
|
||||||
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
|
|
||||||
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
|
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<!--
|
<PropertyGroup Condition="'$(GITHUB_ACTION)' != ''">
|
||||||
SourceLink doesn't support F# deterministic builds out of the box,
|
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
|
||||||
so tell SourceLink that our source root is going to be remapped.
|
</PropertyGroup>
|
||||||
-->
|
|
||||||
<Target Name="MapSourceRoot" BeforeTargets="_GenerateSourceLinkFile" Condition="'$(SourceRootMappedPathsFeatureSupported)' != 'true'">
|
|
||||||
<ItemGroup>
|
|
||||||
<SourceRoot Update="@(SourceRoot)">
|
|
||||||
<MappedPath>Z:\CheckoutRoot\WoofWare.Myriad\</MappedPath>
|
|
||||||
</SourceRoot>
|
|
||||||
</ItemGroup>
|
|
||||||
</Target>
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
102
WoofWare.Myriad.Plugins.Attributes/ArgParserAttributes.fs
Normal file
102
WoofWare.Myriad.Plugins.Attributes/ArgParserAttributes.fs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Attribute indicating a record type to which the "build arg parser" Myriad
|
||||||
|
/// generator should apply during build.
|
||||||
|
///
|
||||||
|
/// 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 ArgParserAttribute (isExtensionMethod : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// The default value of `isExtensionMethod`, the optional argument to the ArgParserAttribute constructor.
|
||||||
|
static member DefaultIsExtensionMethod = false
|
||||||
|
|
||||||
|
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
||||||
|
new () = ArgParserAttribute ArgParserAttribute.DefaultIsExtensionMethod
|
||||||
|
|
||||||
|
/// Attribute indicating that this field shall accumulate all unmatched args,
|
||||||
|
/// as well as any that appear after a bare `--`.
|
||||||
|
///
|
||||||
|
/// Set `includeFlagLike = true` to include args that begin `--` in the
|
||||||
|
/// positional args.
|
||||||
|
/// (By default, `includeFlagLike = false` and we throw when encountering
|
||||||
|
/// an argument which looks like a flag but which we don't recognise.)
|
||||||
|
/// We will still interpret `--help` as requesting help, unless it comes after
|
||||||
|
/// a standalone `--` separator.
|
||||||
|
///
|
||||||
|
/// If the type of the PositionalArgs field is `Choice<'a, 'a>`, then we will
|
||||||
|
/// tell you whether each arg came before or after a standalone `--` separator.
|
||||||
|
/// For example, `MyApp foo bar -- baz` with PositionalArgs of `Choice<string, string>`
|
||||||
|
/// would yield `Choice1Of2 foo, Choice1Of2 bar, Choice2Of2 baz`.
|
||||||
|
type PositionalArgsAttribute (includeFlagLike : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// The default value of `isExtensionMethod`, the optional argument to the ArgParserAttribute constructor.
|
||||||
|
static member DefaultIncludeFlagLike = false
|
||||||
|
|
||||||
|
/// Shorthand for the "includeFlagLike = false" constructor; see documentation there for details.
|
||||||
|
new () = PositionalArgsAttribute PositionalArgsAttribute.DefaultIncludeFlagLike
|
||||||
|
|
||||||
|
/// Attribute indicating that this field shall have a default value derived
|
||||||
|
/// from calling an appropriately named static method on the type.
|
||||||
|
///
|
||||||
|
/// This attribute can only be placed on fields of type `Choice<_, _>` where both type parameters
|
||||||
|
/// are the same.
|
||||||
|
/// After a successful parse, the value is Choice1Of2 if the user supplied an input,
|
||||||
|
/// or Choice2Of2 if the input was obtained by calling the default function.
|
||||||
|
///
|
||||||
|
/// The static method we call for field `FieldName : 'a` is `DefaultFieldName : unit -> 'a`.
|
||||||
|
type ArgumentDefaultFunctionAttribute () =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute indicating that this field shall have a default value derived
|
||||||
|
/// from an environment variable (whose name you give in the attribute constructor).
|
||||||
|
///
|
||||||
|
/// This attribute can only be placed on fields of type `Choice<_, _>` where both type parameters
|
||||||
|
/// are the same.
|
||||||
|
/// After a successful parse, the value is Choice1Of2 if the user supplied an input,
|
||||||
|
/// or Choice2Of2 if the input was obtained by pulling a value from `Environment.GetEnvironmentVariable`.
|
||||||
|
type ArgumentDefaultEnvironmentVariableAttribute (envVar : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute indicating that this field shall have the given help text, when `--help` is invoked
|
||||||
|
/// or when a parse error causes us to print help text.
|
||||||
|
type ArgumentHelpTextAttribute (helpText : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute indicating that this field should be parsed with a ParseExact method on its type.
|
||||||
|
/// For example, on a TimeSpan field, with [<ArgumentParseExact @"hh\:mm\:ss">], we will call
|
||||||
|
/// `TimeSpan.ParseExact (s, @"hh\:mm\:ss", CultureInfo.CurrentCulture).
|
||||||
|
type ParseExactAttribute (format : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute indicating that this field should be parsed in the invariant culture, rather than the
|
||||||
|
/// default current culture.
|
||||||
|
/// For example, on a TimeSpan field, with [<InvariantCulture>] and [<ArgumentParseExact @"hh\:mm\:ss">], we will call
|
||||||
|
/// `TimeSpan.ParseExact (s, @"hh\:mm\:ss", CultureInfo.InvariantCulture).
|
||||||
|
type InvariantCultureAttribute () =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute placed on a field of a two-case no-data discriminated union, indicating that this is "basically a bool".
|
||||||
|
/// For example: `type DryRun = | [<ArgumentFlag true>] Dry | [<ArgumentFlag false>] Wet`
|
||||||
|
/// A record with `{ DryRun : DryRun }` will then be parsed like `{ DryRun : bool }` (so the user supplies `--dry-run`),
|
||||||
|
/// but that you get this strongly-typed value directly in the code (so you `match args.DryRun with | DryRun.Dry ...`).
|
||||||
|
///
|
||||||
|
/// You must put this attribute on both cases of the discriminated union, with opposite values in each case.
|
||||||
|
type ArgumentFlagAttribute (flagValue : bool) =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// Attribute placed on a field of a record to specify a different long form from the default. If you place this
|
||||||
|
/// attribute, you won't get the default: ArgFoo would normally be expressed as `--arg-foo`, but if you instead
|
||||||
|
/// say `[<ArgumentLongForm "thingy-blah">]` or `[<ArgumentLongForm "thingy">]`, you instead use `--thingy-blah`
|
||||||
|
/// or `--thingy` respectively.
|
||||||
|
///
|
||||||
|
/// You can place this argument multiple times.
|
||||||
|
///
|
||||||
|
/// Omit the initial `--` that you expect the user to type.
|
||||||
|
[<AttributeUsage(AttributeTargets.Field, AllowMultiple = true)>]
|
||||||
|
type ArgumentLongForm (s : string) =
|
||||||
|
inherit Attribute ()
|
||||||
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 ()
|
||||||
84
WoofWare.Myriad.Plugins.Attributes/RestEase.fs
Normal file
84
WoofWare.Myriad.Plugins.Attributes/RestEase.fs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
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.
|
||||||
|
///
|
||||||
|
/// We will essentially unconditionally append a slash to this for you, on the grounds that you probably don't
|
||||||
|
/// intend the base path *itself* to be an endpoint.
|
||||||
|
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
|
||||||
|
|
||||||
|
/// Indicates that this argument to a method is passed to the remote API by being serialised into the request
|
||||||
|
/// body.
|
||||||
|
type BodyAttribute () =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// This is interpolated into every URL, between the BaseAddress and the path specified by e.g. [<Get>].
|
||||||
|
/// Note that if the [<Get>]-specified path starts with a slash, the BasePath is ignored, because then [<Get>]
|
||||||
|
/// is considered to be relative to the URL root (i.e. the host part of the BaseAddress).
|
||||||
|
/// Similarly, if the [<BasePath>] starts with a slash, then any path component of the BaseAddress is ignored.
|
||||||
|
///
|
||||||
|
/// We will essentially unconditionally append a slash to this for you, on the grounds that you probably don't
|
||||||
|
/// intend the base path *itself* to be an endpoint.
|
||||||
|
///
|
||||||
|
/// Can contain {placeholders}; hopefully your methods define values for those placeholders with [<Path>]
|
||||||
|
/// attributes!
|
||||||
|
type BasePathAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
81
WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt
Normal file
81
WoofWare.Myriad.Plugins.Attributes/SurfaceBaseline.txt
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
WoofWare.Myriad.Plugins.ArgParserAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ArgParserAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.ArgParserAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.ArgParserAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ArgParserAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentDefaultEnvironmentVariableAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentDefaultEnvironmentVariableAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentDefaultFunctionAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentDefaultFunctionAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentFlagAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentFlagAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentHelpTextAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentHelpTextAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentLongForm inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ArgumentLongForm..ctor [constructor]: string
|
||||||
|
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.InvariantCultureAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.InvariantCultureAttribute..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.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.ParseExactAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.ParseExactAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.PositionalArgsAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.PositionalArgsAttribute..ctor [constructor]: bool
|
||||||
|
WoofWare.Myriad.Plugins.PositionalArgsAttribute..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.PositionalArgsAttribute.DefaultIncludeFlagLike [static property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.PositionalArgsAttribute.get_DefaultIncludeFlagLike [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+BasePathAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BasePathAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BodyAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BodyAttribute..ctor [constructor]: unit
|
||||||
|
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,30 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
|
||||||
|
<IsPackable>false</IsPackable>
|
||||||
|
<IsTestProject>true</IsTestProject>
|
||||||
|
<!--
|
||||||
|
Known high severity vulnerability
|
||||||
|
I have not yet seen a single instance where I care about this warning
|
||||||
|
-->
|
||||||
|
<NoWarn>$(NoWarn),NU1903</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="TestSurface.fs" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="ApiSurface" Version="4.1.20" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0"/>
|
||||||
|
<PackageReference Include="NUnit" Version="4.3.2"/>
|
||||||
|
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes.fsproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
<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="ArgParserAttributes.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.6",
|
||||||
|
"publicReleaseRefSpec": [
|
||||||
|
"^refs/heads/main$"
|
||||||
|
],
|
||||||
|
"pathFilters": [
|
||||||
|
":/README.md",
|
||||||
|
":/LICENSE",
|
||||||
|
":/WoofWare.Myriad.Plugins/logo.png",
|
||||||
|
":/Directory.Build.props",
|
||||||
|
":/global.json",
|
||||||
|
"./",
|
||||||
|
":^Test"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -58,7 +58,7 @@ module PureGymDtos =
|
|||||||
[
|
[
|
||||||
"""{"latitude": 1.0, "longitude": 3.0}""",
|
"""{"latitude": 1.0, "longitude": 3.0}""",
|
||||||
{
|
{
|
||||||
GymLocation.Latitude = 1.0
|
GymLocation.Latitude = 1.0<measure>
|
||||||
Longitude = 3.0
|
Longitude = 3.0
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -96,7 +96,7 @@ module PureGymDtos =
|
|||||||
Location =
|
Location =
|
||||||
{
|
{
|
||||||
Longitude = -0.110252
|
Longitude = -0.110252
|
||||||
Latitude = 51.480401
|
Latitude = 51.480401<measure>
|
||||||
}
|
}
|
||||||
TimeZone = "Europe/London"
|
TimeZone = "Europe/London"
|
||||||
ReopenDate = "2021-04-12T00:00:00+01 Europe/London"
|
ReopenDate = "2021-04-12T00:00:00+01 Europe/London"
|
||||||
|
|||||||
706
WoofWare.Myriad.Plugins.Test/TestArgParser/TestArgParser.fs
Normal file
706
WoofWare.Myriad.Plugins.Test/TestArgParser/TestArgParser.fs
Normal file
@@ -0,0 +1,706 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
open ConsumePlugin
|
||||||
|
open FsCheck
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestArgParser =
|
||||||
|
|
||||||
|
[<TestCase true>]
|
||||||
|
[<TestCase false>]
|
||||||
|
let ``Positionals get parsed: they don't have to be strings`` (sep : bool) =
|
||||||
|
let getEnvVar (_ : string) = failwith "should not call"
|
||||||
|
|
||||||
|
let property
|
||||||
|
(fooSep : bool)
|
||||||
|
(barSep : bool)
|
||||||
|
(bazSep : bool)
|
||||||
|
(pos0 : int list)
|
||||||
|
(pos1 : int list)
|
||||||
|
(pos2 : int list)
|
||||||
|
(pos3 : int list)
|
||||||
|
(pos4 : int list)
|
||||||
|
=
|
||||||
|
let args =
|
||||||
|
[
|
||||||
|
yield! pos0 |> List.map string<int>
|
||||||
|
if fooSep then
|
||||||
|
yield "--foo=3"
|
||||||
|
else
|
||||||
|
yield "--foo"
|
||||||
|
yield "3"
|
||||||
|
yield! pos1 |> List.map string<int>
|
||||||
|
if barSep then
|
||||||
|
yield "--bar=4"
|
||||||
|
else
|
||||||
|
yield "--bar"
|
||||||
|
yield "4"
|
||||||
|
yield! pos2 |> List.map string<int>
|
||||||
|
if bazSep then
|
||||||
|
yield "--baz=true"
|
||||||
|
else
|
||||||
|
yield "--baz"
|
||||||
|
yield "true"
|
||||||
|
yield! pos3 |> List.map string<int>
|
||||||
|
if sep then
|
||||||
|
yield "--"
|
||||||
|
yield! pos4 |> List.map string<int>
|
||||||
|
]
|
||||||
|
|
||||||
|
BasicWithIntPositionals.parse' getEnvVar args
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Foo = 3
|
||||||
|
Bar = "4"
|
||||||
|
Baz = true
|
||||||
|
Rest = pos0 @ pos1 @ pos2 @ pos3 @ pos4
|
||||||
|
}
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Arg-like thing appearing before double dash`` () =
|
||||||
|
let envCalls = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment envCalls |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let args = [ "--foo=3" ; "--non-existent" ; "--bar=4" ; "--baz=true" ]
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar args |> ignore<Basic>)
|
||||||
|
|
||||||
|
envCalls.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Unable to process supplied arg --non-existent. Help text follows.
|
||||||
|
--foo int32 : This is a foo!
|
||||||
|
--bar string
|
||||||
|
--baz bool
|
||||||
|
--rest string (positional args) (can be repeated) : Here's where the rest of the args go"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can supply positional args with key`` () =
|
||||||
|
let envCalls = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment envCalls |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let property (args : (int * bool) list) (afterDoubleDash : int list option) =
|
||||||
|
let flatArgs =
|
||||||
|
args
|
||||||
|
|> List.collect (fun (value, sep) ->
|
||||||
|
if sep then
|
||||||
|
[ $"--rest=%i{value}" ]
|
||||||
|
else
|
||||||
|
[ "--rest" ; string<int> value ]
|
||||||
|
)
|
||||||
|
|> fun l -> l @ [ "--foo=3" ; "--bar=4" ; "--baz=true" ]
|
||||||
|
|
||||||
|
let flatArgs, expected =
|
||||||
|
match afterDoubleDash with
|
||||||
|
| None -> flatArgs, List.map fst args
|
||||||
|
| Some rest -> flatArgs @ [ "--" ] @ (List.map string<int> rest), List.map fst args @ rest
|
||||||
|
|
||||||
|
BasicWithIntPositionals.parse' getEnvVar flatArgs
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Foo = 3
|
||||||
|
Bar = "4"
|
||||||
|
Baz = true
|
||||||
|
Rest = expected
|
||||||
|
}
|
||||||
|
|
||||||
|
Check.QuickThrowOnFailure property
|
||||||
|
envCalls.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Consume multiple occurrences of required arg`` () =
|
||||||
|
let envCalls = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment envCalls |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let args = [ "--foo=3" ; "--rest" ; "7" ; "--bar=4" ; "--baz=true" ; "--rest=8" ]
|
||||||
|
|
||||||
|
let result = BasicNoPositionals.parse' getEnvVar args
|
||||||
|
|
||||||
|
envCalls.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
result
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Foo = 3
|
||||||
|
Bar = "4"
|
||||||
|
Baz = true
|
||||||
|
Rest = [ 7 ; 8 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Gracefully handle invalid multiple occurrences of required arg`` () =
|
||||||
|
let envCalls = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment envCalls |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let args = [ "--foo=3" ; "--foo" ; "9" ; "--bar=4" ; "--baz=true" ; "--baz=false" ]
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar args |> ignore<Basic>)
|
||||||
|
|
||||||
|
envCalls.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Errors during parse!
|
||||||
|
Argument '--foo' was supplied multiple times: 3 and 9
|
||||||
|
Argument '--baz' was supplied multiple times: True and false"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Args appearing after double dash are positional`` () =
|
||||||
|
let envCalls = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment envCalls |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let args = [ "--" ; "--foo=3" ; "--bar=4" ; "--baz=true" ]
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar args |> ignore<Basic>)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Errors during parse!
|
||||||
|
Required argument '--foo' received no value
|
||||||
|
Required argument '--bar' received no value
|
||||||
|
Required argument '--baz' received no value"""
|
||||||
|
|
||||||
|
envCalls.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Help text`` () =
|
||||||
|
let getEnvVar (s : string) =
|
||||||
|
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
|
||||||
|
"hi!"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar [ "--help" ] |> ignore<Basic>)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Help text requested.
|
||||||
|
--foo int32 : This is a foo!
|
||||||
|
--bar string
|
||||||
|
--baz bool
|
||||||
|
--rest string (positional args) (can be repeated) : Here's where the rest of the args go"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Help text, with default values`` () =
|
||||||
|
let envVars = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment envVars |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> LoadsOfTypes.parse' getEnvVar [ "--help" ] |> ignore<LoadsOfTypes>)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Help text requested.
|
||||||
|
--foo int32
|
||||||
|
--bar string
|
||||||
|
--baz bool
|
||||||
|
--some-file FileInfo
|
||||||
|
--some-directory DirectoryInfo
|
||||||
|
--some-list DirectoryInfo (can be repeated)
|
||||||
|
--optional-thing-with-no-default int32 (optional)
|
||||||
|
--optional-thing bool (default value: True)
|
||||||
|
--another-optional-thing int32 (default value: 3)
|
||||||
|
--yet-another-optional-thing string (default value populated from env var CONSUMEPLUGIN_THINGS)
|
||||||
|
--positionals int32 (positional args) (can be repeated)"""
|
||||||
|
|
||||||
|
envVars.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Default values`` () =
|
||||||
|
let getEnvVar (s : string) =
|
||||||
|
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
|
||||||
|
"hi!"
|
||||||
|
|
||||||
|
let args =
|
||||||
|
[
|
||||||
|
"--foo"
|
||||||
|
"3"
|
||||||
|
"--bar=some string"
|
||||||
|
"--baz"
|
||||||
|
"--some-file=/path/to/file"
|
||||||
|
"--some-directory"
|
||||||
|
"/a/dir"
|
||||||
|
"--another-optional-thing"
|
||||||
|
"3000"
|
||||||
|
]
|
||||||
|
|
||||||
|
let result = LoadsOfTypes.parse' getEnvVar args
|
||||||
|
|
||||||
|
result.OptionalThing |> shouldEqual (Choice2Of2 true)
|
||||||
|
result.OptionalThingWithNoDefault |> shouldEqual None
|
||||||
|
result.AnotherOptionalThing |> shouldEqual (Choice1Of2 3000)
|
||||||
|
result.YetAnotherOptionalThing |> shouldEqual (Choice2Of2 "hi!")
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``ParseExact and help`` () =
|
||||||
|
let count = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment count |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> DatesAndTimes.parse' getEnvVar [ "--help" ] |> ignore<DatesAndTimes>)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
@"Help text requested.
|
||||||
|
--plain TimeSpan
|
||||||
|
--invariant TimeSpan
|
||||||
|
--exact TimeSpan : An exact time please [Parse format (.NET): hh\:mm\:ss]
|
||||||
|
--invariant-exact TimeSpan : [Parse format (.NET): hh\:mm\:ss]"
|
||||||
|
|
||||||
|
count.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let rec ``TimeSpans and their attributes`` () =
|
||||||
|
let count = ref 0
|
||||||
|
|
||||||
|
let getEnvVar (_ : string) =
|
||||||
|
Interlocked.Increment count |> ignore<int>
|
||||||
|
""
|
||||||
|
|
||||||
|
let parsed =
|
||||||
|
DatesAndTimes.parse'
|
||||||
|
getEnvVar
|
||||||
|
[
|
||||||
|
"--exact=11:34:00"
|
||||||
|
"--plain=1"
|
||||||
|
"--invariant=23:59"
|
||||||
|
"--invariant-exact=23:59:00"
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed.Plain |> shouldEqual (TimeSpan (1, 0, 0, 0))
|
||||||
|
parsed.Invariant |> shouldEqual (TimeSpan (23, 59, 00))
|
||||||
|
parsed.Exact |> shouldEqual (TimeSpan (11, 34, 00))
|
||||||
|
parsed.InvariantExact |> shouldEqual (TimeSpan (23, 59, 00))
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
DatesAndTimes.parse'
|
||||||
|
getEnvVar
|
||||||
|
[
|
||||||
|
"--exact=11:34:00"
|
||||||
|
"--plain=1"
|
||||||
|
"--invariant=23:59"
|
||||||
|
"--invariant-exact=23:59"
|
||||||
|
]
|
||||||
|
|> ignore<DatesAndTimes>
|
||||||
|
)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Errors during parse!
|
||||||
|
Input string was not in a correct format. (at arg --invariant-exact=23:59)
|
||||||
|
Required argument '--invariant-exact' received no value"""
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
DatesAndTimes.parse'
|
||||||
|
getEnvVar
|
||||||
|
[
|
||||||
|
"--exact=11:34"
|
||||||
|
"--plain=1"
|
||||||
|
"--invariant=23:59"
|
||||||
|
"--invariant-exact=23:59:00"
|
||||||
|
]
|
||||||
|
|> ignore<DatesAndTimes>
|
||||||
|
)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Errors during parse!
|
||||||
|
Input string was not in a correct format. (at arg --exact=11:34)
|
||||||
|
Required argument '--exact' received no value"""
|
||||||
|
|
||||||
|
count.Value |> shouldEqual 0
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can consume stacked record without positionals`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "should not call"
|
||||||
|
|
||||||
|
let parsed =
|
||||||
|
ParentRecord.parse' getEnvVar [ "--and-another=true" ; "--thing1=9" ; "--thing2=a thing!" ]
|
||||||
|
|
||||||
|
parsed
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Child =
|
||||||
|
{
|
||||||
|
Thing1 = 9
|
||||||
|
Thing2 = "a thing!"
|
||||||
|
}
|
||||||
|
AndAnother = true
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can consume stacked record, child has positionals`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "should not call"
|
||||||
|
|
||||||
|
let parsed =
|
||||||
|
ParentRecordChildPos.parse'
|
||||||
|
getEnvVar
|
||||||
|
[
|
||||||
|
"--and-another=true"
|
||||||
|
"--thing1=9"
|
||||||
|
"--thing2=https://example.com"
|
||||||
|
"--thing2=http://example.com"
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed.AndAnother |> shouldEqual true
|
||||||
|
parsed.Child.Thing1 |> shouldEqual 9
|
||||||
|
|
||||||
|
parsed.Child.Thing2
|
||||||
|
|> List.map (fun (x : Uri) -> x.ToString ())
|
||||||
|
|> shouldEqual [ "https://example.com/" ; "http://example.com/" ]
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can consume stacked record, child has no positionals, parent has positionals`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "should not call"
|
||||||
|
|
||||||
|
let parsed =
|
||||||
|
ParentRecordSelfPos.parse'
|
||||||
|
getEnvVar
|
||||||
|
[
|
||||||
|
"--and-another=true"
|
||||||
|
"--and-another=false"
|
||||||
|
"--and-another=true"
|
||||||
|
"--thing1=9"
|
||||||
|
"--thing2=some"
|
||||||
|
]
|
||||||
|
|
||||||
|
parsed
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Child =
|
||||||
|
{
|
||||||
|
Thing1 = 9
|
||||||
|
Thing2 = "some"
|
||||||
|
}
|
||||||
|
AndAnother = [ true ; false ; true ]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Help text for stacked records`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "should not call"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
ParentRecordSelfPos.parse' getEnvVar [ "--help" ] |> ignore<ParentRecordSelfPos>
|
||||||
|
)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Help text requested.
|
||||||
|
--thing1 int32
|
||||||
|
--thing2 string
|
||||||
|
--and-another bool (positional args) (can be repeated)"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Positionals are tagged with Choice`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "should not call"
|
||||||
|
|
||||||
|
ChoicePositionals.parse' getEnvVar [ "a" ; "b" ; "--" ; "--c" ; "--help" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Args = [ Choice1Of2 "a" ; Choice1Of2 "b" ; Choice2Of2 "--c" ; Choice2Of2 "--help" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
let boolCases =
|
||||||
|
[
|
||||||
|
"1", true
|
||||||
|
"0", false
|
||||||
|
"true", true
|
||||||
|
"false", false
|
||||||
|
"TRUE", true
|
||||||
|
"FALSE", false
|
||||||
|
]
|
||||||
|
|> List.map TestCaseData
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof (boolCases))>]
|
||||||
|
let ``Bool env vars can be populated`` (envValue : string, boolValue : bool) =
|
||||||
|
let getEnvVar (s : string) =
|
||||||
|
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
|
||||||
|
envValue
|
||||||
|
|
||||||
|
ContainsBoolEnvVar.parse' getEnvVar []
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
BoolVar = Choice2Of2 boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Bools can be treated with arity 0`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
ContainsBoolEnvVar.parse' getEnvVar [ "--bool-var" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
BoolVar = Choice1Of2 true
|
||||||
|
}
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof boolCases)>]
|
||||||
|
let ``Flag DUs can be parsed from env var`` (envValue : string, boolValue : bool) =
|
||||||
|
let getEnvVar (s : string) =
|
||||||
|
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
|
||||||
|
envValue
|
||||||
|
|
||||||
|
let boolValue = if boolValue then DryRunMode.Dry else DryRunMode.Wet
|
||||||
|
|
||||||
|
ContainsFlagEnvVar.parse' getEnvVar []
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
DryRun = Choice2Of2 boolValue
|
||||||
|
}
|
||||||
|
|
||||||
|
let dryRunData =
|
||||||
|
[
|
||||||
|
[ "--dry-run" ], DryRunMode.Dry
|
||||||
|
[ "--dry-run" ; "true" ], DryRunMode.Dry
|
||||||
|
[ "--dry-run=true" ], DryRunMode.Dry
|
||||||
|
[ "--dry-run" ; "True" ], DryRunMode.Dry
|
||||||
|
[ "--dry-run=True" ], DryRunMode.Dry
|
||||||
|
[ "--dry-run" ; "false" ], DryRunMode.Wet
|
||||||
|
[ "--dry-run=false" ], DryRunMode.Wet
|
||||||
|
[ "--dry-run" ; "False" ], DryRunMode.Wet
|
||||||
|
[ "--dry-run=False" ], DryRunMode.Wet
|
||||||
|
]
|
||||||
|
|> List.map TestCaseData
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof dryRunData)>]
|
||||||
|
let ``Flag DUs can be parsed`` (args : string list, expected : DryRunMode) =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
ContainsFlagEnvVar.parse' getEnvVar args
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
DryRun = Choice1Of2 expected
|
||||||
|
}
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof dryRunData)>]
|
||||||
|
let ``Flag DUs can be parsed, ArgumentDefaultFunction`` (args : string list, expected : DryRunMode) =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
ContainsFlagDefaultValue.parse' getEnvVar args
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
DryRun = Choice1Of2 expected
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Flag DUs can be given a default value`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
ContainsFlagDefaultValue.parse' getEnvVar []
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
DryRun = Choice2Of2 DryRunMode.Wet
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Help text for flag DU`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
ContainsFlagDefaultValue.parse' getEnvVar [ "--help" ]
|
||||||
|
|> ignore<ContainsFlagDefaultValue>
|
||||||
|
)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Help text requested.
|
||||||
|
--dry-run bool (default value: false)"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Help text for flag DU, non default`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> WithFlagDu.parse' getEnvVar [ "--help" ] |> ignore<WithFlagDu>)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Help text requested.
|
||||||
|
--dry-run bool"""
|
||||||
|
|
||||||
|
let longFormCases =
|
||||||
|
let doTheThing =
|
||||||
|
[
|
||||||
|
[ "--do-something-else=foo" ]
|
||||||
|
[ "--anotherarg=foo" ]
|
||||||
|
[ "--do-something-else" ; "foo" ]
|
||||||
|
[ "--anotherarg" ; "foo" ]
|
||||||
|
]
|
||||||
|
|
||||||
|
let someFlag =
|
||||||
|
[
|
||||||
|
[ "--turn-it-on" ], true
|
||||||
|
[ "--dont-turn-it-off" ], true
|
||||||
|
[ "--turn-it-on=true" ], true
|
||||||
|
[ "--dont-turn-it-off=true" ], true
|
||||||
|
[ "--turn-it-on=false" ], false
|
||||||
|
[ "--dont-turn-it-off=false" ], false
|
||||||
|
[ "--turn-it-on" ; "true" ], true
|
||||||
|
[ "--dont-turn-it-off" ; "true" ], true
|
||||||
|
[ "--turn-it-on" ; "false" ], false
|
||||||
|
[ "--dont-turn-it-off" ; "false" ], false
|
||||||
|
]
|
||||||
|
|
||||||
|
List.allPairs doTheThing someFlag
|
||||||
|
|> List.map (fun (doTheThing, (someFlag, someFlagResult)) ->
|
||||||
|
let args = doTheThing @ someFlag
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
{
|
||||||
|
DoTheThing = "foo"
|
||||||
|
SomeFlag = someFlagResult
|
||||||
|
}
|
||||||
|
|
||||||
|
args, expected
|
||||||
|
)
|
||||||
|
|> List.map TestCaseData
|
||||||
|
|
||||||
|
[<TestCaseSource(nameof longFormCases)>]
|
||||||
|
let ``Long-form args`` (args : string list, expected : ManyLongForms) =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
ManyLongForms.parse' getEnvVar args |> shouldEqual expected
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Long-form args can't be referred to by their original name`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
ManyLongForms.parse' getEnvVar [ "--do-the-thing=foo" ] |> ignore<ManyLongForms>
|
||||||
|
)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual """Unable to process argument --do-the-thing=foo as key --do-the-thing and value foo"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Long-form args help text`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () -> ManyLongForms.parse' getEnvVar [ "--help" ] |> ignore<ManyLongForms>)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"""Help text requested.
|
||||||
|
--do-something-else / --anotherarg string
|
||||||
|
--turn-it-on / --dont-turn-it-off bool"""
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can collect *all* non-help args into positional args with includeFlagLike`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
FlagsIntoPositionalArgs.parse' getEnvVar [ "--a" ; "foo" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
A = "foo"
|
||||||
|
GrabEverything = [ "--b=false" ; "--c" ; "hi" ; "--help" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Users might consider this eccentric!
|
||||||
|
// But we're only a simple arg parser; we don't look around to see whether this is "almost"
|
||||||
|
// a valid parse.
|
||||||
|
FlagsIntoPositionalArgs.parse' getEnvVar [ "--a" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
A = "--b=false"
|
||||||
|
GrabEverything = [ "--c" ; "hi" ; "--help" ]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can collect non-help args into positional args with Choice`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
FlagsIntoPositionalArgsChoice.parse' getEnvVar [ "--a" ; "foo" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
A = "foo"
|
||||||
|
GrabEverything =
|
||||||
|
[
|
||||||
|
Choice1Of2 "--b=false"
|
||||||
|
Choice1Of2 "--c"
|
||||||
|
Choice1Of2 "hi"
|
||||||
|
Choice2Of2 "--help"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can collect non-help args into positional args, and we parse on the way`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
FlagsIntoPositionalArgsInt.parse' getEnvVar [ "3" ; "--a" ; "foo" ; "5" ; "--" ; "98" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
A = "foo"
|
||||||
|
GrabEverything = [ 3 ; 5 ; 98 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can collect non-help args into positional args with Choice, and we parse on the way`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
FlagsIntoPositionalArgsIntChoice.parse' getEnvVar [ "3" ; "--a" ; "foo" ; "5" ; "--" ; "98" ]
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
A = "foo"
|
||||||
|
GrabEverything = [ Choice1Of2 3 ; Choice1Of2 5 ; Choice2Of2 98 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can refuse to collect non-help args with PositionalArgs false`` () =
|
||||||
|
let getEnvVar (_ : string) = failwith "do not call"
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
FlagsIntoPositionalArgs'.parse'
|
||||||
|
getEnvVar
|
||||||
|
[ "--a" ; "foo" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|
||||||
|
|> ignore<FlagsIntoPositionalArgs'>
|
||||||
|
)
|
||||||
|
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual """Unable to process argument --b=false as key --b and value false"""
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<exn> (fun () ->
|
||||||
|
FlagsIntoPositionalArgs'.parse' getEnvVar [ "--a" ; "--b=false" ; "--c=hi" ; "--" ; "--help" ]
|
||||||
|
|> ignore<FlagsIntoPositionalArgs'>
|
||||||
|
)
|
||||||
|
|
||||||
|
// Again perhaps eccentric!
|
||||||
|
// Again, we don't try to detect that the user has missed out the desired argument to `--a`.
|
||||||
|
exc.Message
|
||||||
|
|> shouldEqual """Unable to process argument --c=hi as key --c and value hi"""
|
||||||
@@ -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 %O{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
|
||||||
@@ -9,18 +9,18 @@ open FsUnitTyped
|
|||||||
|
|
||||||
[<TestFixture>]
|
[<TestFixture>]
|
||||||
module TestBasePath =
|
module TestBasePath =
|
||||||
|
let replyWithUrl (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
|
let content = new StringContent (message.RequestUri.ToString ())
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Base address is respected`` () =
|
let ``Base address is respected`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
async {
|
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
|
||||||
let content = new StringContent (message.RequestUri.ToString ())
|
|
||||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
|
||||||
resp.Content <- content
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
use client = HttpClientMock.makeNoUri proc
|
|
||||||
let api = PureGymApi.make client
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
let observedUri = api.GetPathParam("param").Result
|
let observedUri = api.GetPathParam("param").Result
|
||||||
@@ -28,38 +28,28 @@ module TestBasePath =
|
|||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Without a base address attr but with BaseAddress on client, request goes through`` () =
|
let ``Without a base address attr but with BaseAddress on client, request goes through`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
use client = HttpClientMock.make (Uri "https://baseaddress.com") replyWithUrl
|
||||||
async {
|
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
|
||||||
let content = new StringContent (message.RequestUri.ToString ())
|
|
||||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
|
||||||
resp.Content <- content
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
use client = HttpClientMock.make (System.Uri "https://baseaddress.com") proc
|
|
||||||
let api = ApiWithoutBaseAddress.make client
|
let api = ApiWithoutBaseAddress.make client
|
||||||
|
|
||||||
let observedUri = api.GetPathParam("param").Result
|
let observedUri = api.GetPathParam("param").Result
|
||||||
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Without a base address attr or BaseAddress on client, request throws`` () =
|
let ``Base address on client takes precedence`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
use client = HttpClientMock.make (Uri "https://baseaddress.com") replyWithUrl
|
||||||
async {
|
let api = PureGymApi.make client
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
|
||||||
let content = new StringContent (message.RequestUri.ToString ())
|
|
||||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
|
||||||
resp.Content <- content
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
use client = HttpClientMock.makeNoUri proc
|
let observedUri = api.GetPathParam("param").Result
|
||||||
|
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Without a base address attr or BaseAddress on client, request throws`` () =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
let api = ApiWithoutBaseAddress.make client
|
let api = ApiWithoutBaseAddress.make client
|
||||||
|
|
||||||
let observedExc =
|
let observedExc =
|
||||||
async {
|
async {
|
||||||
let! result = api.GetPathParam ("param") |> Async.AwaitTask |> Async.Catch
|
let! result = api.GetPathParam "param" |> Async.AwaitTask |> Async.Catch
|
||||||
|
|
||||||
match result with
|
match result with
|
||||||
| Choice1Of2 _ -> return failwith "test failure"
|
| Choice1Of2 _ -> return failwith "test failure"
|
||||||
@@ -78,3 +68,103 @@ module TestBasePath =
|
|||||||
observedExc.Message
|
observedExc.Message
|
||||||
|> shouldEqual
|
|> shouldEqual
|
||||||
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, no base address, relative attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePath.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithBasePath.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/thing/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, base address, relative attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAddress.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/thing/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, no base address, relative attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePath.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePath.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, base address, relative attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAddress.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, no base address, absolute attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAbsoluteEndpoint.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, base address, absolute attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAddressAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, no base address, absolute attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAbsoluteEndpoint.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, base address, absolute attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|||||||
@@ -33,4 +33,4 @@ module TestPathParam =
|
|||||||
let api = PureGymApi.make client
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
api.GetPathParam("hello/world?(hi)").Result
|
api.GetPathParam("hello/world?(hi)").Result
|
||||||
|> shouldEqual "hello%2fworld%3f(hi)"
|
|> shouldEqual "hello%2Fworld%3F%28hi%29"
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -234,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 =
|
||||||
@@ -257,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
|
||||||
|
|||||||
@@ -0,0 +1,126 @@
|
|||||||
|
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"
|
||||||
|
"Something-Else: val"
|
||||||
|
"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"
|
||||||
|
"Something-Else: val"
|
||||||
|
"X-Foo: 11"
|
||||||
|
|]
|
||||||
|
|
||||||
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|
|> Array.sort
|
||||||
|
|> shouldEqual
|
||||||
|
[|
|
||||||
|
"Authorization: -98"
|
||||||
|
"Header-Name: Header-Value"
|
||||||
|
"Something-Else: val"
|
||||||
|
"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
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
open FsUnitTyped
|
||||||
|
open NUnit.Framework
|
||||||
|
open ConsumePlugin
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestJsonNullability =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can consume JsonParseNullness`` () =
|
||||||
|
let options = JsonNodeOptions (PropertyNameCaseInsensitive = true)
|
||||||
|
|
||||||
|
"""{
|
||||||
|
"b": null
|
||||||
|
}"""
|
||||||
|
|> fun s -> JsonNode.Parse (s, options)
|
||||||
|
|> ArrayOfInnerStruct.jsonParse
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
B = null
|
||||||
|
}
|
||||||
@@ -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 =
|
||||||
@@ -47,3 +49,15 @@ module TestJsonParse =
|
|||||||
|
|
||||||
let actual = s |> JsonNode.Parse |> InnerType.jsonParse
|
let actual = s |> JsonNode.Parse |> InnerType.jsonParse
|
||||||
actual |> shouldEqual expected
|
actual |> shouldEqual expected
|
||||||
|
|
||||||
|
[<TestCase("thing", SomeEnum.Thing)>]
|
||||||
|
[<TestCase("Thing", SomeEnum.Thing)>]
|
||||||
|
[<TestCase("THING", SomeEnum.Thing)>]
|
||||||
|
[<TestCase("blah", SomeEnum.Blah)>]
|
||||||
|
[<TestCase("Blah", SomeEnum.Blah)>]
|
||||||
|
[<TestCase("BLAH", SomeEnum.Blah)>]
|
||||||
|
let ``Can deserialise enum`` (str : string, expected : SomeEnum) =
|
||||||
|
sprintf "\"%s\"" str
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|> SomeEnum.jsonParse
|
||||||
|
|> shouldEqual expected
|
||||||
|
|||||||
@@ -3,6 +3,8 @@ namespace WoofWare.Myriad.Plugins.Test
|
|||||||
open System
|
open System
|
||||||
open System.Collections.Generic
|
open System.Collections.Generic
|
||||||
open System.Text.Json.Nodes
|
open System.Text.Json.Nodes
|
||||||
|
open FsCheck.FSharp
|
||||||
|
open Microsoft.FSharp.Reflection
|
||||||
open NUnit.Framework
|
open NUnit.Framework
|
||||||
open FsCheck
|
open FsCheck
|
||||||
open FsUnitTyped
|
open FsUnitTyped
|
||||||
@@ -13,21 +15,21 @@ module TestJsonSerde =
|
|||||||
|
|
||||||
let uriGen : Gen<Uri> =
|
let uriGen : Gen<Uri> =
|
||||||
gen {
|
gen {
|
||||||
let! suffix = Arb.generate<int>
|
let! suffix = ArbMap.generate<int> ArbMap.defaults
|
||||||
return Uri $"https://example.com/%i{suffix}"
|
return Uri $"https://example.com/%i{suffix}"
|
||||||
}
|
}
|
||||||
|
|
||||||
let rec innerGen (count : int) : Gen<InnerTypeWithBoth> =
|
let rec innerGen (count : int) : Gen<InnerTypeWithBoth> =
|
||||||
gen {
|
gen {
|
||||||
let! s = Arb.generate<NonNull<string>>
|
let! guid = ArbMap.generate<Guid> ArbMap.defaults
|
||||||
let! mapKeys = Gen.listOf Arb.generate<NonNull<string>>
|
let! mapKeys = Gen.listOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
|
||||||
let mapKeys = mapKeys |> List.map _.Get |> List.distinct
|
let mapKeys = mapKeys |> List.map _.Get |> List.distinct
|
||||||
let! mapValues = Gen.listOfLength mapKeys.Length uriGen
|
let! mapValues = Gen.listOfLength mapKeys.Length uriGen
|
||||||
let map = List.zip mapKeys mapValues |> Map.ofList
|
let map = List.zip mapKeys mapValues |> Map.ofList
|
||||||
|
|
||||||
let! concreteDictKeys =
|
let! concreteDictKeys =
|
||||||
if count > 0 then
|
if count > 0 then
|
||||||
Gen.listOf Arb.generate<NonNull<string>>
|
Gen.listOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
|
||||||
else
|
else
|
||||||
Gen.constant []
|
Gen.constant []
|
||||||
|
|
||||||
@@ -48,18 +50,21 @@ module TestJsonSerde =
|
|||||||
|> List.map KeyValuePair
|
|> List.map KeyValuePair
|
||||||
|> Dictionary
|
|> Dictionary
|
||||||
|
|
||||||
let! readOnlyDictKeys = Gen.listOf Arb.generate<NonNull<string>>
|
let! readOnlyDictKeys = Gen.listOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
|
||||||
let readOnlyDictKeys = readOnlyDictKeys |> List.map _.Get |> List.distinct
|
let readOnlyDictKeys = readOnlyDictKeys |> List.map _.Get |> List.distinct
|
||||||
let! readOnlyDictValues = Gen.listOfLength readOnlyDictKeys.Length (Gen.listOf Arb.generate<char>)
|
|
||||||
|
let! readOnlyDictValues =
|
||||||
|
Gen.listOfLength readOnlyDictKeys.Length (Gen.listOf (ArbMap.generate<char> ArbMap.defaults))
|
||||||
|
|
||||||
let readOnlyDict = List.zip readOnlyDictKeys readOnlyDictValues |> readOnlyDict
|
let readOnlyDict = List.zip readOnlyDictKeys readOnlyDictValues |> readOnlyDict
|
||||||
|
|
||||||
let! dictKeys = Gen.listOf uriGen
|
let! dictKeys = Gen.listOf uriGen
|
||||||
let! dictValues = Gen.listOfLength dictKeys.Length Arb.generate<bool>
|
let! dictValues = Gen.listOfLength dictKeys.Length (ArbMap.generate<bool> ArbMap.defaults)
|
||||||
let dict = List.zip dictKeys dictValues |> dict
|
let dict = List.zip dictKeys dictValues |> dict
|
||||||
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
Thing = s.Get
|
Thing = guid
|
||||||
Map = map
|
Map = map
|
||||||
ReadOnlyDict = readOnlyDict
|
ReadOnlyDict = readOnlyDict
|
||||||
Dict = dict
|
Dict = dict
|
||||||
@@ -69,13 +74,38 @@ module TestJsonSerde =
|
|||||||
|
|
||||||
let outerGen : Gen<JsonRecordTypeWithBoth> =
|
let outerGen : Gen<JsonRecordTypeWithBoth> =
|
||||||
gen {
|
gen {
|
||||||
let! a = Arb.generate<int>
|
let! a = ArbMap.generate<int> ArbMap.defaults
|
||||||
let! b = Arb.generate<NonNull<string>>
|
let! b = ArbMap.generate<NonNull<string>> ArbMap.defaults
|
||||||
let! c = Gen.listOf Arb.generate<int>
|
let! c = Gen.listOf (ArbMap.generate<int> ArbMap.defaults)
|
||||||
let! depth = Gen.choose (0, 2)
|
let! depth = Gen.choose (0, 2)
|
||||||
let! d = innerGen depth
|
let! d = innerGen depth
|
||||||
let! e = Gen.arrayOf Arb.generate<NonNull<string>>
|
let! e = Gen.arrayOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
|
||||||
let! f = Gen.arrayOf Arb.generate<int>
|
let! arr = Gen.arrayOf (ArbMap.generate<int> ArbMap.defaults)
|
||||||
|
let! byte = ArbMap.generate ArbMap.defaults
|
||||||
|
let! sbyte = ArbMap.generate ArbMap.defaults
|
||||||
|
let! i = ArbMap.generate ArbMap.defaults
|
||||||
|
let! i32 = ArbMap.generate ArbMap.defaults
|
||||||
|
let! i64 = ArbMap.generate ArbMap.defaults
|
||||||
|
let! u = ArbMap.generate ArbMap.defaults
|
||||||
|
let! u32 = ArbMap.generate ArbMap.defaults
|
||||||
|
let! u64 = ArbMap.generate ArbMap.defaults
|
||||||
|
|
||||||
|
let! f =
|
||||||
|
ArbMap.generate ArbMap.defaults
|
||||||
|
|> Gen.filter (fun s -> Double.IsFinite (s / 1.0<measure>))
|
||||||
|
|
||||||
|
let! f32 =
|
||||||
|
ArbMap.generate ArbMap.defaults
|
||||||
|
|> Gen.filter (fun s -> Single.IsFinite (s / 1.0f<measure>))
|
||||||
|
|
||||||
|
let! single =
|
||||||
|
ArbMap.generate ArbMap.defaults
|
||||||
|
|> Gen.filter (fun s -> Single.IsFinite (s / 1.0f<measure>))
|
||||||
|
|
||||||
|
let! intMeasureOption = ArbMap.generate ArbMap.defaults
|
||||||
|
let! intMeasureNullable = ArbMap.generate ArbMap.defaults
|
||||||
|
let! someEnum = Gen.choose (0, 1)
|
||||||
|
let! timestamp = ArbMap.generate ArbMap.defaults
|
||||||
|
|
||||||
return
|
return
|
||||||
{
|
{
|
||||||
@@ -84,7 +114,23 @@ module TestJsonSerde =
|
|||||||
C = c
|
C = c
|
||||||
D = d
|
D = d
|
||||||
E = e |> Array.map _.Get
|
E = e |> Array.map _.Get
|
||||||
|
Arr = arr
|
||||||
|
Byte = byte
|
||||||
|
Sbyte = sbyte
|
||||||
|
I = i
|
||||||
|
I32 = i32
|
||||||
|
I64 = i64
|
||||||
|
U = u
|
||||||
|
U32 = u32
|
||||||
|
U64 = u64
|
||||||
F = f
|
F = f
|
||||||
|
F32 = f32
|
||||||
|
Single = single
|
||||||
|
IntMeasureOption = intMeasureOption
|
||||||
|
IntMeasureNullable = intMeasureNullable
|
||||||
|
Enum = enum<SomeEnum> someEnum
|
||||||
|
Timestamp = timestamp
|
||||||
|
Unit = ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -101,3 +147,342 @@ module TestJsonSerde =
|
|||||||
true
|
true
|
||||||
|
|
||||||
property |> Prop.forAll (Arb.fromGen outerGen) |> Check.QuickThrowOnFailure
|
property |> Prop.forAll (Arb.fromGen outerGen) |> Check.QuickThrowOnFailure
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Single example of big record`` () =
|
||||||
|
let guid = Guid.Parse "dfe24db5-9f8d-447b-8463-4c0bcf1166d5"
|
||||||
|
|
||||||
|
let data =
|
||||||
|
{
|
||||||
|
A = 3
|
||||||
|
B = "hello!"
|
||||||
|
C = [ 1 ; -9 ]
|
||||||
|
D =
|
||||||
|
{
|
||||||
|
Thing = guid
|
||||||
|
Map = Map.ofList []
|
||||||
|
ReadOnlyDict = readOnlyDict []
|
||||||
|
Dict = dict []
|
||||||
|
ConcreteDict = Dictionary ()
|
||||||
|
}
|
||||||
|
E = [| "I'm-a-string" |]
|
||||||
|
Arr = [| -18883 ; 9100 |]
|
||||||
|
Byte = 87uy<measure>
|
||||||
|
Sbyte = 89y<measure>
|
||||||
|
I = 199993345<measure>
|
||||||
|
I32 = -485832<measure>
|
||||||
|
I64 = -13458625689L<measure>
|
||||||
|
U = 458582u<measure>
|
||||||
|
U32 = 857362147u<measure>
|
||||||
|
U64 = 1234567892123414596UL<measure>
|
||||||
|
F = 8833345667.1<measure>
|
||||||
|
F32 = 1000.98f<measure>
|
||||||
|
Single = 0.334f<measure>
|
||||||
|
IntMeasureOption = Some 981<measure>
|
||||||
|
IntMeasureNullable = Nullable -883<measure>
|
||||||
|
Enum = enum<SomeEnum> 1
|
||||||
|
Timestamp = DateTimeOffset (2024, 07, 01, 17, 54, 00, TimeSpan.FromHours 1.0)
|
||||||
|
Unit = ()
|
||||||
|
}
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
"""{
|
||||||
|
"a": 3,
|
||||||
|
"b": "hello!",
|
||||||
|
"c": [1, -9],
|
||||||
|
"d": {
|
||||||
|
"it\u0027s-a-me": "dfe24db5-9f8d-447b-8463-4c0bcf1166d5",
|
||||||
|
"map": {},
|
||||||
|
"readOnlyDict": {},
|
||||||
|
"dict": {},
|
||||||
|
"concreteDict": {}
|
||||||
|
},
|
||||||
|
"e": ["I\u0027m-a-string"],
|
||||||
|
"arr": [-18883, 9100],
|
||||||
|
"byte": 87,
|
||||||
|
"sbyte": 89,
|
||||||
|
"i": 199993345,
|
||||||
|
"i32": -485832,
|
||||||
|
"i64": -13458625689,
|
||||||
|
"u": 458582,
|
||||||
|
"u32": 857362147,
|
||||||
|
"u64": 1234567892123414596,
|
||||||
|
"f": 8833345667.1,
|
||||||
|
"f32": 1000.98,
|
||||||
|
"single": 0.334,
|
||||||
|
"intMeasureOption": 981,
|
||||||
|
"intMeasureNullable": -883,
|
||||||
|
"enum": 1,
|
||||||
|
"timestamp": "2024-07-01T17:54:00.0000000\u002B01:00",
|
||||||
|
"unit": {}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|> fun s -> s.ToCharArray ()
|
||||||
|
|> Array.filter (fun c -> not (Char.IsWhiteSpace c))
|
||||||
|
|> fun s -> new String (s)
|
||||||
|
|
||||||
|
JsonRecordTypeWithBoth.toJsonNode(data).ToJsonString () |> shouldEqual expected
|
||||||
|
JsonRecordTypeWithBoth.jsonParse (JsonNode.Parse expected) |> shouldEqual data
|
||||||
|
|
||||||
|
[<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 =
|
||||||
|
{ r with
|
||||||
|
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
|
||||||
|
Arr =
|
||||||
|
if Object.ReferenceEquals (r.Arr, (null : obj)) then
|
||||||
|
[||]
|
||||||
|
else
|
||||||
|
r.Arr
|
||||||
|
}
|
||||||
|
|
||||||
|
let duGen =
|
||||||
|
gen {
|
||||||
|
let! case = Gen.choose (0, 2)
|
||||||
|
|
||||||
|
match case with
|
||||||
|
| 0 -> return FirstDu.EmptyCase
|
||||||
|
| 1 ->
|
||||||
|
let! s = ArbMap.generate<NonNull<string>> ArbMap.defaults
|
||||||
|
return FirstDu.Case1 s.Get
|
||||||
|
| 2 ->
|
||||||
|
let! i = ArbMap.generate<int> ArbMap.defaults
|
||||||
|
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 cases = FSharpType.GetUnionCases typeof<FirstDu>
|
||||||
|
let counts = Array.zeroCreate<int> cases.Length
|
||||||
|
|
||||||
|
let decompose = FSharpValue.PreComputeUnionTagReader typeof<FirstDu>
|
||||||
|
|
||||||
|
let mutable i = 0
|
||||||
|
|
||||||
|
let property (du : FirstDu) =
|
||||||
|
let tag = decompose du
|
||||||
|
counts.[tag] <- counts.[tag] + 1
|
||||||
|
i <- i + 1
|
||||||
|
true
|
||||||
|
|
||||||
|
Check.One (Config.Quick, Prop.forAll (Arb.fromGen duGen) property)
|
||||||
|
|
||||||
|
for i in counts do
|
||||||
|
i |> shouldBeGreaterThan 0
|
||||||
|
|
||||||
|
let dict<'a, 'b when 'a : equality> (xs : ('a * 'b) seq) : Dictionary<'a, 'b> =
|
||||||
|
let result = Dictionary ()
|
||||||
|
|
||||||
|
for k, v in xs do
|
||||||
|
result.Add (k, v)
|
||||||
|
|
||||||
|
result
|
||||||
|
|
||||||
|
let inline makeJsonArr< ^t, ^u when ^u : (static member op_Implicit : ^t -> JsonNode) and ^u :> JsonNode>
|
||||||
|
(arr : ^t seq)
|
||||||
|
: JsonNode
|
||||||
|
=
|
||||||
|
let result = JsonArray ()
|
||||||
|
|
||||||
|
for a in arr do
|
||||||
|
result.Add a
|
||||||
|
|
||||||
|
result :> JsonNode
|
||||||
|
|
||||||
|
let normalise (d : Dictionary<'a, 'b>) : ('a * 'b) list =
|
||||||
|
d |> Seq.map (fun (KeyValue (a, b)) -> a, b) |> Seq.toList |> List.sortBy fst
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can collect extension data`` () =
|
||||||
|
let str =
|
||||||
|
"""{
|
||||||
|
"message": { "header": "hi", "value": "bye" },
|
||||||
|
"something": 3,
|
||||||
|
"arr": ["egg", "toast"],
|
||||||
|
"str": "whatnot"
|
||||||
|
}"""
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|
||||||
|
let expected =
|
||||||
|
{
|
||||||
|
Rest =
|
||||||
|
[
|
||||||
|
"something", JsonNode.op_Implicit 3
|
||||||
|
"arr", makeJsonArr [| "egg" ; "toast" |]
|
||||||
|
"str", JsonNode.op_Implicit "whatnot"
|
||||||
|
]
|
||||||
|
|> dict
|
||||||
|
Message =
|
||||||
|
Some
|
||||||
|
{
|
||||||
|
Header = "hi"
|
||||||
|
Value = "bye"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual = CollectRemaining.jsonParse str
|
||||||
|
|
||||||
|
actual.Message |> shouldEqual expected.Message
|
||||||
|
|
||||||
|
normalise actual.Rest
|
||||||
|
|> List.map (fun (k, v) -> k, v.ToJsonString ())
|
||||||
|
|> shouldEqual (normalise expected.Rest |> List.map (fun (k, v) -> k, v.ToJsonString ()))
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can write out extension data`` () =
|
||||||
|
let expected =
|
||||||
|
"""{"message":{"header":"hi","value":"bye"},"something":3,"arr":["egg","toast"],"str":"whatnot"}"""
|
||||||
|
|
||||||
|
let toWrite =
|
||||||
|
{
|
||||||
|
Rest =
|
||||||
|
[
|
||||||
|
"something", JsonNode.op_Implicit 3
|
||||||
|
"arr", makeJsonArr [| "egg" ; "toast" |]
|
||||||
|
"str", JsonNode.op_Implicit "whatnot"
|
||||||
|
]
|
||||||
|
|> dict
|
||||||
|
Message =
|
||||||
|
Some
|
||||||
|
{
|
||||||
|
Header = "hi"
|
||||||
|
Value = "bye"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual = CollectRemaining.toJsonNode toWrite |> fun s -> s.ToJsonString ()
|
||||||
|
|
||||||
|
actual |> shouldEqual expected
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can collect extension data, nested`` () =
|
||||||
|
let str =
|
||||||
|
"""{
|
||||||
|
"thing": 99,
|
||||||
|
"baz": -123,
|
||||||
|
"remaining": {
|
||||||
|
"message": { "header": "hi", "value": "bye" },
|
||||||
|
"something": 3,
|
||||||
|
"arr": ["egg", "toast"],
|
||||||
|
"str": "whatnot"
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|
||||||
|
let expected : OuterCollectRemaining =
|
||||||
|
{
|
||||||
|
Remaining =
|
||||||
|
{
|
||||||
|
Message =
|
||||||
|
Some
|
||||||
|
{
|
||||||
|
Header = "hi"
|
||||||
|
Value = "bye"
|
||||||
|
}
|
||||||
|
Rest =
|
||||||
|
[
|
||||||
|
"something", JsonNode.op_Implicit 3
|
||||||
|
"arr", makeJsonArr [| "egg" ; "toast" |]
|
||||||
|
"str", JsonNode.op_Implicit "whatnot"
|
||||||
|
]
|
||||||
|
|> dict
|
||||||
|
}
|
||||||
|
Others = [ "thing", 99 ; "baz", -123 ] |> dict
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual = OuterCollectRemaining.jsonParse str
|
||||||
|
|
||||||
|
normalise actual.Others |> shouldEqual (normalise expected.Others)
|
||||||
|
|
||||||
|
let actual = actual.Remaining
|
||||||
|
let expected = expected.Remaining
|
||||||
|
|
||||||
|
actual.Message |> shouldEqual expected.Message
|
||||||
|
|
||||||
|
normalise actual.Rest
|
||||||
|
|> List.map (fun (k, v) -> k, v.ToJsonString ())
|
||||||
|
|> shouldEqual (normalise expected.Rest |> List.map (fun (k, v) -> k, v.ToJsonString ()))
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Can write out extension data, nested`` () =
|
||||||
|
let expected =
|
||||||
|
"""{"thing":99,"baz":-123,"remaining":{"message":{"header":"hi","value":"bye"},"something":3,"arr":["egg","toast"],"str":"whatnot"}}"""
|
||||||
|
|
||||||
|
let toWrite : OuterCollectRemaining =
|
||||||
|
{
|
||||||
|
Others = [ "thing", 99 ; "baz", -123 ] |> dict
|
||||||
|
Remaining =
|
||||||
|
{
|
||||||
|
Rest =
|
||||||
|
[
|
||||||
|
"something", JsonNode.op_Implicit 3
|
||||||
|
"arr", makeJsonArr [| "egg" ; "toast" |]
|
||||||
|
"str", JsonNode.op_Implicit "whatnot"
|
||||||
|
]
|
||||||
|
|> dict
|
||||||
|
Message =
|
||||||
|
Some
|
||||||
|
{
|
||||||
|
Header = "hi"
|
||||||
|
Value = "bye"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let actual = OuterCollectRemaining.toJsonNode toWrite |> fun s -> s.ToJsonString ()
|
||||||
|
|
||||||
|
actual |> shouldEqual expected
|
||||||
|
|||||||
@@ -34,3 +34,16 @@ module TestMockGenerator =
|
|||||||
mock.Mem1 3 'a' |> shouldEqual "aaa"
|
mock.Mem1 3 'a' |> shouldEqual "aaa"
|
||||||
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: properties`` () =
|
||||||
|
let mock : TypeWithProperties =
|
||||||
|
{ TypeWithPropertiesMock.Empty with
|
||||||
|
Mem1 = fun i -> async { return Option.toArray i }
|
||||||
|
Prop1 = fun () -> 44
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
mock.Mem1 (Some "hi") |> Async.RunSynchronously |> shouldEqual [| "hi" |]
|
||||||
|
|
||||||
|
mock.Prop1 |> shouldEqual 44
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open SomeNamespace
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestMockGeneratorNoAttr =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: IPublicType`` () =
|
||||||
|
let mock : IPublicTypeNoAttr =
|
||||||
|
{ PublicTypeNoAttrMock.Empty with
|
||||||
|
Mem1 = fun (s, count) -> List.replicate count s
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
Assert.Throws<NotImplementedException> (fun () -> mock.Mem2 "hi" |> ignore<int>)
|
||||||
|
|
||||||
|
mock.Mem1 ("hi", 3) |> shouldEqual [ "hi" ; "hi" ; "hi" ]
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: curried args`` () =
|
||||||
|
let mock : CurriedNoAttr<_> =
|
||||||
|
{ CurriedNoAttrMock.Empty () with
|
||||||
|
Mem1 = fun i c -> Array.replicate i c |> String
|
||||||
|
Mem2 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
Mem3 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
mock.Mem1 3 'a' |> shouldEqual "aaa"
|
||||||
|
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
|
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
@@ -6,13 +6,14 @@ 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
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Check version against remote`` () =
|
// https://github.com/nunit/nunit3-vs-adapter/issues/876
|
||||||
|
let CheckVersionAgainstRemote () =
|
||||||
MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins"
|
MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins"
|
||||||
|
|
||||||
[<Test ; Explicit>]
|
[<Test ; Explicit>]
|
||||||
|
|||||||
84
WoofWare.Myriad.Plugins.Test/TestSwagger/TestSwaggerParse.fs
Normal file
84
WoofWare.Myriad.Plugins.Test/TestSwagger/TestSwaggerParse.fs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestSwaggerParse =
|
||||||
|
[<Test>]
|
||||||
|
let ``Can parse parameters`` () : unit =
|
||||||
|
let s =
|
||||||
|
"""{
|
||||||
|
"tags": [
|
||||||
|
"organization"
|
||||||
|
],
|
||||||
|
"summary": "Check if a user is a member of an organization",
|
||||||
|
"operationId": "orgIsMember",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the organization",
|
||||||
|
"name": "org",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "username of the user",
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "user is a member"
|
||||||
|
},
|
||||||
|
"303": {
|
||||||
|
"description": "redirection to /orgs/{org}/public_members/{username}"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "user is not a member"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|
||||||
|
s.AsObject ()
|
||||||
|
|> SwaggerEndpoint.Parse
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Consumes = None
|
||||||
|
Produces = None
|
||||||
|
Tags = [ "organization" ]
|
||||||
|
Summary = "Check if a user is a member of an organization"
|
||||||
|
OperationId = OperationId "orgIsMember"
|
||||||
|
Parameters =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
Type = Definition.String
|
||||||
|
Description = Some "name of the organization"
|
||||||
|
Name = "org"
|
||||||
|
In = ParameterIn.Path "org"
|
||||||
|
Required = Some true
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Type = Definition.String
|
||||||
|
Description = Some "username of the user"
|
||||||
|
Name = "username"
|
||||||
|
In = ParameterIn.Path "username"
|
||||||
|
Required = Some true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|> Some
|
||||||
|
Responses =
|
||||||
|
[
|
||||||
|
204, Definition.Unspecified
|
||||||
|
303, Definition.Unspecified
|
||||||
|
404, Definition.Unspecified
|
||||||
|
]
|
||||||
|
|> Map.ofList
|
||||||
|
}
|
||||||
@@ -1,9 +1,15 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net8.0</TargetFramework>
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
<IsTestProject>true</IsTestProject>
|
<IsTestProject>true</IsTestProject>
|
||||||
|
<!--
|
||||||
|
Known high severity vulnerability
|
||||||
|
I have not yet seen a single instance where I care about this warning
|
||||||
|
-->
|
||||||
|
<NoWarn>$(NoWarn),NU1903</NoWarn>
|
||||||
|
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -12,6 +18,7 @@
|
|||||||
<Compile Include="TestJsonParse\TestJsonParse.fs" />
|
<Compile Include="TestJsonParse\TestJsonParse.fs" />
|
||||||
<Compile Include="TestJsonParse\TestPureGymJson.fs" />
|
<Compile Include="TestJsonParse\TestPureGymJson.fs" />
|
||||||
<Compile Include="TestJsonParse\TestExtensionMethod.fs" />
|
<Compile Include="TestJsonParse\TestExtensionMethod.fs" />
|
||||||
|
<Compile Include="TestJsonParse\TestJsonNullability.fs" />
|
||||||
<Compile Include="TestHttpClient\TestPureGymRestApi.fs" />
|
<Compile Include="TestHttpClient\TestPureGymRestApi.fs" />
|
||||||
<Compile Include="TestHttpClient\TestPathParam.fs" />
|
<Compile Include="TestHttpClient\TestPathParam.fs" />
|
||||||
<Compile Include="TestHttpClient\TestReturnTypes.fs" />
|
<Compile Include="TestHttpClient\TestReturnTypes.fs" />
|
||||||
@@ -19,20 +26,29 @@
|
|||||||
<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="TestMockGenerator\TestMockGeneratorNoAttr.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="TestArgParser\TestArgParser.fs" />
|
||||||
|
<Compile Include="TestSwagger\TestSwaggerParse.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.1.20"/>
|
||||||
<PackageReference Include="FsCheck" Version="2.16.6"/>
|
<PackageReference Include="FsCheck" Version="3.2.0"/>
|
||||||
<PackageReference Include="FsUnit" Version="6.0.0"/>
|
<PackageReference Include="FsUnit" Version="7.0.1"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0"/>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0"/>
|
||||||
<PackageReference Include="NUnit" Version="4.0.1"/>
|
<PackageReference Include="NUnit" Version="4.3.2"/>
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
|
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
|
||||||
<PackageReference Include="NUnit.Analyzers" Version="4.0.0"/>
|
|
||||||
<PackageReference Include="coverlet.collector" Version="6.0.0"/>
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@@ -40,8 +56,4 @@
|
|||||||
<ProjectReference Include="..\ConsumePlugin\ConsumePlugin.fsproj"/>
|
<ProjectReference Include="..\ConsumePlugin\ConsumePlugin.fsproj"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Include="TestJsonSerialize\TestJsonSerde.fs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
201
WoofWare.Myriad.Plugins/ApacheLicence.txt
Normal file
201
WoofWare.Myriad.Plugins/ApacheLicence.txt
Normal file
@@ -0,0 +1,201 @@
|
|||||||
|
Apache License
|
||||||
|
Version 2.0, January 2004
|
||||||
|
http://www.apache.org/licenses/
|
||||||
|
|
||||||
|
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||||
|
|
||||||
|
1. Definitions.
|
||||||
|
|
||||||
|
"License" shall mean the terms and conditions for use, reproduction,
|
||||||
|
and distribution as defined by Sections 1 through 9 of this document.
|
||||||
|
|
||||||
|
"Licensor" shall mean the copyright owner or entity authorized by
|
||||||
|
the copyright owner that is granting the License.
|
||||||
|
|
||||||
|
"Legal Entity" shall mean the union of the acting entity and all
|
||||||
|
other entities that control, are controlled by, or are under common
|
||||||
|
control with that entity. For the purposes of this definition,
|
||||||
|
"control" means (i) the power, direct or indirect, to cause the
|
||||||
|
direction or management of such entity, whether by contract or
|
||||||
|
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||||
|
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||||
|
|
||||||
|
"You" (or "Your") shall mean an individual or Legal Entity
|
||||||
|
exercising permissions granted by this License.
|
||||||
|
|
||||||
|
"Source" form shall mean the preferred form for making modifications,
|
||||||
|
including but not limited to software source code, documentation
|
||||||
|
source, and configuration files.
|
||||||
|
|
||||||
|
"Object" form shall mean any form resulting from mechanical
|
||||||
|
transformation or translation of a Source form, including but
|
||||||
|
not limited to compiled object code, generated documentation,
|
||||||
|
and conversions to other media types.
|
||||||
|
|
||||||
|
"Work" shall mean the work of authorship, whether in Source or
|
||||||
|
Object form, made available under the License, as indicated by a
|
||||||
|
copyright notice that is included in or attached to the work
|
||||||
|
(an example is provided in the Appendix below).
|
||||||
|
|
||||||
|
"Derivative Works" shall mean any work, whether in Source or Object
|
||||||
|
form, that is based on (or derived from) the Work and for which the
|
||||||
|
editorial revisions, annotations, elaborations, or other modifications
|
||||||
|
represent, as a whole, an original work of authorship. For the purposes
|
||||||
|
of this License, Derivative Works shall not include works that remain
|
||||||
|
separable from, or merely link (or bind by name) to the interfaces of,
|
||||||
|
the Work and Derivative Works thereof.
|
||||||
|
|
||||||
|
"Contribution" shall mean any work of authorship, including
|
||||||
|
the original version of the Work and any modifications or additions
|
||||||
|
to that Work or Derivative Works thereof, that is intentionally
|
||||||
|
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||||
|
or by an individual or Legal Entity authorized to submit on behalf of
|
||||||
|
the copyright owner. For the purposes of this definition, "submitted"
|
||||||
|
means any form of electronic, verbal, or written communication sent
|
||||||
|
to the Licensor or its representatives, including but not limited to
|
||||||
|
communication on electronic mailing lists, source code control systems,
|
||||||
|
and issue tracking systems that are managed by, or on behalf of, the
|
||||||
|
Licensor for the purpose of discussing and improving the Work, but
|
||||||
|
excluding communication that is conspicuously marked or otherwise
|
||||||
|
designated in writing by the copyright owner as "Not a Contribution."
|
||||||
|
|
||||||
|
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||||
|
on behalf of whom a Contribution has been received by Licensor and
|
||||||
|
subsequently incorporated within the Work.
|
||||||
|
|
||||||
|
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
copyright license to reproduce, prepare Derivative Works of,
|
||||||
|
publicly display, publicly perform, sublicense, and distribute the
|
||||||
|
Work and such Derivative Works in Source or Object form.
|
||||||
|
|
||||||
|
3. Grant of Patent License. Subject to the terms and conditions of
|
||||||
|
this License, each Contributor hereby grants to You a perpetual,
|
||||||
|
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||||
|
(except as stated in this section) patent license to make, have made,
|
||||||
|
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||||
|
where such license applies only to those patent claims licensable
|
||||||
|
by such Contributor that are necessarily infringed by their
|
||||||
|
Contribution(s) alone or by combination of their Contribution(s)
|
||||||
|
with the Work to which such Contribution(s) was submitted. If You
|
||||||
|
institute patent litigation against any entity (including a
|
||||||
|
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||||
|
or a Contribution incorporated within the Work constitutes direct
|
||||||
|
or contributory patent infringement, then any patent licenses
|
||||||
|
granted to You under this License for that Work shall terminate
|
||||||
|
as of the date such litigation is filed.
|
||||||
|
|
||||||
|
4. Redistribution. You may reproduce and distribute copies of the
|
||||||
|
Work or Derivative Works thereof in any medium, with or without
|
||||||
|
modifications, and in Source or Object form, provided that You
|
||||||
|
meet the following conditions:
|
||||||
|
|
||||||
|
(a) You must give any other recipients of the Work or
|
||||||
|
Derivative Works a copy of this License; and
|
||||||
|
|
||||||
|
(b) You must cause any modified files to carry prominent notices
|
||||||
|
stating that You changed the files; and
|
||||||
|
|
||||||
|
(c) You must retain, in the Source form of any Derivative Works
|
||||||
|
that You distribute, all copyright, patent, trademark, and
|
||||||
|
attribution notices from the Source form of the Work,
|
||||||
|
excluding those notices that do not pertain to any part of
|
||||||
|
the Derivative Works; and
|
||||||
|
|
||||||
|
(d) If the Work includes a "NOTICE" text file as part of its
|
||||||
|
distribution, then any Derivative Works that You distribute must
|
||||||
|
include a readable copy of the attribution notices contained
|
||||||
|
within such NOTICE file, excluding those notices that do not
|
||||||
|
pertain to any part of the Derivative Works, in at least one
|
||||||
|
of the following places: within a NOTICE text file distributed
|
||||||
|
as part of the Derivative Works; within the Source form or
|
||||||
|
documentation, if provided along with the Derivative Works; or,
|
||||||
|
within a display generated by the Derivative Works, if and
|
||||||
|
wherever such third-party notices normally appear. The contents
|
||||||
|
of the NOTICE file are for informational purposes only and
|
||||||
|
do not modify the License. You may add Your own attribution
|
||||||
|
notices within Derivative Works that You distribute, alongside
|
||||||
|
or as an addendum to the NOTICE text from the Work, provided
|
||||||
|
that such additional attribution notices cannot be construed
|
||||||
|
as modifying the License.
|
||||||
|
|
||||||
|
You may add Your own copyright statement to Your modifications and
|
||||||
|
may provide additional or different license terms and conditions
|
||||||
|
for use, reproduction, or distribution of Your modifications, or
|
||||||
|
for any such Derivative Works as a whole, provided Your use,
|
||||||
|
reproduction, and distribution of the Work otherwise complies with
|
||||||
|
the conditions stated in this License.
|
||||||
|
|
||||||
|
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||||
|
any Contribution intentionally submitted for inclusion in the Work
|
||||||
|
by You to the Licensor shall be under the terms and conditions of
|
||||||
|
this License, without any additional terms or conditions.
|
||||||
|
Notwithstanding the above, nothing herein shall supersede or modify
|
||||||
|
the terms of any separate license agreement you may have executed
|
||||||
|
with Licensor regarding such Contributions.
|
||||||
|
|
||||||
|
6. Trademarks. This License does not grant permission to use the trade
|
||||||
|
names, trademarks, service marks, or product names of the Licensor,
|
||||||
|
except as required for reasonable and customary use in describing the
|
||||||
|
origin of the Work and reproducing the content of the NOTICE file.
|
||||||
|
|
||||||
|
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||||
|
agreed to in writing, Licensor provides the Work (and each
|
||||||
|
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||||
|
implied, including, without limitation, any warranties or conditions
|
||||||
|
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||||
|
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||||
|
appropriateness of using or redistributing the Work and assume any
|
||||||
|
risks associated with Your exercise of permissions under this License.
|
||||||
|
|
||||||
|
8. Limitation of Liability. In no event and under no legal theory,
|
||||||
|
whether in tort (including negligence), contract, or otherwise,
|
||||||
|
unless required by applicable law (such as deliberate and grossly
|
||||||
|
negligent acts) or agreed to in writing, shall any Contributor be
|
||||||
|
liable to You for damages, including any direct, indirect, special,
|
||||||
|
incidental, or consequential damages of any character arising as a
|
||||||
|
result of this License or out of the use or inability to use the
|
||||||
|
Work (including but not limited to damages for loss of goodwill,
|
||||||
|
work stoppage, computer failure or malfunction, or any and all
|
||||||
|
other commercial damages or losses), even if such Contributor
|
||||||
|
has been advised of the possibility of such damages.
|
||||||
|
|
||||||
|
9. Accepting Warranty or Additional Liability. While redistributing
|
||||||
|
the Work or Derivative Works thereof, You may choose to offer,
|
||||||
|
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||||
|
or other liability obligations and/or rights consistent with this
|
||||||
|
License. However, in accepting such obligations, You may act only
|
||||||
|
on Your own behalf and on Your sole responsibility, not on behalf
|
||||||
|
of any other Contributor, and only if You agree to indemnify,
|
||||||
|
defend, and hold each Contributor harmless for any liability
|
||||||
|
incurred by, or claims asserted against, such Contributor by reason
|
||||||
|
of your accepting any such warranty or additional liability.
|
||||||
|
|
||||||
|
END OF TERMS AND CONDITIONS
|
||||||
|
|
||||||
|
APPENDIX: How to apply the Apache License to your work.
|
||||||
|
|
||||||
|
To apply the Apache License to your work, attach the following
|
||||||
|
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||||
|
replaced with your own identifying information. (Don't include
|
||||||
|
the brackets!) The text should be enclosed in the appropriate
|
||||||
|
comment syntax for the file format. We also recommend that a
|
||||||
|
file or class name and description of purpose be included on the
|
||||||
|
same "printed page" as the copyright notice for easier
|
||||||
|
identification within third-party archives.
|
||||||
|
|
||||||
|
Copyright [yyyy] [name of copyright owner]
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
1819
WoofWare.Myriad.Plugins/ArgParserGenerator.fs
Normal file
1819
WoofWare.Myriad.Plugins/ArgParserGenerator.fs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,143 +1,53 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
open Fantomas.FCS.Xml
|
open WoofWare.Whippet.Fantomas
|
||||||
open Myriad.Core.AstExtensions
|
|
||||||
|
|
||||||
type internal ParameterInfo =
|
/// Anything that is part of an ADT.
|
||||||
|
/// A record is a product of stuff; this type represents one of those stuffs.
|
||||||
|
type internal AdtNode =
|
||||||
{
|
{
|
||||||
Attributes : SynAttribute list
|
|
||||||
IsOptional : bool
|
|
||||||
Id : Ident option
|
|
||||||
Type : SynType
|
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
|
||||||
}
|
}
|
||||||
|
|
||||||
type internal TupledArg =
|
/// 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 =
|
||||||
{
|
{
|
||||||
HasParen : bool
|
Name : SynIdent
|
||||||
Args : ParameterInfo list
|
Fields : AdtNode list
|
||||||
}
|
/// This AdtProduct represents a product in which there might be
|
||||||
|
/// some bound type parameters. This field lists the bound
|
||||||
type internal MemberInfo =
|
/// type parameters in the order they appeared on the parent type.
|
||||||
{
|
Generics : SynTyparDecl list
|
||||||
ReturnType : SynType
|
|
||||||
Accessibility : SynAccess option
|
|
||||||
/// Each element of this list is a list of args in a tuple, or just one arg if not a tuple.
|
|
||||||
Args : TupledArg list
|
|
||||||
Identifier : Ident
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
IsInline : bool
|
|
||||||
IsMutable : bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type internal InterfaceType =
|
|
||||||
{
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
Name : LongIdent
|
|
||||||
Members : MemberInfo list
|
|
||||||
Generics : SynTyparDecls option
|
|
||||||
Accessibility : SynAccess option
|
|
||||||
}
|
|
||||||
|
|
||||||
type internal RecordType =
|
|
||||||
{
|
|
||||||
Name : Ident
|
|
||||||
Fields : SynField seq
|
|
||||||
Members : SynMemberDefns option
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
Generics : SynTyparDecls option
|
|
||||||
Accessibility : SynAccess option
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal AstHelper =
|
module internal AstHelper =
|
||||||
|
|
||||||
let instantiateRecord (fields : (RecordFieldName * SynExpr option) list) : SynExpr =
|
let isEnum (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) : bool =
|
||||||
let fields =
|
match repr with
|
||||||
fields
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum _, _) -> true
|
||||||
|> List.map (fun (rfn, synExpr) -> SynExprRecordField (rfn, Some range0, synExpr, None))
|
| _ -> false
|
||||||
|
|
||||||
SynExpr.Record (None, None, fields, range0)
|
|
||||||
|
|
||||||
let defineRecordType (record : RecordType) : SynTypeDefn =
|
let defineRecordType (record : RecordType) : SynTypeDefn =
|
||||||
let repr =
|
|
||||||
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (None, Seq.toList record.Fields, range0), range0)
|
|
||||||
|
|
||||||
let name =
|
let name =
|
||||||
SynComponentInfo.Create (
|
SynComponentInfo.create record.Name
|
||||||
[ record.Name ],
|
|> SynComponentInfo.setAccessibility record.TypeAccessibility
|
||||||
?xmldoc = record.XmlDoc,
|
|> match record.XmlDoc with
|
||||||
?parameters = record.Generics,
|
| None -> id
|
||||||
access = record.Accessibility
|
| Some doc -> SynComponentInfo.withDocString doc
|
||||||
)
|
|> SynComponentInfo.setGenerics record.Generics
|
||||||
|
|
||||||
let trivia : SynTypeDefnTrivia =
|
SynTypeDefnRepr.recordWithAccess record.ImplAccessibility (Seq.toList record.Fields)
|
||||||
{
|
|> SynTypeDefn.create name
|
||||||
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
|
|> SynTypeDefn.withMemberDefns (defaultArg record.Members SynMemberDefns.Empty)
|
||||||
EqualsRange = Some range0
|
|
||||||
WithKeyword = Some range0
|
|
||||||
}
|
|
||||||
|
|
||||||
SynTypeDefn (name, repr, defaultArg record.Members SynMemberDefns.Empty, None, range0, trivia)
|
|
||||||
|
|
||||||
let isOptionIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when System.String.Equals (i.idText, "option", System.StringComparison.OrdinalIgnoreCase) -> true
|
|
||||||
// TODO: consider Microsoft.FSharp.Option or whatever it is
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isListIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true
|
|
||||||
// TODO: consider FSharpList or whatever it is
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isArrayIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when
|
|
||||||
System.String.Equals (i.idText, "array", System.StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| System.String.Equals (i.idText, "[]", System.StringComparison.Ordinal)
|
|
||||||
->
|
|
||||||
true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isResponseIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "Response" ]
|
|
||||||
| [ "RestEase" ; "Response" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isMapIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "Map" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isReadOnlyDictionaryIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "IReadOnlyDictionary" ]
|
|
||||||
| [ "Generic" ; "IReadOnlyDictionary" ]
|
|
||||||
| [ "Collections" ; "Generic" ; "IReadOnlyDictionary" ]
|
|
||||||
| [ "System" ; "Collections" ; "Generic" ; "IReadOnlyDictionary" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isDictionaryIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "Dictionary" ]
|
|
||||||
| [ "Generic" ; "Dictionary" ]
|
|
||||||
| [ "Collections" ; "Generic" ; "Dictionary" ]
|
|
||||||
| [ "System" ; "Collections" ; "Generic" ; "Dictionary" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isIDictionaryIdent (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "IDictionary" ]
|
|
||||||
| [ "Generic" ; "IDictionary" ]
|
|
||||||
| [ "Collections" ; "Generic" ; "IDictionary" ]
|
|
||||||
| [ "System" ; "Collections" ; "Generic" ; "IDictionary" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let rec private extractOpensFromDecl (moduleDecls : SynModuleDecl list) : SynOpenDeclTarget list =
|
let rec private extractOpensFromDecl (moduleDecls : SynModuleDecl list) : SynOpenDeclTarget list =
|
||||||
moduleDecls
|
moduleDecls
|
||||||
@@ -159,12 +69,12 @@ module internal AstHelper =
|
|||||||
| SynType.Paren (inner, _) ->
|
| SynType.Paren (inner, _) ->
|
||||||
let result, _ = convertSigParam inner
|
let result, _ = convertSigParam inner
|
||||||
result, true
|
result, true
|
||||||
| SynType.LongIdent ident ->
|
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
||||||
{
|
{
|
||||||
Attributes = []
|
Attributes = []
|
||||||
IsOptional = false
|
IsOptional = false
|
||||||
Id = None
|
Id = None
|
||||||
Type = SynType.CreateLongIdent ident
|
Type = SynType.createLongIdent ident
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
| SynType.SignatureParameter (attrs, opt, id, usedType, _) ->
|
| SynType.SignatureParameter (attrs, opt, id, usedType, _) ->
|
||||||
@@ -182,7 +92,7 @@ module internal AstHelper =
|
|||||||
Attributes = []
|
Attributes = []
|
||||||
IsOptional = false
|
IsOptional = false
|
||||||
Id = None
|
Id = None
|
||||||
Type = SynType.Var (typar, range0)
|
Type = SynType.var typar
|
||||||
},
|
},
|
||||||
false
|
false
|
||||||
| _ -> failwithf "expected SignatureParameter, got: %+A" ty
|
| _ -> failwithf "expected SignatureParameter, got: %+A" ty
|
||||||
@@ -211,10 +121,6 @@ module internal AstHelper =
|
|||||||
}
|
}
|
||||||
| _ -> failwithf "Didn't have alternating type-and-star in interface member definition: %+A" tupleType
|
| _ -> failwithf "Didn't have alternating type-and-star in interface member definition: %+A" tupleType
|
||||||
|
|
||||||
let toFun (inputs : SynType list) (ret : SynType) : SynType =
|
|
||||||
(ret, List.rev inputs)
|
|
||||||
||> List.fold (fun ty input -> SynType.CreateFun (input, ty))
|
|
||||||
|
|
||||||
/// Returns the args (where these are tuple types if curried) in order, and the return type.
|
/// Returns the args (where these are tuple types if curried) in order, and the return type.
|
||||||
let rec getType (ty : SynType) : (SynType * bool) list * SynType =
|
let rec getType (ty : SynType) : (SynType * bool) list * SynType =
|
||||||
match ty with
|
match ty with
|
||||||
@@ -227,9 +133,122 @@ module internal AstHelper =
|
|||||||
| SynType.Paren (argType, _) -> getType argType, true
|
| SynType.Paren (argType, _) -> getType argType, true
|
||||||
| _ -> getType argType, false
|
| _ -> getType argType, false
|
||||||
|
|
||||||
((toFun (List.map fst inputArgs) inputRet), hasParen) :: args, ret
|
((SynType.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 ident
|
||||||
|
}
|
||||||
|
|> List.singleton
|
||||||
|
}
|
||||||
|
| SynType.Var (typar, _) ->
|
||||||
|
{
|
||||||
|
HasParen = false
|
||||||
|
Args =
|
||||||
|
{
|
||||||
|
Attributes = []
|
||||||
|
IsOptional = false
|
||||||
|
Id = None
|
||||||
|
Type = SynType.var typar
|
||||||
|
}
|
||||||
|
|> 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, _),
|
||||||
@@ -242,271 +261,114 @@ 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
|
||||||
|
|
||||||
[<AutoOpen>]
|
let typars =
|
||||||
module internal SynTypePatterns =
|
match typars with
|
||||||
let (|OptionType|_|) (fieldType : SynType) =
|
| None -> []
|
||||||
match fieldType with
|
| Some (SynTyparDecls.PrefixList (decls, _)) -> decls
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isOptionIdent ident ->
|
| Some (SynTyparDecls.SinglePrefix (l, _)) -> [ l ]
|
||||||
Some innerType
|
| Some (SynTyparDecls.PostfixList (decls, constraints, _)) ->
|
||||||
| _ -> None
|
if not constraints.IsEmpty then
|
||||||
|
failwith "Constrained type parameters not currently supported"
|
||||||
|
|
||||||
let (|ListType|_|) (fieldType : SynType) =
|
decls
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isListIdent ident ->
|
|
||||||
Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|ArrayType|_|) (fieldType : SynType) =
|
match repr with
|
||||||
match fieldType with
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_, cases, _), _) ->
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isArrayIdent ident ->
|
let cases =
|
||||||
Some innerType
|
cases
|
||||||
| SynType.Array (1, innerType, _) -> Some innerType
|
|> List.map (fun (SynUnionCase.SynUnionCase (_, ident, kind, _, _, _, _)) ->
|
||||||
| _ -> None
|
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
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
let (|RestEaseResponseType|_|) (fieldType : SynType) =
|
cases, typars, access
|
||||||
match fieldType with
|
| _ -> failwithf "Failed to get union cases for type that was: %+A" repr
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isResponseIdent ident ->
|
|
||||||
Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DictionaryType|_|) (fieldType : SynType) =
|
let getRecordFields (SynTypeDefn.SynTypeDefn (typeInfo, repr, _, _, _, _)) : AdtNode list =
|
||||||
match fieldType with
|
let (SynComponentInfo.SynComponentInfo (typeParams = typars)) = typeInfo
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isDictionaryIdent ident ->
|
|
||||||
Some (key, value)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|IDictionaryType|_|) (fieldType : SynType) =
|
let typars =
|
||||||
match fieldType with
|
match typars with
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isIDictionaryIdent ident ->
|
| None -> []
|
||||||
Some (key, value)
|
| Some (SynTyparDecls.PrefixList (decls, _)) -> decls
|
||||||
| _ -> None
|
| Some (SynTyparDecls.SinglePrefix (l, _)) -> [ l ]
|
||||||
|
| Some (SynTyparDecls.PostfixList (decls, constraints, _)) ->
|
||||||
|
if not constraints.IsEmpty then
|
||||||
|
failwith "Constrained type parameters not currently supported"
|
||||||
|
|
||||||
let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) =
|
decls
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when
|
|
||||||
AstHelper.isReadOnlyDictionaryIdent ident
|
|
||||||
->
|
|
||||||
Some (key, value)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|MapType|_|) (fieldType : SynType) =
|
match repr with
|
||||||
match fieldType with
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_, fields, _), _) ->
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isMapIdent ident ->
|
fields
|
||||||
Some (key, value)
|
|> List.map (fun (SynField.SynField (_, _, ident, ty, _, _, _, _, _)) ->
|
||||||
| _ -> None
|
{
|
||||||
|
Name = ident
|
||||||
|
Type = ty
|
||||||
|
GenericsOfParent = typars
|
||||||
|
}
|
||||||
|
)
|
||||||
|
| _ -> failwithf "Failed to get record elements for type that was: %+A" repr
|
||||||
|
|
||||||
/// Returns the string name of the type.
|
let raiseIfNull (variable : Ident) : SynExpr =
|
||||||
let (|PrimitiveType|_|) (fieldType : SynType) =
|
SynExpr.createMatch
|
||||||
match fieldType with
|
(SynExpr.createIdent' variable)
|
||||||
| SynType.LongIdent ident ->
|
[
|
||||||
match ident.LongIdent with
|
SynMatchClause.create
|
||||||
| [ i ] ->
|
SynPat.createNull
|
||||||
[ "string" ; "float" ; "int" ; "bool" ; "char" ]
|
(SynExpr.applyFunction
|
||||||
|> List.tryFind (fun s -> s = i.idText)
|
(SynExpr.createIdent "raise")
|
||||||
| _ -> None
|
(SynExpr.paren (
|
||||||
| _ -> None
|
SynExpr.applyFunction
|
||||||
|
(SynExpr.createLongIdent [ "System" ; "ArgumentNullException" ])
|
||||||
let (|String|_|) (fieldType : SynType) : unit option =
|
(SynExpr.CreateConst ())
|
||||||
match fieldType with
|
)))
|
||||||
| SynType.LongIdent ident ->
|
SynMatchClause.create (SynPat.named "v") (SynExpr.createIdent "v")
|
||||||
match ident.LongIdent with
|
]
|
||||||
| [ i ] ->
|
|
||||||
[ "string" ]
|
|
||||||
|> List.tryFind (fun s -> s = i.idText)
|
|
||||||
|> Option.map ignore<string>
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Byte|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] -> [ "byte" ] |> List.tryFind (fun s -> s = i.idText) |> Option.map ignore<string>
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Net" ; "Http" ; "HttpResponseMessage" ]
|
|
||||||
| [ "Net" ; "Http" ; "HttpResponseMessage" ]
|
|
||||||
| [ "Http" ; "HttpResponseMessage" ]
|
|
||||||
| [ "HttpResponseMessage" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|HttpContent|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Net" ; "Http" ; "HttpContent" ]
|
|
||||||
| [ "Net" ; "Http" ; "HttpContent" ]
|
|
||||||
| [ "Http" ; "HttpContent" ]
|
|
||||||
| [ "HttpContent" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Stream|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "IO" ; "Stream" ]
|
|
||||||
| [ "IO" ; "Stream" ]
|
|
||||||
| [ "Stream" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|NumberType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] -> [ "string" ; "float" ; "int" ; "bool" ] |> List.tryFind (fun s -> s = i.idText)
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DateOnly|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "DateOnly" ]
|
|
||||||
| [ "DateOnly" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DateTime|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "DateTime" ]
|
|
||||||
| [ "DateTime" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Uri|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Uri" ]
|
|
||||||
| [ "Uri" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Task|_|) (fieldType : SynType) : SynType option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)), _, args, _, _, _, _) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "Task" ]
|
|
||||||
| [ "Tasks" ; "Task" ]
|
|
||||||
| [ "Threading" ; "Tasks" ; "Task" ]
|
|
||||||
| [ "System" ; "Threading" ; "Tasks" ; "Task" ] ->
|
|
||||||
match args with
|
|
||||||
| [ arg ] -> Some arg
|
|
||||||
| _ -> failwithf "Expected Task to be applied to exactly one arg, but got: %+A" args
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|||||||
1257
WoofWare.Myriad.Plugins/CataGenerator.fs
Normal file
1257
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
@@ -2,186 +2,134 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
open Myriad.Core
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
/// 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 =
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
open Myriad.Core.Ast
|
|
||||||
|
|
||||||
let private getName (SynField (_, _, id, _, _, _, _, _, _)) =
|
let private getName (SynField (_, _, id, _, _, _, _, _, _)) =
|
||||||
match id with
|
match id with
|
||||||
| 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
|
||||||
let failwithFun =
|
| x -> failwithf "Unrecognised type in inheritance: %+A" x
|
||||||
SynExpr.createLambda
|
|
||||||
"x"
|
|
||||||
(SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateIdentString "raise",
|
|
||||||
SynExpr.CreateParen (
|
|
||||||
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
|
|
||||||
)
|
)
|
||||||
|
|> Set.ofSeq
|
||||||
|
|
||||||
|
let failwithFun (SynField (_, _, idOpt, _, _, _, _, _, _)) =
|
||||||
|
let failString =
|
||||||
|
match idOpt with
|
||||||
|
| None -> SynExpr.CreateConst "Unimplemented mock function"
|
||||||
|
| Some ident -> SynExpr.CreateConst $"Unimplemented mock function: %s{ident.idText}"
|
||||||
|
|
||||||
|
SynExpr.createLongIdent [ "System" ; "NotImplementedException" ]
|
||||||
|
|> SynExpr.applyTo failString
|
||||||
|
|> SynExpr.paren
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createIdent "raise")
|
||||||
|
|> SynExpr.createLambda "_"
|
||||||
|
|
||||||
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)
|
||||||
generics,
|
|
||||||
List.replicate (generics.Length - 1) range0,
|
SynType.app name generics
|
||||||
Some range0,
|
|
||||||
false,
|
let constructorFields =
|
||||||
range0
|
let extras =
|
||||||
)
|
if inherits.Contains KnownInheritance.IDisposable then
|
||||||
|> SynBindingReturnInfo.Create
|
let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
|
||||||
|
|
||||||
|
[ SynLongIdent.createS "Dispose", unitFun ]
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
let nonExtras =
|
||||||
|
fields
|
||||||
|
|> List.map (fun field -> SynLongIdent.createI (getName field), failwithFun field)
|
||||||
|
|
||||||
|
extras @ nonExtras
|
||||||
|
|
||||||
let constructor =
|
let constructor =
|
||||||
SynMemberDefn.Member (
|
SynBinding.basic
|
||||||
SynBinding.SynBinding (
|
[ Ident.create "Empty" ]
|
||||||
None,
|
(if interfaceType.Generics.IsNone then
|
||||||
SynBindingKind.Normal,
|
[]
|
||||||
false,
|
else
|
||||||
false,
|
[ SynPat.unit ])
|
||||||
[],
|
(SynExpr.createRecord None constructorFields)
|
||||||
PreXmlDoc.Empty,
|
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every method throws.")
|
||||||
SynValData.SynValData (Some synValData, SynValInfo.Empty, None),
|
|> SynBinding.withReturnAnnotation constructorReturnType
|
||||||
constructorIdent,
|
|> SynMemberDefn.staticMember
|
||||||
Some constructorReturnType,
|
|
||||||
AstHelper.instantiateRecord (
|
let fields =
|
||||||
fields
|
let extras =
|
||||||
|> List.map (fun field ->
|
if inherits.Contains KnownInheritance.IDisposable then
|
||||||
((SynLongIdent.CreateFromLongIdent [ getName field ], true), Some failwithFun)
|
{
|
||||||
)
|
Attrs = []
|
||||||
),
|
Ident = Some (Ident.create "Dispose")
|
||||||
range0,
|
Type = SynType.funFromDomain SynType.unit SynType.unit
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
{ SynExpr.synBindingTriviaZero true with
|
|
||||||
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0)
|
|
||||||
}
|
}
|
||||||
),
|
|> SynField.make
|
||||||
range0
|
|> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|
||||||
)
|
|> List.singleton
|
||||||
|
else
|
||||||
|
[]
|
||||||
|
|
||||||
|
extras @ fields
|
||||||
|
|
||||||
let interfaceMembers =
|
let interfaceMembers =
|
||||||
let members =
|
let members =
|
||||||
interfaceType.Members
|
interfaceType.Members
|
||||||
|> List.map (fun memberInfo ->
|
|> List.map (fun memberInfo ->
|
||||||
|
|
||||||
let synValData =
|
|
||||||
SynValData.SynValData (
|
|
||||||
Some
|
|
||||||
{
|
|
||||||
IsInstance = true
|
|
||||||
IsDispatchSlot = false
|
|
||||||
IsOverrideOrExplicitImpl = true
|
|
||||||
IsFinal = false
|
|
||||||
GetterOrSetterIsCompilerGenerated = false
|
|
||||||
MemberKind = SynMemberKind.Member
|
|
||||||
},
|
|
||||||
valInfo =
|
|
||||||
SynValInfo.SynValInfo (
|
|
||||||
curriedArgInfos =
|
|
||||||
[
|
|
||||||
yield
|
|
||||||
[
|
|
||||||
SynArgInfo.SynArgInfo (
|
|
||||||
attributes = [],
|
|
||||||
optional = false,
|
|
||||||
ident = None
|
|
||||||
)
|
|
||||||
]
|
|
||||||
yield!
|
|
||||||
memberInfo.Args
|
|
||||||
|> List.mapi (fun i arg ->
|
|
||||||
arg.Args
|
|
||||||
|> List.mapi (fun j arg ->
|
|
||||||
SynArgInfo.CreateIdString $"arg_%i{i}_%i{j}"
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
returnInfo =
|
|
||||||
SynArgInfo.SynArgInfo (attributes = [], optional = false, ident = None)
|
|
||||||
),
|
|
||||||
thisIdOpt = None
|
|
||||||
)
|
|
||||||
|
|
||||||
let headArgs =
|
let headArgs =
|
||||||
memberInfo.Args
|
memberInfo.Args
|
||||||
|> 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.unit
|
||||||
|
| _ -> SynPat.named $"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"
|
||||||
|> fun i -> if tupledArgs.HasParen then SynPat.Paren (i, range0) else i
|
| [ arg ] -> arg
|
||||||
)
|
| args -> SynPat.tuple args
|
||||||
|
|> fun i -> if tupledArgs.HasParen then SynPat.paren i else i
|
||||||
let headPat =
|
|
||||||
SynPat.LongIdent (
|
|
||||||
SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; memberInfo.Identifier ],
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
SynArgPats.Pats headArgs,
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
)
|
)
|
||||||
|
|
||||||
let body =
|
let body =
|
||||||
@@ -189,8 +137,12 @@ 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 ->
|
||||||
|> SynExpr.CreateParenedTuple
|
match arg.Type with
|
||||||
|
| UnitType -> SynExpr.CreateConst ()
|
||||||
|
| _ -> SynExpr.createIdent $"arg_%i{i}_%i{j}"
|
||||||
|
)
|
||||||
|
|> SynExpr.tuple
|
||||||
)
|
)
|
||||||
|
|
||||||
match tuples |> List.rev with
|
match tuples |> List.rev with
|
||||||
@@ -198,42 +150,26 @@ module internal InterfaceMockGenerator =
|
|||||||
| last :: rest ->
|
| last :: rest ->
|
||||||
|
|
||||||
(last, rest)
|
(last, rest)
|
||||||
||> List.fold (fun trail next -> SynExpr.CreateApp (next, trail))
|
||> List.fold SynExpr.applyTo
|
||||||
|> 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 (
|
SynBinding.basic [ Ident.create "this" ; memberInfo.Identifier ] headArgs body
|
||||||
SynBinding.SynBinding (
|
|> SynMemberDefn.memberImplementation
|
||||||
None,
|
)
|
||||||
SynBindingKind.Normal,
|
|
||||||
false,
|
let properties =
|
||||||
false,
|
interfaceType.Properties
|
||||||
[],
|
|> List.map (fun pi ->
|
||||||
PreXmlDoc.Empty,
|
SynExpr.createLongIdent' [ Ident.create "this" ; pi.Identifier ]
|
||||||
synValData,
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
headPat,
|
|> SynBinding.basic [ Ident.create "this" ; pi.Identifier ] []
|
||||||
None,
|
|> SynMemberDefn.memberImplementation
|
||||||
body,
|
|
||||||
range0,
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
{
|
|
||||||
LeadingKeyword = SynLeadingKeyword.Member range0
|
|
||||||
InlineKeyword = None
|
|
||||||
EqualsRange = Some range0
|
|
||||||
}
|
|
||||||
),
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
let interfaceName =
|
let interfaceName =
|
||||||
let baseName =
|
let baseName = SynType.createLongIdent interfaceType.Name
|
||||||
SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent interfaceType.Name)
|
|
||||||
|
|
||||||
match interfaceType.Generics with
|
match interfaceType.Generics with
|
||||||
| None -> baseName
|
| None -> baseName
|
||||||
@@ -243,36 +179,52 @@ module internal InterfaceMockGenerator =
|
|||||||
| SynTyparDecls.PostfixList (decls, _, _) -> decls
|
| SynTyparDecls.PostfixList (decls, _, _) -> decls
|
||||||
| SynTyparDecls.PrefixList (decls, _) -> decls
|
| SynTyparDecls.PrefixList (decls, _) -> decls
|
||||||
| SynTyparDecls.SinglePrefix (decl, _) -> [ decl ]
|
| SynTyparDecls.SinglePrefix (decl, _) -> [ decl ]
|
||||||
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.Var (typar, range0))
|
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.var typar)
|
||||||
|
|
||||||
SynType.App (
|
SynType.app' baseName generics
|
||||||
baseName,
|
|
||||||
|
SynMemberDefn.Interface (interfaceName, Some range0, Some (members @ properties), range0)
|
||||||
|
|
||||||
|
let access =
|
||||||
|
match interfaceType.Accessibility, spec.IsInternal with
|
||||||
|
| Some (SynAccess.Public _), true
|
||||||
|
| None, true -> SynAccess.Internal range0
|
||||||
|
| Some (SynAccess.Public _), false -> SynAccess.Public 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 mem =
|
||||||
|
SynExpr.createLongIdent [ "this" ; "Dispose" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
|
|> SynBinding.basic [ Ident.create "this" ; Ident.create "Dispose" ] [ SynPat.unit ]
|
||||||
|
|> SynBinding.withReturnAnnotation SynType.unit
|
||||||
|
|> SynMemberDefn.memberImplementation
|
||||||
|
|
||||||
|
SynMemberDefn.Interface (
|
||||||
|
SynType.createLongIdent' [ "System" ; "IDisposable" ],
|
||||||
Some range0,
|
Some range0,
|
||||||
generics,
|
Some [ mem ],
|
||||||
List.replicate (generics.Length - 1) range0,
|
|
||||||
Some range0,
|
|
||||||
false,
|
|
||||||
range0
|
range0
|
||||||
)
|
)
|
||||||
|
)
|
||||||
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
|
|> Seq.toList
|
||||||
|
|
||||||
// TODO: allow an arg to the attribute, specifying a custom visibility
|
|
||||||
let access =
|
|
||||||
match interfaceType.Accessibility with
|
|
||||||
| Some (SynAccess.Public _)
|
|
||||||
| Some (SynAccess.Internal _)
|
|
||||||
| None -> SynAccess.Internal range0
|
|
||||||
| Some (SynAccess.Private _) -> SynAccess.Private range0
|
|
||||||
|
|
||||||
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
|
TypeAccessibility = Some access
|
||||||
|
ImplAccessibility = None
|
||||||
|
Attributes = []
|
||||||
}
|
}
|
||||||
|
|
||||||
let typeDecl = AstHelper.defineRecordType record
|
let typeDecl = AstHelper.defineRecordType record
|
||||||
@@ -281,55 +233,70 @@ module internal InterfaceMockGenerator =
|
|||||||
|
|
||||||
let private buildType (x : ParameterInfo) : SynType =
|
let private buildType (x : ParameterInfo) : SynType =
|
||||||
if x.IsOptional then
|
if x.IsOptional then
|
||||||
SynType.App (SynType.CreateLongIdent "option", Some range0, [ x.Type ], [], Some range0, false, range0)
|
SynType.app "option" [ x.Type ]
|
||||||
else
|
else
|
||||||
x.Type
|
x.Type
|
||||||
|
|
||||||
let private constructMemberSinglePlace (tuple : TupledArg) : SynType =
|
let private constructMemberSinglePlace (tuple : TupledArg) : SynType =
|
||||||
match tuple.Args |> List.rev |> List.map buildType with
|
tuple.Args
|
||||||
| [] -> failwith "no-arg functions not supported yet"
|
|> List.map buildType
|
||||||
| [ x ] -> x
|
|> SynType.tupleNoParen
|
||||||
| last :: rest ->
|
|> Option.defaultWith (fun () -> failwith "no-arg functions not supported yet")
|
||||||
([ SynTupleTypeSegment.Type last ], rest)
|
|> if tuple.HasParen then SynType.paren else id
|
||||||
||> List.fold (fun ty nextArg -> SynTupleTypeSegment.Type nextArg :: SynTupleTypeSegment.Star range0 :: ty)
|
|
||||||
|> fun segs -> SynType.Tuple (false, segs, range0)
|
|
||||||
|> fun ty -> if tuple.HasParen then SynType.Paren (ty, range0) else ty
|
|
||||||
|
|
||||||
let constructMember (mem : MemberInfo) : SynField =
|
let constructMember (mem : MemberInfo) : SynField =
|
||||||
let inputType = mem.Args |> List.map constructMemberSinglePlace
|
let inputType = mem.Args |> List.map constructMemberSinglePlace
|
||||||
|
|
||||||
let funcType = AstHelper.toFun inputType mem.ReturnType
|
let funcType = SynType.toFun inputType mem.ReturnType
|
||||||
|
|
||||||
SynField.SynField (
|
{
|
||||||
[],
|
Type = funcType
|
||||||
false,
|
Attrs = []
|
||||||
Some mem.Identifier,
|
Ident = Some mem.Identifier
|
||||||
funcType,
|
}
|
||||||
false,
|
|> SynField.make
|
||||||
mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty,
|
|> SynField.withDocString (mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
SynFieldTrivia.Zero
|
|
||||||
)
|
|
||||||
|
|
||||||
let createRecord (namespaceId : LongIdent) (interfaceType : SynTypeDefn) : SynModuleOrNamespace =
|
let constructProperty (prop : PropertyInfo) : SynField =
|
||||||
|
{
|
||||||
|
Attrs = []
|
||||||
|
Ident = Some prop.Identifier
|
||||||
|
Type = SynType.toFun [ SynType.unit ] prop.Type
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> SynField.withDocString (prop.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)
|
||||||
|
|
||||||
|
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 docString = PreXmlDoc.Create " Mock record type for an interface"
|
let fields =
|
||||||
|
interfaceType.Members
|
||||||
|
|> List.map constructMember
|
||||||
|
|> List.append (interfaceType.Properties |> List.map constructProperty)
|
||||||
|
|
||||||
|
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 ])
|
[ yield! opens |> List.map SynModuleDecl.openAny ; yield typeDecl ]
|
||||||
|
|> SynModuleOrNamespace.createNamespace namespaceId
|
||||||
|
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
/// 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.
|
||||||
@@ -340,23 +307,69 @@ type InterfaceMockGenerator () =
|
|||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) =
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let targetedTypes =
|
||||||
|
MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|
||||||
let ast, _ =
|
let ast, _ =
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
let types = Ast.extractTypeDefn ast
|
let types = Ast.getTypes ast
|
||||||
|
|
||||||
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 SynTypeDefn.getAttribute typeof<GenerateMockAttribute>.Name typeDef with
|
||||||
|
| None ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| DesiredGenerator.InterfaceMock arg ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
IsInternal =
|
||||||
|
arg
|
||||||
|
|> Option.defaultValue GenerateMockAttribute.DefaultIsInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> 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
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -3,26 +3,7 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
open System
|
open System
|
||||||
open System.Text
|
open System.Text
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
open WoofWare.Whippet.Fantomas
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
open Myriad.Core
|
|
||||||
|
|
||||||
/// 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 ()
|
|
||||||
|
|
||||||
/// If changing this, *adjust the documentation strings*
|
|
||||||
static member internal DefaultIsExtensionMethod = false
|
|
||||||
|
|
||||||
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
|
|
||||||
new () = JsonSerializeAttribute JsonSerializeAttribute.DefaultIsExtensionMethod
|
|
||||||
|
|
||||||
type internal JsonSerializeOutputSpec =
|
type internal JsonSerializeOutputSpec =
|
||||||
{
|
{
|
||||||
@@ -32,174 +13,147 @@ type internal JsonSerializeOutputSpec =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal JsonSerializeGenerator =
|
module internal JsonSerializeGenerator =
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
open Myriad.Core.Ast
|
|
||||||
|
|
||||||
|
// 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.
|
||||||
|
let private jsonNull () =
|
||||||
|
SynExpr.createNull ()
|
||||||
|
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|
||||||
|
|
||||||
/// Given `input.Ident`, for example, choose how to add it to the ambient `node`.
|
/// 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)`.
|
/// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`.
|
||||||
let rec serializeNode (fieldType : SynType) : SynExpr =
|
/// Returns also a bool which is true if the resulting SynExpr represents something of type JsonNode.
|
||||||
|
let rec serializeNode (fieldType : SynType) : SynExpr * bool =
|
||||||
// TODO: serialization format for DateTime etc
|
// TODO: serialization format for DateTime etc
|
||||||
match fieldType with
|
match fieldType with
|
||||||
| DateOnly
|
| DateOnly
|
||||||
| DateTime
|
| DateTime
|
||||||
| NumberType _
|
| NumberType _
|
||||||
|
| Measure _
|
||||||
| PrimitiveType _
|
| PrimitiveType _
|
||||||
|
| Guid
|
||||||
| Uri ->
|
| Uri ->
|
||||||
// JsonValue.Create<{type}>
|
// JsonValue.Create<type>
|
||||||
SynExpr.TypeApp (
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|
||||||
SynExpr.CreateLongIdent (
|
|> SynExpr.typeApp [ fieldType ]
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|
|> fun e -> e, false
|
||||||
),
|
| DateTimeOffset ->
|
||||||
range0,
|
// fun field -> field.ToString("o") |> JsonValue.Create<string>
|
||||||
[ fieldType ],
|
let create =
|
||||||
[],
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|
||||||
Some range0,
|
|> SynExpr.typeApp [ SynType.named "string" ]
|
||||||
range0,
|
|
||||||
range0
|
SynExpr.createIdent "field"
|
||||||
)
|
|> SynExpr.callMethodArg "ToString" (SynExpr.CreateConst "o")
|
||||||
|
|> SynExpr.pipeThroughFunction create
|
||||||
|
|> SynExpr.createLambda "field"
|
||||||
|
|> fun e -> e, false
|
||||||
|
| NullableType ty ->
|
||||||
|
// fun field -> if field.HasValue then {serializeNode ty} field.Value else JsonValue.Create null
|
||||||
|
let inner, innerIsJsonNode = serializeNode ty
|
||||||
|
|
||||||
|
SynExpr.applyFunction inner (SynExpr.createLongIdent [ "field" ; "Value" ])
|
||||||
|
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|
||||||
|
|> SynExpr.ifThenElse (SynExpr.createLongIdent [ "field" ; "HasValue" ]) (jsonNull ())
|
||||||
|
|> SynExpr.createLambda "field"
|
||||||
|
|> fun e -> e, innerIsJsonNode
|
||||||
| OptionType ty ->
|
| OptionType ty ->
|
||||||
// fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field
|
// fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field
|
||||||
SynExpr.CreateMatch (
|
let noneClause = jsonNull () |> SynMatchClause.create (SynPat.named "None")
|
||||||
SynExpr.CreateIdentString "field",
|
|
||||||
[
|
|
||||||
SynMatchClause.Create (
|
|
||||||
SynPat.CreateLongIdent (SynLongIdent.CreateString "None", []),
|
|
||||||
None,
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|
|
||||||
),
|
|
||||||
SynExpr.CreateNull
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
SynMatchClause.Create (
|
let someClause =
|
||||||
SynPat.CreateLongIdent (
|
let inner, innerIsJsonNode = serializeNode ty
|
||||||
SynLongIdent.CreateString "Some",
|
let target = SynExpr.applyFunction inner (SynExpr.createIdent "field")
|
||||||
[ SynPat.CreateNamed (Ident.Create "field") ]
|
|
||||||
),
|
if innerIsJsonNode then
|
||||||
None,
|
target
|
||||||
SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "field")
|
else
|
||||||
)
|
target
|
||||||
]
|
|> SynExpr.paren
|
||||||
)
|
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|
||||||
|
|> SynMatchClause.create (SynPat.nameWithArgs "Some" [ SynPat.named "field" ])
|
||||||
|
|
||||||
|
[ noneClause ; someClause ]
|
||||||
|
|> SynExpr.createMatch (SynExpr.createIdent "field")
|
||||||
|> SynExpr.createLambda "field"
|
|> SynExpr.createLambda "field"
|
||||||
|
|> fun e -> e, true
|
||||||
| ArrayType ty
|
| ArrayType ty
|
||||||
| ListType ty ->
|
| ListType ty ->
|
||||||
// fun field ->
|
// fun field ->
|
||||||
// let arr = JsonArray ()
|
// let arr = JsonArray ()
|
||||||
// for mem in field do arr.Add ({serializeNode} mem)
|
// for mem in field do arr.Add ({serializeNode} mem)
|
||||||
// arr
|
// arr
|
||||||
SynExpr.LetOrUse (
|
[
|
||||||
false,
|
SynExpr.ForEach (
|
||||||
false,
|
DebugPointAtFor.Yes range0,
|
||||||
|
DebugPointAtInOrTo.Yes range0,
|
||||||
|
SeqExprOnly.SeqExprOnly false,
|
||||||
|
true,
|
||||||
|
SynPat.named "mem",
|
||||||
|
SynExpr.createIdent "field",
|
||||||
|
SynExpr.applyFunction
|
||||||
|
(SynExpr.createLongIdent [ "arr" ; "Add" ])
|
||||||
|
(SynExpr.paren (SynExpr.applyFunction (fst (serializeNode ty)) (SynExpr.createIdent "mem"))),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
SynExpr.createIdent "arr"
|
||||||
|
]
|
||||||
|
|> SynExpr.sequential
|
||||||
|
|> SynExpr.createLet
|
||||||
[
|
[
|
||||||
SynBinding.Let (
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonArray" ]
|
||||||
pattern = SynPat.CreateNamed (Ident.Create "arr"),
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
expr =
|
|> SynBinding.basic [ Ident.create "arr" ] []
|
||||||
SynExpr.CreateApp (
|
]
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonArray" ]
|
|
||||||
),
|
|
||||||
SynExpr.CreateConst SynConst.Unit
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
SynExpr.CreateSequential
|
|
||||||
[
|
|
||||||
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"
|
|
||||||
],
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> SynExpr.createLambda "field"
|
|> SynExpr.createLambda "field"
|
||||||
| IDictionaryType (keyType, valueType)
|
|> fun e -> e, false
|
||||||
| DictionaryType (keyType, valueType)
|
| IDictionaryType (_keyType, valueType)
|
||||||
| IReadOnlyDictionaryType (keyType, valueType)
|
| DictionaryType (_keyType, valueType)
|
||||||
| MapType (keyType, valueType) ->
|
| IReadOnlyDictionaryType (_keyType, valueType)
|
||||||
|
| MapType (_keyType, valueType) ->
|
||||||
// fun field ->
|
// fun field ->
|
||||||
// let ret = JsonObject ()
|
// let ret = JsonObject ()
|
||||||
// for (KeyValue(key, value)) in field do
|
// for (KeyValue(key, value)) in field do
|
||||||
// ret.Add (key.ToString (), {serializeNode} value)
|
// ret.Add (key.ToString (), {serializeNode} value)
|
||||||
// ret
|
// ret
|
||||||
SynExpr.LetOrUse (
|
[
|
||||||
false,
|
SynExpr.ForEach (
|
||||||
false,
|
DebugPointAtFor.Yes range0,
|
||||||
|
DebugPointAtInOrTo.Yes range0,
|
||||||
|
SeqExprOnly.SeqExprOnly false,
|
||||||
|
true,
|
||||||
|
SynPat.paren (SynPat.nameWithArgs "KeyValue" [ SynPat.named "key" ; SynPat.named "value" ]),
|
||||||
|
SynExpr.createIdent "field",
|
||||||
|
SynExpr.applyFunction
|
||||||
|
(SynExpr.createLongIdent [ "ret" ; "Add" ])
|
||||||
|
(SynExpr.tuple
|
||||||
|
[
|
||||||
|
SynExpr.createLongIdent [ "key" ; "ToString" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
|
SynExpr.applyFunction (fst (serializeNode valueType)) (SynExpr.createIdent "value")
|
||||||
|
]),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
SynExpr.createIdent "ret"
|
||||||
|
]
|
||||||
|
|> SynExpr.sequential
|
||||||
|
|> SynExpr.createLet
|
||||||
[
|
[
|
||||||
SynBinding.Let (
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
pattern = SynPat.CreateNamed (Ident.Create "ret"),
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
expr =
|
|> SynBinding.basic [ Ident.create "ret" ] []
|
||||||
SynExpr.CreateApp (
|
]
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
|
||||||
),
|
|
||||||
SynExpr.CreateConst SynConst.Unit
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
SynExpr.CreateSequential
|
|
||||||
[
|
|
||||||
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 (SynLongIdent.Create [ "ret" ; "Add" ]),
|
|
||||||
SynExpr.CreateParenedTuple
|
|
||||||
[
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "key" ; "ToString" ]),
|
|
||||||
SynExpr.CreateConst SynConst.Unit
|
|
||||||
)
|
|
||||||
SynExpr.CreateApp (serializeNode valueType, SynExpr.CreateIdentString "value")
|
|
||||||
]
|
|
||||||
),
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
SynExpr.CreateIdentString "ret"
|
|
||||||
],
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> SynExpr.createLambda "field"
|
|> SynExpr.createLambda "field"
|
||||||
|
|> fun e -> e, false
|
||||||
|
| JsonNode -> SynExpr.createIdent "id", true
|
||||||
|
| UnitType ->
|
||||||
|
SynExpr.createLambda
|
||||||
|
"value"
|
||||||
|
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst ())),
|
||||||
|
false
|
||||||
| _ ->
|
| _ ->
|
||||||
// {type}.toJsonNode
|
// {type}.toJsonNode
|
||||||
let typeName =
|
let typeName =
|
||||||
@@ -207,213 +161,279 @@ module internal JsonSerializeGenerator =
|
|||||||
| SynType.LongIdent ident -> ident.LongIdent
|
| SynType.LongIdent ident -> ident.LongIdent
|
||||||
| _ -> failwith $"Unrecognised type: %+A{fieldType}"
|
| _ -> failwith $"Unrecognised type: %+A{fieldType}"
|
||||||
|
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent (typeName @ [ Ident.Create "toJsonNode" ]))
|
SynExpr.createLongIdent' (typeName @ [ Ident.create "toJsonNode" ]), true
|
||||||
|
|
||||||
/// 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
|
||||||
/// `node.Add ({propertyName}, {toJsonNode})`
|
/// `node.Add ({propertyName}, {toJsonNode})`
|
||||||
let createSerializeRhs (propertyName : SynExpr) (fieldId : Ident) (fieldType : SynType) : SynExpr =
|
let createSerializeRhsRecord (propertyName : SynExpr) (fieldId : Ident) (fieldType : SynType) : SynExpr =
|
||||||
let func = SynExpr.CreateLongIdent (SynLongIdent.Create [ "node" ; "Add" ])
|
[
|
||||||
|
propertyName
|
||||||
|
SynExpr.pipeThroughFunction
|
||||||
|
(fst (serializeNode fieldType))
|
||||||
|
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ])
|
||||||
|
|> SynExpr.paren
|
||||||
|
]
|
||||||
|
|> SynExpr.tuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
|
||||||
|
|
||||||
let args =
|
let getPropertyName (fieldId : Ident) (attrs : SynAttribute list) : SynExpr =
|
||||||
SynExpr.CreateParenedTuple
|
let propertyNameAttr =
|
||||||
[
|
attrs
|
||||||
propertyName
|
|> List.tryFind (fun attr ->
|
||||||
SynExpr.CreateApp (
|
(SynLongIdent.toString attr.TypeName).EndsWith ("JsonPropertyName", StringComparison.Ordinal)
|
||||||
serializeNode fieldType,
|
)
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent [ Ident.Create "input" ; fieldId ])
|
|
||||||
)
|
|
||||||
]
|
|
||||||
|
|
||||||
SynExpr.CreateApp (func, args)
|
match propertyNameAttr with
|
||||||
|
| None ->
|
||||||
|
let sb = StringBuilder fieldId.idText.Length
|
||||||
|
sb.Append (Char.ToLowerInvariant fieldId.idText.[0]) |> ignore
|
||||||
|
|
||||||
let createMaker (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (fields : SynField list) =
|
if fieldId.idText.Length > 1 then
|
||||||
let xmlDoc = PreXmlDoc.Create " Serialize to a JSON node"
|
sb.Append fieldId.idText.[1..] |> ignore
|
||||||
|
|
||||||
|
sb.ToString () |> SynExpr.CreateConst
|
||||||
|
| Some name -> name.ArgExpr
|
||||||
|
|
||||||
|
let getIsJsonExtension (attrs : SynAttribute list) : bool =
|
||||||
|
attrs
|
||||||
|
|> List.tryFind (fun attr ->
|
||||||
|
(SynLongIdent.toString attr.TypeName).EndsWith ("JsonExtensionData", StringComparison.Ordinal)
|
||||||
|
)
|
||||||
|
|> Option.isSome
|
||||||
|
|
||||||
|
/// `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 =
|
let returnInfo =
|
||||||
SynBindingReturnInfo.Create (
|
SynLongIdent.createS' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
SynType.LongIdent (SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|
|> SynType.LongIdent
|
||||||
)
|
|
||||||
|
|
||||||
let inputArg = Ident.Create "input"
|
let functionName = Ident.create "toJsonNode"
|
||||||
let functionName = Ident.Create "toJsonNode"
|
|
||||||
|
|
||||||
let inputVal =
|
|
||||||
let memberFlags =
|
|
||||||
if spec.ExtensionMethods then
|
|
||||||
{
|
|
||||||
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 =
|
let assignments =
|
||||||
fields
|
[
|
||||||
|> List.map (fun (SynField (attrs, _, id, fieldType, _, _, _, _, _)) ->
|
populateNode
|
||||||
let id =
|
SynExpr.Upcast (SynExpr.createIdent "node", SynType.Anon range0, range0)
|
||||||
match id with
|
]
|
||||||
| None -> failwith "didn't get an ID on field"
|
|> SynExpr.sequential
|
||||||
| Some id -> id
|
|> SynExpr.createLet
|
||||||
|
|
||||||
let attrs = attrs |> List.collect (fun l -> l.Attributes)
|
|
||||||
|
|
||||||
let propertyNameAttr =
|
|
||||||
attrs
|
|
||||||
|> List.tryFind (fun attr ->
|
|
||||||
attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
|
|
||||||
)
|
|
||||||
|
|
||||||
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
|
|
||||||
)
|
|
||||||
|
|
||||||
createSerializeRhs propertyName id fieldType
|
|
||||||
)
|
|
||||||
|
|
||||||
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 = assignments |> SynExpr.CreateSequential
|
|
||||||
|
|
||||||
let assignments =
|
|
||||||
SynExpr.LetOrUse (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
[
|
[
|
||||||
SynBinding.Let (
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
pattern = SynPat.CreateNamed (Ident.Create "node"),
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
expr =
|
|> SynBinding.basic [ Ident.create "node" ] []
|
||||||
SynExpr.CreateApp (
|
]
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
|
||||||
),
|
|
||||||
SynExpr.CreateConst SynConst.Unit
|
|
||||||
)
|
|
||||||
)
|
|
||||||
],
|
|
||||||
SynExpr.CreateSequential
|
|
||||||
[
|
|
||||||
SynExpr.Do (assignments, range0)
|
|
||||||
SynExpr.Upcast (SynExpr.CreateIdentString "node", SynType.Anon range0, range0)
|
|
||||||
],
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let pattern =
|
let pattern =
|
||||||
SynPat.LongIdent (
|
SynPat.namedI inputArgName
|
||||||
SynLongIdent.CreateFromLongIdent [ functionName ],
|
|> SynPat.annotateType (SynType.LongIdent (SynLongIdent.create typeName))
|
||||||
None,
|
|
||||||
None,
|
|
||||||
SynArgPats.Pats
|
|
||||||
[
|
|
||||||
SynPat.CreateTyped (
|
|
||||||
SynPat.CreateNamed inputArg,
|
|
||||||
SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName)
|
|
||||||
)
|
|
||||||
|> SynPat.CreateParen
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
if spec.ExtensionMethods then
|
if spec.ExtensionMethods then
|
||||||
let binding =
|
let componentInfo =
|
||||||
SynBinding.SynBinding (
|
SynComponentInfo.createLong typeName
|
||||||
None,
|
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for JSON parsing")
|
||||||
SynBindingKind.Normal,
|
|
||||||
false,
|
|
||||||
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 memberDef =
|
||||||
|
assignments
|
||||||
|
|> SynBinding.basic [ functionName ] [ pattern ]
|
||||||
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
|
|> SynMemberDefn.staticMember
|
||||||
|
|
||||||
let containingType =
|
let containingType =
|
||||||
SynTypeDefn.SynTypeDefn (
|
SynTypeDefnRepr.augmentation ()
|
||||||
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create " Extension methods for JSON parsing"),
|
|> SynTypeDefn.create componentInfo
|
||||||
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0),
|
|> SynTypeDefn.withMemberDefns [ memberDef ]
|
||||||
[ mem ],
|
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
|
|
||||||
EqualsRange = None
|
|
||||||
WithKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SynModuleDecl.Types ([ containingType ], range0)
|
SynModuleDecl.Types ([ containingType ], range0)
|
||||||
else
|
else
|
||||||
let binding =
|
assignments
|
||||||
SynBinding.Let (
|
|> SynBinding.basic [ functionName ] [ pattern ]
|
||||||
isInline = false,
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
isMutable = false,
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
xmldoc = xmlDoc,
|
|> SynModuleDecl.createLet
|
||||||
returnInfo = returnInfo,
|
|
||||||
expr = assignments,
|
let recordModule (spec : JsonSerializeOutputSpec) (_typeName : LongIdent) (fields : SynField list) =
|
||||||
valData = inputVal,
|
let fields = fields |> List.map SynField.extractWithIdent
|
||||||
pattern = pattern
|
|
||||||
|
fields
|
||||||
|
|> List.map (fun fieldData ->
|
||||||
|
let propertyName = getPropertyName fieldData.Ident fieldData.Attrs
|
||||||
|
let isJsonExtension = getIsJsonExtension fieldData.Attrs
|
||||||
|
|
||||||
|
if isJsonExtension then
|
||||||
|
let valType =
|
||||||
|
match fieldData.Type with
|
||||||
|
| DictionaryType (String, v) -> v
|
||||||
|
| _ -> failwith "Expected JsonExtensionData to be a Dictionary<string, something>"
|
||||||
|
|
||||||
|
let serialise = fst (serializeNode valType)
|
||||||
|
|
||||||
|
SynExpr.createIdent "node"
|
||||||
|
|> SynExpr.callMethodArg
|
||||||
|
"Add"
|
||||||
|
(SynExpr.tuple
|
||||||
|
[
|
||||||
|
SynExpr.createIdent "key"
|
||||||
|
SynExpr.applyFunction serialise (SynExpr.createIdent "value")
|
||||||
|
])
|
||||||
|
|> SynExpr.createForEach
|
||||||
|
(SynPat.identWithArgs
|
||||||
|
[ Ident.create "KeyValue" ]
|
||||||
|
(SynArgPats.create [ SynPat.named "key" ; SynPat.named "value" ]))
|
||||||
|
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldData.Ident ])
|
||||||
|
else
|
||||||
|
createSerializeRhsRecord propertyName fieldData.Ident fieldData.Type
|
||||||
|
)
|
||||||
|
|> SynExpr.sequential
|
||||||
|
|> fun expr -> SynExpr.Do (expr, range0)
|
||||||
|
|
||||||
|
let unionModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (cases : SynUnionCase list) =
|
||||||
|
let inputArg = Ident.create "input"
|
||||||
|
let fields = cases |> List.map UnionCase.ofSynUnionCase
|
||||||
|
|
||||||
|
fields
|
||||||
|
|> List.map (fun unionCase ->
|
||||||
|
let propertyName = getPropertyName unionCase.Name unionCase.Attributes
|
||||||
|
|
||||||
|
let caseNames = unionCase.Fields |> List.mapi (fun i _ -> $"arg%i{i}")
|
||||||
|
|
||||||
|
let argPats = SynArgPats.createNamed caseNames
|
||||||
|
|
||||||
|
let pattern =
|
||||||
|
SynPat.LongIdent (
|
||||||
|
SynLongIdent.create (typeName @ [ unionCase.Name ]),
|
||||||
|
None,
|
||||||
|
None,
|
||||||
|
argPats,
|
||||||
|
None,
|
||||||
|
range0
|
||||||
)
|
)
|
||||||
|
|
||||||
SynModuleDecl.CreateLet [ binding ]
|
let typeLine =
|
||||||
|
[
|
||||||
|
SynExpr.CreateConst "type"
|
||||||
|
SynExpr.applyFunction
|
||||||
|
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ])
|
||||||
|
propertyName
|
||||||
|
]
|
||||||
|
|> SynExpr.tuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
|
||||||
|
|
||||||
let createRecordModule
|
let dataNode =
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
|
|> SynExpr.applyTo (SynExpr.CreateConst ())
|
||||||
|
|> SynBinding.basic [ Ident.create "dataNode" ] []
|
||||||
|
|
||||||
|
let dataBindings =
|
||||||
|
(unionCase.Fields, caseNames)
|
||||||
|
||> List.zip
|
||||||
|
|> List.map (fun (fieldData, caseName) ->
|
||||||
|
let propertyName = getPropertyName (Option.get fieldData.Ident) fieldData.Attrs
|
||||||
|
|
||||||
|
let node =
|
||||||
|
SynExpr.applyFunction (fst (serializeNode fieldData.Type)) (SynExpr.createIdent caseName)
|
||||||
|
|
||||||
|
[ propertyName ; node ]
|
||||||
|
|> SynExpr.tuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "dataNode" ; "Add" ])
|
||||||
|
)
|
||||||
|
|
||||||
|
let assignToNode =
|
||||||
|
[ SynExpr.CreateConst "data" ; SynExpr.createIdent "dataNode" ]
|
||||||
|
|> SynExpr.tuple
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
|
||||||
|
|
||||||
|
let dataNode =
|
||||||
|
SynExpr.sequential (dataBindings @ [ assignToNode ])
|
||||||
|
|> SynExpr.createLet [ dataNode ]
|
||||||
|
|
||||||
|
let action =
|
||||||
|
[
|
||||||
|
yield typeLine
|
||||||
|
if not dataBindings.IsEmpty then
|
||||||
|
yield dataNode
|
||||||
|
]
|
||||||
|
|> SynExpr.sequential
|
||||||
|
|
||||||
|
SynMatchClause.create pattern action
|
||||||
|
)
|
||||||
|
|> SynExpr.createMatch (SynExpr.createIdent' inputArg)
|
||||||
|
|
||||||
|
let enumModule
|
||||||
|
(spec : JsonSerializeOutputSpec)
|
||||||
|
(typeName : LongIdent)
|
||||||
|
(cases : (Ident * SynExpr) list)
|
||||||
|
: SynModuleDecl
|
||||||
|
=
|
||||||
|
let fail =
|
||||||
|
SynExpr.CreateConst "Unrecognised value for enum: %O"
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createIdent "sprintf")
|
||||||
|
|> SynExpr.applyTo (SynExpr.createIdent "v")
|
||||||
|
|> SynExpr.paren
|
||||||
|
|> SynExpr.applyFunction (SynExpr.createIdent "failwith")
|
||||||
|
|
||||||
|
let body =
|
||||||
|
cases
|
||||||
|
|> List.map (fun (caseName, value) ->
|
||||||
|
value
|
||||||
|
|> SynExpr.applyFunction (
|
||||||
|
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
|
||||||
|
)
|
||||||
|
|> SynMatchClause.create (SynPat.identWithArgs (typeName @ [ caseName ]) (SynArgPats.create []))
|
||||||
|
)
|
||||||
|
|> fun l -> l @ [ SynMatchClause.create (SynPat.named "v") fail ]
|
||||||
|
|> SynExpr.createMatch (SynExpr.createIdent "input")
|
||||||
|
|
||||||
|
let xmlDoc = PreXmlDoc.create "Serialize to a JSON node"
|
||||||
|
|
||||||
|
let returnInfo =
|
||||||
|
SynLongIdent.createS' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
|> SynType.LongIdent
|
||||||
|
|
||||||
|
let functionName = Ident.create "toJsonNode"
|
||||||
|
|
||||||
|
let pattern =
|
||||||
|
SynPat.named "input"
|
||||||
|
|> SynPat.annotateType (SynType.LongIdent (SynLongIdent.create typeName))
|
||||||
|
|
||||||
|
if spec.ExtensionMethods then
|
||||||
|
let componentInfo =
|
||||||
|
SynComponentInfo.createLong typeName
|
||||||
|
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for JSON parsing")
|
||||||
|
|
||||||
|
let memberDef =
|
||||||
|
body
|
||||||
|
|> SynBinding.basic [ functionName ] [ pattern ]
|
||||||
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
|
|> SynMemberDefn.staticMember
|
||||||
|
|
||||||
|
let containingType =
|
||||||
|
SynTypeDefnRepr.augmentation ()
|
||||||
|
|> SynTypeDefn.create componentInfo
|
||||||
|
|> SynTypeDefn.withMemberDefns [ memberDef ]
|
||||||
|
|
||||||
|
SynModuleDecl.Types ([ containingType ], range0)
|
||||||
|
else
|
||||||
|
body
|
||||||
|
|> SynBinding.basic [ functionName ] [ pattern ]
|
||||||
|
|> SynBinding.withReturnAnnotation returnInfo
|
||||||
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
|
|> SynModuleDecl.createLet
|
||||||
|
|
||||||
|
let createModule
|
||||||
(namespaceId : LongIdent)
|
(namespaceId : LongIdent)
|
||||||
(opens : SynOpenDeclTarget list)
|
(opens : SynOpenDeclTarget list)
|
||||||
(spec : JsonSerializeOutputSpec)
|
(spec : JsonSerializeOutputSpec)
|
||||||
@@ -422,60 +442,72 @@ module internal JsonSerializeGenerator =
|
|||||||
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
|
||||||
|
[ SynAttribute.autoOpen ]
|
||||||
|
else
|
||||||
|
[ SynAttribute.requireQualifiedAccess ; 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 serializing %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 type name"
|
||||||
"methods"
|
| ident ->
|
||||||
|
let expanded =
|
||||||
|
List.last ident
|
||||||
|
|> fun i -> i.idText
|
||||||
|
|> fun s -> s + "JsonSerializeExtension"
|
||||||
|
|> Ident.create
|
||||||
|
|
||||||
$" Module containing JSON serializing %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.createLong moduleName
|
||||||
match recordId with
|
|> SynComponentInfo.addAttributes attributes
|
||||||
| [] -> failwith "unexpectedly got an empty identifier for record name"
|
|> SynComponentInfo.setAccessibility access
|
||||||
| recordId ->
|
|> SynComponentInfo.withDocString xmlDoc
|
||||||
let expanded =
|
|
||||||
List.last recordId
|
|
||||||
|> fun i -> i.idText
|
|
||||||
|> fun s -> s + "JsonSerializeExtension"
|
|
||||||
|> Ident.Create
|
|
||||||
|
|
||||||
List.take (List.length recordId - 1) recordId @ [ expanded ]
|
let decls =
|
||||||
else
|
match synTypeDefnRepr with
|
||||||
recordId
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _range), _) ->
|
||||||
|
recordModule spec ident recordFields
|
||||||
|
|> scaffolding spec ident (Ident.create "input")
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, unionFields, _range), _) ->
|
||||||
|
unionModule spec ident unionFields
|
||||||
|
|> scaffolding spec ident (Ident.create "input")
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum (cases, _range), _) ->
|
||||||
|
cases
|
||||||
|
|> List.map (fun c ->
|
||||||
|
match c with
|
||||||
|
| SynEnumCase.SynEnumCase (_, SynIdent.SynIdent (ident, _), value, _, _, _) -> ident, value
|
||||||
|
)
|
||||||
|
|> enumModule spec ident
|
||||||
|
| ty -> failwithf "Unsupported type: got %O" ty
|
||||||
|
|
||||||
let info =
|
[
|
||||||
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc)
|
yield! opens |> List.map SynModuleDecl.openAny
|
||||||
|
yield decls |> List.singleton |> SynModuleDecl.nestedModule info
|
||||||
|
]
|
||||||
|
|> SynModuleOrNamespace.createNamespace namespaceId
|
||||||
|
|
||||||
let mdl = SynModuleDecl.CreateNestedModule (info, decls)
|
open Myriad.Core
|
||||||
|
|
||||||
SynModuleOrNamespace.CreateNamespace (
|
|
||||||
namespaceId,
|
|
||||||
decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ mdl ]
|
|
||||||
)
|
|
||||||
| _ -> failwithf "Not a record type"
|
|
||||||
|
|
||||||
/// 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 serialization function.
|
/// containing a JSON serialization function.
|
||||||
@@ -486,18 +518,54 @@ type JsonSerializeGenerator () =
|
|||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) =
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let targetedTypes =
|
||||||
|
MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|
||||||
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 relevantTypes =
|
||||||
|
Ast.getTypes ast
|
||||||
|
|> List.map (fun (name, defns) ->
|
||||||
|
defns
|
||||||
|
|> List.choose (fun defn ->
|
||||||
|
if SynTypeDefn.isRecord defn then Some defn
|
||||||
|
elif SynTypeDefn.isDu defn then Some defn
|
||||||
|
elif SynTypeDefn.isEnum defn then Some defn
|
||||||
|
else None
|
||||||
|
)
|
||||||
|
|> fun defns -> name, defns
|
||||||
|
)
|
||||||
|
|
||||||
let namespaceAndRecords =
|
let namespaceAndTypes =
|
||||||
records
|
relevantTypes
|
||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<JsonSerializeAttribute> typeDef with
|
match SynTypeDefn.getAttribute typeof<JsonSerializeAttribute>.Name typeDef with
|
||||||
| None -> None
|
| None ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| DesiredGenerator.JsonSerialize arg ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
ExtensionMethods =
|
||||||
|
arg
|
||||||
|
|> Option.defaultValue
|
||||||
|
JsonSerializeAttribute.DefaultIsExtensionMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
| Some attr ->
|
| Some attr ->
|
||||||
let arg =
|
let arg =
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
@@ -522,13 +590,10 @@ type JsonSerializeGenerator () =
|
|||||||
let opens = AstHelper.extractOpens ast
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
let modules =
|
let modules =
|
||||||
namespaceAndRecords
|
namespaceAndTypes
|
||||||
|> List.collect (fun (ns, records) ->
|
|> List.collect (fun (ns, types) ->
|
||||||
records
|
types
|
||||||
|> List.map (fun (record, spec) ->
|
|> List.map (fun (ty, spec) -> JsonSerializeGenerator.createModule ns opens spec ty)
|
||||||
let recordModule = JsonSerializeGenerator.createRecordModule ns opens spec record
|
|
||||||
recordModule
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
Output.Ast modules
|
Output.Ast modules
|
||||||
|
|||||||
23
WoofWare.Myriad.Plugins/List.fs
Normal file
23
WoofWare.Myriad.Plugins/List.fs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
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
|
||||||
|
|
||||||
|
let allSome<'a> (l : 'a option list) : 'a list option =
|
||||||
|
let rec go acc (l : 'a option list) =
|
||||||
|
match l with
|
||||||
|
| [] -> Some (List.rev acc)
|
||||||
|
| None :: _ -> None
|
||||||
|
| Some head :: tail -> go (head :: acc) tail
|
||||||
|
|
||||||
|
go [] l
|
||||||
24
WoofWare.Myriad.Plugins/Measure.fs
Normal file
24
WoofWare.Myriad.Plugins/Measure.fs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal Measure =
|
||||||
|
|
||||||
|
let getLanguagePrimitivesMeasure (typeName : LongIdent) : SynExpr =
|
||||||
|
match typeName |> List.map _.idText with
|
||||||
|
| [ "System" ; "Single" ] -> [ "LanguagePrimitives" ; "Float32WithMeasure" ]
|
||||||
|
| [ "System" ; "Double" ] -> [ "LanguagePrimitives" ; "FloatWithMeasure" ]
|
||||||
|
| [ "System" ; "Byte" ] -> [ "LanguagePrimitives" ; "ByteWithMeasure" ]
|
||||||
|
| [ "System" ; "SByte" ] -> [ "LanguagePrimitives" ; "SByteWithMeasure" ]
|
||||||
|
| [ "System" ; "Int16" ] -> [ "LanguagePrimitives" ; "Int16WithMeasure" ]
|
||||||
|
| [ "System" ; "Int32" ] -> [ "LanguagePrimitives" ; "Int32WithMeasure" ]
|
||||||
|
| [ "System" ; "Int64" ] -> [ "LanguagePrimitives" ; "Int64WithMeasure" ]
|
||||||
|
| [ "System" ; "UInt16" ] -> [ "LanguagePrimitives" ; "UInt16WithMeasure" ]
|
||||||
|
| [ "System" ; "UInt32" ] -> [ "LanguagePrimitives" ; "UInt32WithMeasure" ]
|
||||||
|
| [ "System" ; "UInt64" ] -> [ "LanguagePrimitives" ; "UInt64WithMeasure" ]
|
||||||
|
| l ->
|
||||||
|
let l = String.concat "." l
|
||||||
|
failwith $"unrecognised type for measure: %s{l}"
|
||||||
|
|> SynExpr.createLongIdent
|
||||||
42
WoofWare.Myriad.Plugins/MyriadParamParser.fs
Normal file
42
WoofWare.Myriad.Plugins/MyriadParamParser.fs
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System.Collections.Generic
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal MyriadParamParser =
|
||||||
|
(*
|
||||||
|
An apparent bug in Myriad's argument parsing means that this:
|
||||||
|
|
||||||
|
<MyriadParams>
|
||||||
|
<Foo>bar</Foo>
|
||||||
|
<Baz>quux</Baz>
|
||||||
|
</MyriadParams>
|
||||||
|
|
||||||
|
leads to this:
|
||||||
|
|
||||||
|
Foo = "bar;Baz=quux"
|
||||||
|
|
||||||
|
I'm not going to put effort into fixing Myriad, though, because I want
|
||||||
|
to build something much more powerful instead.
|
||||||
|
*)
|
||||||
|
|
||||||
|
/// Call this with `context.AdditionalParameters`.
|
||||||
|
let render (pars : IDictionary<string, string>) : Map<string, string> =
|
||||||
|
match pars.Count with
|
||||||
|
| 0 -> Map.empty
|
||||||
|
| 1 ->
|
||||||
|
let (KeyValue (key, value)) = pars |> Seq.exactlyOne
|
||||||
|
|
||||||
|
match value.Split ';' |> Seq.toList with
|
||||||
|
| [] -> failwith "LOGIC ERROR"
|
||||||
|
| value :: rest ->
|
||||||
|
rest
|
||||||
|
|> Seq.map (fun v ->
|
||||||
|
let split = v.Split '='
|
||||||
|
split.[0], String.concat "=" split.[1..]
|
||||||
|
)
|
||||||
|
|> Seq.append (Seq.singleton (key, value))
|
||||||
|
|> Map.ofSeq
|
||||||
|
| _ ->
|
||||||
|
// assume the Myriad bug is fixed!
|
||||||
|
pars |> Seq.map (fun (KeyValue (k, v)) -> k, v) |> Map.ofSeq
|
||||||
23
WoofWare.Myriad.Plugins/Parameters.fs
Normal file
23
WoofWare.Myriad.Plugins/Parameters.fs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
type internal DesiredGenerator =
|
||||||
|
| InterfaceMock of isInternal : bool option
|
||||||
|
| JsonParse of extensionMethod : bool option
|
||||||
|
| JsonSerialize of extensionMethod : bool option
|
||||||
|
| HttpClient of extensionMethod : bool option
|
||||||
|
|
||||||
|
static member Parse (s : string) =
|
||||||
|
match s with
|
||||||
|
| "GenerateMock" -> DesiredGenerator.InterfaceMock None
|
||||||
|
| "GenerateMock(true)" -> DesiredGenerator.InterfaceMock (Some true)
|
||||||
|
| "GenerateMock(false)" -> DesiredGenerator.InterfaceMock (Some false)
|
||||||
|
| "JsonParse" -> DesiredGenerator.JsonParse None
|
||||||
|
| "JsonParse(true)" -> DesiredGenerator.JsonParse (Some true)
|
||||||
|
| "JsonParse(false)" -> DesiredGenerator.JsonParse (Some false)
|
||||||
|
| "JsonSerialize" -> DesiredGenerator.JsonSerialize None
|
||||||
|
| "JsonSerialize(true)" -> DesiredGenerator.JsonSerialize (Some true)
|
||||||
|
| "JsonSerialize(false)" -> DesiredGenerator.JsonSerialize (Some false)
|
||||||
|
| "HttpClient" -> DesiredGenerator.HttpClient None
|
||||||
|
| "HttpClient(true)" -> DesiredGenerator.HttpClient (Some true)
|
||||||
|
| "HttpClient(false)" -> DesiredGenerator.HttpClient (Some false)
|
||||||
|
| _ -> failwith $"Failed to parse as a generator specification: %s{s}"
|
||||||
@@ -2,20 +2,12 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
|
|
||||||
open System
|
open System
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
open Myriad.Core
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
/// 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
|
||||||
open Myriad.Core.Ast
|
|
||||||
|
|
||||||
let private removeOption (s : SynField) : SynField =
|
let private removeOption (s : SynField) : SynField =
|
||||||
let (SynField.SynField (synAttributeLists,
|
let (SynField.SynField (synAttributeLists,
|
||||||
@@ -46,15 +38,15 @@ module internal RemoveOptionsGenerator =
|
|||||||
trivia
|
trivia
|
||||||
)
|
)
|
||||||
|
|
||||||
// TODO: this option seems a bit odd
|
|
||||||
let createType
|
let createType
|
||||||
(xmlDoc : PreXmlDoc option)
|
(xmlDoc : PreXmlDoc option)
|
||||||
(accessibility : SynAccess option)
|
(accessibility : SynAccess option)
|
||||||
(generics : SynTyparDecls option)
|
(generics : SynTyparDecls option)
|
||||||
(fields : SynField list)
|
(fields : SynField list)
|
||||||
|
: SynModuleDecl
|
||||||
=
|
=
|
||||||
let fields : SynField list = fields |> List.map removeOption
|
let fields : SynField list = fields |> List.map removeOption
|
||||||
let name = Ident.Create "Short"
|
let name = Ident.create "Short"
|
||||||
|
|
||||||
let record =
|
let record =
|
||||||
{
|
{
|
||||||
@@ -63,138 +55,85 @@ module internal RemoveOptionsGenerator =
|
|||||||
Members = None
|
Members = None
|
||||||
XmlDoc = xmlDoc
|
XmlDoc = xmlDoc
|
||||||
Generics = generics
|
Generics = generics
|
||||||
Accessibility = accessibility
|
TypeAccessibility = accessibility
|
||||||
|
ImplAccessibility = None
|
||||||
|
Attributes = []
|
||||||
}
|
}
|
||||||
|
|
||||||
let typeDecl = AstHelper.defineRecordType record
|
let typeDecl = RecordType.ToAst record
|
||||||
|
|
||||||
SynModuleDecl.Types ([ typeDecl ], range0)
|
SynModuleDecl.Types ([ typeDecl ], range0)
|
||||||
|
|
||||||
let createMaker (withOptionsType : LongIdent) (withoutOptionsType : LongIdent) (fields : SynField list) =
|
let createMaker (withOptionsType : LongIdent) (withoutOptionsType : Ident) (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 inputArg = Ident.create "input"
|
||||||
SynBindingReturnInfo.Create (SynType.LongIdent (SynLongIdent.CreateFromLongIdent withOptionsType))
|
let functionName = Ident.create "shorten"
|
||||||
|
|
||||||
let inputArg = Ident.Create "input"
|
|
||||||
let functionName = Ident.Create "shorten"
|
|
||||||
|
|
||||||
let inputVal =
|
|
||||||
SynValData.SynValData (
|
|
||||||
None,
|
|
||||||
SynValInfo.SynValInfo ([ [ SynArgInfo.CreateId functionName ] ], SynArgInfo.Empty),
|
|
||||||
Some inputArg
|
|
||||||
)
|
|
||||||
|
|
||||||
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 (
|
accessor
|
||||||
SynExpr.CreateAppInfix (
|
|> SynExpr.pipeThroughFunction (
|
||||||
SynExpr.LongIdent (
|
SynExpr.applyFunction
|
||||||
false,
|
(SynExpr.createLongIdent [ "Option" ; "defaultWith" ])
|
||||||
SynLongIdent.SynLongIdent (
|
(SynExpr.createLongIdent' (
|
||||||
[ Ident.Create "op_PipeRight" ],
|
[ withoutOptionsType ]
|
||||||
[],
|
@ [ Ident.create (sprintf "Default%s" fieldData.Ident.idText) ]
|
||||||
[ Some (IdentTrivia.OriginalNotation "|>") ]
|
))
|
||||||
),
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
),
|
|
||||||
accessor
|
|
||||||
),
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateString "Option.defaultWith"),
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.CreateFromLongIdent (
|
|
||||||
withoutOptionsType @ [ Ident.Create (sprintf "Default%s" id.idText) ]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
| _ -> accessor
|
| _ -> accessor
|
||||||
|
|
||||||
(SynLongIdent.CreateFromLongIdent [ id ], true), Some body
|
SynLongIdent.createI fieldData.Ident, body
|
||||||
)
|
)
|
||||||
|> AstHelper.instantiateRecord
|
|> SynExpr.createRecord None
|
||||||
|
|
||||||
let pattern =
|
SynBinding.basic
|
||||||
SynPat.LongIdent (
|
[ functionName ]
|
||||||
SynLongIdent.CreateFromLongIdent [ functionName ],
|
[
|
||||||
None,
|
SynPat.named inputArg.idText
|
||||||
None,
|
|> SynPat.annotateType (SynType.LongIdent (SynLongIdent.createI withoutOptionsType))
|
||||||
SynArgPats.Pats
|
]
|
||||||
[
|
body
|
||||||
SynPat.CreateTyped (
|
|> SynBinding.withXmlDoc xmlDoc
|
||||||
SynPat.CreateNamed inputArg,
|
|> SynBinding.withReturnAnnotation (SynType.LongIdent (SynLongIdent.create withOptionsType))
|
||||||
SynType.LongIdent (SynLongIdent.CreateFromLongIdent withoutOptionsType)
|
|> SynModuleDecl.createLet
|
||||||
)
|
|
||||||
|> SynPat.CreateParen
|
|
||||||
],
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
let binding =
|
let createRecordModule (namespaceId : LongIdent) (typeDefn : RecordType) =
|
||||||
SynBinding.Let (
|
let fieldData = typeDefn.Fields |> List.map SynField.extractWithIdent
|
||||||
isInline = false,
|
|
||||||
isMutable = false,
|
|
||||||
xmldoc = xmlDoc,
|
|
||||||
returnInfo = returnInfo,
|
|
||||||
expr = body,
|
|
||||||
valData = inputVal,
|
|
||||||
pattern = pattern
|
|
||||||
)
|
|
||||||
|
|
||||||
SynModuleDecl.CreateLet [ binding ]
|
let decls =
|
||||||
|
[
|
||||||
|
createType typeDefn.XmlDoc typeDefn.TypeAccessibility typeDefn.Generics typeDefn.Fields
|
||||||
|
createMaker [ Ident.create "Short" ] typeDefn.Name fieldData
|
||||||
|
]
|
||||||
|
|
||||||
let createRecordModule (namespaceId : LongIdent) (typeDefn : SynTypeDefn) =
|
let xmlDoc =
|
||||||
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
|
sprintf "Module containing an option-truncated version of the %s type" typeDefn.Name.idText
|
||||||
typeDefn
|
|> PreXmlDoc.create
|
||||||
|
|
||||||
let (SynComponentInfo (_attributes, typeParams, _constraints, recordId, doc, _preferPostfix, _access, _)) =
|
let info =
|
||||||
synComponentInfo
|
SynComponentInfo.create typeDefn.Name
|
||||||
|
|> SynComponentInfo.withDocString xmlDoc
|
||||||
|
|> SynComponentInfo.addAttributes [ SynAttribute.compilationRepresentation ]
|
||||||
|
|> SynComponentInfo.addAttributes [ SynAttribute.requireQualifiedAccess ]
|
||||||
|
|
||||||
match synTypeDefnRepr with
|
SynModuleDecl.nestedModule info decls
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (accessibility, recordFields, _recordRange), _) ->
|
|> List.singleton
|
||||||
|
|> SynModuleOrNamespace.createNamespace namespaceId
|
||||||
|
|
||||||
let decls =
|
open Myriad.Core
|
||||||
[
|
|
||||||
createType (Some doc) accessibility typeParams recordFields
|
|
||||||
createMaker [ Ident.Create "Short" ] recordId recordFields
|
|
||||||
]
|
|
||||||
|
|
||||||
let attributes =
|
|
||||||
[
|
|
||||||
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
|
|
||||||
SynAttributeList.Create SynAttribute.compilationRepresentation
|
|
||||||
]
|
|
||||||
|
|
||||||
let xmlDoc =
|
|
||||||
recordId
|
|
||||||
|> Seq.map (fun i -> i.idText)
|
|
||||||
|> String.concat "."
|
|
||||||
|> sprintf " Module containing an option-truncated version of the %s type"
|
|
||||||
|> PreXmlDoc.Create
|
|
||||||
|
|
||||||
let info =
|
|
||||||
SynComponentInfo.Create (recordId, attributes = attributes, xmldoc = xmlDoc)
|
|
||||||
|
|
||||||
let mdl = SynModuleDecl.CreateNestedModule (info, decls)
|
|
||||||
|
|
||||||
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ])
|
|
||||||
| _ -> failwithf "Not a record type"
|
|
||||||
|
|
||||||
/// Myriad generator that stamps out a record with option types stripped
|
/// Myriad generator that stamps out a record with option types stripped
|
||||||
/// from the fields at the top level.
|
/// from the fields at the top level.
|
||||||
@@ -208,24 +147,31 @@ type RemoveOptionsGenerator () =
|
|||||||
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 records = Ast.getRecords ast
|
||||||
|
|
||||||
let namespaceAndRecords =
|
let namespaceAndRecords =
|
||||||
records
|
records
|
||||||
|> List.choose (fun (ns, types) ->
|
|> List.collect (fun (ns, ty) ->
|
||||||
match types |> List.filter Ast.hasAttribute<RemoveOptionsAttribute> with
|
ty
|
||||||
| [] -> None
|
|> List.filter (fun record ->
|
||||||
| types -> Some (ns, types)
|
record.Attributes
|
||||||
|
|> List.exists (fun attr ->
|
||||||
|
attr.TypeName.LongIdent
|
||||||
|
|> List.last
|
||||||
|
|> _.idText
|
||||||
|
|> fun s ->
|
||||||
|
if s.EndsWith ("Attribute", StringComparison.Ordinal) then
|
||||||
|
s
|
||||||
|
else
|
||||||
|
$"%s{s}Attribute"
|
||||||
|
|> (=) typeof<RemoveOptionsAttribute>.Name
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|> List.map (fun ty -> ns, ty)
|
||||||
)
|
)
|
||||||
|
|
||||||
let modules =
|
let modules =
|
||||||
namespaceAndRecords
|
namespaceAndRecords
|
||||||
|> List.collect (fun (ns, records) ->
|
|> List.map (fun (ns, record) -> RemoveOptionsGenerator.createRecordModule ns record)
|
||||||
records
|
|
||||||
|> List.map (fun record ->
|
|
||||||
let recordModule = RemoveOptionsGenerator.createRecordModule ns record
|
|
||||||
recordModule
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
Output.Ast modules
|
Output.Ast modules
|
||||||
|
|||||||
@@ -1,22 +1,295 @@
|
|||||||
WoofWare.Myriad.Plugins.GenerateMockAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.AdditionalProperties inherit obj, implements WoofWare.Myriad.Plugins.AdditionalProperties System.IEquatable, System.Collections.IStructuralEquatable - union type with 2 cases
|
||||||
WoofWare.Myriad.Plugins.GenerateMockAttribute..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained inherit WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
WoofWare.Myriad.Plugins.HttpClientAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained.get_Item [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained.Item [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags.Constrained [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags.Never [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Equals [method]: (WoofWare.Myriad.Plugins.AdditionalProperties, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_IsConstrained [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_IsNever [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_Never [static method]: unit -> WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.IsConstrained [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.IsNever [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Never [static property]: [read-only] WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.NewConstrained [static method]: WoofWare.Myriad.Plugins.Definition -> WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ArgParserGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
|
WoofWare.Myriad.Plugins.ArgParserGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition inherit obj, implements WoofWare.Myriad.Plugins.ArrayTypeDefinition System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition..ctor [constructor]: WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Equals [method]: (WoofWare.Myriad.Plugins.ArrayTypeDefinition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.get_Items [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Items [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Parse [static method]: System.Text.Json.Nodes.JsonNode -> WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.Definition inherit obj, implements WoofWare.Myriad.Plugins.Definition System.IEquatable, System.Collections.IStructuralEquatable - union type with 8 cases
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array.get_Item [method]: unit -> WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array.Item [property]: [read-only] WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer.format [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer.get_format [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object.get_Item [method]: unit -> WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object.Item [property]: [read-only] WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Array [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Boolean [static field]: int = 4
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.File [static field]: int = 7
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Handle [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Integer [static field]: int = 6
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Object [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.String [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Unspecified [static field]: int = 5
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Boolean [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Equals [method]: (WoofWare.Myriad.Plugins.Definition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.File [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Boolean [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_File [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsArray [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsBoolean [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsFile [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsHandle [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsInteger [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsObject [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsString [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsUnspecified [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_String [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Unspecified [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsArray [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsBoolean [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsFile [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsHandle [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsInteger [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsObject [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsString [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsUnspecified [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewArray [static method]: WoofWare.Myriad.Plugins.ArrayTypeDefinition -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewHandle [static method]: string -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewInteger [static method]: string option -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewObject [static method]: WoofWare.Myriad.Plugins.ObjectTypeDefinition -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.String [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Unspecified [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
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.HttpMethod inherit obj, implements WoofWare.Myriad.Plugins.HttpMethod System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.HttpMethod System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 8 cases
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Delete [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Get [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Head [static field]: int = 5
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Options [static field]: int = 4
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Patch [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Post [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Put [static field]: int = 6
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Trace [static field]: int = 7
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Delete [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Equals [method]: (WoofWare.Myriad.Plugins.HttpMethod, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Get [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Delete [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Get [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Head [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsDelete [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsGet [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsHead [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsOptions [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPatch [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPost [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPut [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsTrace [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Options [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Patch [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Post [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Put [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Trace [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Head [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsDelete [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsGet [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsHead [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsOptions [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPatch [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPost [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPut [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsTrace [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Options [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Parse [static method]: string -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Patch [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Post [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Put [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.ToDotNet [method]: unit -> System.Net.Http.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Trace [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
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.JsonSerializeAttribute inherit System.Attribute
|
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: bool
|
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: unit
|
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.MimeType inherit obj, implements WoofWare.Myriad.Plugins.MimeType System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.MimeType System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.MimeType.Equals [method]: (WoofWare.Myriad.Plugins.MimeType, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.NewMimeType [static method]: string -> WoofWare.Myriad.Plugins.MimeType
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition inherit obj, implements WoofWare.Myriad.Plugins.ObjectTypeDefinition System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition..ctor [constructor]: (string option, Map<string, WoofWare.Myriad.Plugins.Definition> option, Map<string, System.Text.Json.Nodes.JsonNode>, string list option, WoofWare.Myriad.Plugins.AdditionalProperties option, System.Text.Json.Nodes.JsonObject option)
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.AdditionalProperties [property]: [read-only] WoofWare.Myriad.Plugins.AdditionalProperties option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Description [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Equals [method]: (WoofWare.Myriad.Plugins.ObjectTypeDefinition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Example [property]: [read-only] System.Text.Json.Nodes.JsonObject option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Extras [property]: [read-only] Map<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_AdditionalProperties [method]: unit -> WoofWare.Myriad.Plugins.AdditionalProperties option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Description [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Example [method]: unit -> System.Text.Json.Nodes.JsonObject option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Extras [method]: unit -> Map<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Properties [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Definition> option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Required [method]: unit -> string list option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Properties [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Definition> option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Required [property]: [read-only] string list option
|
||||||
|
WoofWare.Myriad.Plugins.OperationId inherit obj, implements WoofWare.Myriad.Plugins.OperationId System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.OperationId System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Equals [method]: (WoofWare.Myriad.Plugins.OperationId, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.NewOperationId [static method]: string -> WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn inherit obj, implements WoofWare.Myriad.Plugins.ParameterIn System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.ParameterIn System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 4 cases
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Body [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Path [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Query [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Unrecognised [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.get_op [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.op [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Body [static property]: [read-only] WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Equals [method]: (WoofWare.Myriad.Plugins.ParameterIn, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_Body [static method]: unit -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsBody [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsPath [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsQuery [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsUnrecognised [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsBody [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsPath [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsQuery [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsUnrecognised [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewPath [static method]: string -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewQuery [static method]: string -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewUnrecognised [static method]: (string, string) -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Tag [property]: [read-only] int
|
||||||
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
|
||||||
|
WoofWare.Myriad.Plugins.Response inherit obj, implements WoofWare.Myriad.Plugins.Response System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.Response..ctor [constructor]: (string, WoofWare.Myriad.Plugins.Definition)
|
||||||
|
WoofWare.Myriad.Plugins.Response.Description [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Response.Equals [method]: (WoofWare.Myriad.Plugins.Response, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Response.get_Description [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Response.get_Schema [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Response.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.Response
|
||||||
|
WoofWare.Myriad.Plugins.Response.Schema [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Scheme inherit obj, implements WoofWare.Myriad.Plugins.Scheme System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.Scheme System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.Equals [method]: (WoofWare.Myriad.Plugins.Scheme, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.NewScheme [static method]: string -> WoofWare.Myriad.Plugins.Scheme
|
||||||
|
WoofWare.Myriad.Plugins.Scheme.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.Swagger inherit obj, implements WoofWare.Myriad.Plugins.Swagger System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.Swagger..ctor [constructor]: (WoofWare.Myriad.Plugins.MimeType list, WoofWare.Myriad.Plugins.MimeType list, WoofWare.Myriad.Plugins.Scheme list, System.Version, WoofWare.Myriad.Plugins.SwaggerInfo, string, Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>, Map<string, WoofWare.Myriad.Plugins.Definition>, Map<string, WoofWare.Myriad.Plugins.Response>)
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.BasePath [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Consumes [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Definitions [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Equals [method]: (WoofWare.Myriad.Plugins.Swagger, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_BasePath [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Consumes [method]: unit -> WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Definitions [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Info [method]: unit -> WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Paths [method]: unit -> Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Produces [method]: unit -> WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Responses [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Response>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Schemes [method]: unit -> WoofWare.Myriad.Plugins.Scheme list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Swagger [method]: unit -> System.Version
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Info [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Paths [property]: [read-only] Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Produces [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Responses [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Response>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Schemes [property]: [read-only] WoofWare.Myriad.Plugins.Scheme list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Swagger [property]: [read-only] System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerClientGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint inherit obj, implements WoofWare.Myriad.Plugins.SwaggerEndpoint System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint..ctor [constructor]: (WoofWare.Myriad.Plugins.MimeType list option, WoofWare.Myriad.Plugins.MimeType list option, string list, string, WoofWare.Myriad.Plugins.OperationId, WoofWare.Myriad.Plugins.SwaggerParameter list option, Map<int, WoofWare.Myriad.Plugins.Definition>)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Consumes [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerEndpoint, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Consumes [method]: unit -> WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_OperationId [method]: unit -> WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Parameters [method]: unit -> WoofWare.Myriad.Plugins.SwaggerParameter list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Produces [method]: unit -> WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Responses [method]: unit -> Map<int, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Summary [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Tags [method]: unit -> string list
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.OperationId [property]: [read-only] WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Parameters [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerParameter list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerEndpoint
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Produces [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Responses [property]: [read-only] Map<int, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Summary [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Tags [property]: [read-only] string list
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo inherit obj, implements WoofWare.Myriad.Plugins.SwaggerInfo System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo..ctor [constructor]: (string, string, WoofWare.Myriad.Plugins.SwaggerLicense, System.Version)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Description [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerInfo, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Description [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_License [method]: unit -> WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Title [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Version [method]: unit -> System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.License [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Title [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Version [property]: [read-only] System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense inherit obj, implements WoofWare.Myriad.Plugins.SwaggerLicense System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense..ctor [constructor]: (string, System.Uri option, string option)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerLicense, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Identifier [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Url [method]: unit -> System.Uri option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Identifier [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Url [property]: [read-only] System.Uri option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerModule inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerModule.parse [static method]: string -> WoofWare.Myriad.Plugins.Swagger
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter inherit obj, implements WoofWare.Myriad.Plugins.SwaggerParameter System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter..ctor [constructor]: (WoofWare.Myriad.Plugins.Definition, string option, WoofWare.Myriad.Plugins.ParameterIn, string, bool option)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Description [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerParameter, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Description [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_In [method]: unit -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Required [method]: unit -> bool option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Type [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.In [property]: [read-only] WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerParameter
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Required [property]: [read-only] bool option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Type [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
576
WoofWare.Myriad.Plugins/Swagger.fs
Normal file
576
WoofWare.Myriad.Plugins/Swagger.fs
Normal file
@@ -0,0 +1,576 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal JsonHelpers =
|
||||||
|
let inline asString (n : JsonNode) (key : string) : string =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| s -> s.GetValue<string> ()
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asOpt<'ret> (n : JsonNode) (key : string) : 'ret option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| s -> s.GetValue<'ret> () |> Some
|
||||||
|
|
||||||
|
let inline asObj (n : JsonNode) (key : string) : JsonObject =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsObject ()
|
||||||
|
|
||||||
|
let inline asObjOpt (n : JsonNode) (key : string) : JsonObject option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsObject () |> Some
|
||||||
|
|
||||||
|
let inline asArr (n : JsonNode) (key : string) : JsonArray =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsArray ()
|
||||||
|
|
||||||
|
let inline asArrOpt (n : JsonNode) (key : string) : JsonArray option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsArray () |> Some
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asArr'<'v> (n : JsonNode) (key : string) : 'v list =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsArray () |> Seq.map (fun v -> v.GetValue<'v> ()) |> Seq.toList
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asArrOpt'<'v> (n : JsonNode) (key : string) : 'v list option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsArray () |> Seq.map (fun v -> v.GetValue<'v> ()) |> Seq.toList |> Some
|
||||||
|
|
||||||
|
/// A MIME type, like "application/json"
|
||||||
|
type MimeType =
|
||||||
|
/// A MIME type, like "application/json"
|
||||||
|
| MimeType of string
|
||||||
|
|
||||||
|
/// A URL scheme, like "https"
|
||||||
|
type Scheme =
|
||||||
|
/// A URL scheme, like "https"
|
||||||
|
| Scheme of string
|
||||||
|
|
||||||
|
/// "Licence information for the exposed API", whatever that means.
|
||||||
|
type SwaggerLicense =
|
||||||
|
{
|
||||||
|
/// "The license name used for the API", whatever that means.
|
||||||
|
Name : string
|
||||||
|
/// Link to the license used. Mutually exclusive with `Identifier`.
|
||||||
|
Url : Uri option
|
||||||
|
/// SPDX license identifier. Mutually exclusive with `Url`.
|
||||||
|
Identifier : string option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerLicense =
|
||||||
|
let name = asString node "name"
|
||||||
|
let url = asOpt<string> node "url" |> Option.map Uri
|
||||||
|
let identifier = asOpt<string> node "identifier"
|
||||||
|
|
||||||
|
match url, identifier with
|
||||||
|
| Some _, Some _ -> failwith "Invalid license spec: cannot supply both URL and identifier"
|
||||||
|
| _, _ -> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
Url = url
|
||||||
|
Identifier = identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overall information about the API described by this Swagger spec.
|
||||||
|
type SwaggerInfo =
|
||||||
|
{
|
||||||
|
/// Human-readable description of what this Swagger API is for.
|
||||||
|
/// Supports GitHub-flavoured markdown, apparently.
|
||||||
|
Description : string
|
||||||
|
/// Human-readable title of the service to which this is an API.
|
||||||
|
Title : string
|
||||||
|
/// The license applying to this schema. It's very unclear what this means.
|
||||||
|
/// The spec just says:
|
||||||
|
/// "Licence information for the exposed API"
|
||||||
|
License : SwaggerLicense
|
||||||
|
/// The version of this API (not the version of Swagger or the file defining the API!).
|
||||||
|
/// Strictly speaking this can be anything, but I am assuming it's roughly
|
||||||
|
/// SemVer.
|
||||||
|
Version : Version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerInfo =
|
||||||
|
let description = asString node "description"
|
||||||
|
let title = asString node "title"
|
||||||
|
let version = asString node "version" |> Version.Parse
|
||||||
|
let license = asObj node "license" |> SwaggerLicense.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = description
|
||||||
|
Title = title
|
||||||
|
License = license
|
||||||
|
Version = version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "optional unique string used to describe an operation".
|
||||||
|
/// If present, these are assumed to be unique among all operations described
|
||||||
|
/// in the API.
|
||||||
|
type OperationId =
|
||||||
|
/// An "optional unique string used to describe an operation".
|
||||||
|
/// If present, these are assumed to be unique among all operations described
|
||||||
|
/// in the API.
|
||||||
|
| OperationId of string
|
||||||
|
|
||||||
|
/// Round-trip string representation.
|
||||||
|
override this.ToString () =
|
||||||
|
match this with
|
||||||
|
| OperationId.OperationId s -> s
|
||||||
|
|
||||||
|
/// Constraints on the `additionalProperties` (in the JSON schema sense).
|
||||||
|
/// "Additional properties" are properties of a JSON object which were not
|
||||||
|
/// listed in the schema.
|
||||||
|
type AdditionalProperties =
|
||||||
|
/// No additional properties are allowed: all properties must have been
|
||||||
|
/// mentioned in the schema.
|
||||||
|
| Never
|
||||||
|
/// Additional properties are permitted, but if they exist, they must
|
||||||
|
/// match this schema definition.
|
||||||
|
| Constrained of Definition
|
||||||
|
|
||||||
|
/// The Swagger schema lets you define types. An ObjectTypeDefinition
|
||||||
|
/// is specifically the information about types defined as `"type": "object"`.
|
||||||
|
and ObjectTypeDefinition =
|
||||||
|
{
|
||||||
|
/// Human-readable description of the purpose of this type.
|
||||||
|
Description : string option
|
||||||
|
/// Fields which any object must have to satisfy this type.
|
||||||
|
Properties : Map<string, Definition> option
|
||||||
|
/// Extra properties in the type description. In Gitea, these are
|
||||||
|
/// (for example) "x-go-package":"code.gitea.io/gitea/modules/structs".
|
||||||
|
Extras : Map<string, JsonNode>
|
||||||
|
/// List of fields which are required; all other fields are optional.
|
||||||
|
Required : string list option
|
||||||
|
/// Constraints, if any, placed on fields which are not mentioned in
|
||||||
|
/// the schema. If absent, there are no constraints.
|
||||||
|
AdditionalProperties : AdditionalProperties option
|
||||||
|
/// Example of an object which satisfies this schema.
|
||||||
|
Example : JsonObject option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : ObjectTypeDefinition =
|
||||||
|
let description =
|
||||||
|
match asOpt<string> node "description", asOpt<string> node "title" with
|
||||||
|
| None, None -> None
|
||||||
|
| Some v, None
|
||||||
|
| None, Some v -> Some v
|
||||||
|
| Some v1, Some v2 -> failwith "both description and title were given"
|
||||||
|
|
||||||
|
let additionalProperties =
|
||||||
|
match node.["additionalProperties"] with
|
||||||
|
| null -> None
|
||||||
|
| :? JsonValue as p ->
|
||||||
|
if not (p.GetValue<bool> ()) then
|
||||||
|
Some AdditionalProperties.Never
|
||||||
|
else
|
||||||
|
failwith $"additionalProperties should be 'false' or an object, but was: %s{p.ToJsonString ()}"
|
||||||
|
| p ->
|
||||||
|
let p = p.AsObject ()
|
||||||
|
Definition.Parse p |> AdditionalProperties.Constrained |> Some
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
match node.["properties"] with
|
||||||
|
| null -> None
|
||||||
|
| p ->
|
||||||
|
p.AsObject ()
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
let example = asObjOpt node "example"
|
||||||
|
|
||||||
|
let required = asArrOpt'<string> node "required"
|
||||||
|
|
||||||
|
let extras =
|
||||||
|
node.AsObject ()
|
||||||
|
|> Seq.choose (fun (KeyValue (key, value)) ->
|
||||||
|
match key with
|
||||||
|
| "type"
|
||||||
|
| "description"
|
||||||
|
| "title"
|
||||||
|
| "additionalProperties"
|
||||||
|
| "example"
|
||||||
|
| "required"
|
||||||
|
| "properties" -> None
|
||||||
|
| _ -> Some (key, value)
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = description
|
||||||
|
Properties = properties
|
||||||
|
AdditionalProperties = additionalProperties
|
||||||
|
Required = required
|
||||||
|
Extras = extras
|
||||||
|
Example = example
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Swagger schema lets you define types. An ArrayTypeDefinition
|
||||||
|
/// is specifically the information about types defined as `"type": "array"`.
|
||||||
|
and ArrayTypeDefinition =
|
||||||
|
{
|
||||||
|
/// The type is `'a array`; this field describes `'a`.
|
||||||
|
Items : Definition
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonNode into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (n : JsonNode) : ArrayTypeDefinition =
|
||||||
|
let items = asObj n "items" |> Definition.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Items = items
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any definition of a type in the Swagger document. This is basically any
|
||||||
|
/// information associated with the `"type": "blah"` field.
|
||||||
|
and Definition =
|
||||||
|
/// For example, if `"$ref": "#/responses/Blah", then this is "#/responses/Blah".
|
||||||
|
| Handle of string
|
||||||
|
/// A type definition with "type": "object".
|
||||||
|
| Object of ObjectTypeDefinition
|
||||||
|
/// A type definition with "type": "array".
|
||||||
|
| Array of ArrayTypeDefinition
|
||||||
|
/// A type definition with "type": "string".
|
||||||
|
| String
|
||||||
|
/// A type definition with "type": "boolean".
|
||||||
|
| Boolean
|
||||||
|
/// A response without a body has no "schema" specified.
|
||||||
|
| Unspecified
|
||||||
|
/// A type definition with "type": "integer".
|
||||||
|
/// The format is an optional hint which could be e.g. "int64" or "int32".
|
||||||
|
/// https://swagger.io/docs/specification/data-models/data-types/#numbers
|
||||||
|
| Integer of format : string option
|
||||||
|
/// Not a JSON schema type, but a Swagger 2.0 type.
|
||||||
|
| File
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (n : JsonObject) : Definition =
|
||||||
|
match n.["$ref"] |> Option.ofObj with
|
||||||
|
| Some ref -> Definition.Handle (ref.GetValue<string> ())
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let ty = asOpt<string> n "type"
|
||||||
|
|
||||||
|
match ty with
|
||||||
|
| None -> Definition.Unspecified
|
||||||
|
| Some "object" -> ObjectTypeDefinition.Parse n |> Definition.Object
|
||||||
|
| Some "array" -> ArrayTypeDefinition.Parse n |> Definition.Array
|
||||||
|
| Some "string" -> Definition.String
|
||||||
|
| Some "boolean" -> Definition.Boolean
|
||||||
|
| Some "file" -> Definition.File
|
||||||
|
| Some "integer" ->
|
||||||
|
let format = asOpt<string> n "format"
|
||||||
|
Definition.Integer format
|
||||||
|
| Some ty -> failwith $"Unrecognised type: %s{ty}"
|
||||||
|
|
||||||
|
/// REST APIs allow their parameters to be passed in various ways. This describes
|
||||||
|
/// how one single parameter is passed.
|
||||||
|
type ParameterIn =
|
||||||
|
/// The parameter is interpolated into the path, e.g. "/foo/{blah}".
|
||||||
|
/// The "name" is what we replace in the path: e.g. "/foo/{person}" would
|
||||||
|
/// have a name of "person".
|
||||||
|
| Path of name : string
|
||||||
|
/// The parameter is appended to the URL's query params, e.g. "?<name>=blah"
|
||||||
|
| Query of name : string
|
||||||
|
/// The parameter is passed in the body of the HTTP request.
|
||||||
|
| Body
|
||||||
|
/// Some spec that WoofWare.Myriad doesn't support.
|
||||||
|
| Unrecognised of op : string * name : string
|
||||||
|
|
||||||
|
/// Description of a single input parameter to an endpoint.
|
||||||
|
type SwaggerParameter =
|
||||||
|
{
|
||||||
|
/// The type schema to which this parameter must conform.
|
||||||
|
Type : Definition
|
||||||
|
/// Optional human-readable description of this parameter.
|
||||||
|
Description : string option
|
||||||
|
/// How this parameter is passed.
|
||||||
|
In : ParameterIn
|
||||||
|
/// Name of this parameter. For most `In` values, this name is the
|
||||||
|
/// name of the parameter as supplied to the API at runtime, and in WoofWare's
|
||||||
|
/// strongly-typed domain types this information is also contained in the `In` field.
|
||||||
|
/// For `Body` parameters, this is purely for dev-time information.
|
||||||
|
Name : string
|
||||||
|
/// Whether this parameter is required for validation to succeed.
|
||||||
|
/// I think this defaults to "no".
|
||||||
|
Required : bool option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerParameter =
|
||||||
|
let ty =
|
||||||
|
match asObjOpt node "schema" with
|
||||||
|
| None -> Definition.Parse node
|
||||||
|
| Some node -> Definition.Parse node
|
||||||
|
|
||||||
|
let description = asOpt<string> node "description"
|
||||||
|
let name = asString node "name"
|
||||||
|
|
||||||
|
let paramIn =
|
||||||
|
match asString node "in" with
|
||||||
|
| "path" -> ParameterIn.Path name
|
||||||
|
| "query" -> ParameterIn.Query name
|
||||||
|
| "body" -> ParameterIn.Body
|
||||||
|
| f -> ParameterIn.Unrecognised (f, name)
|
||||||
|
|
||||||
|
let required = asOpt<bool> node "required"
|
||||||
|
|
||||||
|
{
|
||||||
|
Type = ty
|
||||||
|
Description = description
|
||||||
|
In = paramIn
|
||||||
|
Name = name
|
||||||
|
Required = required
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "endpoint" is basically a single HTTP verb, applied to some path.
|
||||||
|
type SwaggerEndpoint =
|
||||||
|
{
|
||||||
|
/// The MIME types we should send our request body in.
|
||||||
|
/// This overrides (does not extend) any global definitions on the spec itself.
|
||||||
|
Consumes : MimeType list option
|
||||||
|
/// The MIME types we should expect to receive in response to this request.
|
||||||
|
/// This overrides (does not extend) any global definitions on the spec itself.
|
||||||
|
Produces : MimeType list option
|
||||||
|
/// Arbitrary list of [tags](https://swagger.io/docs/specification/2-0/grouping-operations-with-tags/).
|
||||||
|
Tags : string list
|
||||||
|
/// Human-readable description of the endpoint.
|
||||||
|
Summary : string
|
||||||
|
/// Arbitrary identifier of this endpoint; this must be unique across *all* endpoints
|
||||||
|
/// in this entire spec.
|
||||||
|
OperationId : OperationId
|
||||||
|
/// Parameters that must be supplied at HTTP-request-time to the endpoint.
|
||||||
|
/// (Each parameter knows how it needs to be supplied: e.g. if it's a query parameter or
|
||||||
|
/// if it's interpolated into the path.)
|
||||||
|
Parameters : SwaggerParameter list option
|
||||||
|
/// Map of HTTP response code to the type that we expect to receive in the body if we
|
||||||
|
/// get that response code back.
|
||||||
|
Responses : Map<int, Definition>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (r : JsonObject) : SwaggerEndpoint =
|
||||||
|
let produces = asArrOpt'<string> r "produces" |> Option.map (List.map MimeType)
|
||||||
|
let consumes = asArrOpt'<string> r "consumes" |> Option.map (List.map MimeType)
|
||||||
|
let tags = asArr'<string> r "tags"
|
||||||
|
let summary = asString r "summary"
|
||||||
|
let operationId = asString r "operationId" |> OperationId
|
||||||
|
|
||||||
|
let responses =
|
||||||
|
asObj r "responses"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
Int32.Parse key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let parameters =
|
||||||
|
asArrOpt r "parameters"
|
||||||
|
|> Option.map (fun pars ->
|
||||||
|
pars
|
||||||
|
|> Seq.map (fun par -> par.AsObject () |> SwaggerParameter.Parse)
|
||||||
|
|> Seq.toList
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
Produces = produces
|
||||||
|
Consumes = consumes
|
||||||
|
Tags = tags
|
||||||
|
Summary = summary
|
||||||
|
OperationId = operationId
|
||||||
|
Parameters = parameters
|
||||||
|
Responses = responses
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies the form a response to an endpoint will take if it's complying with this spec.
|
||||||
|
type Response =
|
||||||
|
{
|
||||||
|
/// Human-readable description.
|
||||||
|
Description : string
|
||||||
|
/// Specification of the type to which responses will conform under this spec.
|
||||||
|
Schema : Definition
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (r : JsonObject) : Response =
|
||||||
|
let desc = asString r "description"
|
||||||
|
|
||||||
|
let schema =
|
||||||
|
match asObjOpt r "schema" with
|
||||||
|
| None -> Definition.Unspecified
|
||||||
|
| Some s -> Definition.Parse s
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = desc
|
||||||
|
Schema = schema
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An HTTP method. This is System.Net.Http.HttpMethod, but
|
||||||
|
/// a proper discriminated union.
|
||||||
|
type HttpMethod =
|
||||||
|
/// HTTP Get
|
||||||
|
| Get
|
||||||
|
/// HTTP Post
|
||||||
|
| Post
|
||||||
|
/// HTTP Delete
|
||||||
|
| Delete
|
||||||
|
/// HTTP Patch
|
||||||
|
| Patch
|
||||||
|
/// HTTP Options
|
||||||
|
| Options
|
||||||
|
/// HTTP Head
|
||||||
|
| Head
|
||||||
|
/// HTTP Put
|
||||||
|
| Put
|
||||||
|
/// HTTP Trace
|
||||||
|
| Trace
|
||||||
|
|
||||||
|
/// Convert to the standard library's enum type.
|
||||||
|
member this.ToDotNet () : System.Net.Http.HttpMethod =
|
||||||
|
match this with
|
||||||
|
| HttpMethod.Get -> System.Net.Http.HttpMethod.Get
|
||||||
|
| HttpMethod.Post -> System.Net.Http.HttpMethod.Post
|
||||||
|
| HttpMethod.Delete -> System.Net.Http.HttpMethod.Delete
|
||||||
|
| HttpMethod.Patch -> System.Net.Http.HttpMethod.Patch
|
||||||
|
| HttpMethod.Options -> System.Net.Http.HttpMethod.Options
|
||||||
|
| HttpMethod.Head -> System.Net.Http.HttpMethod.Head
|
||||||
|
| HttpMethod.Put -> System.Net.Http.HttpMethod.Put
|
||||||
|
| HttpMethod.Trace -> System.Net.Http.HttpMethod.Trace
|
||||||
|
|
||||||
|
/// Human-readable string representation.
|
||||||
|
override this.ToString () : string =
|
||||||
|
match this with
|
||||||
|
| HttpMethod.Get -> "Get"
|
||||||
|
| HttpMethod.Post -> "Post"
|
||||||
|
| HttpMethod.Delete -> "Delete"
|
||||||
|
| HttpMethod.Patch -> "Post"
|
||||||
|
| HttpMethod.Options -> "Options"
|
||||||
|
| HttpMethod.Head -> "Head"
|
||||||
|
| HttpMethod.Put -> "Put"
|
||||||
|
| HttpMethod.Trace -> "Trace"
|
||||||
|
|
||||||
|
/// Throws on invalid inputs.
|
||||||
|
static member Parse (s : string) : HttpMethod =
|
||||||
|
if String.Equals (s, "get", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Get
|
||||||
|
elif String.Equals (s, "post", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Post
|
||||||
|
elif String.Equals (s, "patch", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Patch
|
||||||
|
elif String.Equals (s, "delete", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Delete
|
||||||
|
elif String.Equals (s, "head", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Head
|
||||||
|
elif String.Equals (s, "options", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Options
|
||||||
|
elif String.Equals (s, "put", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Put
|
||||||
|
else
|
||||||
|
failwith $"Unrecognised method: %s{s}"
|
||||||
|
|
||||||
|
/// A Swagger API specification.
|
||||||
|
type Swagger =
|
||||||
|
{
|
||||||
|
/// Global collection of MIME types which any endpoint expects to consume its inputs in.
|
||||||
|
/// This may be overridden on any individual endpoint by that endpoint.
|
||||||
|
Consumes : MimeType list
|
||||||
|
/// Global collection of MIME types which any endpoint will produce.
|
||||||
|
/// This may be overridden on any individual endpoint by that endpoint.
|
||||||
|
Produces : MimeType list
|
||||||
|
/// HTTP or HTTPS, for example. Indicates which scheme to access the API on.
|
||||||
|
Schemes : Scheme list
|
||||||
|
/// The version of OpenAPI this specification is written against.
|
||||||
|
/// (As of this writing, we only support 2.0.)
|
||||||
|
Swagger : Version
|
||||||
|
/// General information about this API.
|
||||||
|
Info : SwaggerInfo
|
||||||
|
/// Path under the URI host, which should be prefixed (with trailing slash if necessary)
|
||||||
|
/// to all requests.
|
||||||
|
BasePath : string
|
||||||
|
/// Map from relative path to "what is served at that path".
|
||||||
|
Paths : Map<string, Map<HttpMethod, SwaggerEndpoint>>
|
||||||
|
/// Types defined in the schema. Requests may use these definitions just like in any other JSON schema.
|
||||||
|
/// Key is a domain type name, e.g. "APIError".
|
||||||
|
Definitions : Map<string, Definition>
|
||||||
|
/// Types of each response.
|
||||||
|
/// Key is a domain type name, e.g. "AccessToken".
|
||||||
|
Responses : Map<string, Response>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Swagger =
|
||||||
|
/// Parse a JSON-schema-based specification of a Swagger 2.0 API and
|
||||||
|
/// build the strongly-typed version. Throws on invalid inputs.
|
||||||
|
let parse (s : string) : Swagger =
|
||||||
|
let node = JsonNode.Parse s
|
||||||
|
let consumes = asArr'<string> node "consumes" |> List.map MimeType
|
||||||
|
let produces = asArr'<string> node "produces" |> List.map MimeType
|
||||||
|
let schemes = asArr'<string> node "schemes" |> List.map Scheme
|
||||||
|
let swagger = asString node "swagger" |> Version.Parse
|
||||||
|
let info = asObj node "info" |> SwaggerInfo.Parse
|
||||||
|
let basePath = asString node "basePath"
|
||||||
|
|
||||||
|
let definitions =
|
||||||
|
asObj node "definitions"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let paths =
|
||||||
|
asObj node "paths"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let contents =
|
||||||
|
value.AsObject ()
|
||||||
|
|> Seq.map (fun (KeyValue (endpoint, contents)) ->
|
||||||
|
let contents = contents.AsObject ()
|
||||||
|
HttpMethod.Parse endpoint, SwaggerEndpoint.Parse contents
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
key, contents
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let responses =
|
||||||
|
asObj node "responses"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Response.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
Consumes = consumes
|
||||||
|
Produces = produces
|
||||||
|
Schemes = schemes
|
||||||
|
Swagger = swagger
|
||||||
|
Info = info
|
||||||
|
BasePath = basePath
|
||||||
|
Paths = paths
|
||||||
|
Definitions = definitions
|
||||||
|
Responses = responses
|
||||||
|
}
|
||||||
725
WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs
Normal file
725
WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs
Normal file
@@ -0,0 +1,725 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.IO
|
||||||
|
open System.Threading
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
|
type internal SwaggerClientConfig =
|
||||||
|
{
|
||||||
|
/// Additionally create a mock with `InterfaceMockGenerator`, with the given boolean arg.
|
||||||
|
/// (`None` means "no mock".)
|
||||||
|
CreateMock : bool option
|
||||||
|
ClassName : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal Produces =
|
||||||
|
// TODO: this will cope with decoding JSON, plain text, etc
|
||||||
|
| Produces of string
|
||||||
|
|
||||||
|
type internal Endpoint =
|
||||||
|
{
|
||||||
|
DocString : PreXmlDoc
|
||||||
|
Produces : Produces
|
||||||
|
ReturnType : Definition
|
||||||
|
Method : WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
Operation : OperationId
|
||||||
|
Parameters : SwaggerParameter list
|
||||||
|
Endpoint : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal TypeEntry =
|
||||||
|
{
|
||||||
|
/// If we had to define a type for this, here it is.
|
||||||
|
FSharpDefinition : SynTypeDefn option
|
||||||
|
/// SynType you use in e.g. a type annotation to refer to this type in F# code.
|
||||||
|
Signature : SynType
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal Types =
|
||||||
|
{
|
||||||
|
ByHandle : IReadOnlyDictionary<string, TypeEntry>
|
||||||
|
ByDefinition : IReadOnlyDictionary<Definition, TypeEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SwaggerClientGenerator =
|
||||||
|
let outputFile = FileInfo "/tmp/output.txt"
|
||||||
|
|
||||||
|
// do
|
||||||
|
// use _ = File.Create outputFile.FullName
|
||||||
|
// ()
|
||||||
|
|
||||||
|
let log (line : string) =
|
||||||
|
// use w = outputFile.AppendText ()
|
||||||
|
// w.WriteLine line
|
||||||
|
()
|
||||||
|
|
||||||
|
let renderType (types : Types) (defn : Definition) : SynType option =
|
||||||
|
match types.ByDefinition.TryGetValue defn with
|
||||||
|
| true, v -> Some v.Signature
|
||||||
|
| false, _ ->
|
||||||
|
|
||||||
|
match defn with
|
||||||
|
| Definition.Handle h ->
|
||||||
|
match types.ByHandle.TryGetValue h with
|
||||||
|
| false, _ -> None
|
||||||
|
| true, v -> Some v.Signature
|
||||||
|
| Definition.Object _ -> failwith "should not hit"
|
||||||
|
| Definition.Array _ -> failwith "should not hit"
|
||||||
|
| Definition.Unspecified -> failwith "should not hit"
|
||||||
|
| Definition.String -> SynType.string |> Some
|
||||||
|
| Definition.Boolean -> SynType.bool |> Some
|
||||||
|
| Definition.Integer _ -> SynType.int |> Some
|
||||||
|
| Definition.File -> SynType.createLongIdent' [ "System" ; "IO" ; "Stream" ] |> Some
|
||||||
|
|
||||||
|
/// Returns None if we lacked the information required to do this.
|
||||||
|
/// bigCache is a map of e.g. {"securityDefinition": {Defn : F# type}}.
|
||||||
|
let rec defnToType
|
||||||
|
(anonymousTypeCount : int ref)
|
||||||
|
(handlesMap : Dictionary<string, TypeEntry>)
|
||||||
|
(bigCache : Dictionary<string, Dictionary<Definition, TypeEntry>>)
|
||||||
|
(thisKey : string)
|
||||||
|
(typeName : string option)
|
||||||
|
(d : Definition)
|
||||||
|
: TypeEntry option
|
||||||
|
=
|
||||||
|
let cache =
|
||||||
|
match bigCache.TryGetValue thisKey with
|
||||||
|
| false, _ ->
|
||||||
|
let d = Dictionary ()
|
||||||
|
bigCache.Add (thisKey, d)
|
||||||
|
d
|
||||||
|
| true, d -> d
|
||||||
|
|
||||||
|
let handleKey =
|
||||||
|
match typeName with
|
||||||
|
| None -> None
|
||||||
|
| Some typeName -> $"#/%s{thisKey}/%s{typeName}" |> Some
|
||||||
|
|
||||||
|
match handleKey with
|
||||||
|
| Some hk when handlesMap.ContainsKey hk ->
|
||||||
|
let result = handlesMap.[hk]
|
||||||
|
cache.[d] <- result
|
||||||
|
Some result
|
||||||
|
|
||||||
|
| _ ->
|
||||||
|
|
||||||
|
match cache.TryGetValue d with
|
||||||
|
| true, v ->
|
||||||
|
match handleKey with
|
||||||
|
| None -> ()
|
||||||
|
| Some key -> handlesMap.Add (key, v)
|
||||||
|
|
||||||
|
Some v
|
||||||
|
| false, _ ->
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match d with
|
||||||
|
| Definition.Object obj ->
|
||||||
|
let requiredFields = obj.Required |> Option.defaultValue [] |> Set.ofList
|
||||||
|
|
||||||
|
let namedProperties =
|
||||||
|
obj.Properties
|
||||||
|
|> Option.map Seq.cast
|
||||||
|
|> Option.defaultValue Seq.empty
|
||||||
|
|> Seq.map (fun (KeyValue (fieldName, defn)) ->
|
||||||
|
// TODO this is a horrible hack and is incomplete, e.g. if we contain an array of ourself
|
||||||
|
// Special case for when this is a reference to this very type
|
||||||
|
let isOurself =
|
||||||
|
match defn with
|
||||||
|
| Definition.Handle h ->
|
||||||
|
match h.Split '/' with
|
||||||
|
| [| "#" ; location ; ty |] when location = thisKey && Some ty = typeName ->
|
||||||
|
SynType.named ty |> Some
|
||||||
|
| _ -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
let jsonPropertyName =
|
||||||
|
SynExpr.CreateConst (fieldName : string)
|
||||||
|
|> SynAttribute.create (
|
||||||
|
SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonPropertyName" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
match isOurself with
|
||||||
|
| Some alreadyDone ->
|
||||||
|
let ty =
|
||||||
|
if Set.contains fieldName requiredFields then
|
||||||
|
alreadyDone
|
||||||
|
else
|
||||||
|
SynType.option alreadyDone
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = [ jsonPropertyName ]
|
||||||
|
Type = ty
|
||||||
|
Ident = Some (Ident.createSanitisedTypeName fieldName)
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> Some
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let defn' = defnToType anonymousTypeCount handlesMap bigCache thisKey None defn
|
||||||
|
|
||||||
|
match defn' with
|
||||||
|
| None -> None
|
||||||
|
| Some defn' ->
|
||||||
|
let ty =
|
||||||
|
if Set.contains fieldName requiredFields then
|
||||||
|
defn'.Signature
|
||||||
|
else
|
||||||
|
defn'.Signature |> SynType.option
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = [ jsonPropertyName ]
|
||||||
|
Ident = Ident.createSanitisedTypeName fieldName |> Some
|
||||||
|
Type = ty
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let additionalProperties =
|
||||||
|
match obj.AdditionalProperties with
|
||||||
|
| None ->
|
||||||
|
{
|
||||||
|
Attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonExtensionData" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
]
|
||||||
|
Ident = Ident.create "AdditionalProperties" |> Some
|
||||||
|
Type =
|
||||||
|
SynType.app'
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|
||||||
|
[
|
||||||
|
SynType.string
|
||||||
|
SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
|> Some
|
||||||
|
| Some AdditionalProperties.Never -> Some []
|
||||||
|
| Some (AdditionalProperties.Constrained defn) ->
|
||||||
|
let defn' = defnToType anonymousTypeCount handlesMap bigCache thisKey None defn
|
||||||
|
|
||||||
|
match defn' with
|
||||||
|
| None -> None
|
||||||
|
| Some defn' ->
|
||||||
|
{
|
||||||
|
Attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonExtensionData" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
]
|
||||||
|
Ident = Ident.create "AdditionalProperties" |> Some
|
||||||
|
Type =
|
||||||
|
SynType.app'
|
||||||
|
(SynType.createLongIdent'
|
||||||
|
[ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|
||||||
|
[ SynType.string ; defn'.Signature ]
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
match additionalProperties with
|
||||||
|
| None -> None
|
||||||
|
| Some additionalProperties ->
|
||||||
|
|
||||||
|
match List.allSome namedProperties with
|
||||||
|
| None -> None
|
||||||
|
| Some namedProperties ->
|
||||||
|
|
||||||
|
let fSharpTypeName =
|
||||||
|
match typeName with
|
||||||
|
| None -> $"Type%i{Interlocked.Increment anonymousTypeCount}"
|
||||||
|
| Some typeName -> typeName
|
||||||
|
|
||||||
|
let properties = additionalProperties @ namedProperties
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
if properties.IsEmpty then
|
||||||
|
// sigh, they didn't give us any properties at all; let's make one up
|
||||||
|
{
|
||||||
|
Attrs = []
|
||||||
|
Ident = Some (Ident.create "_SchemaUnspecified")
|
||||||
|
Type = SynType.obj
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
else
|
||||||
|
properties
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
let sci =
|
||||||
|
SynComponentInfo.create (Ident.createSanitisedTypeName fSharpTypeName)
|
||||||
|
|> SynComponentInfo.addAttributes
|
||||||
|
[
|
||||||
|
SynAttribute.create (SynLongIdent.createS' [ "JsonParse" ]) (SynExpr.CreateConst true)
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "JsonSerialize" ])
|
||||||
|
(SynExpr.CreateConst true)
|
||||||
|
]
|
||||||
|
|> fun sci ->
|
||||||
|
match obj.Description with
|
||||||
|
| None -> sci
|
||||||
|
| Some doc -> sci |> SynComponentInfo.withDocString (PreXmlDoc.create doc)
|
||||||
|
|
||||||
|
properties |> SynTypeDefnRepr.record |> SynTypeDefn.create sci
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
Signature = SynType.named fSharpTypeName
|
||||||
|
FSharpDefinition = Some defn
|
||||||
|
}
|
||||||
|
|
||||||
|
defn |> Some
|
||||||
|
|
||||||
|
| Definition.Array elt ->
|
||||||
|
let child = defnToType anonymousTypeCount handlesMap bigCache thisKey None elt.Items
|
||||||
|
|
||||||
|
match child with
|
||||||
|
| None -> None
|
||||||
|
| Some child ->
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
Signature = SynType.list child.Signature
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some defn
|
||||||
|
| Definition.String ->
|
||||||
|
{
|
||||||
|
Signature = SynType.string
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Boolean ->
|
||||||
|
{
|
||||||
|
Signature = SynType.bool
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Unspecified ->
|
||||||
|
{
|
||||||
|
Signature = SynType.unit
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Integer _ ->
|
||||||
|
{
|
||||||
|
Signature = SynType.createLongIdent' [ "int" ]
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.File ->
|
||||||
|
{
|
||||||
|
Signature = SynType.createLongIdent' [ "System" ; "IO" ; "Stream" ]
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Handle s ->
|
||||||
|
let split = s.Split '/' |> List.ofArray
|
||||||
|
|
||||||
|
match split with
|
||||||
|
| [ "#" ; _location ; _handle ] ->
|
||||||
|
match handlesMap.TryGetValue s with
|
||||||
|
| false, _ -> None
|
||||||
|
| true, computed ->
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
FSharpDefinition = None
|
||||||
|
Signature = computed.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
defn |> Some
|
||||||
|
| _ -> failwith $"we don't know how to deal with object handle %s{s}"
|
||||||
|
|
||||||
|
match result with
|
||||||
|
| None -> None
|
||||||
|
| Some result ->
|
||||||
|
|
||||||
|
match handleKey with
|
||||||
|
| None -> ()
|
||||||
|
| Some handleKey -> handlesMap.Add (handleKey, result)
|
||||||
|
|
||||||
|
cache.Add (d, result)
|
||||||
|
Some result
|
||||||
|
|
||||||
|
let instantiateRequiredTypes (types : Types) : SynModuleDecl =
|
||||||
|
types.ByDefinition
|
||||||
|
|> Seq.choose (fun (KeyValue (_defn, typeEntry)) -> typeEntry.FSharpDefinition)
|
||||||
|
|> Seq.toList
|
||||||
|
|> SynModuleDecl.createTypes
|
||||||
|
|
||||||
|
type private IsIn =
|
||||||
|
| Path of str : string
|
||||||
|
| Query of str : string
|
||||||
|
| Body
|
||||||
|
|
||||||
|
let computeType
|
||||||
|
(options : SwaggerClientConfig)
|
||||||
|
(basePath : string)
|
||||||
|
(types : Types)
|
||||||
|
(clientDocString : PreXmlDoc)
|
||||||
|
(endpoints : Endpoint list)
|
||||||
|
: SynModuleDecl list
|
||||||
|
=
|
||||||
|
endpoints
|
||||||
|
|> List.choose (fun ep ->
|
||||||
|
let name = (Ident.createSanitisedTypeName (ep.Operation.ToString ())).idText
|
||||||
|
|
||||||
|
match renderType types ep.ReturnType with
|
||||||
|
| None ->
|
||||||
|
log $"Skipping %O{ep.Operation}: Couldn't render return type: %O{ep.ReturnType}"
|
||||||
|
None
|
||||||
|
| Some returnType ->
|
||||||
|
|
||||||
|
let pars =
|
||||||
|
ep.Parameters
|
||||||
|
|> List.map (fun par ->
|
||||||
|
let inParam =
|
||||||
|
match par.In with
|
||||||
|
| ParameterIn.Unrecognised (f, name) ->
|
||||||
|
log
|
||||||
|
$"Skipping %O{ep.Operation} at %s{ep.Endpoint}: unrecognised In parameter %s{f} with name %s{name}"
|
||||||
|
|
||||||
|
None
|
||||||
|
| ParameterIn.Body -> Some IsIn.Body
|
||||||
|
| ParameterIn.Query name -> Some (IsIn.Query name)
|
||||||
|
| ParameterIn.Path name -> Some (IsIn.Path name)
|
||||||
|
|
||||||
|
match inParam with
|
||||||
|
| None -> None
|
||||||
|
| Some inParam ->
|
||||||
|
|
||||||
|
match renderType types par.Type with
|
||||||
|
| None ->
|
||||||
|
// Couldn't render the return type
|
||||||
|
// failwith "Did not have a type here"
|
||||||
|
log $"Skipping %O{ep.Operation}: Couldn't render parameter: %O{par.Type}"
|
||||||
|
None
|
||||||
|
| Some v -> Some (Ident.createSanitisedParamName par.Name, inParam, v)
|
||||||
|
)
|
||||||
|
|> List.allSome
|
||||||
|
|
||||||
|
match pars with
|
||||||
|
| None -> None
|
||||||
|
| Some pars ->
|
||||||
|
|
||||||
|
let arity =
|
||||||
|
SynValInfo.SynValInfo (
|
||||||
|
[
|
||||||
|
ep.Parameters
|
||||||
|
|> List.map (fun par ->
|
||||||
|
let name = par.Name |> Ident.create |> Some
|
||||||
|
SynArgInfo.SynArgInfo ([], false, name)
|
||||||
|
)
|
||||||
|
|> fun l -> l @ [ SynArgInfo.SynArgInfo ([], true, Some (Ident.create "ct")) ]
|
||||||
|
],
|
||||||
|
SynArgInfo.SynArgInfo ([], false, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
let domain =
|
||||||
|
let ctParam =
|
||||||
|
SynType.signatureParamOfType
|
||||||
|
[]
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Threading" ; "CancellationToken" ])
|
||||||
|
true
|
||||||
|
(Some (Ident.create "ct"))
|
||||||
|
|
||||||
|
let argParams =
|
||||||
|
pars
|
||||||
|
|> List.map (fun (ident, isIn, t) ->
|
||||||
|
let attr : SynAttribute list =
|
||||||
|
match isIn with
|
||||||
|
| IsIn.Path name ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Path" ])
|
||||||
|
(SynExpr.CreateConst name)
|
||||||
|
|> List.singleton
|
||||||
|
| IsIn.Query name ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Query" ])
|
||||||
|
(SynExpr.CreateConst name)
|
||||||
|
|> List.singleton
|
||||||
|
| IsIn.Body ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Body" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
SynType.signatureParamOfType attr t false (Some ident)
|
||||||
|
)
|
||||||
|
|
||||||
|
SynType.tupleNoParen (argParams @ [ ctParam ]) |> Option.get
|
||||||
|
|
||||||
|
let attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; ep.Method.ToString () ])
|
||||||
|
// Gitea, at least, starts with a `/`, which `Uri` then takes to indicate an absolute path.
|
||||||
|
(SynExpr.CreateConst (ep.Endpoint.TrimStart '/'))
|
||||||
|
|
||||||
|
match ep.Produces with
|
||||||
|
| Produces.Produces contentType ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Header" ])
|
||||||
|
// Gitea, at least, starts with a `/`, which `Uri` then takes to indicate an absolute path.
|
||||||
|
(SynExpr.tuple [ SynExpr.CreateConst "Content-Type" ; SynExpr.CreateConst contentType ])
|
||||||
|
]
|
||||||
|
|
||||||
|
returnType
|
||||||
|
|> SynType.task
|
||||||
|
|> SynType.toFun [ domain ]
|
||||||
|
|> SynMemberDefn.abstractMember attrs (SynIdent.createS name) None arity ep.DocString
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> SynTypeDefnRepr.interfaceType
|
||||||
|
|> SynTypeDefn.create (
|
||||||
|
let attrs =
|
||||||
|
[
|
||||||
|
yield SynAttribute.create (SynLongIdent.createS' [ "HttpClient" ]) (SynExpr.CreateConst false)
|
||||||
|
yield
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "BasePath" ])
|
||||||
|
(SynExpr.CreateConst basePath)
|
||||||
|
match options.CreateMock with
|
||||||
|
| None -> ()
|
||||||
|
| Some createMockValue ->
|
||||||
|
yield
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "GenerateMock" ])
|
||||||
|
(SynExpr.CreateConst createMockValue)
|
||||||
|
]
|
||||||
|
|
||||||
|
SynComponentInfo.create (Ident.create ("I" + options.ClassName))
|
||||||
|
|> SynComponentInfo.withDocString clientDocString
|
||||||
|
|> SynComponentInfo.addAttributes attrs
|
||||||
|
)
|
||||||
|
|> List.singleton
|
||||||
|
|> SynModuleDecl.createTypes
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
|
/// Myriad generator that stamps out an interface and class to access a Swagger-specified API.
|
||||||
|
[<MyriadGenerator("swagger-client")>]
|
||||||
|
type SwaggerClientGenerator () =
|
||||||
|
|
||||||
|
interface IMyriadGenerator with
|
||||||
|
member _.ValidInputExtensions = [ ".json" ]
|
||||||
|
|
||||||
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let contents = File.ReadAllText context.InputFilename |> Swagger.parse
|
||||||
|
|
||||||
|
let scheme =
|
||||||
|
let preferred = Scheme "https"
|
||||||
|
|
||||||
|
if List.isEmpty contents.Schemes then
|
||||||
|
failwith "no schemes specified in API spec!"
|
||||||
|
|
||||||
|
if List.contains preferred contents.Schemes then
|
||||||
|
preferred
|
||||||
|
else
|
||||||
|
List.head contents.Schemes
|
||||||
|
|
||||||
|
let clientDocstring = contents.Info.Description |> PreXmlDoc.create
|
||||||
|
|
||||||
|
let basePath = contents.BasePath
|
||||||
|
|
||||||
|
let typeDefs =
|
||||||
|
let bigCache = Dictionary<_, Dictionary<_, _>> ()
|
||||||
|
|
||||||
|
let countAll () =
|
||||||
|
(0, bigCache) ||> Seq.fold (fun count (KeyValue (_, v)) -> count + v.Count)
|
||||||
|
|
||||||
|
let byHandle = Dictionary ()
|
||||||
|
let anonymousTypeCount = ref 0
|
||||||
|
|
||||||
|
let rec go (contents : ((string * Definition) * string) list) =
|
||||||
|
let lastRound = countAll ()
|
||||||
|
|
||||||
|
contents
|
||||||
|
|> List.filter (fun ((name, defn), defnClass) ->
|
||||||
|
let doIt =
|
||||||
|
SwaggerClientGenerator.defnToType
|
||||||
|
anonymousTypeCount
|
||||||
|
byHandle
|
||||||
|
bigCache
|
||||||
|
defnClass
|
||||||
|
(Some name)
|
||||||
|
defn
|
||||||
|
|
||||||
|
match doIt with
|
||||||
|
| None -> true
|
||||||
|
| Some _ -> false
|
||||||
|
)
|
||||||
|
|> fun remaining ->
|
||||||
|
if not remaining.IsEmpty then
|
||||||
|
let currentCount = countAll ()
|
||||||
|
|
||||||
|
if currentCount = lastRound then
|
||||||
|
for (name, remaining), kind in remaining do
|
||||||
|
SwaggerClientGenerator.log $"Remaining: %s{name} (%s{kind})"
|
||||||
|
|
||||||
|
SwaggerClientGenerator.log "--------"
|
||||||
|
|
||||||
|
for KeyValue (handle, defn) in byHandle do
|
||||||
|
SwaggerClientGenerator.log $"Known: %s{handle} %O{defn}"
|
||||||
|
|
||||||
|
// TODO: ohh noooooo the Gitea spec is genuinely circular,
|
||||||
|
// it's impossible to construct a Repository type
|
||||||
|
// we're going to have to somehow detect this case and break the cycle
|
||||||
|
// by artificially making a property optional
|
||||||
|
// :sob: Gitea why are you like this
|
||||||
|
// failwith "Made no further progress rendering types"
|
||||||
|
()
|
||||||
|
else
|
||||||
|
go remaining
|
||||||
|
|
||||||
|
seq {
|
||||||
|
for defnClass in [ "definitions" ; "responses" ] do
|
||||||
|
match defnClass with
|
||||||
|
| "definitions" ->
|
||||||
|
for KeyValue (k, v) in contents.Definitions do
|
||||||
|
yield (k, v), defnClass
|
||||||
|
| "responses" ->
|
||||||
|
for KeyValue (k, v) in contents.Responses do
|
||||||
|
yield (k, v.Schema), defnClass
|
||||||
|
| _ -> failwith "oh no"
|
||||||
|
}
|
||||||
|
|> Seq.toList
|
||||||
|
|> go
|
||||||
|
|
||||||
|
let result = Dictionary ()
|
||||||
|
|
||||||
|
for KeyValue (_container, types) in bigCache do
|
||||||
|
for KeyValue (defn, rendered) in types do
|
||||||
|
result.TryAdd (defn, rendered) |> ignore<bool>
|
||||||
|
|
||||||
|
{
|
||||||
|
ByHandle = byHandle
|
||||||
|
ByDefinition = result :> IReadOnlyDictionary<_, _>
|
||||||
|
}
|
||||||
|
|
||||||
|
let summary =
|
||||||
|
contents.Paths
|
||||||
|
|> Seq.collect (fun (KeyValue (path, endpoints)) ->
|
||||||
|
endpoints
|
||||||
|
|> Seq.choose (fun (KeyValue (method, endpoint)) ->
|
||||||
|
let docstring = endpoint.Summary |> PreXmlDoc.create
|
||||||
|
|
||||||
|
let produces =
|
||||||
|
match endpoint.Produces with
|
||||||
|
| None -> Produces "json"
|
||||||
|
| Some [] -> failwith $"API specified empty Produces: %s{path} (%O{method})"
|
||||||
|
| Some [ MimeType "application/json" ] -> Produces "json"
|
||||||
|
| Some [ MimeType (StartsWith "text/" t) ] -> Produces t
|
||||||
|
| Some [ MimeType s ] ->
|
||||||
|
failwithf
|
||||||
|
$"we don't support non-JSON Produces right now, got: %s{s} (%s{path} %O{method})"
|
||||||
|
| Some (_ :: _) ->
|
||||||
|
failwith $"we don't support multiple Produces right now, at %s{path} (%O{method})"
|
||||||
|
|
||||||
|
let returnType =
|
||||||
|
endpoint.Responses
|
||||||
|
|> Seq.choose (fun (KeyValue (response, defn)) ->
|
||||||
|
if 200 <= response && response < 300 then
|
||||||
|
Some defn
|
||||||
|
else
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let returnType =
|
||||||
|
match returnType with
|
||||||
|
| [ t ] -> Some t
|
||||||
|
| [] -> failwith $"got no successful response results, %s{path} %O{method}"
|
||||||
|
| _ ->
|
||||||
|
SwaggerClientGenerator.log
|
||||||
|
$"Ignoring %s{path} %O{method} due to multiple success responses"
|
||||||
|
// can't be bothered to work out how to deal with multiple success
|
||||||
|
// results right now
|
||||||
|
None
|
||||||
|
|
||||||
|
match returnType with
|
||||||
|
| None -> None
|
||||||
|
| Some returnType ->
|
||||||
|
|
||||||
|
{
|
||||||
|
Method = method
|
||||||
|
Produces = produces
|
||||||
|
DocString = docstring
|
||||||
|
ReturnType = returnType
|
||||||
|
Operation = endpoint.OperationId
|
||||||
|
Parameters = endpoint.Parameters |> Option.defaultValue []
|
||||||
|
Endpoint = path
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let config =
|
||||||
|
let pars = MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|
||||||
|
let pars =
|
||||||
|
pars
|
||||||
|
|> Map.toSeq
|
||||||
|
|> Seq.map (fun (k, v) -> k.ToUpperInvariant (), v)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
if pars.IsEmpty then
|
||||||
|
failwith "No parameters given. You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
|
||||||
|
let createMock =
|
||||||
|
match Map.tryFind "GENERATEMOCKVISIBILITY" pars with
|
||||||
|
| None -> None
|
||||||
|
| Some v ->
|
||||||
|
match v.ToLowerInvariant () with
|
||||||
|
| "internal" -> Some true
|
||||||
|
| "public" -> Some false
|
||||||
|
| _ ->
|
||||||
|
failwith
|
||||||
|
$"Expected GenerateMockVisibility parameter to be 'internal' or 'public', but was: '%s{v.ToLowerInvariant ()}'"
|
||||||
|
|
||||||
|
let className =
|
||||||
|
match Map.tryFind "CLASSNAME" pars with
|
||||||
|
| None -> failwith "You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
| Some v -> v
|
||||||
|
|
||||||
|
{
|
||||||
|
CreateMock = createMock
|
||||||
|
ClassName = className
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty =
|
||||||
|
SwaggerClientGenerator.computeType config basePath typeDefs clientDocstring summary
|
||||||
|
|
||||||
|
[
|
||||||
|
yield
|
||||||
|
SynModuleDecl.Open (
|
||||||
|
SynOpenDeclTarget.ModuleOrNamespace (
|
||||||
|
SynLongIdent.createS' [ "WoofWare" ; "Myriad" ; "Plugins" ],
|
||||||
|
range0
|
||||||
|
),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
yield SwaggerClientGenerator.instantiateRequiredTypes typeDefs
|
||||||
|
yield! ty
|
||||||
|
]
|
||||||
|
|> SynModuleOrNamespace.createNamespace [ Ident.create config.ClassName ]
|
||||||
|
|> List.singleton
|
||||||
|
|> Output.Ast
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
open Myriad.Core
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynAttribute =
|
|
||||||
let internal compilationRepresentation : SynAttribute =
|
|
||||||
{
|
|
||||||
TypeName = SynLongIdent.CreateString "CompilationRepresentation"
|
|
||||||
ArgExpr =
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
false,
|
|
||||||
SynLongIdent.Create [ "CompilationRepresentationFlags" ; "ModuleSuffix" ],
|
|
||||||
None
|
|
||||||
)
|
|
||||||
|> SynExpr.CreateParen
|
|
||||||
Target = None
|
|
||||||
AppliesToGetterAndSetter = false
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
|
|
||||||
let internal autoOpen : SynAttribute =
|
|
||||||
{
|
|
||||||
TypeName = SynLongIdent.CreateString "AutoOpen"
|
|
||||||
ArgExpr = SynExpr.CreateConst SynConst.Unit
|
|
||||||
Target = None
|
|
||||||
AppliesToGetterAndSetter = false
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
@@ -1,275 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Myriad.Core
|
|
||||||
open Myriad.Core.Ast
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
type internal CompExprBinding =
|
|
||||||
| LetBang of varName : string * rhs : SynExpr
|
|
||||||
| Let of varName : string * rhs : SynExpr
|
|
||||||
| Use of varName : string * rhs : SynExpr
|
|
||||||
| Do of body : SynExpr
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynExpr =
|
|
||||||
|
|
||||||
/// {expr} |> {func}
|
|
||||||
let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateAppInfix (
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
[ Ident.Create "op_PipeRight" ],
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "|>") ]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
expr
|
|
||||||
),
|
|
||||||
func
|
|
||||||
)
|
|
||||||
|
|
||||||
/// if {cond} then {trueBranch} else {falseBranch}
|
|
||||||
/// Note that this function puts the trueBranch last, for pipelining convenience:
|
|
||||||
/// we assume that the `else` branch is more like an error case and is less interesting.
|
|
||||||
let ifThenElse (cond : SynExpr) (falseBranch : SynExpr) (trueBranch : SynExpr) : SynExpr =
|
|
||||||
SynExpr.IfThenElse (
|
|
||||||
cond,
|
|
||||||
trueBranch,
|
|
||||||
Some falseBranch,
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
false,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
IfKeyword = range0
|
|
||||||
IsElif = false
|
|
||||||
ThenKeyword = range0
|
|
||||||
ElseKeyword = Some range0
|
|
||||||
IfToThenRange = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/// try {body} with | {exc} as exc -> {handler}
|
|
||||||
let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr =
|
|
||||||
let clause =
|
|
||||||
SynMatchClause.Create (SynPat.As (exc, SynPat.CreateNamed (Ident.Create "exc"), range0), None, handler)
|
|
||||||
|
|
||||||
SynExpr.TryWith (
|
|
||||||
body,
|
|
||||||
[ clause ],
|
|
||||||
range0,
|
|
||||||
DebugPointAtTry.Yes range0,
|
|
||||||
DebugPointAtWith.Yes range0,
|
|
||||||
{
|
|
||||||
TryKeyword = range0
|
|
||||||
TryToWithRange = range0
|
|
||||||
WithKeyword = range0
|
|
||||||
WithToEndRange = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/// {a} = {b}
|
|
||||||
let equals (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateAppInfix (
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
Ident.CreateLong "op_Equality",
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "=") ]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
a
|
|
||||||
),
|
|
||||||
b
|
|
||||||
)
|
|
||||||
|
|
||||||
/// {a} + {b}
|
|
||||||
let plus (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateAppInfix (
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
Ident.CreateLong "op_Addition",
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "+") ]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
a
|
|
||||||
),
|
|
||||||
b
|
|
||||||
)
|
|
||||||
|
|
||||||
let rec stripOptionalParen (expr : SynExpr) : SynExpr =
|
|
||||||
match expr with
|
|
||||||
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen 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}
|
|
||||||
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.DotGet (
|
|
||||||
obj,
|
|
||||||
range0,
|
|
||||||
SynLongIdent.SynLongIdent (id = [ Ident.Create meth ], dotRanges = [], trivia = [ None ]),
|
|
||||||
range0
|
|
||||||
),
|
|
||||||
arg
|
|
||||||
)
|
|
||||||
|
|
||||||
/// {obj}.{meth}()
|
|
||||||
let callMethod (meth : string) (obj : SynExpr) : SynExpr =
|
|
||||||
callMethodArg meth (SynExpr.CreateConst SynConst.Unit) obj
|
|
||||||
|
|
||||||
/// {obj}.{meth}<ty>()
|
|
||||||
let callGenericMethod (meth : string) (ty : string) (obj : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.TypeApp (
|
|
||||||
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0),
|
|
||||||
range0,
|
|
||||||
[ SynType.CreateLongIdent ty ],
|
|
||||||
[],
|
|
||||||
Some range0,
|
|
||||||
range0,
|
|
||||||
range0
|
|
||||||
),
|
|
||||||
SynExpr.CreateConst SynConst.Unit
|
|
||||||
)
|
|
||||||
|
|
||||||
let index (property : SynExpr) (obj : SynExpr) : SynExpr =
|
|
||||||
SynExpr.DotIndexedGet (obj, property, range0, range0)
|
|
||||||
|
|
||||||
/// (fun {varName} -> {body})
|
|
||||||
let createLambda (varName : string) (body : SynExpr) : SynExpr =
|
|
||||||
let parsedDataPat = [ SynPat.CreateNamed (Ident.Create varName) ]
|
|
||||||
|
|
||||||
SynExpr.Lambda (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
SynSimplePats.Create [ SynSimplePat.CreateId (Ident.Create varName) ],
|
|
||||||
body,
|
|
||||||
Some (parsedDataPat, body),
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
ArrowRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> SynExpr.CreateParen
|
|
||||||
|
|
||||||
let reraise : SynExpr =
|
|
||||||
SynExpr.CreateApp (SynExpr.CreateIdent (Ident.Create "reraise"), SynExpr.CreateConst SynConst.Unit)
|
|
||||||
|
|
||||||
/// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct)
|
|
||||||
let startAsTask (body : SynExpr) =
|
|
||||||
let lambda =
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "StartAsTask" ]),
|
|
||||||
SynExpr.CreateParenedTuple
|
|
||||||
[
|
|
||||||
SynExpr.CreateLongIdent (SynLongIdent.CreateString "a")
|
|
||||||
equals
|
|
||||||
(SynExpr.LongIdent (true, SynLongIdent.CreateString "cancellationToken", None, range0))
|
|
||||||
(SynExpr.CreateLongIdent (SynLongIdent.CreateString "ct"))
|
|
||||||
]
|
|
||||||
)
|
|
||||||
|> createLambda "a"
|
|
||||||
|
|
||||||
pipeThroughFunction lambda body
|
|
||||||
|
|
||||||
/// {compExpr} { {lets} ; return {ret} }
|
|
||||||
let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr =
|
|
||||||
let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0)
|
|
||||||
|
|
||||||
let contents : SynExpr =
|
|
||||||
(retStatement, List.rev lets)
|
|
||||||
||> List.fold (fun state binding ->
|
|
||||||
match binding with
|
|
||||||
| LetBang (lhs, rhs) ->
|
|
||||||
SynExpr.LetOrUseBang (
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
SynPat.CreateNamed (Ident.Create lhs),
|
|
||||||
rhs,
|
|
||||||
[],
|
|
||||||
state,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
EqualsRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
| Let (lhs, rhs) ->
|
|
||||||
SynExpr.LetOrUse (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
[ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ],
|
|
||||||
state,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
SynExprLetOrUseTrivia.InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
| Use (lhs, rhs) ->
|
|
||||||
SynExpr.LetOrUse (
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
[ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ],
|
|
||||||
state,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
SynExprLetOrUseTrivia.InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
| Do body -> SynExpr.CreateSequential [ SynExpr.Do (body, range0) ; state ]
|
|
||||||
)
|
|
||||||
|
|
||||||
SynExpr.CreateApp (
|
|
||||||
SynExpr.CreateIdent (Ident.Create compExpr),
|
|
||||||
SynExpr.ComputationExpr (false, contents, range0)
|
|
||||||
)
|
|
||||||
|
|
||||||
/// {expr} |> Async.AwaitTask
|
|
||||||
let awaitTask (expr : SynExpr) : SynExpr =
|
|
||||||
expr
|
|
||||||
|> pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "AwaitTask" ]))
|
|
||||||
|
|
||||||
/// {ident}.ToString ()
|
|
||||||
/// with special casing for some types like DateTime
|
|
||||||
let toString (ty : SynType) (ident : SynExpr) =
|
|
||||||
match ty with
|
|
||||||
| DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-dd")
|
|
||||||
| DateTime ->
|
|
||||||
ident
|
|
||||||
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
|
|
||||||
| _ -> callMethod "ToString" ident
|
|
||||||
|
|
||||||
let synBindingTriviaZero (isMember : bool) =
|
|
||||||
{
|
|
||||||
SynBindingTrivia.EqualsRange = Some range0
|
|
||||||
InlineKeyword = None
|
|
||||||
LeadingKeyword =
|
|
||||||
if isMember then
|
|
||||||
SynLeadingKeyword.Member range0
|
|
||||||
else
|
|
||||||
SynLeadingKeyword.Let range0
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
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
|
|
||||||
11
WoofWare.Myriad.Plugins/Text.fs
Normal file
11
WoofWare.Myriad.Plugins/Text.fs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal Text =
|
||||||
|
let (|StartsWith|_|) (prefix : string) (s : string) : string option =
|
||||||
|
if s.StartsWith (prefix, StringComparison.Ordinal) then
|
||||||
|
Some (s.Substring prefix.Length)
|
||||||
|
else
|
||||||
|
None
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net6.0</TargetFramework>
|
<TargetFramework>net6.0</TargetFramework>
|
||||||
@@ -15,24 +15,35 @@
|
|||||||
<WarnOn>FS3559</WarnOn>
|
<WarnOn>FS3559</WarnOn>
|
||||||
<PackageId>WoofWare.Myriad.Plugins</PackageId>
|
<PackageId>WoofWare.Myriad.Plugins</PackageId>
|
||||||
<PackageIcon>logo.png</PackageIcon>
|
<PackageIcon>logo.png</PackageIcon>
|
||||||
|
<NoWarn>NU5118</NoWarn>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Myriad.Core" Version="0.8.3"/>
|
<PackageReference Include="Myriad.Core" Version="0.8.3" />
|
||||||
|
<PackageReference Include="TypeEquality" Version="0.3.0" />
|
||||||
|
<PackageReference Include="WoofWare.Whippet.Fantomas" Version="0.5.1" />
|
||||||
<!-- 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="AstHelper.fs"/>
|
<Compile Include="List.fs"/>
|
||||||
<Compile Include="SynExpr.fs"/>
|
<Compile Include="Text.fs" />
|
||||||
<Compile Include="SynType.fs" />
|
<Compile Include="Measure.fs" />
|
||||||
<Compile Include="SynAttribute.fs"/>
|
<Compile Include="AstHelper.fs" />
|
||||||
|
<Compile Include="Parameters.fs" />
|
||||||
<Compile Include="RemoveOptionsGenerator.fs"/>
|
<Compile Include="RemoveOptionsGenerator.fs"/>
|
||||||
<Compile Include="InterfaceMockGenerator.fs" />
|
<Compile Include="MyriadParamParser.fs" />
|
||||||
<Compile Include="JsonSerializeGenerator.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" />
|
||||||
|
<Compile Include="ArgParserGenerator.fs" />
|
||||||
|
<Compile Include="Swagger.fs" />
|
||||||
|
<Compile Include="SwaggerClientGenerator.fs" />
|
||||||
|
<None Include="ApacheLicence.txt" />
|
||||||
<EmbeddedResource Include="version.json"/>
|
<EmbeddedResource Include="version.json"/>
|
||||||
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
||||||
<None Include="..\README.md">
|
<None Include="..\README.md">
|
||||||
@@ -45,4 +56,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)\*.dll" Pack="true" PackagePath="lib\$(TargetFramework)"/>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
{
|
{
|
||||||
"version": "1.4",
|
"version": "4.0",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
"pathFilters": null
|
"pathFilters": [
|
||||||
|
"./",
|
||||||
|
":/WoofWare.Myriad.Plugins.Attributes",
|
||||||
|
":^/WoofWare.Myriad.Plugins.Attributes/Test",
|
||||||
|
":/global.json",
|
||||||
|
":/README.md",
|
||||||
|
":/Directory.Build.props"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
@@ -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.14.0]" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
12
flake.lock
generated
12
flake.lock
generated
@@ -5,11 +5,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1701680307,
|
"lastModified": 1731533236,
|
||||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1706367331,
|
"lastModified": 1744868846,
|
||||||
"narHash": "sha256-AqgkGHRrI6h/8FWuVbnkfFmXr4Bqsr4fV23aISqj/xg=",
|
"narHash": "sha256-5RJTdUHDmj12Qsv7XOhuospjAjATNiTMElplWnJE9Hs=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "160b762eda6d139ac10ae081f8f78d640dd523eb",
|
"rev": "ebe4301cbd8f81c4f8d3244b3632338bbeb6d49c",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
|||||||
70
flake.nix
70
flake.nix
@@ -7,7 +7,6 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
self,
|
|
||||||
nixpkgs,
|
nixpkgs,
|
||||||
flake-utils,
|
flake-utils,
|
||||||
...
|
...
|
||||||
@@ -15,10 +14,10 @@
|
|||||||
flake-utils.lib.eachDefaultSystem (system: let
|
flake-utils.lib.eachDefaultSystem (system: let
|
||||||
pkgs = nixpkgs.legacyPackages.${system};
|
pkgs = nixpkgs.legacyPackages.${system};
|
||||||
pname = "WoofWare.Myriad.Plugins";
|
pname = "WoofWare.Myriad.Plugins";
|
||||||
dotnet-sdk = pkgs.dotnet-sdk_8;
|
dotnet-sdk = pkgs.dotnetCorePackages.sdk_9_0;
|
||||||
dotnet-runtime = pkgs.dotnetCorePackages.runtime_8_0;
|
dotnet-runtime = pkgs.dotnetCorePackages.runtime_9_0;
|
||||||
version = "0.1";
|
version = "0.1";
|
||||||
dotnetTool = dllOverride: toolName: toolVersion: sha256:
|
dotnetTool = dllOverride: toolName: toolVersion: hash:
|
||||||
pkgs.stdenvNoCC.mkDerivation rec {
|
pkgs.stdenvNoCC.mkDerivation rec {
|
||||||
name = toolName;
|
name = toolName;
|
||||||
version = toolVersion;
|
version = toolVersion;
|
||||||
@@ -26,64 +25,43 @@
|
|||||||
src = pkgs.fetchNuGet {
|
src = pkgs.fetchNuGet {
|
||||||
pname = name;
|
pname = name;
|
||||||
version = version;
|
version = version;
|
||||||
sha256 = sha256;
|
hash = hash;
|
||||||
installPhase = ''mkdir -p $out/bin && cp -r tools/net6.0/any/* $out/bin'';
|
installPhase = ''mkdir -p $out/bin && cp -r tools/net*/any/* $out/bin'';
|
||||||
};
|
};
|
||||||
installPhase = let
|
installPhase = let
|
||||||
dll =
|
dll =
|
||||||
if isNull dllOverride
|
if isNull dllOverride
|
||||||
then name
|
then name
|
||||||
else dllOverride;
|
else dllOverride;
|
||||||
in ''
|
in
|
||||||
runHook preInstall
|
# fsharp-analyzers requires the .NET SDK at runtime, so we use that instead of dotnet-runtime.
|
||||||
mkdir -p "$out/lib"
|
''
|
||||||
cp -r ./bin/* "$out/lib"
|
runHook preInstall
|
||||||
makeWrapper "${dotnet-runtime}/bin/dotnet" "$out/bin/${name}" --add-flags "$out/lib/${dll}.dll"
|
mkdir -p "$out/lib"
|
||||||
runHook postInstall
|
cp -r ./bin/* "$out/lib"
|
||||||
'';
|
makeWrapper "${dotnet-sdk}/bin/dotnet" "$out/bin/${name}" --set DOTNET_HOST_PATH "${dotnet-sdk}/bin/dotnet" --add-flags "$out/lib/${dll}.dll"
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
packages = {
|
packages = let
|
||||||
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;
|
deps = builtins.fromJSON (builtins.readFile ./nix/deps.json);
|
||||||
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;
|
in {
|
||||||
fetchDeps = let
|
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") deps)).hash;
|
||||||
flags = [];
|
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") deps)).hash;
|
||||||
runtimeIds = ["win-x64"] ++ map (system: pkgs.dotnetCorePackages.systemToDotnetRid system) dotnet-sdk.meta.platforms;
|
|
||||||
in
|
|
||||||
pkgs.writeShellScriptBin "fetch-${pname}-deps" (builtins.readFile (pkgs.substituteAll {
|
|
||||||
src = ./nix/fetchDeps.sh;
|
|
||||||
pname = pname;
|
|
||||||
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"];
|
|
||||||
testProjectFiles = ["./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj"];
|
|
||||||
rids = pkgs.lib.concatStringsSep "\" \"" runtimeIds;
|
|
||||||
packages = dotnet-sdk.packages;
|
|
||||||
storeSrc = pkgs.srcOnly {
|
|
||||||
src = ./.;
|
|
||||||
pname = pname;
|
|
||||||
version = version;
|
|
||||||
};
|
|
||||||
}));
|
|
||||||
default = pkgs.buildDotnetModule {
|
default = pkgs.buildDotnetModule {
|
||||||
pname = pname;
|
inherit pname version dotnet-sdk dotnet-runtime;
|
||||||
name = "WoofWare.Myriad.Plugins";
|
name = "WoofWare.Myriad.Plugins";
|
||||||
version = version;
|
|
||||||
src = ./.;
|
src = ./.;
|
||||||
projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj";
|
projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj";
|
||||||
nugetDeps = ./nix/deps.nix;
|
testProjectFile = "./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj";
|
||||||
|
disabledTests = ["WoofWare.Myriad.Plugins.Test.TestSurface.CheckVersionAgainstRemote"];
|
||||||
|
nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json`
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
dotnet-sdk = dotnet-sdk;
|
|
||||||
dotnet-runtime = dotnet-runtime;
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
devShell = pkgs.mkShell {
|
devShell = pkgs.mkShell {
|
||||||
buildInputs = with pkgs; [
|
buildInputs = [dotnet-sdk];
|
||||||
(with dotnetCorePackages;
|
|
||||||
combinePackages [
|
|
||||||
dotnet-sdk_8
|
|
||||||
dotnetPackages.Nuget
|
|
||||||
])
|
|
||||||
];
|
|
||||||
packages = [
|
packages = [
|
||||||
pkgs.alejandra
|
pkgs.alejandra
|
||||||
pkgs.nodePackages.markdown-link-check
|
pkgs.nodePackages.markdown-link-check
|
||||||
|
|||||||
12
global.json
12
global.json
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"sdk": {
|
"sdk": {
|
||||||
"version": "8.0.100",
|
"version": "9.0.100",
|
||||||
"rollForward": "latestFeature"
|
"rollForward": "latestMajor"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
387
nix/deps.json
Normal file
387
nix/deps.json
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"pname": "ApiSurface",
|
||||||
|
"version": "4.1.20",
|
||||||
|
"hash": "sha256-koWgO9FC9ax+Ij56ug8kxeyknl0yhLqnNLOUdxtqqo4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "fantomas",
|
||||||
|
"version": "7.0.1",
|
||||||
|
"hash": "sha256-2aGD6Kjh83gmssRqqZ/Uihi7VbNqNUelX4otIfCuhTI="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Fantomas.Core",
|
||||||
|
"version": "6.1.1",
|
||||||
|
"hash": "sha256-FcTLHQFvKkQY/kV08jhhy/St/+FmXpp3epp/R3zUXMA="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Fantomas.FCS",
|
||||||
|
"version": "6.1.1",
|
||||||
|
"hash": "sha256-NuZ8msPEHYA8T3EYREB28F1RcNgUU8V54eg2+UttYxw="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "FsCheck",
|
||||||
|
"version": "3.2.0",
|
||||||
|
"hash": "sha256-ksZ4vLgWwyQOzFuK2BczdtDtWWYmedG7UBAg4pYuI8g="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "fsharp-analyzers",
|
||||||
|
"version": "0.30.0",
|
||||||
|
"hash": "sha256-7oaSwpHAU1opzpz4szLU/gDaJC/ww9eiFkPu0nr4Mj4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "FSharp.Core",
|
||||||
|
"version": "4.3.4",
|
||||||
|
"hash": "sha256-styyo+6mJy+yxE0NZG/b1hxkAjPOnJfMgd9zWzCJ5uk="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "FSharp.Core",
|
||||||
|
"version": "6.0.1",
|
||||||
|
"hash": "sha256-Ehsgt3nCJijpaVuJguC1TPVEKSkJd6PSc07D2ZQSemI="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "FSharp.Core",
|
||||||
|
"version": "9.0.202",
|
||||||
|
"hash": "sha256-64Gub0qemmCoMa1tDus6TeTuB1+5sHfE6KD2j4o84mA="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "FsUnit",
|
||||||
|
"version": "7.0.1",
|
||||||
|
"hash": "sha256-K85CIdxMeFSHEKZk6heIXp/oFjWAn7dBILKrw49pJUY="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.ApplicationInsights",
|
||||||
|
"version": "2.22.0",
|
||||||
|
"hash": "sha256-mUQ63atpT00r49ca50uZu2YCiLg3yd6r3HzTryqcuEA="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.AspNetCore.App.Ref",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-9jDkWbjw/nd8yqdzVTagCuqr6owJ/DUMi4BlUZT4hWU="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.AspNetCore.App.Runtime.linux-arm64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-JQULJyF0ivLoUU1JaFfK/HHg+/qzpN7V2RR2Cc+WlQ4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.AspNetCore.App.Runtime.linux-x64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-zUsVIpV481vMLAXaLEEUpEMA9/f1HGOnvaQnaWdzlyY="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.AspNetCore.App.Runtime.osx-arm64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-2seqZcz0JeUjkzh3QcGa9TcJ4LUafpFjTRk+Nm8T6T0="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.AspNetCore.App.Runtime.osx-x64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-yxLafxiBKkvfkDggPk0P9YZIHBkDJOsFTO7/V9mEHuU="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.CodeCoverage",
|
||||||
|
"version": "17.13.0",
|
||||||
|
"hash": "sha256-GKrIxeyQo5Az1mztfQgea1kGtJwonnNOrXK/0ULfu8o="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NET.Test.Sdk",
|
||||||
|
"version": "17.13.0",
|
||||||
|
"hash": "sha256-sc2wvyV8cGm1FrNP2GGHEI584RCvRPu15erYCsgw5QY="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Host.linux-arm64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-9lC/LYnthYhjkWWz2kkFCvlA5LJOv11jdt59SDnpdy0="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Host.linux-x64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-VFRDzx7LJuvI5yzKdGmw/31NYVbwHWPKQvueQt5xc10="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Host.osx-arm64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-DaSWwYACJGolEBuMhzDVCj/rQTdDt061xCVi+gyQnuo="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Host.osx-x64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-FrRny9EI6HKCKQbu6mcLj5w4ooSRrODD4Vj2ZMGnMd4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Ref",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-9LZgVoIFF8qNyUu8kdJrYGLutMF/cL2K82HN2ywwlx8="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Runtime.linux-arm64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-k3rxvUhCEU0pVH8KgEMtkPiSOibn+nBh+0zT2xIfId8="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Runtime.linux-x64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-U8wJ2snSDFqeAgDVLXjnniidC7Cr5aJ1/h/BMSlyu0c="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Runtime.osx-arm64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-UfLcrL2Gj/OLz0s92Oo+OCJeDpZFAcQLPLiSNND8D5Y="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.App.Runtime.osx-x64",
|
||||||
|
"version": "6.0.36",
|
||||||
|
"hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.Platforms",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.Platforms",
|
||||||
|
"version": "1.1.1",
|
||||||
|
"hash": "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.Platforms",
|
||||||
|
"version": "2.0.0",
|
||||||
|
"hash": "sha256-IEvBk6wUXSdyCnkj6tHahOJv290tVVT8tyemYcR0Yro="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.Targets",
|
||||||
|
"version": "1.1.0",
|
||||||
|
"hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.NETCore.Targets",
|
||||||
|
"version": "1.1.3",
|
||||||
|
"hash": "sha256-WLsf1NuUfRWyr7C7Rl9jiua9jximnVvzy6nk2D2bVRc="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.Testing.Extensions.Telemetry",
|
||||||
|
"version": "1.5.3",
|
||||||
|
"hash": "sha256-bIXwPSa3jkr2b6xINOqMUs6/uj/r4oVFM7xq3uVIZDU="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
|
||||||
|
"version": "1.5.3",
|
||||||
|
"hash": "sha256-IfMRfcyaIKEMRtx326ICKtinDBEfGw/Sv8ZHawJ96Yc="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.Testing.Extensions.VSTestBridge",
|
||||||
|
"version": "1.5.3",
|
||||||
|
"hash": "sha256-XpM/yFjhLSsuzyDV+xKubs4V1zVVYiV05E0+N4S1h0g="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.Testing.Platform",
|
||||||
|
"version": "1.5.3",
|
||||||
|
"hash": "sha256-y61Iih6w5D79dmrj2V675mcaeIiHoj1HSa1FRit2BLM="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.Testing.Platform.MSBuild",
|
||||||
|
"version": "1.5.3",
|
||||||
|
"hash": "sha256-YspvjE5Jfi587TAfsvfDVJXNrFOkx1B3y1CKV6m7YLY="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.TestPlatform.ObjectModel",
|
||||||
|
"version": "17.12.0",
|
||||||
|
"hash": "sha256-3XBHBSuCxggAIlHXmKNQNlPqMqwFlM952Av6RrLw1/w="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.TestPlatform.ObjectModel",
|
||||||
|
"version": "17.13.0",
|
||||||
|
"hash": "sha256-6S0fjfj8vA+h6dJVNwLi6oZhYDO/I/6hBZaq2VTW+Uk="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Microsoft.TestPlatform.TestHost",
|
||||||
|
"version": "17.13.0",
|
||||||
|
"hash": "sha256-L/CJzou7dhmShUgXq3aXL3CaLTJll17Q+JY2DBdUUpo="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Myriad.Core",
|
||||||
|
"version": "0.8.3",
|
||||||
|
"hash": "sha256-vBOxfq8QriX/yUtaXN69rEQaY/psRNJWxqATLidrt2g="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Myriad.Sdk",
|
||||||
|
"version": "0.8.3",
|
||||||
|
"hash": "sha256-7O397WKhskKOvE3MkJT37BvxorDWngDR6gTUogtDZ2M="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Nerdbank.GitVersioning",
|
||||||
|
"version": "3.8.38-alpha",
|
||||||
|
"hash": "sha256-gPMrVbjOZxXoofczF/pn6eVkLhjVSJIyQrLO2oljrDc="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NETStandard.Library",
|
||||||
|
"version": "2.0.3",
|
||||||
|
"hash": "sha256-Prh2RPebz/s8AzHb2sPHg3Jl8s31inv9k+Qxd293ybo="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Newtonsoft.Json",
|
||||||
|
"version": "13.0.1",
|
||||||
|
"hash": "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "Newtonsoft.Json",
|
||||||
|
"version": "13.0.3",
|
||||||
|
"hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NuGet.Common",
|
||||||
|
"version": "6.13.2",
|
||||||
|
"hash": "sha256-ASLa/Jigg5Eop0ZrXPl98RW2rxnJRC7pbbxhuV74hFw="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NuGet.Configuration",
|
||||||
|
"version": "6.13.2",
|
||||||
|
"hash": "sha256-z8VW1YdRDanyyRTDYRvRkSv/XPR3c/hMM1y8cNNjx0Y="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NuGet.Frameworks",
|
||||||
|
"version": "6.13.2",
|
||||||
|
"hash": "sha256-caDyc+WgYOo43AUTjtbP0MyvYDb6JweEKDdIul61Cac="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NuGet.Packaging",
|
||||||
|
"version": "6.13.2",
|
||||||
|
"hash": "sha256-lhO+SFwIYZ4aPHxIGm5ubkkE2a5Ve2xgtroRbNh7hpw="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NuGet.Protocol",
|
||||||
|
"version": "6.13.2",
|
||||||
|
"hash": "sha256-5lnAHHZjy7A4vgv65AeBAs64mSNpuoUjxW3HnrMpuzY="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NuGet.Versioning",
|
||||||
|
"version": "6.13.2",
|
||||||
|
"hash": "sha256-gmpyBpKnt+GHqgx/2uFKp+J2csbxEAy1E7WdVT117sw="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NUnit",
|
||||||
|
"version": "4.3.2",
|
||||||
|
"hash": "sha256-0RWe8uFoxYp6qhPlDDEghOMcKJgyw2ybvEoAqBLebeE="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "NUnit3TestAdapter",
|
||||||
|
"version": "5.0.0",
|
||||||
|
"hash": "sha256-7jZM4qAbIzne3AcdFfMbvbgogqpxvVe6q2S7Ls8xQy0="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "RestEase",
|
||||||
|
"version": "1.6.4",
|
||||||
|
"hash": "sha256-FFmqFwlHhIln46k56Z8KM1G+xuPEh/bceKCQnJcdcdc="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "runtime.any.System.Runtime",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"hash": "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "runtime.native.System",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"hash": "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "runtime.unix.System.Private.Uri",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"hash": "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Collections.Immutable",
|
||||||
|
"version": "8.0.0",
|
||||||
|
"hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Diagnostics.DiagnosticSource",
|
||||||
|
"version": "5.0.0",
|
||||||
|
"hash": "sha256-6mW3N6FvcdNH/pB58pl+pFSCGWgyaP4hfVtC/SMWDV4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Diagnostics.DiagnosticSource",
|
||||||
|
"version": "7.0.0",
|
||||||
|
"hash": "sha256-9Wk8cHSkjKtqkN6xW7KnXoQVtF/VNbKeBq79WqDesMs="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Formats.Asn1",
|
||||||
|
"version": "6.0.0",
|
||||||
|
"hash": "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Formats.Asn1",
|
||||||
|
"version": "8.0.1",
|
||||||
|
"hash": "sha256-may/Wg+esmm1N14kQTG4ESMBi+GQKPp0ZrrBo/o6OXM="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.IO.Abstractions",
|
||||||
|
"version": "4.2.13",
|
||||||
|
"hash": "sha256-nkC/PiqE6+c1HJ2yTwg3x+qdBh844Z8n3ERWDW8k6Gg="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.IO.FileSystem.AccessControl",
|
||||||
|
"version": "4.5.0",
|
||||||
|
"hash": "sha256-ck44YBQ0M+2Im5dw0VjBgFD1s0XuY54cujrodjjSBL8="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Memory",
|
||||||
|
"version": "4.5.5",
|
||||||
|
"hash": "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Private.Uri",
|
||||||
|
"version": "4.3.0",
|
||||||
|
"hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Reflection.Metadata",
|
||||||
|
"version": "1.6.0",
|
||||||
|
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Runtime",
|
||||||
|
"version": "4.3.1",
|
||||||
|
"hash": "sha256-R9T68AzS1PJJ7v6ARz9vo88pKL1dWqLOANg4pkQjkA0="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Runtime.CompilerServices.Unsafe",
|
||||||
|
"version": "6.0.0",
|
||||||
|
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Security.AccessControl",
|
||||||
|
"version": "4.5.0",
|
||||||
|
"hash": "sha256-AFsKPb/nTk2/mqH/PYpaoI8PLsiKKimaXf+7Mb5VfPM="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Security.Cryptography.Pkcs",
|
||||||
|
"version": "6.0.4",
|
||||||
|
"hash": "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Security.Cryptography.ProtectedData",
|
||||||
|
"version": "4.4.0",
|
||||||
|
"hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Security.Principal.Windows",
|
||||||
|
"version": "4.5.0",
|
||||||
|
"hash": "sha256-BkUYNguz0e4NJp1kkW7aJBn3dyH9STwB5N8XqnlCsmY="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "System.Text.Json",
|
||||||
|
"version": "8.0.5",
|
||||||
|
"hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "TypeEquality",
|
||||||
|
"version": "0.3.0",
|
||||||
|
"hash": "sha256-V50xAOzzyUJrY+MYPRxtnqW5MVeATXCes89wPprv1r4="
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pname": "WoofWare.Whippet.Fantomas",
|
||||||
|
"version": "0.5.1",
|
||||||
|
"hash": "sha256-59CwnOZQAq5ZJoUkd87OiP8KUwx8xYDLMimMMTlKeZA="
|
||||||
|
}
|
||||||
|
]
|
||||||
444
nix/deps.nix
444
nix/deps.nix
@@ -1,444 +0,0 @@
|
|||||||
# This file was automatically generated by passthru.fetch-deps.
|
|
||||||
# Please don't edit it manually, your changes might get overwritten!
|
|
||||||
{fetchNuGet}: [
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "fsharp-analyzers";
|
|
||||||
version = "0.23.0";
|
|
||||||
sha256 = "sha256-CWMW06ncSs8QkQvxNPNrgn3TAzMU6qCT1k2A3pnGrYQ=";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "fantomas";
|
|
||||||
version = "6.3.0-alpha-007";
|
|
||||||
sha256 = "sha256-uZw6h6k/DS4BcYtK9cv8TLS0H8MZDO3WBaPPTdtTgu0=";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "ApiSurface";
|
|
||||||
version = "4.0.25";
|
|
||||||
sha256 = "0zjq8an9cr0l7wxdmm9n9s3iyq5m0zl4x0h0wmy5cz7am8y15qc4";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "coverlet.collector";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "12j34vrkmph8lspbafnqmfnj2qvysz1jcrks2khw798s6dwv0j90";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Fantomas.Core";
|
|
||||||
version = "6.1.1";
|
|
||||||
sha256 = "1h2wsiy4fzwsg9vrlpk6w7zsvx6bc4wg4x25zqc48akg04fwpi0m";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Fantomas.FCS";
|
|
||||||
version = "6.1.1";
|
|
||||||
sha256 = "0733dm5zjdp8w5wwalqlv1q52pghfr04863i9wy807f4qfd7rrin";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "FsCheck";
|
|
||||||
version = "2.16.6";
|
|
||||||
sha256 = "176rwky6b5rk8dzldiz4068p7m9c5y9ygzbhadrs14jkl94pc56n";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "FSharp.Core";
|
|
||||||
version = "6.0.1";
|
|
||||||
sha256 = "0qks2aadkhsffg9a6xq954ll9xacnph852avd7ljh9n2g6vj06qj";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "FSharp.Core";
|
|
||||||
version = "8.0.101";
|
|
||||||
sha256 = "0prgcnki6s0rlrfbarrcv50w1bbhaalsyhhw5gsnjs2is7qrjbii";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "FsUnit";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "18q3p0z155znwj1l0qq3vq9nh9wl2i4mlfx4pmrnia4czr0xdkmb";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Ref";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1d8nkz24vsm0iy2xm8y5ak2q1w1p99dxyz0y26acs6sfk2na0vm6";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Ref";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0yaaiqq7mi6sclyrb1v0fyncanbx0ifmnnhv9whynqj8439jsdwh";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1za8lc52m4z54d68wd64c2nhzy05g3gx171k5cdlx73fbymiys9z";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0dsdgqg7566qximmjfza4x9if3icy4kskq698ddj5apdia88h2mw";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1zpbmz6z8758gwywzg0bac8kx9x39sxxc9j4a4r2jl74l9ssw4vm";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "1gjz379y61ag9whi78qxx09bwkwcznkx2mzypgycibxk61g11da1";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1i8ydlwjzk7j0mzvn0rpljxfp1h50zwaqalnyvfxai1fwgigzgw5";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0w3mrs4zdl9mfanl1j81759xwwrzmicsjxn6yfxv5yrxbxzq695n";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "02src68hd3213sd1a2ms1my7i92knfmdxclvv90il9cky2zsq8kw";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0a9aljr4fy4haq6ndz2y723liv5hbfpss1rn45s88nmgcp27m15m";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1gxlmfdkfzmhw9pac5jiv674nn6i1zymcp2hj81irjwhhjk01mf5";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "01kzndyqmsvcq49i2jrv7ymfp0l71yxfylv1cy3nhkdbprqz8ipx";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.Build.Tasks.Git";
|
|
||||||
version = "8.0.0";
|
|
||||||
sha256 = "0055f69q3hbagqp8gl3nk0vfn4qyqyxsxyy7pd0g7wm3z28byzmx";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.CodeCoverage";
|
|
||||||
version = "17.8.0";
|
|
||||||
sha256 = "173wjadp3gan4x2jfjchngnc4ca4mb95h1sbb28jydfkfw0z1zvj";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NET.Test.Sdk";
|
|
||||||
version = "17.8.0";
|
|
||||||
sha256 = "1syvl3g0hbrcgfi9rq6pld8s8hqqww4dflf1lxn59ccddyyx0gmv";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "19y6c6v20bgf7x7rrh4rx9y7s5fy8vp5m4j9b6gi1wp4rpb5mza4";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-arm64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0dhpdlcdz7adcfh9w01fc867051m35fqaxnvj3fqvqhgcm2n3143";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "0p7hhidaa3mnyiwnsijwy8578v843x8hh99255s69qwwyld6falv";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.linux-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "1aw6mc7zcmzs1grxz2wa9cw9kfj8pz7zpj417xnp1a9n4ix1bxgr";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1mq11xsv9g1vsasp6k80y7xlvwi9hrpk5dgm773fvy8538s01gfv";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-arm64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "1dzg3prng9zfdzz7gcgywjdbwzhwm85j89z0jahynxx4q2dra4b9";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1chac9b4424ihrrnlzvc7qz6j4ymfjyv4kzyazzzw19yhymdkh2s";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.osx-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "010f8wn15s2kv7yyzgys3pv9i1mxw20hpv1ig2zhybjxs8lpj8jj";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.win-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "0i7g9fsqjnbh9rc6807m57r2idg5pkcw6xjfwhnxkcpgqm96258v";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Host.win-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "1ssj1cyam3nfidm8q82kvh4i3fzm2lzb3bxw6ck09hwhvwh909z4";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Ref";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "12gb52dhg5h9hgnyqh1zgj2w46paxv2pfh33pphl9ajhrdr7hlsb";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Ref";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "02r4jg4ha0qksix9v6s3cpmvavmz54gkawkxy9bvknw5ynxhhl1l";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "164hfrwqz5dxcbb441lridk4mzcqmarb0b7ckgvqhsvpawyjw88v";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0353whnjgz3sqhzsfrviad3a3db4pk7hl7m4wwppv5mqdg9i9ri5";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "0islayddpnflviqpbq4djc4f3v9nhsa2y76k5x6il3csq5vdw2hq";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "1g5b30f4l8a1zjjr3b8pk9mcqxkxqwa86362f84646xaj4iw3a4d";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "1acn5zw1pxzmcg3c0pbf9hal36fbdh9mvbsiwra7simrk7hzqpdc";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "0cdrpdaq5sl3602anfx1p0z0ncx2sjjvl6mgsd6y38g47n7f95jc";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "00f9l9dkdz0zv5csaw8fkm6s8ckrj5n9k3ygz12daa22l3bcn6ii";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "1fk1flqp6ji0l4c2gvh83ykndpx7a2nkkgrgkgql3c75j1k2v1s9";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
|
||||||
version = "6.0.26";
|
|
||||||
sha256 = "0i2p356phfc5y6qnr3vyrzjfi1mrbwfb6g85k4q37bbyxjfp7zl9";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.App.Runtime.win-x64";
|
|
||||||
version = "8.0.1";
|
|
||||||
sha256 = "198576cdkl72xs29zznff9ls763p8pfr0zji7b74dqxd5ga0s3bd";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.Platforms";
|
|
||||||
version = "1.1.1";
|
|
||||||
sha256 = "164wycgng4mi9zqi2pnsf1pq6gccbqvw6ib916mqizgjmd8f44pj";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.Platforms";
|
|
||||||
version = "2.0.0";
|
|
||||||
sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.NETCore.Targets";
|
|
||||||
version = "1.1.3";
|
|
||||||
sha256 = "05smkcyxir59rgrmp7d6327vvrlacdgldfxhmyr1azclvga1zfsq";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.SourceLink.Common";
|
|
||||||
version = "8.0.0";
|
|
||||||
sha256 = "0xrr8yd34ij7dqnyddkp2awfmf9qn3c89xmw2f3npaa4wnajmx81";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.SourceLink.GitHub";
|
|
||||||
version = "8.0.0";
|
|
||||||
sha256 = "1gdx7n45wwia3yvang3ls92sk3wrymqcx9p349j8wba2lyjf9m44";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.TestPlatform.ObjectModel";
|
|
||||||
version = "17.8.0";
|
|
||||||
sha256 = "0b0i7lmkrcfvim8i3l93gwqvkhhhfzd53fqfnygdqvkg6np0cg7m";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Microsoft.TestPlatform.TestHost";
|
|
||||||
version = "17.8.0";
|
|
||||||
sha256 = "0f5jah93kjkvxwmhwb78lw11m9pkkq9fvf135hpymmmpxqbdh97q";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Myriad.Core";
|
|
||||||
version = "0.8.3";
|
|
||||||
sha256 = "0s5pdckjw4x0qrbd4i3cz9iili5cppg5qnjbr7zjbbhhmxzb24xw";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Myriad.Sdk";
|
|
||||||
version = "0.8.3";
|
|
||||||
sha256 = "0qv78c5s5m04xb8h17nnn2ig26zcyya91k2dpj745cm1cbnzvvgc";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Nerdbank.GitVersioning";
|
|
||||||
version = "3.6.133";
|
|
||||||
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Newtonsoft.Json";
|
|
||||||
version = "13.0.1";
|
|
||||||
sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "Newtonsoft.Json";
|
|
||||||
version = "13.0.3";
|
|
||||||
sha256 = "0xrwysmrn4midrjal8g2hr1bbg38iyisl0svamb11arqws4w2bw7";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Common";
|
|
||||||
version = "6.8.0";
|
|
||||||
sha256 = "0l3ij8iwy7wj6s7f93lzi9168r4wz8zyin6a08iwgk7hvq44cia1";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Configuration";
|
|
||||||
version = "6.8.0";
|
|
||||||
sha256 = "0x03p408smkmv1gv7pmvsia4lkn0xaj4wfrkl58pjf8bbv51y0yw";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Frameworks";
|
|
||||||
version = "6.5.0";
|
|
||||||
sha256 = "0s37d1p4md0k6d4cy6sq36f2dgkd9qfbzapxhkvi8awwh0vrynhj";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Frameworks";
|
|
||||||
version = "6.8.0";
|
|
||||||
sha256 = "0i2xvhgkjkjr496i3pg8hamwv6505fia45qhn7jg5m01wb3cvsjl";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Packaging";
|
|
||||||
version = "6.8.0";
|
|
||||||
sha256 = "031z4s905bxi94h3f0qy4j1b6jxdxgqgpkzqvvpfxch07szxcbim";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Protocol";
|
|
||||||
version = "6.7.0";
|
|
||||||
sha256 = "1v5ibnq2mp801vw68zyj169hkj3xm7h55824i33n1jxxj2vs3vbk";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NuGet.Versioning";
|
|
||||||
version = "6.8.0";
|
|
||||||
sha256 = "1sd25h46fd12ng780r02q4ijcx1imkb53kj1y2y7cwg5myh537ks";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NUnit";
|
|
||||||
version = "4.0.1";
|
|
||||||
sha256 = "0jgiq3dbwli5r70j0bw7021d69r7bhr58s8kphlpjmf7k47l5pcd";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NUnit.Analyzers";
|
|
||||||
version = "4.0.0";
|
|
||||||
sha256 = "01a2a7hx1dwkmdyy4dw7ipsj77dlbfidkzscwfmgyfdf197agd2d";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "NUnit3TestAdapter";
|
|
||||||
version = "4.5.0";
|
|
||||||
sha256 = "1srx1629s0k1kmf02nmz251q07vj6pv58mdafcr5dr0bbn1fh78i";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "RestEase";
|
|
||||||
version = "1.6.4";
|
|
||||||
sha256 = "1mvi3nbrr450g3fgd1y4wg3bwl9k1agyjfd9wdkqk12714bsln8l";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "runtime.any.System.Runtime";
|
|
||||||
version = "4.3.0";
|
|
||||||
sha256 = "1cqh1sv3h5j7ixyb7axxbdkqx6cxy00p4np4j91kpm492rf4s25b";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "runtime.native.System";
|
|
||||||
version = "4.3.0";
|
|
||||||
sha256 = "15hgf6zaq9b8br2wi1i3x0zvmk410nlmsmva9p0bbg73v6hml5k4";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "runtime.unix.System.Private.Uri";
|
|
||||||
version = "4.3.0";
|
|
||||||
sha256 = "1jx02q6kiwlvfksq1q9qr17fj78y5v6mwsszav4qcz9z25d5g6vk";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Diagnostics.DiagnosticSource";
|
|
||||||
version = "7.0.0";
|
|
||||||
sha256 = "1jxhvsh5mzdf0sgb4dfmbys1b12ylyr5pcfyj1map354fiq3qsgm";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Formats.Asn1";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "1vvr7hs4qzjqb37r0w1mxq7xql2b17la63jwvmgv65s1hj00g8r9";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.IO.Abstractions";
|
|
||||||
version = "4.2.13";
|
|
||||||
sha256 = "0s784iphsmj4vhkrzq9q3w39vsn76w44zclx3hsygsw458zbyh4y";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.IO.FileSystem.AccessControl";
|
|
||||||
version = "4.5.0";
|
|
||||||
sha256 = "1gq4s8w7ds1sp8f9wqzf8nrzal40q5cd2w4pkf4fscrl2ih3hkkj";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Memory";
|
|
||||||
version = "4.5.5";
|
|
||||||
sha256 = "08jsfwimcarfzrhlyvjjid61j02irx6xsklf32rv57x2aaikvx0h";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Private.Uri";
|
|
||||||
version = "4.3.0";
|
|
||||||
sha256 = "04r1lkdnsznin0fj4ya1zikxiqr0h6r6a1ww2dsm60gqhdrf0mvx";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Reflection.Metadata";
|
|
||||||
version = "1.6.0";
|
|
||||||
sha256 = "1wdbavrrkajy7qbdblpbpbalbdl48q3h34cchz24gvdgyrlf15r4";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Runtime";
|
|
||||||
version = "4.3.1";
|
|
||||||
sha256 = "03ch4d2acf6q037a4njxpll2kkx3dwzlg07yxr4z5m6j1kqgmm27";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Runtime.CompilerServices.Unsafe";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "0qm741kh4rh57wky16sq4m0v05fxmkjjr87krycf5vp9f0zbahbc";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Security.AccessControl";
|
|
||||||
version = "4.5.0";
|
|
||||||
sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Security.Cryptography.Pkcs";
|
|
||||||
version = "6.0.4";
|
|
||||||
sha256 = "0hh5h38pnxmlrnvs72f2hzzpz4b2caiiv6xf8y7fzdg84r3imvfr";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Security.Cryptography.ProtectedData";
|
|
||||||
version = "4.4.0";
|
|
||||||
sha256 = "1q8ljvqhasyynp94a1d7jknk946m20lkwy2c3wa8zw2pc517fbj6";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Security.Principal.Windows";
|
|
||||||
version = "4.5.0";
|
|
||||||
sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Text.Encodings.Web";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "06n9ql3fmhpjl32g3492sj181zjml5dlcc5l76xq2h38c4f87sai";
|
|
||||||
})
|
|
||||||
(fetchNuGet {
|
|
||||||
pname = "System.Text.Json";
|
|
||||||
version = "6.0.0";
|
|
||||||
sha256 = "1si2my1g0q0qv1hiqnji4xh9wd05qavxnzj9dwgs23iqvgjky0gl";
|
|
||||||
})
|
|
||||||
]
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# This file was adapted from
|
|
||||||
# https://github.com/NixOS/nixpkgs/blob/b981d811453ab84fb3ea593a9b33b960f1ab9147/pkgs/build-support/dotnet/build-dotnet-module/default.nix#L173
|
|
||||||
set -euo pipefail
|
|
||||||
export PATH="@binPath@"
|
|
||||||
for arg in "$@"; do
|
|
||||||
case "$arg" in
|
|
||||||
--keep-sources|-k)
|
|
||||||
keepSources=1
|
|
||||||
shift
|
|
||||||
;;
|
|
||||||
--help|-h)
|
|
||||||
echo "usage: $0 [--keep-sources] [--help] <output path>"
|
|
||||||
echo " <output path> The path to write the lockfile to. A temporary file is used if this is not set"
|
|
||||||
echo " --keep-sources Don't remove temporary directories upon exit, useful for debugging"
|
|
||||||
echo " --help Show this help message"
|
|
||||||
exit
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
done
|
|
||||||
tmp=$(mktemp -td "@pname@-tmp-XXXXXX")
|
|
||||||
export tmp
|
|
||||||
HOME=$tmp/home
|
|
||||||
exitTrap() {
|
|
||||||
test -n "${ranTrap-}" && return
|
|
||||||
ranTrap=1
|
|
||||||
if test -n "${keepSources-}"; then
|
|
||||||
echo -e "Path to the source: $tmp/src\nPath to the fake home: $tmp/home"
|
|
||||||
else
|
|
||||||
rm -rf "$tmp"
|
|
||||||
fi
|
|
||||||
# Since mktemp is used this will be empty if the script didnt succesfully complete
|
|
||||||
if ! test -s "$depsFile"; then
|
|
||||||
rm -rf "$depsFile"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
trap exitTrap EXIT INT TERM
|
|
||||||
dotnetRestore() {
|
|
||||||
local -r project="${1-}"
|
|
||||||
local -r rid="$2"
|
|
||||||
dotnet restore "${project-}" \
|
|
||||||
-p:ContinuousIntegrationBuild=true \
|
|
||||||
-p:Deterministic=true \
|
|
||||||
--packages "$tmp/nuget_pkgs" \
|
|
||||||
--runtime "$rid" \
|
|
||||||
--no-cache \
|
|
||||||
--force
|
|
||||||
}
|
|
||||||
declare -a projectFiles=( @projectFiles@ )
|
|
||||||
declare -a testProjectFiles=( @testProjectFiles@ )
|
|
||||||
export DOTNET_NOLOGO=1
|
|
||||||
export DOTNET_CLI_TELEMETRY_OPTOUT=1
|
|
||||||
depsFile=$(realpath "${1:-$(mktemp -t "@pname@-deps-XXXXXX.nix")}")
|
|
||||||
mkdir -p "$tmp/nuget_pkgs"
|
|
||||||
storeSrc="@storeSrc@"
|
|
||||||
src="$tmp/src"
|
|
||||||
cp -rT "$storeSrc" "$src"
|
|
||||||
chmod -R +w "$src"
|
|
||||||
cd "$src"
|
|
||||||
echo "Restoring project..."
|
|
||||||
rids=("@rids@")
|
|
||||||
for rid in "${rids[@]}"; do
|
|
||||||
(( ${#projectFiles[@]} == 0 )) && dotnetRestore "" "$rid"
|
|
||||||
for project in "${projectFiles[@]-}" "${testProjectFiles[@]-}"; do
|
|
||||||
dotnetRestore "$project" "$rid"
|
|
||||||
done
|
|
||||||
done
|
|
||||||
echo "Successfully restored project"
|
|
||||||
echo "Writing lockfile..."
|
|
||||||
echo -e "# This file was automatically generated by passthru.fetch-deps.\n# Please don't edit it manually, your changes might get overwritten!\n" > "$depsFile"
|
|
||||||
nuget-to-nix "$tmp/nuget_pkgs" "@packages@" >> "$depsFile"
|
|
||||||
echo "Successfully wrote lockfile to $depsFile"
|
|
||||||
Reference in New Issue
Block a user