Compare commits

...

28 Commits

Author SHA1 Message Date
Patrick Stevens
8e47f39efc Make more extensive use of our own DSLs (#153) 2024-05-31 16:54:05 +00:00
Patrick Stevens
6942ba42b9 Update changelog (#152) 2024-05-30 22:37:05 +01:00
Patrick Stevens
b98080690d Finish DU parsing (#151) 2024-05-30 22:27:15 +01:00
Patrick Stevens
81b7e5361d Another grand refactor (#150) 2024-05-30 20:34:53 +01:00
Patrick Stevens
94b88a4143 Reduce duplication (#149) 2024-05-30 14:28:56 +01:00
Patrick Stevens
ed3ffecb52 Fix and test GitHub release script (#148) 2024-05-30 12:32:40 +00:00
Patrick Stevens
c696dcf31f Fix curl failing logic (#147) 2024-05-30 11:35:30 +00:00
Patrick Stevens
d5bb2726d3 Tighten the tagging logic (#146) 2024-05-30 11:28:43 +00:00
Patrick Stevens
f17290d0f1 Check generation of files is accurate (#145) 2024-05-30 12:10:49 +01:00
Patrick Stevens
35cd94cba1 Add JSON serialisation of DUs (#144) 2024-05-30 12:00:55 +01:00
Patrick Stevens
1b3eb03380 NerdBank.GitVersioning heights (#143) 2024-05-29 00:44:16 +01:00
dependabot[bot]
b846ce08a3 Bump Microsoft.NET.Test.Sdk from 17.9.0 to 17.10.0 (#141)
* Bump Microsoft.NET.Test.Sdk from 17.9.0 to 17.10.0

Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.9.0 to 17.10.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.9.0...v17.10.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump ApiSurface from 4.0.39 to 4.0.40

Bumps [ApiSurface](https://github.com/G-Research/ApiSurface) from 4.0.39 to 4.0.40.
- [Release notes](https://github.com/G-Research/ApiSurface/releases)
- [Commits](https://github.com/G-Research/ApiSurface/compare/ApiSurface.4.0.39...ApiSurface.4.0.40)

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Update deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-05-27 12:03:40 +01:00
Patrick Stevens
4b9f63d374 Express HttpClient as extension method (#140) 2024-05-24 22:09:33 +01:00
Patrick Stevens
b9ba07a8a7 JSON parse all primitive types (#139) 2024-05-24 21:19:04 +01:00
Patrick Stevens
e80ed51498 Strip parens in Path parameter (#138) 2024-05-24 20:36:12 +01:00
dependabot[bot]
61b07ad802 Bump fsharp-analyzers from 0.25.0 to 0.26.0 (#134)
* Bump fsharp-analyzers from 0.25.0 to 0.26.0

Bumps [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK) from 0.25.0 to 0.26.0.
- [Release notes](https://github.com/ionide/FSharp.Analyzers.SDK/releases)
- [Changelog](https://github.com/ionide/FSharp.Analyzers.SDK/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ionide/FSharp.Analyzers.SDK/compare/v0.25.0...v0.26.0)

---
updated-dependencies:
- dependency-name: fsharp-analyzers
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-05-20 19:02:54 +00:00
dependabot[bot]
59369bcb94 Bump cachix/install-nix-action from 26 to 27 (#133) 2024-05-20 12:47:58 +01:00
dependabot[bot]
072169e4e3 Bump ApiSurface from 4.0.36 to 4.0.39 (#132)
* Bump ApiSurface from 4.0.36 to 4.0.39

Bumps [ApiSurface](https://github.com/G-Research/ApiSurface) from 4.0.36 to 4.0.39.
- [Release notes](https://github.com/G-Research/ApiSurface/releases)
- [Commits](https://github.com/G-Research/ApiSurface/compare/ApiSurface.4.0.36...ApiSurface.4.0.39)

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-05-06 13:59:29 +01:00
Patrick Stevens
91136a25ab Enable query params in Get request endpoint (#131) 2024-04-30 19:03:20 +00:00
Patrick Stevens
c51038448a Be more forgiving about the source of the attributes (#129) 2024-04-29 20:46:14 +01:00
Patrick Stevens
09780efb07 Add RestEase attributes (#128) 2024-04-29 17:51:26 +01:00
dependabot[bot]
f562271c12 Bump fantomas from 6.3.3 to 6.3.4 (#126)
* Bump ApiSurface from 4.0.33 to 4.0.36

Bumps [ApiSurface](https://github.com/G-Research/ApiSurface) from 4.0.33 to 4.0.36.
- [Release notes](https://github.com/G-Research/ApiSurface/releases)
- [Commits](https://github.com/G-Research/ApiSurface/compare/ApiSurface.4.0.33...ApiSurface.4.0.36)

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Bump fantomas from 6.3.3 to 6.3.4

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.3 to 6.3.4.
- [Release notes](https://github.com/fsprojects/fantomas/releases)
- [Changelog](https://github.com/fsprojects/fantomas/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsprojects/fantomas/compare/v6.3.3...v6.3.4)

---
updated-dependencies:
- dependency-name: fantomas
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Drive-by

* Fix deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-04-22 23:36:02 +01:00
Patrick Stevens
e3081c3136 Deal with unit type in generated mock (#124) 2024-04-17 08:44:31 +01:00
Patrick Stevens
232d2ba5ec Relax arg checking strictness (#123) 2024-04-16 22:47:06 +01:00
Patrick Stevens
f7458f521e Track inheritance in GenerateMock (#122) 2024-04-16 22:23:32 +01:00
dependabot[bot]
bfc25a672b Bump fantomas from 6.3.0 to 6.3.3 (#120)
* Bump fantomas from 6.3.0 to 6.3.3

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.0 to 6.3.3.
- [Release notes](https://github.com/fsprojects/fantomas/releases)
- [Changelog](https://github.com/fsprojects/fantomas/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsprojects/fantomas/compare/v6.3.0...v6.3.3)

---
updated-dependencies:
- dependency-name: fantomas
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

* Fix dep

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-04-15 21:23:09 +00:00
dependabot[bot]
af7fcb3028 Bump fantomas from 6.3.0-alpha-008 to 6.3.0 (#118)
* Bump fantomas from 6.3.0-alpha-008 to 6.3.0

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.0-alpha-008 to 6.3.0.
- [Release notes](https://github.com/fsprojects/fantomas/releases)
- [Changelog](https://github.com/fsprojects/fantomas/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsprojects/fantomas/commits/v6.3.0)

---
updated-dependencies:
- dependency-name: fantomas
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-03-19 23:07:38 +00:00
dependabot[bot]
91853b1fff Bump cachix/install-nix-action from 25 to 26 (#116) 2024-03-11 10:10:04 +00:00
64 changed files with 4133 additions and 2788 deletions

View File

@@ -3,13 +3,13 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"fantomas": { "fantomas": {
"version": "6.3.0-alpha-008", "version": "6.3.4",
"commands": [ "commands": [
"fantomas" "fantomas"
] ]
}, },
"fsharp-analyzers": { "fsharp-analyzers": {
"version": "0.25.0", "version": "0.26.0",
"commands": [ "commands": [
"fsharp-analyzers" "fsharp-analyzers"
] ]

View File

@@ -1,3 +1,4 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
name: .NET name: .NET
on: on:
@@ -28,7 +29,7 @@ jobs:
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -49,7 +50,7 @@ jobs:
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -66,7 +67,7 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -79,20 +80,41 @@ jobs:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Run Fantomas - name: Run Fantomas
run: nix run .#fantomas -- --check . run: nix run .#fantomas -- --check .
check-accurate-generations:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Whitespace change
run: "echo ' ' >> ConsumePlugin/List.fs"
- name: Generate code
run: nix develop --command dotnet build
- name: Run Fantomas
run: nix run .#fantomas -- .
- name: Verify there is no diff
run: git diff --name-only --no-color --exit-code
check-nix-format: check-nix-format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -105,7 +127,7 @@ jobs:
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -118,7 +140,7 @@ jobs:
steps: steps:
- uses: actions/checkout@master - uses: actions/checkout@master
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -132,7 +154,7 @@ jobs:
with: with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -174,8 +196,27 @@ jobs:
# Verify that there is exactly one nupkg in the artifact that would be NuGet published # Verify that there is exactly one nupkg in the artifact that would be NuGet published
run: if [[ $(find packed-attribute -maxdepth 1 -name 'WoofWare.Myriad.Plugins.Attributes.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi run: if [[ $(find packed-attribute -maxdepth 1 -name 'WoofWare.Myriad.Plugins.Attributes.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
github-release-plugin-dry-run:
needs: [nuget-pack]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Download NuGet artifact (plugin)
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
- name: Download NuGet artifact (attribute)
uses: actions/download-artifact@v4
with:
name: nuget-package-attribute
- name: Tag and release plugin
env:
DRY_RUN: 1
GITHUB_TOKEN: mock-token
run: sh .github/workflows/tag.sh
all-required-checks-complete: all-required-checks-complete:
needs: [check-dotnet-format, check-nix-format, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack] needs: [check-dotnet-format, check-nix-format, check-accurate-generations, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack, github-release-plugin-dry-run]
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo "All required checks complete." - run: echo "All required checks complete."
@@ -188,7 +229,7 @@ jobs:
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
uses: cachix/install-nix-action@v25 uses: cachix/install-nix-action@V27
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,6 +1,13 @@
#!/bin/sh #!/bin/bash
find . -maxdepth 1 -type f -name '*.nupkg' -exec sh -c 'tag=$(basename "$1" .nupkg); git tag "$tag"; git push origin "$tag"' shell {} \; echo "Dry-run? $DRY_RUN!"
find . -maxdepth 1 -type f ! -name "$(printf "*\n*")" -name '*.nupkg' | while IFS= read -r file
do
tag=$(basename "$file" .nupkg)
git tag "$tag"
${DRY_RUN:+echo} git push origin "$tag"
done
export TAG export TAG
TAG=$(find . -maxdepth 1 -type f -name 'WoofWare.Myriad.Plugins.*.nupkg' -exec sh -c 'basename "$1" .nupkg' shell {} \; | grep -v Attributes) TAG=$(find . -maxdepth 1 -type f -name 'WoofWare.Myriad.Plugins.*.nupkg' -exec sh -c 'basename "$1" .nupkg' shell {} \; | grep -v Attributes)
@@ -14,4 +21,100 @@ case "$TAG" in
esac esac
# target_commitish empty indicates the repo default branch # target_commitish empty indicates the repo default branch
curl -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases -d '{"tag_name":"'"$TAG"'","target_commitish":"","name":"'"$TAG"'","draft":false,"prerelease":false,"generate_release_notes":false}' curl_body='{"tag_name":"'"$TAG"'","target_commitish":"","name":"'"$TAG"'","draft":false,"prerelease":false,"generate_release_notes":false}'
echo "cURL body: $curl_body"
failed_output=$(cat <<'EOF'
{
"message": "Validation Failed",
"errors": [
{
"resource": "Release",
"code": "already_exists",
"field": "tag_name"
}
],
"documentation_url": "https://docs.github.com/rest/releases/releases#create-a-release"
}
EOF
)
success_output=$(cat <<'EOF'
{
"url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases/158152116",
"assets_url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases/158152116/assets",
"upload_url": "https://uploads.github.com/repos/Smaug123/WoofWare.Myriad/releases/158152116/assets{?name,label}",
"html_url": "https://github.com/Smaug123/WoofWare.Myriad/releases/tag/WoofWare.Myriad.Plugins.2.1.30",
"id": 158152116,
"author": {
"login": "github-actions[bot]",
"id": 41898282,
"node_id": "MDM6Qm90NDE4OTgyODI=",
"avatar_url": "https://avatars.githubusercontent.com/in/15368?v=4",
"gravatar_id": "",
"url": "https://api.github.com/users/github-actions%5Bbot%5D",
"html_url": "https://github.com/apps/github-actions",
"followers_url": "https://api.github.com/users/github-actions%5Bbot%5D/followers",
"following_url": "https://api.github.com/users/github-actions%5Bbot%5D/following{/other_user}",
"gists_url": "https://api.github.com/users/github-actions%5Bbot%5D/gists{/gist_id}",
"starred_url": "https://api.github.com/users/github-actions%5Bbot%5D/starred{/owner}{/repo}",
"subscriptions_url": "https://api.github.com/users/github-actions%5Bbot%5D/subscriptions",
"organizations_url": "https://api.github.com/users/github-actions%5Bbot%5D/orgs",
"repos_url": "https://api.github.com/users/github-actions%5Bbot%5D/repos",
"events_url": "https://api.github.com/users/github-actions%5Bbot%5D/events{/privacy}",
"received_events_url": "https://api.github.com/users/github-actions%5Bbot%5D/received_events",
"type": "Bot",
"site_admin": false
},
"node_id": "RE_kwDOJfksgc4JbTW0",
"tag_name": "WoofWare.Myriad.Plugins.2.1.30",
"target_commitish": "main",
"name": "WoofWare.Myriad.Plugins.2.1.30",
"draft": false,
"prerelease": false,
"created_at": "2024-05-30T11:00:55Z",
"published_at": "2024-05-30T11:03:02Z",
"assets": [
],
"tarball_url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/tarball/WoofWare.Myriad.Plugins.2.1.30",
"zipball_url": "https://api.github.com/repos/Smaug123/WoofWare.Myriad/zipball/WoofWare.Myriad.Plugins.2.1.30",
"body": null
}
EOF
)
HANDLE_OUTPUT=''
handle_error() {
ERROR_OUTPUT="$1"
exit_message=$(echo "$ERROR_OUTPUT" | jq -r --exit-status 'if .errors | length == 1 then .errors[0].code else null end')
if [ "$exit_message" = "already_exists" ] ; then
HANDLE_OUTPUT="Did not create GitHub release because it already exists at this version."
else
echo "Unexpected error output from curl: $(cat curl_output.json)"
echo "JQ output: $(exit_message)"
exit 2
fi
}
run_tests() {
handle_error "$failed_output"
if [ "$HANDLE_OUTPUT" != "Did not create GitHub release because it already exists at this version." ]; then
echo "Bad output from handler: $HANDLE_OUTPUT"
exit 3
fi
HANDLE_OUTPUT=''
echo "Tests passed."
}
run_tests
if [ "$DRY_RUN" != 1 ] ; then
if curl --fail-with-body -L -X POST -H "Accept: application/vnd.github+json" -H "Authorization: Bearer $GITHUB_TOKEN" -H "X-GitHub-Api-Version: 2022-11-28" https://api.github.com/repos/Smaug123/WoofWare.Myriad/releases -d "$curl_body" > curl_output.json; then
echo "Curl succeeded."
else
handle_error "$(cat curl_output.json)"
echo "$HANDLE_OUTPUT"
fi
fi

View File

@@ -1,6 +1,27 @@
Notable changes are recorded here. Notable changes are recorded here.
# WoofWare.Myriad.Plugins 1.4 -> 2.0 # 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. 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. The new assembly has minimal dependencies, so you may safely use it from your own code.

View File

@@ -60,7 +60,7 @@ module TreeCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__TreeBuilder x -> | Instruction.Process__TreeBuilder (x) ->
match x with match x with
| TreeBuilder.Child (arg0_0) -> | TreeBuilder.Child (arg0_0) ->
instructions.Add Instruction.TreeBuilder_Child instructions.Add Instruction.TreeBuilder_Child
@@ -68,7 +68,7 @@ module TreeCata =
| TreeBuilder.Parent (arg0_0) -> | TreeBuilder.Parent (arg0_0) ->
instructions.Add Instruction.TreeBuilder_Parent instructions.Add Instruction.TreeBuilder_Parent
instructions.Add (Instruction.Process__Tree arg0_0) instructions.Add (Instruction.Process__Tree arg0_0)
| Instruction.Process__Tree x -> | Instruction.Process__Tree (x) ->
match x with match x with
| Tree.Const (arg0_0, arg1_0) -> cata.Tree.Const arg0_0 arg1_0 |> treeStack.Add | Tree.Const (arg0_0, arg1_0) -> cata.Tree.Const arg0_0 arg1_0 |> treeStack.Add
| Tree.Pair (arg0_0, arg1_0, arg2_0) -> | Tree.Pair (arg0_0, arg1_0, arg2_0) ->

View File

@@ -41,7 +41,7 @@ module FileSystemItemCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__FileSystemItem x -> | Instruction.Process__FileSystemItem (x) ->
match x with match x with
| FileSystemItem.Directory ({ | FileSystemItem.Directory ({
Name = name Name = name
@@ -116,7 +116,7 @@ module GiftCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__Gift x -> | Instruction.Process__Gift (x) ->
match x with match x with
| Gift.Book (arg0_0) -> cata.Gift.Book arg0_0 |> giftStack.Add | Gift.Book (arg0_0) -> cata.Gift.Book arg0_0 |> giftStack.Add
| Gift.Chocolate (arg0_0) -> cata.Gift.Chocolate arg0_0 |> giftStack.Add | Gift.Chocolate (arg0_0) -> cata.Gift.Chocolate arg0_0 |> giftStack.Add

View File

@@ -8,12 +8,11 @@
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 (
@@ -26,17 +25,16 @@ module InnerType =
.GetValue<string> () .GetValue<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 (
@@ -49,7 +47,7 @@ module JsonRecordType =
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|> Array.ofSeq |> Array.ofSeq
let E = let arg_4 =
(match node.["e"] with (match node.["e"] with
| null -> | null ->
raise ( raise (
@@ -62,7 +60,7 @@ module JsonRecordType =
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> Array.ofSeq |> Array.ofSeq
let D = let arg_3 =
InnerType.jsonParse ( InnerType.jsonParse (
match node.["d"] with match node.["d"] with
| null -> | null ->
@@ -74,7 +72,7 @@ module JsonRecordType =
| v -> v | v -> v
) )
let C = let arg_2 =
(match node.["hi"] with (match node.["hi"] with
| null -> | null ->
raise ( raise (
@@ -87,7 +85,7 @@ module JsonRecordType =
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|> List.ofSeq |> List.ofSeq
let B = let arg_1 =
(match node.["another-thing"] with (match node.["another-thing"] with
| null -> | null ->
raise ( raise (
@@ -99,7 +97,7 @@ module JsonRecordType =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let A = let arg_0 =
(match node.["a"] with (match node.["a"] with
| null -> | null ->
raise ( raise (
@@ -112,12 +110,12 @@ module JsonRecordType =
.GetValue<int> () .GetValue<int> ()
{ {
A = A A = arg_0
B = B B = arg_1
C = C C = arg_2
D = D D = arg_3
E = E E = arg_4
F = F F = arg_5
} }
namespace ConsumePlugin namespace ConsumePlugin
@@ -129,24 +127,230 @@ 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 = System.Numerics.BigInteger.Parse (node.["whiskey"].ToJsonString ())
(match node.["sailor"] with
let arg_19 =
(match node.["victor"] with
| null -> | null ->
raise ( raise (
System.Collections.Generic.KeyNotFoundException ( System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("sailor") sprintf "Required key '%s' not found on JSON object" ("victor")
)
)
| v -> v)
.AsValue()
.GetValue<System.Char> ()
let arg_18 =
(match node.["uniform"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("uniform")
)
)
| v -> v)
.AsValue()
.GetValue<System.Decimal> ()
let arg_17 =
(match node.["tango"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("tango")
)
)
| v -> v)
.AsValue()
.GetValue<System.SByte> ()
let arg_16 =
(match node.["quebec"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("quebec")
)
)
| v -> v)
.AsValue()
.GetValue<System.Byte> ()
let arg_15 =
(match node.["papa"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("papa")
)
)
| v -> v)
.AsValue()
.GetValue<System.Byte> ()
let arg_14 =
(match node.["oscar"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("oscar")
)
)
| v -> v)
.AsValue()
.GetValue<System.SByte> ()
let arg_13 =
(match node.["november"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("november")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt16> ()
let arg_12 =
(match node.["mike"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("mike")
)
)
| v -> v)
.AsValue()
.GetValue<System.Int16> ()
let arg_11 =
(match node.["lima"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("lima")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt32> ()
let arg_10 =
(match node.["kilo"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("kilo")
)
)
| v -> v)
.AsValue()
.GetValue<System.Int32> ()
let arg_9 =
(match node.["juliette"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("juliette")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt32> ()
let arg_8 =
(match node.["india"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("india")
)
)
| v -> v)
.AsValue()
.GetValue<int> ()
let arg_7 =
(match node.["hotel"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("hotel")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt64> ()
let arg_6 =
(match node.["golf"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("golf")
)
)
| v -> v)
.AsValue()
.GetValue<System.Int64> ()
let arg_5 =
(match node.["foxtrot"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("foxtrot")
)
)
| v -> v)
.AsValue()
.GetValue<System.Double> ()
let arg_4 =
(match node.["echo"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("echo")
)
)
| v -> v)
.AsValue()
.GetValue<System.Single> ()
let arg_3 =
(match node.["delta"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("delta")
)
)
| v -> v)
.AsValue()
.GetValue<System.Single> ()
let arg_2 =
(match node.["charlie"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("charlie")
) )
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<float> () .GetValue<float> ()
let Soldier = let arg_1 =
(match node.["soldier"] with (match node.["bravo"] with
| null -> | null ->
raise ( raise (
System.Collections.Generic.KeyNotFoundException ( System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("soldier") sprintf "Required key '%s' not found on JSON object" ("bravo")
) )
) )
| v -> v) | v -> v)
@@ -154,24 +358,12 @@ module ToGetExtensionMethodJsonParseExtension =
.GetValue<string> () .GetValue<string> ()
|> System.Uri |> System.Uri
let Tailor = let arg_0 =
(match node.["tailor"] with (match node.["alpha"] with
| null -> | null ->
raise ( raise (
System.Collections.Generic.KeyNotFoundException ( System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("tailor") sprintf "Required key '%s' not found on JSON object" ("alpha")
)
)
| v -> v)
.AsValue()
.GetValue<int> ()
let Tinker =
(match node.["tinker"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("tinker")
) )
) )
| v -> v) | v -> v)
@@ -179,8 +371,25 @@ module ToGetExtensionMethodJsonParseExtension =
.GetValue<string> () .GetValue<string> ()
{ {
Tinker = Tinker Alpha = arg_0
Tailor = Tailor Bravo = arg_1
Soldier = Soldier Charlie = arg_2
Sailor = Sailor Delta = arg_3
Echo = arg_4
Foxtrot = arg_5
Golf = arg_6
Hotel = arg_7
India = arg_8
Juliette = arg_9
Kilo = arg_10
Lima = arg_11
Mike = arg_12
November = arg_13
Oscar = arg_14
Papa = arg_15
Quebec = arg_16
Tango = arg_17
Uniform = arg_18
Victor = arg_19
Whiskey = arg_20
} }

View File

@@ -5,6 +5,7 @@
namespace SomeNamespace namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -18,17 +19,18 @@ type internal PublicTypeMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PublicTypeMock = static member Empty : PublicTypeMock =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface IPublicType with interface IPublicType with
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1) member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
member this.Mem2 (arg_0_0) = this.Mem2 (arg_0_0) member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1) member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
namespace SomeNamespace namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -42,17 +44,18 @@ type public PublicTypeInternalFalseMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PublicTypeInternalFalseMock = static member Empty : PublicTypeInternalFalseMock =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface IPublicTypeInternalFalse with interface IPublicTypeInternalFalse 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 open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -65,15 +68,16 @@ type internal InternalTypeMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : InternalTypeMock = static member Empty : InternalTypeMock =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface InternalType with interface InternalType with
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1) member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
member this.Mem2 (arg_0_0) = this.Mem2 (arg_0_0) member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
namespace SomeNamespace namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -86,15 +90,16 @@ type private PrivateTypeMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PrivateTypeMock = static member Empty : PrivateTypeMock =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface PrivateType with interface PrivateType with
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1) member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
member this.Mem2 (arg_0_0) = this.Mem2 (arg_0_0) member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
namespace SomeNamespace namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -107,15 +112,16 @@ type private PrivateTypeInternalFalseMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PrivateTypeInternalFalseMock = static member Empty : PrivateTypeInternalFalseMock =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface PrivateTypeInternalFalse with interface PrivateTypeInternalFalse 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 open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -127,13 +133,14 @@ type internal VeryPublicTypeMock<'a, 'b> =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty () : VeryPublicTypeMock<'a, 'b> = static member Empty () : VeryPublicTypeMock<'a, 'b> =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface VeryPublicType<'a, 'b> with interface VeryPublicType<'a, 'b> with
member this.Mem1 (arg_0_0) = this.Mem1 (arg_0_0) member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
namespace SomeNamespace namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins open WoofWare.Myriad.Plugins
/// Mock record type for an interface /// Mock record type for an interface
@@ -150,18 +157,18 @@ type internal CurriedMock<'a> =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty () : CurriedMock<'a> = static member Empty () : CurriedMock<'a> =
{ {
Mem1 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem3 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem4 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem5 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem6 = (fun x -> raise (System.NotImplementedException "Unimplemented mock function")) Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
} }
interface Curried<'a> with interface Curried<'a> with
member this.Mem1 (arg_0_0) (arg_1_0) = this.Mem1 (arg_0_0) (arg_1_0) member this.Mem1 arg_0_0 arg_1_0 = this.Mem1 (arg_0_0) (arg_1_0)
member this.Mem2 (arg_0_0, arg_0_1) (arg_1_0) = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0) member this.Mem2 (arg_0_0, arg_0_1) arg_1_0 = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0)
member this.Mem3 ((arg_0_0, arg_0_1)) (arg_1_0) = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0) member this.Mem3 ((arg_0_0, arg_0_1)) arg_1_0 = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0)
member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) = member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) =
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
@@ -171,3 +178,31 @@ type internal CurriedMock<'a> =
member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) = member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) =
this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins
/// Mock record type for an interface
type internal TypeWithInterfaceMock =
{
/// Implementation of IDisposable.Dispose
Dispose : unit -> unit
Mem1 : string option -> string[] Async
Mem2 : unit -> string[] Async
}
/// An implementation where every method throws.
static member Empty : TypeWithInterfaceMock =
{
Dispose = (fun _ -> ())
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function"))
}
interface TypeWithInterface with
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
member this.Mem2 () = this.Mem2 (())
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()

View File

@@ -41,12 +41,11 @@ module MemberJsonSerializeExtension =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymOpeningHours type /// Module containing JSON parsing methods for the GymOpeningHours type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymOpeningHours = module GymOpeningHours =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymOpeningHours = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymOpeningHours =
let OpeningHours = let arg_1 =
(match node.["openingHours"] with (match node.["openingHours"] with
| null -> | null ->
raise ( raise (
@@ -59,7 +58,7 @@ module GymOpeningHours =
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> List.ofSeq |> List.ofSeq
let IsAlwaysOpen = let arg_0 =
(match node.["isAlwaysOpen"] with (match node.["isAlwaysOpen"] with
| null -> | null ->
raise ( raise (
@@ -72,18 +71,17 @@ module GymOpeningHours =
.GetValue<bool> () .GetValue<bool> ()
{ {
IsAlwaysOpen = IsAlwaysOpen IsAlwaysOpen = arg_0
OpeningHours = OpeningHours OpeningHours = arg_1
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymAccessOptions type /// Module containing JSON parsing methods for the GymAccessOptions type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAccessOptions = module GymAccessOptions =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAccessOptions = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAccessOptions =
let QrCodeAccess = let arg_1 =
(match node.["qrCodeAccess"] with (match node.["qrCodeAccess"] with
| null -> | null ->
raise ( raise (
@@ -95,7 +93,7 @@ module GymAccessOptions =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let PinAccess = let arg_0 =
(match node.["pinAccess"] with (match node.["pinAccess"] with
| null -> | null ->
raise ( raise (
@@ -108,18 +106,17 @@ module GymAccessOptions =
.GetValue<bool> () .GetValue<bool> ()
{ {
PinAccess = PinAccess PinAccess = arg_0
QrCodeAccess = QrCodeAccess QrCodeAccess = arg_1
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymLocation type /// Module containing JSON parsing methods for the GymLocation type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymLocation = module GymLocation =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymLocation = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymLocation =
let Latitude = let arg_1 =
try try
(match node.["latitude"] with (match node.["latitude"] with
| null -> | null ->
@@ -152,7 +149,7 @@ module GymLocation =
else else
reraise () reraise ()
let Longitude = let arg_0 =
try try
(match node.["longitude"] with (match node.["longitude"] with
| null -> | null ->
@@ -186,18 +183,17 @@ module GymLocation =
reraise () reraise ()
{ {
Longitude = Longitude Longitude = arg_0
Latitude = Latitude Latitude = arg_1
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymAddress type /// Module containing JSON parsing methods for the GymAddress type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAddress = module GymAddress =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAddress = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAddress =
let Postcode = let arg_5 =
(match node.["postcode"] with (match node.["postcode"] with
| null -> | null ->
raise ( raise (
@@ -209,12 +205,12 @@ module GymAddress =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let County = let arg_4 =
match node.["county"] with match node.["county"] with
| null -> None | null -> None
| v -> v.AsValue().GetValue<string> () |> Some | v -> v.AsValue().GetValue<string> () |> Some
let Town = let arg_3 =
(match node.["town"] with (match node.["town"] with
| null -> | null ->
raise ( raise (
@@ -226,17 +222,17 @@ module GymAddress =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let AddressLine3 = let arg_2 =
match node.["addressLine3"] with match node.["addressLine3"] with
| null -> None | null -> None
| v -> v.AsValue().GetValue<string> () |> Some | v -> v.AsValue().GetValue<string> () |> Some
let AddressLine2 = let arg_1 =
match node.["addressLine2"] with match node.["addressLine2"] with
| null -> None | null -> None
| v -> v.AsValue().GetValue<string> () |> Some | v -> v.AsValue().GetValue<string> () |> Some
let AddressLine1 = let arg_0 =
(match node.["addressLine1"] with (match node.["addressLine1"] with
| null -> | null ->
raise ( raise (
@@ -249,22 +245,21 @@ module GymAddress =
.GetValue<string> () .GetValue<string> ()
{ {
AddressLine1 = AddressLine1 AddressLine1 = arg_0
AddressLine2 = AddressLine2 AddressLine2 = arg_1
AddressLine3 = AddressLine3 AddressLine3 = arg_2
Town = Town Town = arg_3
County = County County = arg_4
Postcode = Postcode Postcode = arg_5
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the Gym type /// Module containing JSON parsing methods for the Gym type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Gym = module Gym =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Gym = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Gym =
let ReopenDate = let arg_10 =
(match node.["reopenDate"] with (match node.["reopenDate"] with
| null -> | null ->
raise ( raise (
@@ -276,7 +271,7 @@ module Gym =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let TimeZone = let arg_9 =
(match node.["timeZone"] with (match node.["timeZone"] with
| null -> | null ->
raise ( raise (
@@ -288,7 +283,7 @@ module Gym =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Location = let arg_8 =
GymLocation.jsonParse ( GymLocation.jsonParse (
match node.["location"] with match node.["location"] with
| null -> | null ->
@@ -300,7 +295,7 @@ module Gym =
| v -> v | v -> v
) )
let AccessOptions = let arg_7 =
GymAccessOptions.jsonParse ( GymAccessOptions.jsonParse (
match node.["accessOptions"] with match node.["accessOptions"] with
| null -> | null ->
@@ -312,7 +307,7 @@ module Gym =
| v -> v | v -> v
) )
let GymOpeningHours = let arg_6 =
GymOpeningHours.jsonParse ( GymOpeningHours.jsonParse (
match node.["gymOpeningHours"] with match node.["gymOpeningHours"] with
| null -> | null ->
@@ -324,7 +319,7 @@ module Gym =
| v -> v | v -> v
) )
let EmailAddress = let arg_5 =
(match node.["emailAddress"] with (match node.["emailAddress"] with
| null -> | null ->
raise ( raise (
@@ -336,7 +331,7 @@ module Gym =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let PhoneNumber = let arg_4 =
(match node.["phoneNumber"] with (match node.["phoneNumber"] with
| null -> | null ->
raise ( raise (
@@ -348,7 +343,7 @@ module Gym =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Address = let arg_3 =
GymAddress.jsonParse ( GymAddress.jsonParse (
match node.["address"] with match node.["address"] with
| null -> | null ->
@@ -360,7 +355,7 @@ module Gym =
| v -> v | v -> v
) )
let Status = let arg_2 =
(match node.["status"] with (match node.["status"] with
| null -> | null ->
raise ( raise (
@@ -372,7 +367,7 @@ module Gym =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Id = let arg_1 =
(match node.["id"] with (match node.["id"] with
| null -> | null ->
raise ( raise (
@@ -384,7 +379,7 @@ module Gym =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Name = let arg_0 =
(match node.["name"] with (match node.["name"] with
| null -> | null ->
raise ( raise (
@@ -397,17 +392,17 @@ module Gym =
.GetValue<string> () .GetValue<string> ()
{ {
Name = Name Name = arg_0
Id = Id Id = arg_1
Status = Status Status = arg_2
Address = Address Address = arg_3
PhoneNumber = PhoneNumber PhoneNumber = arg_4
EmailAddress = EmailAddress EmailAddress = arg_5
GymOpeningHours = GymOpeningHours GymOpeningHours = arg_6
AccessOptions = AccessOptions AccessOptions = arg_7
Location = Location Location = arg_8
TimeZone = TimeZone TimeZone = arg_9
ReopenDate = ReopenDate ReopenDate = arg_10
} }
namespace PureGym namespace PureGym
@@ -419,7 +414,7 @@ module MemberJsonParseExtension =
/// Parse from a JSON node. /// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : Member = static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : Member =
let MemberStatus = let arg_14 =
(match node.["memberStatus"] with (match node.["memberStatus"] with
| null -> | null ->
raise ( raise (
@@ -431,7 +426,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let SuspendedReason = let arg_13 =
(match node.["suspendedReason"] with (match node.["suspendedReason"] with
| null -> | null ->
raise ( raise (
@@ -443,7 +438,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let MembershipLevel = let arg_12 =
(match node.["membershipLevel"] with (match node.["membershipLevel"] with
| null -> | null ->
raise ( raise (
@@ -455,7 +450,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let MembershipName = let arg_11 =
(match node.["membershipName"] with (match node.["membershipName"] with
| null -> | null ->
raise ( raise (
@@ -467,7 +462,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Postcode = let arg_10 =
(match node.["postCode"] with (match node.["postCode"] with
| null -> | null ->
raise ( raise (
@@ -479,7 +474,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let MobileNumber = let arg_9 =
(match node.["mobileNumber"] with (match node.["mobileNumber"] with
| null -> | null ->
raise ( raise (
@@ -491,7 +486,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let DateOfBirth = let arg_8 =
(match node.["dateofBirth"] with (match node.["dateofBirth"] with
| null -> | null ->
raise ( raise (
@@ -504,7 +499,7 @@ module MemberJsonParseExtension =
.GetValue<string> () .GetValue<string> ()
|> System.DateOnly.Parse |> System.DateOnly.Parse
let GymAccessPin = let arg_7 =
(match node.["gymAccessPin"] with (match node.["gymAccessPin"] with
| null -> | null ->
raise ( raise (
@@ -516,7 +511,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let EmailAddress = let arg_6 =
(match node.["emailAddress"] with (match node.["emailAddress"] with
| null -> | null ->
raise ( raise (
@@ -528,7 +523,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let HomeGymName = let arg_5 =
(match node.["homeGymName"] with (match node.["homeGymName"] with
| null -> | null ->
raise ( raise (
@@ -540,7 +535,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let HomeGymId = let arg_4 =
(match node.["homeGymId"] with (match node.["homeGymId"] with
| null -> | null ->
raise ( raise (
@@ -552,7 +547,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let LastName = let arg_3 =
(match node.["lastName"] with (match node.["lastName"] with
| null -> | null ->
raise ( raise (
@@ -564,7 +559,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let FirstName = let arg_2 =
(match node.["firstName"] with (match node.["firstName"] with
| null -> | null ->
raise ( raise (
@@ -576,7 +571,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let CompoundMemberId = let arg_1 =
(match node.["compoundMemberId"] with (match node.["compoundMemberId"] with
| null -> | null ->
raise ( raise (
@@ -588,7 +583,7 @@ module MemberJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Id = let arg_0 =
(match node.["id"] with (match node.["id"] with
| null -> | null ->
raise ( raise (
@@ -601,31 +596,30 @@ module MemberJsonParseExtension =
.GetValue<int> () .GetValue<int> ()
{ {
Id = Id Id = arg_0
CompoundMemberId = CompoundMemberId CompoundMemberId = arg_1
FirstName = FirstName FirstName = arg_2
LastName = LastName LastName = arg_3
HomeGymId = HomeGymId HomeGymId = arg_4
HomeGymName = HomeGymName HomeGymName = arg_5
EmailAddress = EmailAddress EmailAddress = arg_6
GymAccessPin = GymAccessPin GymAccessPin = arg_7
DateOfBirth = DateOfBirth DateOfBirth = arg_8
MobileNumber = MobileNumber MobileNumber = arg_9
Postcode = Postcode Postcode = arg_10
MembershipName = MembershipName MembershipName = arg_11
MembershipLevel = MembershipLevel MembershipLevel = arg_12
SuspendedReason = SuspendedReason SuspendedReason = arg_13
MemberStatus = MemberStatus MemberStatus = arg_14
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymAttendance type /// Module containing JSON parsing methods for the GymAttendance type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAttendance = module GymAttendance =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAttendance = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAttendance =
let MaximumCapacity = let arg_8 =
(match node.["maximumCapacity"] with (match node.["maximumCapacity"] with
| null -> | null ->
raise ( raise (
@@ -637,7 +631,7 @@ module GymAttendance =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let LastRefreshedPeopleInClasses = let arg_7 =
(match node.["lastRefreshedPeopleInClasses"] with (match node.["lastRefreshedPeopleInClasses"] with
| null -> | null ->
raise ( raise (
@@ -650,7 +644,7 @@ module GymAttendance =
.GetValue<string> () .GetValue<string> ()
|> System.DateTime.Parse |> System.DateTime.Parse
let LastRefreshed = let arg_6 =
(match node.["lastRefreshed"] with (match node.["lastRefreshed"] with
| null -> | null ->
raise ( raise (
@@ -663,7 +657,7 @@ module GymAttendance =
.GetValue<string> () .GetValue<string> ()
|> System.DateTime.Parse |> System.DateTime.Parse
let AttendanceTime = let arg_5 =
(match node.["attendanceTime"] with (match node.["attendanceTime"] with
| null -> | null ->
raise ( raise (
@@ -676,7 +670,7 @@ module GymAttendance =
.GetValue<string> () .GetValue<string> ()
|> System.DateTime.Parse |> System.DateTime.Parse
let IsApproximate = let arg_4 =
(match node.["isApproximate"] with (match node.["isApproximate"] with
| null -> | null ->
raise ( raise (
@@ -688,12 +682,12 @@ module GymAttendance =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let TotalPeopleSuffix = let arg_3 =
match node.["totalPeopleSuffix"] with match node.["totalPeopleSuffix"] with
| null -> None | null -> None
| v -> v.AsValue().GetValue<string> () |> Some | v -> v.AsValue().GetValue<string> () |> Some
let TotalPeopleInClasses = let arg_2 =
(match node.["totalPeopleInClasses"] with (match node.["totalPeopleInClasses"] with
| null -> | null ->
raise ( raise (
@@ -705,7 +699,7 @@ module GymAttendance =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let TotalPeopleInGym = let arg_1 =
(match node.["totalPeopleInGym"] with (match node.["totalPeopleInGym"] with
| null -> | null ->
raise ( raise (
@@ -717,7 +711,7 @@ module GymAttendance =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Description = let arg_0 =
(match node.["description"] with (match node.["description"] with
| null -> | null ->
raise ( raise (
@@ -730,25 +724,24 @@ module GymAttendance =
.GetValue<string> () .GetValue<string> ()
{ {
Description = Description Description = arg_0
TotalPeopleInGym = TotalPeopleInGym TotalPeopleInGym = arg_1
TotalPeopleInClasses = TotalPeopleInClasses TotalPeopleInClasses = arg_2
TotalPeopleSuffix = TotalPeopleSuffix TotalPeopleSuffix = arg_3
IsApproximate = IsApproximate IsApproximate = arg_4
AttendanceTime = AttendanceTime AttendanceTime = arg_5
LastRefreshed = LastRefreshed LastRefreshed = arg_6
LastRefreshedPeopleInClasses = LastRefreshedPeopleInClasses LastRefreshedPeopleInClasses = arg_7
MaximumCapacity = MaximumCapacity MaximumCapacity = arg_8
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the MemberActivityDto type /// Module containing JSON parsing methods for the MemberActivityDto type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MemberActivityDto = module MemberActivityDto =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : MemberActivityDto = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : MemberActivityDto =
let LastRefreshed = let arg_5 =
(match node.["lastRefreshed"] with (match node.["lastRefreshed"] with
| null -> | null ->
raise ( raise (
@@ -761,7 +754,7 @@ module MemberActivityDto =
.GetValue<string> () .GetValue<string> ()
|> System.DateTime.Parse |> System.DateTime.Parse
let IsEstimated = let arg_4 =
(match node.["isEstimated"] with (match node.["isEstimated"] with
| null -> | null ->
raise ( raise (
@@ -773,7 +766,7 @@ module MemberActivityDto =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let TotalClasses = let arg_3 =
(match node.["totalClasses"] with (match node.["totalClasses"] with
| null -> | null ->
raise ( raise (
@@ -785,7 +778,7 @@ module MemberActivityDto =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let TotalVisits = let arg_2 =
(match node.["totalVisits"] with (match node.["totalVisits"] with
| null -> | null ->
raise ( raise (
@@ -797,7 +790,7 @@ module MemberActivityDto =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let AverageDuration = let arg_1 =
(match node.["averageDuration"] with (match node.["averageDuration"] with
| null -> | null ->
raise ( raise (
@@ -809,7 +802,7 @@ module MemberActivityDto =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let TotalDuration = let arg_0 =
(match node.["totalDuration"] with (match node.["totalDuration"] with
| null -> | null ->
raise ( raise (
@@ -822,22 +815,21 @@ module MemberActivityDto =
.GetValue<int> () .GetValue<int> ()
{ {
TotalDuration = TotalDuration TotalDuration = arg_0
AverageDuration = AverageDuration AverageDuration = arg_1
TotalVisits = TotalVisits TotalVisits = arg_2
TotalClasses = TotalClasses TotalClasses = arg_3
IsEstimated = IsEstimated IsEstimated = arg_4
LastRefreshed = LastRefreshed LastRefreshed = arg_5
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the SessionsAggregate type /// Module containing JSON parsing methods for the SessionsAggregate type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module SessionsAggregate = module SessionsAggregate =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsAggregate = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsAggregate =
let Duration = let arg_2 =
(match node.["Duration"] with (match node.["Duration"] with
| null -> | null ->
raise ( raise (
@@ -849,7 +841,7 @@ module SessionsAggregate =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Visits = let arg_1 =
(match node.["Visits"] with (match node.["Visits"] with
| null -> | null ->
raise ( raise (
@@ -861,7 +853,7 @@ module SessionsAggregate =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Activities = let arg_0 =
(match node.["Activities"] with (match node.["Activities"] with
| null -> | null ->
raise ( raise (
@@ -874,19 +866,18 @@ module SessionsAggregate =
.GetValue<int> () .GetValue<int> ()
{ {
Activities = Activities Activities = arg_0
Visits = Visits Visits = arg_1
Duration = Duration Duration = arg_2
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the VisitGym type /// Module containing JSON parsing methods for the VisitGym type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module VisitGym = module VisitGym =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : VisitGym = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : VisitGym =
let Status = let arg_2 =
(match node.["Status"] with (match node.["Status"] with
| null -> | null ->
raise ( raise (
@@ -898,7 +889,7 @@ module VisitGym =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Name = let arg_1 =
(match node.["Name"] with (match node.["Name"] with
| null -> | null ->
raise ( raise (
@@ -910,7 +901,7 @@ module VisitGym =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Id = let arg_0 =
(match node.["Id"] with (match node.["Id"] with
| null -> | null ->
raise ( raise (
@@ -923,19 +914,18 @@ module VisitGym =
.GetValue<int> () .GetValue<int> ()
{ {
Id = Id Id = arg_0
Name = Name Name = arg_1
Status = Status Status = arg_2
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the Visit type /// Module containing JSON parsing methods for the Visit type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Visit = module Visit =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Visit = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Visit =
let Gym = let arg_3 =
VisitGym.jsonParse ( VisitGym.jsonParse (
match node.["Gym"] with match node.["Gym"] with
| null -> | null ->
@@ -947,7 +937,7 @@ module Visit =
| v -> v | v -> v
) )
let Duration = let arg_2 =
(match node.["Duration"] with (match node.["Duration"] with
| null -> | null ->
raise ( raise (
@@ -959,7 +949,7 @@ module Visit =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let StartTime = let arg_1 =
(match node.["StartTime"] with (match node.["StartTime"] with
| null -> | null ->
raise ( raise (
@@ -972,7 +962,7 @@ module Visit =
.GetValue<string> () .GetValue<string> ()
|> System.DateTime.Parse |> System.DateTime.Parse
let IsDurationEstimated = let arg_0 =
(match node.["IsDurationEstimated"] with (match node.["IsDurationEstimated"] with
| null -> | null ->
raise ( raise (
@@ -985,20 +975,19 @@ module Visit =
.GetValue<bool> () .GetValue<bool> ()
{ {
IsDurationEstimated = IsDurationEstimated IsDurationEstimated = arg_0
StartTime = StartTime StartTime = arg_1
Duration = Duration Duration = arg_2
Gym = Gym Gym = arg_3
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the SessionsSummary type /// Module containing JSON parsing methods for the SessionsSummary type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module SessionsSummary = module SessionsSummary =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsSummary = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsSummary =
let ThisWeek = let arg_1 =
SessionsAggregate.jsonParse ( SessionsAggregate.jsonParse (
match node.["ThisWeek"] with match node.["ThisWeek"] with
| null -> | null ->
@@ -1010,7 +999,7 @@ module SessionsSummary =
| v -> v | v -> v
) )
let Total = let arg_0 =
SessionsAggregate.jsonParse ( SessionsAggregate.jsonParse (
match node.["Total"] with match node.["Total"] with
| null -> | null ->
@@ -1023,18 +1012,17 @@ module SessionsSummary =
) )
{ {
Total = Total Total = arg_0
ThisWeek = ThisWeek ThisWeek = arg_1
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the Sessions type /// Module containing JSON parsing methods for the Sessions type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Sessions = module Sessions =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Sessions = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Sessions =
let Visits = let arg_1 =
(match node.["Visits"] with (match node.["Visits"] with
| null -> | null ->
raise ( raise (
@@ -1047,7 +1035,7 @@ module Sessions =
|> Seq.map (fun elt -> Visit.jsonParse elt) |> Seq.map (fun elt -> Visit.jsonParse elt)
|> List.ofSeq |> List.ofSeq
let Summary = let arg_0 =
SessionsSummary.jsonParse ( SessionsSummary.jsonParse (
match node.["Summary"] with match node.["Summary"] with
| null -> | null ->
@@ -1060,18 +1048,17 @@ module Sessions =
) )
{ {
Summary = Summary Summary = arg_0
Visits = Visits Visits = arg_1
} }
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the UriThing type /// Module containing JSON parsing methods for the UriThing type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module UriThing = module UriThing =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : UriThing = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : UriThing =
let SomeUri = let arg_0 =
(match node.["someUri"] with (match node.["someUri"] with
| null -> | null ->
raise ( raise (
@@ -1085,5 +1072,5 @@ module UriThing =
|> System.Uri |> System.Uri
{ {
SomeUri = SomeUri SomeUri = arg_0
} }

View File

@@ -17,8 +17,7 @@ open System.Net.Http
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 PureGymApi = module PureGymApi =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IPureGymApi = let make (client : System.Net.Http.HttpClient) : IPureGymApi =
@@ -87,6 +86,40 @@ module PureGymApi =
} }
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetGymAttendance' (gymId : int, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null -> System.Uri "https://whatnot.com"
| v -> v),
System.Uri (
"v1/gyms/{gym_id}/attendance"
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
let! jsonNode =
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|> Async.AwaitTask
return GymAttendance.jsonParse jsonNode
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetMember (ct : CancellationToken option) = member _.GetMember (ct : CancellationToken option) =
async { async {
let! ct = Async.CancellationToken let! ct = Async.CancellationToken
@@ -288,7 +321,52 @@ module PureGymApi =
| v -> v), | v -> v),
System.Uri ( System.Uri (
("/v2/gymSessions/member" ("/v2/gymSessions/member"
+ "?fromDate=" + (if "/v2/gymSessions/member".IndexOf (char 63) >= 0 then
"&"
else
"?")
+ "fromDate="
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
+ "&toDate="
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
let! jsonNode =
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|> Async.AwaitTask
return Sessions.jsonParse jsonNode
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetSessionsWithQuery (fromDate : DateOnly, toDate : DateOnly, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null -> System.Uri "https://whatnot.com"
| v -> v),
System.Uri (
("/v2/gymSessions/member?foo=1"
+ (if "/v2/gymSessions/member?foo=1".IndexOf (char 63) >= 0 then
"&"
else
"?")
+ "fromDate="
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode) + ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
+ "&toDate=" + "&toDate="
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)), + ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
@@ -976,8 +1054,7 @@ open System.Net.Http
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 internal ApiWithoutBaseAddress = module internal ApiWithoutBaseAddress =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithoutBaseAddress = let make (client : System.Net.Http.HttpClient) : IApiWithoutBaseAddress =
@@ -1028,8 +1105,7 @@ open System.Net.Http
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 ApiWithBasePath = module ApiWithBasePath =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithBasePath = let make (client : System.Net.Http.HttpClient) : IApiWithBasePath =
@@ -1080,8 +1156,7 @@ open System.Net.Http
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 ApiWithBasePathAndAddress = module ApiWithBasePathAndAddress =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAddress = let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAddress =
@@ -1126,8 +1201,7 @@ open System.Net.Http
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 ApiWithHeaders = module ApiWithHeaders =
/// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties. /// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties.
let make let make
@@ -1140,6 +1214,68 @@ module ApiWithHeaders =
member _.SomeHeader : string = someHeader () member _.SomeHeader : string = someHeader ()
member _.SomeOtherHeader : int = someOtherHeader () member _.SomeOtherHeader : int = someOtherHeader ()
member this.GetPathParam (parameter : string, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri (
"endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
do httpMessage.Headers.Add ("X-Foo", this.SomeHeader.ToString ())
do httpMessage.Headers.Add ("Authorization", this.SomeOtherHeader.ToString ())
do httpMessage.Headers.Add ("Header-Name", "Header-Value")
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithHeaders2 =
/// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties.
let make
(someHeader : unit -> string)
(someOtherHeader : unit -> int)
(client : System.Net.Http.HttpClient)
: IApiWithHeaders2
=
{ new IApiWithHeaders2 with
member _.SomeHeader : string = someHeader ()
member _.SomeOtherHeader : int = someOtherHeader ()
member this.GetPathParam (parameter : string, ct : CancellationToken option) = member this.GetPathParam (parameter : string, ct : CancellationToken option) =
async { async {
let! ct = Async.CancellationToken let! ct = Async.CancellationToken

View File

@@ -149,6 +149,37 @@ module JsonRecordTypeWithBothJsonSerializeExtension =
) )
node :> _ 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 namespace ConsumePlugin
@@ -160,7 +191,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 +209,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 (
@@ -195,7 +226,7 @@ module InnerTypeWithBothJsonParseExtension =
) )
|> dict |> dict
let ReadOnlyDict = let arg_2 =
(match node.["readOnlyDict"] with (match node.["readOnlyDict"] with
| null -> | null ->
raise ( raise (
@@ -210,14 +241,14 @@ module InnerTypeWithBothJsonParseExtension =
let value = let value =
(kvp.Value).AsArray () (kvp.Value).AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<char> ()) |> 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 +265,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 (
@@ -248,11 +279,11 @@ module InnerTypeWithBothJsonParseExtension =
|> System.Guid.Parse |> 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
@@ -264,7 +295,7 @@ 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_5 =
(match node.["f"] with (match node.["f"] with
| null -> | null ->
raise ( raise (
@@ -277,7 +308,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|> Array.ofSeq |> Array.ofSeq
let E = let arg_4 =
(match node.["e"] with (match node.["e"] with
| null -> | null ->
raise ( raise (
@@ -290,7 +321,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> Array.ofSeq |> Array.ofSeq
let D = let arg_3 =
InnerTypeWithBoth.jsonParse ( InnerTypeWithBoth.jsonParse (
match node.["d"] with match node.["d"] with
| null -> | null ->
@@ -302,7 +333,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
| v -> v | v -> v
) )
let C = let arg_2 =
(match node.["c"] with (match node.["c"] with
| null -> | null ->
raise ( raise (
@@ -315,7 +346,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<int> ())
|> List.ofSeq |> List.ofSeq
let B = let arg_1 =
(match node.["b"] with (match node.["b"] with
| null -> | null ->
raise ( raise (
@@ -327,7 +358,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let A = let arg_0 =
(match node.["a"] with (match node.["a"] with
| null -> | null ->
raise ( raise (
@@ -340,10 +371,90 @@ module JsonRecordTypeWithBothJsonParseExtension =
.GetValue<int> () .GetValue<int> ()
{ {
A = A A = arg_0
B = B B = arg_1
C = C C = arg_2
D = D D = arg_3
E = E E = arg_4
F = F F = arg_5
} }
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the FirstDu type
[<AutoOpen>]
module FirstDuJsonParseExtension =
/// Extension methods for JSON parsing
type FirstDu with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : FirstDu =
let ty =
(match node.["type"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("type")
)
)
| v -> v)
|> (fun v -> v.GetValue<string> ())
match ty with
| "emptyCase" -> FirstDu.EmptyCase
| "case1" ->
let node =
(match node.["data"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("data")
)
)
| v -> v)
FirstDu.Case1 (
(match node.["data"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("data")
)
)
| v -> v)
.AsValue()
.GetValue<string> ()
)
| "case2" ->
let node =
(match node.["data"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("data")
)
)
| v -> v)
FirstDu.Case2 (
JsonRecordTypeWithBoth.jsonParse (
match node.["record"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("record")
)
)
| v -> v
),
(match node.["i"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("i")
)
)
| v -> v)
.AsValue()
.GetValue<int> ()
)
| v -> failwith ("Unrecognised 'type' field value: " + v)

View File

@@ -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 (
@@ -25,7 +24,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Orphan = let arg_9 =
(match node.["orphan"] with (match node.["orphan"] with
| null -> | null ->
raise ( raise (
@@ -37,7 +36,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let EntityId = let arg_8 =
(match node.["entity_id"] with (match node.["entity_id"] with
| null -> | null ->
raise ( raise (
@@ -49,7 +48,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let TokenType = let arg_7 =
(match node.["token_type"] with (match node.["token_type"] with
| null -> | null ->
raise ( raise (
@@ -61,7 +60,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let Renewable = let arg_6 =
(match node.["renewable"] with (match node.["renewable"] with
| null -> | null ->
raise ( raise (
@@ -73,7 +72,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let LeaseDuration = let arg_5 =
(match node.["lease_duration"] with (match node.["lease_duration"] with
| null -> | null ->
raise ( raise (
@@ -85,7 +84,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let IdentityPolicies = let arg_4 =
(match node.["identity_policies"] with (match node.["identity_policies"] with
| null -> | null ->
raise ( raise (
@@ -98,7 +97,7 @@ module JwtVaultAuthResponse =
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> List.ofSeq |> List.ofSeq
let TokenPolicies = let arg_3 =
(match node.["token_policies"] with (match node.["token_policies"] with
| null -> | null ->
raise ( raise (
@@ -111,7 +110,7 @@ module JwtVaultAuthResponse =
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> List.ofSeq |> List.ofSeq
let Policies = let arg_2 =
(match node.["policies"] with (match node.["policies"] with
| null -> | null ->
raise ( raise (
@@ -124,7 +123,7 @@ module JwtVaultAuthResponse =
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<string> ())
|> List.ofSeq |> List.ofSeq
let Accessor = let arg_1 =
(match node.["accessor"] with (match node.["accessor"] with
| null -> | null ->
raise ( raise (
@@ -136,7 +135,7 @@ module JwtVaultAuthResponse =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let ClientToken = let arg_0 =
(match node.["client_token"] with (match node.["client_token"] with
| null -> | null ->
raise ( raise (
@@ -149,27 +148,26 @@ module JwtVaultAuthResponse =
.GetValue<string> () .GetValue<string> ()
{ {
ClientToken = ClientToken ClientToken = arg_0
Accessor = Accessor Accessor = arg_1
Policies = Policies Policies = arg_2
TokenPolicies = TokenPolicies TokenPolicies = arg_3
IdentityPolicies = IdentityPolicies IdentityPolicies = arg_4
LeaseDuration = LeaseDuration LeaseDuration = arg_5
Renewable = Renewable Renewable = arg_6
TokenType = TokenType TokenType = arg_7
EntityId = EntityId EntityId = arg_8
Orphan = Orphan Orphan = arg_9
NumUses = NumUses NumUses = arg_10
} }
namespace ConsumePlugin namespace ConsumePlugin
/// 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 +179,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 (
@@ -193,7 +191,7 @@ module JwtVaultResponse =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Renewable = let arg_2 =
(match node.["renewable"] with (match node.["renewable"] with
| null -> | null ->
raise ( raise (
@@ -205,7 +203,7 @@ module JwtVaultResponse =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let LeaseId = let arg_1 =
(match node.["lease_id"] with (match node.["lease_id"] with
| null -> | null ->
raise ( raise (
@@ -217,7 +215,7 @@ module JwtVaultResponse =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let RequestId = let arg_0 =
(match node.["request_id"] with (match node.["request_id"] with
| null -> | null ->
raise ( raise (
@@ -230,21 +228,20 @@ module JwtVaultResponse =
.GetValue<string> () .GetValue<string> ()
{ {
RequestId = RequestId RequestId = arg_0
LeaseId = LeaseId LeaseId = arg_1
Renewable = Renewable Renewable = arg_2
LeaseDuration = LeaseDuration LeaseDuration = arg_3
Auth = Auth Auth = arg_4
} }
namespace ConsumePlugin namespace ConsumePlugin
/// 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 +259,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 (
@@ -279,7 +276,7 @@ module JwtSecretResponse =
) )
|> Map.ofSeq |> Map.ofSeq
let Data6 = let arg_9 =
(match node.["data6"] with (match node.["data6"] with
| null -> | null ->
raise ( raise (
@@ -296,7 +293,7 @@ module JwtSecretResponse =
) )
|> dict |> dict
let Data5 = let arg_8 =
(match node.["data5"] with (match node.["data5"] with
| null -> | null ->
raise ( raise (
@@ -313,7 +310,7 @@ module JwtSecretResponse =
) )
|> readOnlyDict |> readOnlyDict
let Data4 = let arg_7 =
(match node.["data4"] with (match node.["data4"] with
| null -> | null ->
raise ( raise (
@@ -330,7 +327,7 @@ module JwtSecretResponse =
) )
|> Map.ofSeq |> Map.ofSeq
let Data3 = let arg_6 =
(match node.["data3"] with (match node.["data3"] with
| null -> | null ->
raise ( raise (
@@ -348,7 +345,7 @@ module JwtSecretResponse =
|> Seq.map System.Collections.Generic.KeyValuePair |> Seq.map System.Collections.Generic.KeyValuePair
|> System.Collections.Generic.Dictionary |> System.Collections.Generic.Dictionary
let Data2 = let arg_5 =
(match node.["data2"] with (match node.["data2"] with
| null -> | null ->
raise ( raise (
@@ -365,7 +362,7 @@ module JwtSecretResponse =
) )
|> dict |> dict
let Data = let arg_4 =
(match node.["data"] with (match node.["data"] with
| null -> | null ->
raise ( raise (
@@ -382,7 +379,7 @@ module JwtSecretResponse =
) )
|> readOnlyDict |> readOnlyDict
let LeaseDuration = let arg_3 =
(match node.["lease_duration"] with (match node.["lease_duration"] with
| null -> | null ->
raise ( raise (
@@ -394,7 +391,7 @@ module JwtSecretResponse =
.AsValue() .AsValue()
.GetValue<int> () .GetValue<int> ()
let Renewable = let arg_2 =
(match node.["renewable"] with (match node.["renewable"] with
| null -> | null ->
raise ( raise (
@@ -406,7 +403,7 @@ module JwtSecretResponse =
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<bool> ()
let LeaseId = let arg_1 =
(match node.["lease_id"] with (match node.["lease_id"] with
| null -> | null ->
raise ( raise (
@@ -418,7 +415,7 @@ module JwtSecretResponse =
.AsValue() .AsValue()
.GetValue<string> () .GetValue<string> ()
let RequestId = let arg_0 =
(match node.["request_id"] with (match node.["request_id"] with
| null -> | null ->
raise ( raise (
@@ -431,18 +428,18 @@ module JwtSecretResponse =
.GetValue<string> () .GetValue<string> ()
{ {
RequestId = RequestId RequestId = arg_0
LeaseId = LeaseId LeaseId = arg_1
Renewable = Renewable Renewable = arg_2
LeaseDuration = LeaseDuration LeaseDuration = arg_3
Data = Data Data = arg_4
Data2 = Data2 Data2 = arg_5
Data3 = Data3 Data3 = arg_6
Data4 = Data4 Data4 = arg_7
Data5 = Data5 Data5 = arg_8
Data6 = Data6 Data6 = arg_9
Data7 = Data7 Data7 = arg_10
Data8 = Data8 Data8 = arg_11
} }
namespace ConsumePlugin namespace ConsumePlugin
@@ -455,8 +452,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 =
@@ -543,3 +539,200 @@ module VaultClient =
} }
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct)) |> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
} }
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
open System.Threading
open System.Threading.Tasks
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module VaultClientNonExtensionMethod =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IVaultClientNonExtensionMethod =
{ new IVaultClientNonExtensionMethod with
member _.GetSecret
(jwt : JwtVaultResponse, path : string, mountPoint : string, ct : CancellationToken option)
=
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri (
"v1/{mountPoint}/{path}"
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
.Replace (
"{mountPoint}",
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
let! jsonNode =
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|> Async.AwaitTask
return JwtSecretResponse.jsonParse jsonNode
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetJwt (role : string, jwt : string, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri ("v1/auth/jwt/login", System.UriKind.Relative)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
let! jsonNode =
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|> Async.AwaitTask
return JwtVaultResponse.jsonParse jsonNode
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
open System.Threading
open System.Threading.Tasks
open RestEase
/// Extension methods for constructing a REST client.
[<AutoOpen>]
module VaultClientExtensionMethodHttpClientExtension =
/// Extension methods for HTTP clients
type VaultClientExtensionMethod with
/// Create a REST client.
static member make (client : System.Net.Http.HttpClient) : IVaultClientExtensionMethod =
{ new IVaultClientExtensionMethod with
member _.GetSecret
(jwt : JwtVaultResponse, path : string, mountPoint : string, ct : CancellationToken option)
=
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri (
"v1/{mountPoint}/{path}"
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
.Replace (
"{mountPoint}",
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
let! jsonNode =
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|> Async.AwaitTask
return JwtSecretResponse.jsonParse jsonNode
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
member _.GetJwt (role : string, jwt : string, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri ("v1/auth/jwt/login", System.UriKind.Relative)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseStream = response.Content.ReadAsStreamAsync ct |> Async.AwaitTask
let! jsonNode =
System.Text.Json.Nodes.JsonNode.ParseAsync (responseStream, cancellationToken = ct)
|> Async.AwaitTask
return JwtVaultResponse.jsonParse jsonNode
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}

View File

@@ -32,10 +32,27 @@ type JsonRecordType =
[<WoofWare.Myriad.Plugins.JsonParse true>] [<WoofWare.Myriad.Plugins.JsonParse true>]
type ToGetExtensionMethod = type ToGetExtensionMethod =
{ {
Tinker : string Alpha : string
Tailor : int Bravo : System.Uri
Soldier : System.Uri Charlie : float
Sailor : float Delta : float32
Echo : single
Foxtrot : double
Golf : int64
Hotel : uint64
India : int
Juliette : uint
Kilo : int32
Lima : uint32
Mike : int16
November : uint16
Oscar : int8
Papa : uint8
Quebec : byte
Tango : sbyte
Uniform : decimal
Victor : char
Whiskey : bigint
} }
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]

View File

@@ -41,7 +41,7 @@ module MyListCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__MyList x -> | Instruction.Process__MyList (x) ->
match x with match x with
| MyList.Nil -> cata.MyList.Nil |> myListStack.Add | MyList.Nil -> cata.MyList.Nil |> myListStack.Add
| MyList.Cons ({ | MyList.Cons ({
@@ -97,7 +97,7 @@ module MyList2Cata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__MyList2 x -> | Instruction.Process__MyList2 (x) ->
match x with match x with
| MyList2.Nil -> cata.MyList2.Nil |> myList2Stack.Add | MyList2.Nil -> cata.MyList2.Nil |> myList2Stack.Add
| MyList2.Cons (arg0_0, arg1_0) -> | MyList2.Cons (arg0_0, arg1_0) ->

View File

@@ -1,5 +1,6 @@
namespace SomeNamespace namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins open WoofWare.Myriad.Plugins
[<GenerateMock>] [<GenerateMock>]
@@ -41,3 +42,9 @@ type Curried<'a> =
abstract Mem4 : (int * string) -> ('a * int) -> string abstract Mem4 : (int * string) -> ('a * int) -> string
abstract Mem5 : x : int * string -> ('a * int) -> string abstract Mem5 : x : int * string -> ('a * int) -> string
abstract Mem6 : int * string -> y : 'a * int -> string abstract Mem6 : int * string -> y : 'a * int -> string
[<GenerateMock>]
type TypeWithInterface =
inherit IDisposable
abstract Mem1 : string option -> string[] Async
abstract Mem2 : unit -> string[] Async

View File

@@ -17,6 +17,9 @@ type IPureGymApi =
[<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
@@ -38,6 +41,10 @@ type IPureGymApi =
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>
@@ -120,7 +127,8 @@ type internal IApiWithoutBaseAddress =
[<WoofWare.Myriad.Plugins.HttpClient>] [<WoofWare.Myriad.Plugins.HttpClient>]
[<BasePath "foo">] [<BasePath "foo">]
type IApiWithBasePath = type IApiWithBasePath =
[<Get "endpoint/{param}">] // Example where we use the bundled attributes rather than RestEase's
[<WoofWare.Myriad.Plugins.RestEase.Get "endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string> abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>] [<WoofWare.Myriad.Plugins.HttpClient>]
@@ -141,3 +149,16 @@ type IApiWithHeaders =
[<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>]
[<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>

View File

@@ -27,3 +27,10 @@ type JsonRecordTypeWithBoth =
E : string array E : string array
F : int[] F : int[]
} }
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
[<WoofWare.Myriad.Plugins.JsonParse true>]
type FirstDu =
| EmptyCase
| Case1 of data : string
| Case2 of record : JsonRecordTypeWithBoth * i : int

View File

@@ -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

View File

@@ -143,6 +143,9 @@ module InnerTypeWithBoth =
node node
``` ```
Also includes an *opinionated* serializer for discriminated unions.
(Any such serializer must be opinionated, because JSON does not natively model DUs.)
As in `JsonParse`, you can optionally supply the boolean `true` to the attribute, As in `JsonParse`, you can optionally supply the boolean `true` to the attribute,
which will cause Myriad to stamp out an extension method rather than a module with the same name as the type. which will cause Myriad to stamp out an extension method rather than a module with the same name as the type.

View File

@@ -60,8 +60,17 @@ type JsonParseAttribute (isExtensionMethod : bool) =
/// generator should apply during build. /// generator should apply during build.
/// This generator is intended to replicate much of the functionality of RestEase, /// 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. /// i.e. to stamp out HTTP REST clients from interfaces defining the API.
type HttpClientAttribute () = ///
/// 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 () 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 /// Attribute indicating a DU type to which the "create catamorphism" Myriad
/// generator should apply during build. /// generator should apply during build.

View File

@@ -0,0 +1,63 @@
namespace WoofWare.Myriad.Plugins
open System
/// Module containing duplicates of the supported RestEase attributes, in case you don't want
/// to take a dependency on RestEase.
[<RequireQualifiedAccess>]
module RestEase =
/// Indicates that a method represents an HTTP Get query to the specified endpoint.
type GetAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Post query to the specified endpoint.
type PostAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Delete query to the specified endpoint.
type DeleteAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Head query to the specified endpoint.
type HeadAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Options query to the specified endpoint.
type OptionsAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Put query to the specified endpoint.
type PutAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Patch query to the specified endpoint.
type PatchAttribute (path : string) =
inherit Attribute ()
/// Indicates that a method represents an HTTP Trace query to the specified endpoint.
type TraceAttribute (path : string) =
inherit Attribute ()
/// Indicates that this argument to a method is interpolated into the HTTP request at runtime
/// by setting a query parameter (with the given name) to the value of the annotated argument.
type QueryAttribute (paramName : string) =
inherit Attribute ()
/// Indicates that this interface represents a REST client which accesses an API whose paths are
/// all relative to the given address.
type BaseAddressAttribute (addr : string) =
inherit Attribute ()
/// Indicates that this interface member causes the interface to set a header with the given name,
/// whose value is obtained whenever required by a fresh call to the interface member.
type HeaderAttribute (header : string, value : string option) =
inherit Attribute ()
new (header : string) = HeaderAttribute (header, None)
new (header : string, value : string) = HeaderAttribute (header, Some value)
/// Indicates that this argument to a method is interpolated into the request path at runtime
/// by writing it into the templated string that specifies the HTTP query e.g. in the `[<Get "/foo/{template}">]`.
type PathAttribute (path : string option) =
inherit Attribute ()
new (path : string) = PathAttribute (Some path)
new () = PathAttribute None

View File

@@ -6,7 +6,10 @@ WoofWare.Myriad.Plugins.GenerateMockAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.GenerateMockAttribute.DefaultIsInternal [static property]: [read-only] bool WoofWare.Myriad.Plugins.GenerateMockAttribute.DefaultIsInternal [static property]: [read-only] bool
WoofWare.Myriad.Plugins.GenerateMockAttribute.get_DefaultIsInternal [static method]: unit -> bool WoofWare.Myriad.Plugins.GenerateMockAttribute.get_DefaultIsInternal [static method]: unit -> bool
WoofWare.Myriad.Plugins.HttpClientAttribute inherit System.Attribute 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..ctor [constructor]: unit
WoofWare.Myriad.Plugins.HttpClientAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
WoofWare.Myriad.Plugins.HttpClientAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
WoofWare.Myriad.Plugins.JsonParseAttribute inherit System.Attribute WoofWare.Myriad.Plugins.JsonParseAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: bool WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: unit
@@ -18,4 +21,33 @@ WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.JsonSerializeAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool WoofWare.Myriad.Plugins.JsonSerializeAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
WoofWare.Myriad.Plugins.JsonSerializeAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool WoofWare.Myriad.Plugins.JsonSerializeAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.RestEase inherit obj
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+GetAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+GetAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+HeadAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+HeadAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: (string, string option)
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: (string, string)
WoofWare.Myriad.Plugins.RestEase+HeaderAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+OptionsAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+OptionsAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+PatchAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+PatchAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+PathAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: string option
WoofWare.Myriad.Plugins.RestEase+PathAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.RestEase+PostAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+PostAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+PutAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+PutAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+QueryAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+QueryAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+TraceAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+TraceAttribute..ctor [constructor]: string

View File

@@ -11,11 +11,9 @@ module TestSurface =
[<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`` () = let ``Check version against remote`` () =
MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins.Attributes" MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins.Attributes"
*)
[<Test ; Explicit>] [<Test ; Explicit>]
let ``Update API surface`` () = let ``Update API surface`` () =

View File

@@ -12,8 +12,8 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.33" /> <PackageReference Include="ApiSurface" Version="4.0.40" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NUnit" Version="4.1.0"/> <PackageReference Include="NUnit" Version="4.1.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
</ItemGroup> </ItemGroup>

View File

@@ -19,6 +19,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Attributes.fs"/> <Compile Include="Attributes.fs"/>
<Compile Include="RestEase.fs" />
<EmbeddedResource Include="version.json"/> <EmbeddedResource Include="version.json"/>
<EmbeddedResource Include="SurfaceBaseline.txt"/> <EmbeddedResource Include="SurfaceBaseline.txt"/>
<None Include="..\README.md"> <None Include="..\README.md">

View File

@@ -1,7 +1,15 @@
{ {
"version": "2.2", "version": "3.1",
"publicReleaseRefSpec": [ "publicReleaseRefSpec": [
"^refs/heads/main$" "^refs/heads/main$"
], ],
"pathFilters": null "pathFilters": [
":/README.md",
":/LICENSE",
":/WoofWare.Myriad.Plugins/logo.png",
":/Directory.Build.props",
":/global.json",
"./",
"^./Test"
]
} }

View File

@@ -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 =

View File

@@ -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

View File

@@ -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

View File

@@ -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 =

View File

@@ -2,10 +2,9 @@ namespace WoofWare.Myriad.Plugins.Test
open System open System
open System.Collections.Generic open System.Collections.Generic
open System.IO
open System.Text
open System.Text.Json
open System.Text.Json.Nodes open System.Text.Json.Nodes
open FsCheck.Random
open Microsoft.FSharp.Reflection
open NUnit.Framework open NUnit.Framework
open FsCheck open FsCheck
open FsUnitTyped open FsUnitTyped
@@ -124,3 +123,82 @@ module TestJsonSerde =
|> shouldEqual ( |> shouldEqual (
sprintf """{"it\u0027s-a-me":"%s","map":{},"readOnlyDict":{},"dict":{},"concreteDict":{}}""" guidStr sprintf """{"it\u0027s-a-me":"%s","map":{},"readOnlyDict":{},"dict":{},"concreteDict":{}}""" guidStr
) )
type Generators =
static member TestCase () =
{ new Arbitrary<InnerTypeWithBoth>() with
override x.Generator = innerGen 5
}
let sanitiseInner (r : InnerTypeWithBoth) : InnerTypeWithBoth =
{
Thing = r.Thing
Map = r.Map
ReadOnlyDict = r.ReadOnlyDict
Dict = r.Dict
ConcreteDict = r.ConcreteDict
}
let sanitiseRec (r : JsonRecordTypeWithBoth) : JsonRecordTypeWithBoth =
{
A = r.A
B = if isNull r.B then "<null>" else r.B
C =
if Object.ReferenceEquals (r.C, (null : obj)) then
[]
else
r.C
D = sanitiseInner r.D
E = if isNull r.E then [||] else r.E
F =
if Object.ReferenceEquals (r.F, (null : obj)) then
[||]
else
r.F
}
let duGen =
gen {
let! case = Gen.choose (0, 2)
match case with
| 0 -> return FirstDu.EmptyCase
| 1 ->
let! s = Arb.generate<NonNull<string>>
return FirstDu.Case1 s.Get
| 2 ->
let! i = Arb.generate<int>
let! record = outerGen
return FirstDu.Case2 (record, i)
| _ -> return failwith $"unexpected: %i{case}"
}
[<Test>]
let ``Discriminated union works`` () =
let property (du : FirstDu) : unit =
du
|> FirstDu.toJsonNode
|> fun s -> s.ToJsonString ()
|> JsonNode.Parse
|> FirstDu.jsonParse
|> shouldEqual du
property |> Prop.forAll (Arb.fromGen duGen) |> Check.QuickThrowOnFailure
[<Test>]
let ``DU generator covers all cases`` () =
let rand = Random ()
let cases = FSharpType.GetUnionCases typeof<FirstDu>
let counts = Array.zeroCreate<int> cases.Length
let decompose = FSharpValue.PreComputeUnionTagReader typeof<FirstDu>
Gen.listOf duGen
|> Gen.eval 100 (StdGen.StdGen (rand.Next (), rand.Next ()))
|> List.iter (fun du ->
let tag = decompose du
counts.[tag] <- counts.[tag] + 1
)
for i in counts do
i |> shouldBeGreaterThan 0

View File

@@ -33,10 +33,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.33"/> <PackageReference Include="ApiSurface" Version="4.0.40"/>
<PackageReference Include="FsCheck" Version="2.16.6"/> <PackageReference Include="FsCheck" Version="2.16.6"/>
<PackageReference Include="FsUnit" Version="6.0.0"/> <PackageReference Include="FsUnit" Version="6.0.0"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageReference Include="NUnit" Version="4.1.0"/> <PackageReference Include="NUnit" Version="4.1.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/>
</ItemGroup> </ItemGroup>

View File

@@ -1,7 +1,6 @@
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 Fantomas.FCS.Xml
open Myriad.Core.AstExtensions open Myriad.Core.AstExtensions
@@ -54,6 +53,7 @@ type internal InterfaceType =
{ {
Attributes : SynAttribute list Attributes : SynAttribute list
Name : LongIdent Name : LongIdent
Inherits : SynType list
Members : MemberInfo list Members : MemberInfo list
Properties : PropertyInfo list Properties : PropertyInfo list
Generics : SynTyparDecls option Generics : SynTyparDecls option
@@ -105,81 +105,17 @@ module internal AstHelper =
SynExpr.Record (None, None, fields, range0) 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.Accessibility
?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.record (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
@@ -224,7 +160,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
@@ -326,7 +262,7 @@ module internal AstHelper =
Attributes = [] Attributes = []
IsOptional = false IsOptional = false
Id = None Id = None
Type = SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent ident) Type = SynType.createLongIdent ident
} }
|> List.singleton |> List.singleton
} }
@@ -338,11 +274,22 @@ module internal AstHelper =
Attributes = [] Attributes = []
IsOptional = false IsOptional = false
Id = None Id = None
Type = SynType.Var (typar, range0) Type = SynType.var typar
}
|> List.singleton
}
| arg ->
{
HasParen = false
Args =
{
Attributes = []
IsOptional = false
Id = None
Type = arg
} }
|> List.singleton |> List.singleton
} }
| _ -> failwith $"Unrecognised args in interface method declaration: %+A{args}"
|> fun ty -> |> fun ty ->
{ ty with { ty with
HasParen = ty.HasParen || hasParen HasParen = ty.HasParen || hasParen
@@ -386,22 +333,26 @@ module internal AstHelper =
let attrs = attrs |> List.collect (fun s -> s.Attributes) let attrs = attrs |> List.collect (fun s -> s.Attributes)
let members, properties = 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, _, _) -> parseMember slotSig flags | SynMemberDefn.AbstractSlot (slotSig, flags, _, _) -> Choice1Of2 (parseMember slotSig flags)
| SynMemberDefn.Inherit (baseType, _asIdent, _) -> Choice2Of2 baseType
| _ -> 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 |> List.partitionChoice
let members, properties = members |> List.partitionChoice
{ {
Members = members Members = members
Properties = properties Properties = properties
Name = interfaceName Name = interfaceName
Inherits = inherits
Attributes = attrs Attributes = attrs
Generics = typars Generics = typars
Accessibility = accessibility Accessibility = accessibility
@@ -477,176 +428,3 @@ module internal AstHelper =
} }
) )
| _ -> failwithf "Failed to get record elements for type that was: %+A" repr | _ -> failwithf "Failed to get record elements for type that was: %+A" repr
[<AutoOpen>]
module internal SynTypePatterns =
let (|OptionType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isOptionIdent ident ->
Some innerType
| _ -> None
let (|ListType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isListIdent ident ->
Some innerType
| _ -> None
let (|ArrayType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isArrayIdent ident ->
Some innerType
| SynType.Array (1, innerType, _) -> Some innerType
| _ -> None
let (|RestEaseResponseType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isResponseIdent ident ->
Some innerType
| _ -> None
let (|DictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isDictionaryIdent ident ->
Some (key, value)
| _ -> None
let (|IDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isIDictionaryIdent ident ->
Some (key, value)
| _ -> None
let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when
AstHelper.isReadOnlyDictionaryIdent ident
->
Some (key, value)
| _ -> None
let (|MapType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isMapIdent ident ->
Some (key, value)
| _ -> None
/// Returns the string name of the type.
let (|PrimitiveType|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] ->
[ "string" ; "float" ; "int" ; "bool" ; "char" ]
|> List.tryFind (fun s -> s = i.idText)
| _ -> None
| _ -> None
let (|String|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
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 (|Guid|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Guid" ]
| [ "Guid" ] -> Some ()
| _ -> None
| _ -> None
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
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

File diff suppressed because it is too large Load Diff

View File

@@ -1,13 +1,16 @@
namespace WoofWare.Myriad.Plugins namespace WoofWare.Myriad.Plugins
open System
open System.Net.Http open System.Net.Http
open System.Text
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml open Fantomas.FCS.Xml
open Myriad.Core open Myriad.Core
type internal HttpClientGeneratorOutputSpec =
{
ExtensionMethods : bool
}
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal HttpClientGenerator = module internal HttpClientGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
@@ -82,34 +85,50 @@ module internal HttpClientGenerator =
match attr.TypeName.AsString with match attr.TypeName.AsString with
| "Get" | "Get"
| "GetAttribute" | "GetAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Get"
| "WoofWare.Myriad.Plugins.RestEase.GetAttribute"
| "RestEase.Get" | "RestEase.Get"
| "RestEase.GetAttribute" -> Some (HttpMethod.Get, attr.ArgExpr) | "RestEase.GetAttribute" -> Some (HttpMethod.Get, attr.ArgExpr)
| "Post" | "Post"
| "PostAttribute" | "PostAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Post"
| "WoofWare.Myriad.Plugins.RestEase.PostAttribute"
| "RestEase.Post" | "RestEase.Post"
| "RestEase.PostAttribute" -> Some (HttpMethod.Post, attr.ArgExpr) | "RestEase.PostAttribute" -> Some (HttpMethod.Post, attr.ArgExpr)
| "Put" | "Put"
| "PutAttribute" | "PutAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Put"
| "WoofWare.Myriad.Plugins.RestEase.PutAttribute"
| "RestEase.Put" | "RestEase.Put"
| "RestEase.PutAttribute" -> Some (HttpMethod.Put, attr.ArgExpr) | "RestEase.PutAttribute" -> Some (HttpMethod.Put, attr.ArgExpr)
| "Delete" | "Delete"
| "DeleteAttribute" | "DeleteAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Delete"
| "WoofWare.Myriad.Plugins.RestEase.DeleteAttribute"
| "RestEase.Delete" | "RestEase.Delete"
| "RestEase.DeleteAttribute" -> Some (HttpMethod.Delete, attr.ArgExpr) | "RestEase.DeleteAttribute" -> Some (HttpMethod.Delete, attr.ArgExpr)
| "Head" | "Head"
| "HeadAttribute" | "HeadAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Head"
| "WoofWare.Myriad.Plugins.RestEase.HeadAttribute"
| "RestEase.Head" | "RestEase.Head"
| "RestEase.HeadAttribute" -> Some (HttpMethod.Head, attr.ArgExpr) | "RestEase.HeadAttribute" -> Some (HttpMethod.Head, attr.ArgExpr)
| "Options" | "Options"
| "OptionsAttribute" | "OptionsAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Options"
| "WoofWare.Myriad.Plugins.RestEase.OptionsAttribute"
| "RestEase.Options" | "RestEase.Options"
| "RestEase.OptionsAttribute" -> Some (HttpMethod.Options, attr.ArgExpr) | "RestEase.OptionsAttribute" -> Some (HttpMethod.Options, attr.ArgExpr)
| "Patch" | "Patch"
| "PatchAttribute" | "PatchAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Patch"
| "WoofWare.Myriad.Plugins.RestEase.PatchAttribute"
| "RestEase.Patch" | "RestEase.Patch"
| "RestEase.PatchAttribute" -> Some (HttpMethod.Patch, attr.ArgExpr) | "RestEase.PatchAttribute" -> Some (HttpMethod.Patch, attr.ArgExpr)
| "Trace" | "Trace"
| "TraceAttribute" | "TraceAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Trace"
| "WoofWare.Myriad.Plugins.RestEase.TraceAttribute"
| "RestEase.Trace" | "RestEase.Trace"
| "RestEase.TraceAttribute" -> Some (HttpMethod.Trace, attr.ArgExpr) | "RestEase.TraceAttribute" -> Some (HttpMethod.Trace, attr.ArgExpr)
| _ -> None | _ -> None
@@ -127,7 +146,8 @@ module internal HttpClientGenerator =
|> List.choose (fun attr -> |> List.choose (fun attr ->
match attr.TypeName.AsString with match attr.TypeName.AsString with
| "Header" | "Header"
| "RestEase.Header" -> | "RestEase.Header"
| "WoofWare.Myriad.Plugins.RestEase.Header" ->
match attr.ArgExpr with match attr.ArgExpr with
| SynExpr.Paren (SynExpr.Tuple (_, [ v1 ; v2 ], _, _), _, _, _) -> | SynExpr.Paren (SynExpr.Tuple (_, [ v1 ; v2 ], _, _), _, _, _) ->
Some [ SynExpr.stripOptionalParen v1 ; SynExpr.stripOptionalParen v2 ] Some [ SynExpr.stripOptionalParen v1 ; SynExpr.stripOptionalParen v2 ]
@@ -193,11 +213,7 @@ module internal HttpClientGenerator =
let argType = let argType =
if arg.IsOptional then if arg.IsOptional then
SynType.CreateApp ( SynType.appPostfix "option" arg.Type
SynType.CreateLongIdent (SynLongIdent.CreateString "option"),
[ arg.Type ],
isPostfix = true
)
else else
arg.Type arg.Type
@@ -221,7 +237,7 @@ module internal HttpClientGenerator =
let thisIdent = if variableHeaders.IsEmpty then "_" else "this" let thisIdent = if variableHeaders.IsEmpty then "_" else "this"
SynPat.LongIdent ( SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent [ Ident.Create thisIdent ; info.Identifier ], SynLongIdent.create [ Ident.create thisIdent ; info.Identifier ],
None, None,
None, None,
argPats, argPats,
@@ -251,12 +267,10 @@ module internal HttpClientGenerator =
"Replace" "Replace"
(SynExpr.CreateParenedTuple (SynExpr.CreateParenedTuple
[ [
SynExpr.CreateConstString ("{" + substituteId + "}") SynExpr.CreateConst ("{" + substituteId + "}")
SynExpr.callMethod "ToString" (SynExpr.CreateIdent varName) SynExpr.callMethod "ToString" (SynExpr.CreateIdent varName)
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent ( SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
SynLongIdent.Create [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
)
) )
]) ])
| _ -> template | _ -> template
@@ -293,15 +307,30 @@ module internal HttpClientGenerator =
| None -> failwith "Unable to get parameter variable name from anonymous parameter" | None -> failwith "Unable to get parameter variable name from anonymous parameter"
| Some id -> id | Some id -> id
let urlSeparator =
// apparent Myriad bug: `IndexOf '?'` gets formatted as `IndexOf ?` which is clearly wrong
let questionMark =
SynExpr.CreateConst 63
|> SynExpr.applyFunction (SynExpr.createIdent "char")
|> SynExpr.paren
let containsQuestion =
info.UrlTemplate
|> SynExpr.callMethodArg "IndexOf" questionMark
|> SynExpr.greaterThanOrEqual (SynExpr.CreateConst 0)
SynExpr.ifThenElse containsQuestion (SynExpr.CreateConst "?") (SynExpr.CreateConst "&")
|> SynExpr.paren
let prefix = let prefix =
SynExpr.CreateIdent firstValueId SynExpr.createIdent' firstValueId
|> SynExpr.toString firstValue.Type |> SynExpr.toString firstValue.Type
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]) SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
) )
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.plus (SynExpr.CreateConstString ("?" + firstKey + "=")) |> SynExpr.plus (SynExpr.plus urlSeparator (SynExpr.CreateConst (firstKey + "=")))
(prefix, queryParams) (prefix, queryParams)
||> List.fold (fun uri (paramKey, paramValue) -> ||> List.fold (fun uri (paramKey, paramValue) ->
@@ -310,63 +339,44 @@ module internal HttpClientGenerator =
| None -> failwith "Unable to get parameter variable name from anonymous parameter" | None -> failwith "Unable to get parameter variable name from anonymous parameter"
| Some id -> id | Some id -> id
SynExpr.toString paramValue.Type (SynExpr.CreateIdent paramValueId) SynExpr.toString paramValue.Type (SynExpr.createIdent' paramValueId)
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent ( SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
SynLongIdent.Create [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
)
) )
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.plus (SynExpr.plus uri (SynExpr.CreateConstString ("&" + paramKey + "="))) |> SynExpr.plus (SynExpr.plus uri (SynExpr.CreateConst ("&" + paramKey + "=")))
) )
|> SynExpr.plus requestUriTrailer |> SynExpr.plus requestUriTrailer
|> SynExpr.CreateParen |> SynExpr.paren
let requestUri = let requestUri =
let uriIdent = SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Uri" ]) let uriIdent = SynExpr.createLongIdent [ "System" ; "Uri" ]
let baseAddress = SynExpr.createLongIdent [ "client" ; "BaseAddress" ]
let baseAddress = let baseAddress =
SynExpr.CreateLongIdent (SynLongIdent.Create [ "client" ; "BaseAddress" ]) [
SynMatchClause.Create (
let baseAddress = SynPat.CreateNull,
SynExpr.CreateMatch ( None,
baseAddress, match info.BaseAddress with
[ | None ->
SynMatchClause.Create ( [
SynPat.CreateNull, SynExpr.applyFunction (SynExpr.createIdent "nameof") (SynExpr.paren baseAddress)
None, SynExpr.CreateConst
match info.BaseAddress with "No base address was supplied on the type, and no BaseAddress was on the HttpClient."
| None -> ]
SynExpr.CreateApp ( |> SynExpr.CreateParenedTuple
SynExpr.CreateIdentString "raise", |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "ArgumentNullException" ])
SynExpr.CreateParen ( |> SynExpr.paren
SynExpr.CreateApp ( |> SynExpr.applyFunction (SynExpr.createIdent "raise")
SynExpr.CreateLongIdent ( | Some expr -> SynExpr.applyFunction uriIdent expr
SynLongIdent.Create [ "System" ; "ArgumentNullException" ] )
), SynMatchClause.Create (SynPat.named "v", None, SynExpr.createIdent "v")
SynExpr.CreateParenedTuple ]
[ |> SynExpr.createMatch baseAddress
SynExpr.CreateApp ( |> SynExpr.paren
SynExpr.CreateIdentString "nameof",
SynExpr.CreateParen baseAddress
)
SynExpr.CreateConstString
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
]
)
)
)
| Some expr -> SynExpr.CreateApp (uriIdent, expr)
)
SynMatchClause.Create (
SynPat.CreateNamed (Ident.Create "v"),
None,
SynExpr.CreateIdentString "v"
)
]
)
|> SynExpr.CreateParen
SynExpr.App ( SynExpr.App (
ExprAtomicFlag.Atomic, ExprAtomicFlag.Atomic,
@@ -380,7 +390,7 @@ module internal HttpClientGenerator =
SynExpr.CreateParenedTuple SynExpr.CreateParenedTuple
[ [
requestUriTrailer requestUriTrailer
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "UriKind" ; "Relative" ]) SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ]
] ]
) )
], ],
@@ -419,58 +429,43 @@ module internal HttpClientGenerator =
let httpReqMessageConstructor = let httpReqMessageConstructor =
[ [
SynExpr.equals SynExpr.equals
(SynExpr.CreateIdentString "Method") (SynExpr.createIdent "Method")
(SynExpr.CreateLongIdent ( (SynExpr.createLongIdent
SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "HttpMethod" ; httpMethodString info.HttpMethod ])
[ "System" ; "Net" ; "Http" ; "HttpMethod" ; httpMethodString info.HttpMethod ] SynExpr.equals (SynExpr.createIdent "RequestUri") (SynExpr.createIdent "uri")
))
SynExpr.equals (SynExpr.CreateIdentString "RequestUri") (SynExpr.CreateIdentString "uri")
] ]
|> SynExpr.CreateParenedTuple |> SynExpr.CreateTuple
let returnExpr = let returnExpr =
match info.TaskReturnType with match info.TaskReturnType with
| HttpResponseMessage -> SynExpr.CreateIdentString "response" | HttpResponseMessage -> SynExpr.createIdent "response"
| String -> SynExpr.CreateIdentString "responseString" | String -> SynExpr.createIdent "responseString"
| Stream -> SynExpr.CreateIdentString "responseStream" | Stream -> SynExpr.createIdent "responseStream"
| RestEaseResponseType contents -> | RestEaseResponseType contents ->
let deserialiser = let deserialiser =
SynExpr.CreateLambda ( JsonParseGenerator.parseNode
[ SynPat.CreateConst SynConst.Unit ], None
SynExpr.CreateParen ( JsonParseGenerator.JsonParseOption.None
JsonParseGenerator.parseNode contents
None (SynExpr.createIdent "jsonNode")
JsonParseGenerator.JsonParseOption.None |> SynExpr.paren
contents |> SynExpr.createThunk
(SynExpr.CreateIdentString "jsonNode")
)
)
// new RestEase.Response (content : string, response : HttpResponseMessage, deserialiser : unit -> 'T) // new RestEase.Response (content : string, response : HttpResponseMessage, deserialiser : unit -> 'T)
SynExpr.New ( SynExpr.createNew
false, (SynType.app' (SynType.createLongIdent' [ "RestEase" ; "Response" ]) [ SynType.Anon range0 ])
SynType.App ( (SynExpr.CreateTuple
SynType.CreateLongIdent (SynLongIdent.Create [ "RestEase" ; "Response" ]),
Some range0,
[ SynType.Anon range0 ],
[],
Some range0,
false,
range0
),
SynExpr.CreateParenedTuple
[ [
SynExpr.CreateIdentString "responseString" SynExpr.createIdent "responseString"
SynExpr.CreateIdentString "response" SynExpr.createIdent "response"
SynExpr.CreateParen deserialiser deserialiser
], ])
range0
)
| retType -> | retType ->
JsonParseGenerator.parseNode JsonParseGenerator.parseNode
None None
JsonParseGenerator.JsonParseOption.None JsonParseGenerator.JsonParseOption.None
retType retType
(SynExpr.CreateIdentString "jsonNode") (SynExpr.createIdent "jsonNode")
let handleBodyParams = let handleBodyParams =
match bodyParam with match bodyParam with
@@ -483,20 +478,15 @@ module internal HttpClientGenerator =
[ [
Let ( Let (
"queryParams", "queryParams",
SynExpr.New ( SynExpr.createNew
false, (SynType.createLongIdent'
SynType.CreateLongIdent ( [ "System" ; "Net" ; "Http" ; (bodyParamType : BodyParamMethods).ToString () ])
SynLongIdent.Create (SynExpr.createIdent' bodyParamName)
[ "System" ; "Net" ; "Http" ; (bodyParamType : BodyParamMethods).ToString () ]
),
SynExpr.CreateParen (SynExpr.CreateIdent bodyParamName),
range0
)
) )
Do ( Do (
SynExpr.LongIdentSet ( SynExpr.LongIdentSet (
SynLongIdent.Create [ "httpMessage" ; "Content" ], SynLongIdent.createS' [ "httpMessage" ; "Content" ],
SynExpr.CreateIdentString "queryParams", SynExpr.createIdent "queryParams",
range0 range0
) )
) )
@@ -505,8 +495,8 @@ module internal HttpClientGenerator =
[ [
Do ( Do (
SynExpr.LongIdentSet ( SynExpr.LongIdentSet (
SynLongIdent.Create [ "httpMessage" ; "Content" ], SynLongIdent.createS' [ "httpMessage" ; "Content" ],
SynExpr.CreateIdent bodyParamName, SynExpr.createIdent' bodyParamName,
range0 range0
) )
) )
@@ -515,38 +505,27 @@ module internal HttpClientGenerator =
[ [
Let ( Let (
"queryParams", "queryParams",
SynExpr.New ( SynExpr.createNew
false, (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ])
SynType.CreateLongIdent ( (SynExpr.createIdent' bodyParamName
SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "StringContent" ] |> SynExpr.pipeThroughFunction (JsonSerializeGenerator.serializeNode ty)
), |> SynExpr.pipeThroughFunction (
SynExpr.CreateParen ( SynExpr.createLambda
SynExpr.CreateIdent bodyParamName "node"
|> SynExpr.pipeThroughFunction (JsonSerializeGenerator.serializeNode ty) (SynExpr.ifThenElse
|> SynExpr.pipeThroughFunction ( (SynExpr.applyFunction
SynExpr.createLambda (SynExpr.createIdent "isNull")
"node" (SynExpr.createIdent "node"))
(SynExpr.ifThenElse (SynExpr.applyFunction
(SynExpr.CreateApp ( (SynExpr.createLongIdent [ "node" ; "ToJsonString" ])
SynExpr.CreateIdentString "isNull", (SynExpr.CreateConst ()))
SynExpr.CreateIdentString "node" (SynExpr.CreateConst "null"))
)) ))
(SynExpr.CreateApp (
SynExpr.CreateLongIdent (
SynLongIdent.Create [ "node" ; "ToJsonString" ]
),
SynExpr.CreateConst SynConst.Unit
))
(SynExpr.CreateConst (SynConst.CreateString "null")))
)
),
range0
)
) )
Do ( Do (
SynExpr.LongIdentSet ( SynExpr.LongIdentSet (
SynLongIdent.Create [ "httpMessage" ; "Content" ], SynLongIdent.createS' [ "httpMessage" ; "Content" ],
SynExpr.CreateIdent (Ident.Create "queryParams"), SynExpr.createIdent "queryParams",
range0 range0
) )
) )
@@ -557,12 +536,9 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"responseString", "responseString",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent [ "response" ; "Content" ; "ReadAsStringAsync" ])
SynLongIdent.Create [ "response" ; "Content" ; "ReadAsStringAsync" ] (SynExpr.createIdent "ct")
),
SynExpr.CreateIdentString "ct"
)
) )
) )
@@ -570,12 +546,9 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"responseStream", "responseStream",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent [ "response" ; "Content" ; "ReadAsStreamAsync" ])
SynLongIdent.Create [ "response" ; "Content" ; "ReadAsStreamAsync" ] (SynExpr.createIdent "ct")
),
SynExpr.CreateIdentString "ct"
)
) )
) )
@@ -583,67 +556,50 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"jsonNode", "jsonNode",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ; "ParseAsync" ] [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ; "ParseAsync" ])
), (SynExpr.CreateParenedTuple
SynExpr.CreateParenedTuple
[ [
SynExpr.CreateIdentString "responseStream" SynExpr.createIdent "responseStream"
SynExpr.equals SynExpr.equals (SynExpr.createIdent "cancellationToken") (SynExpr.createIdent "ct")
(SynExpr.CreateIdentString "cancellationToken") ])
(SynExpr.CreateIdentString "ct")
]
)
) )
) )
let setVariableHeaders = let setVariableHeaders =
variableHeaders variableHeaders
|> List.map (fun (headerName, callToGetValue) -> |> List.map (fun (headerName, callToGetValue) ->
Do ( [
SynExpr.CreateApp ( headerName
SynExpr.CreateLongIdent (SynLongIdent.Create [ "httpMessage" ; "Headers" ; "Add" ]), SynExpr.applyFunction
SynExpr.CreateParenedTuple (SynExpr.createLongIdent'
[ [ Ident.Create "this" ; callToGetValue ; Ident.Create "ToString" ])
headerName (SynExpr.CreateConst ())
SynExpr.CreateApp ( ]
SynExpr.CreateLongIdent ( |> SynExpr.CreateParenedTuple
SynLongIdent.CreateFromLongIdent |> SynExpr.applyFunction (SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ])
[ Ident.Create "this" ; callToGetValue ; Ident.Create "ToString" ] |> Do
),
SynExpr.CreateConst SynConst.Unit
)
]
)
)
) )
let setConstantHeaders = let setConstantHeaders =
constantHeaders constantHeaders
|> List.map (fun (headerName, headerValue) -> |> List.map (fun (headerName, headerValue) ->
Do ( SynExpr.applyFunction
SynExpr.CreateApp ( (SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ])
SynExpr.CreateLongIdent (SynLongIdent.Create [ "httpMessage" ; "Headers" ; "Add" ]), (SynExpr.CreateParenedTuple [ headerName ; headerValue ])
SynExpr.CreateParenedTuple [ headerName ; headerValue ] |> Do
)
)
) )
[ [
yield LetBang ("ct", SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "CancellationToken" ])) yield LetBang ("ct", SynExpr.createLongIdent [ "Async" ; "CancellationToken" ])
yield Let ("uri", requestUri) yield Let ("uri", requestUri)
yield yield
Use ( Use (
"httpMessage", "httpMessage",
SynExpr.New ( SynExpr.createNew
false, (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "HttpRequestMessage" ])
SynType.CreateLongIdent ( httpReqMessageConstructor
SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "HttpRequestMessage" ]
),
httpReqMessageConstructor,
range0
)
) )
yield! handleBodyParams yield! handleBodyParams
@@ -655,21 +611,19 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"response", "response",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "client" ; "SendAsync" ]), (SynExpr.createLongIdent [ "client" ; "SendAsync" ])
SynExpr.CreateParenedTuple (SynExpr.CreateParenedTuple
[ SynExpr.CreateIdentString "httpMessage" ; SynExpr.CreateIdentString "ct" ] [ SynExpr.createIdent "httpMessage" ; SynExpr.createIdent "ct" ])
)
) )
) )
if info.EnsureSuccessHttpCode then if info.EnsureSuccessHttpCode then
yield yield
Let ( Let (
"response", "response",
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "response" ; "EnsureSuccessStatusCode" ]), (SynExpr.createLongIdent [ "response" ; "EnsureSuccessStatusCode" ])
SynExpr.CreateConst SynConst.Unit (SynExpr.CreateConst ())
)
) )
match info.TaskReturnType with match info.TaskReturnType with
| HttpResponseMessage -> () | HttpResponseMessage -> ()
@@ -684,31 +638,34 @@ module internal HttpClientGenerator =
yield jsonNode yield jsonNode
] ]
|> SynExpr.createCompExpr "async" returnExpr |> SynExpr.createCompExpr "async" returnExpr
|> SynExpr.startAsTask (SynLongIdent.CreateFromLongIdent [ cancellationTokenArg ]) |> SynExpr.startAsTask (SynLongIdent.createI cancellationTokenArg)
SynMemberDefn.Member ( SynBinding.SynBinding (
SynBinding.SynBinding ( None,
info.Accessibility, SynBindingKind.Normal,
SynBindingKind.Normal, false,
false, false,
false, [],
[], PreXmlDoc.Empty,
PreXmlDoc.Empty, valData,
valData, headPat,
headPat, None,
None, implementation,
implementation, range0,
range0, DebugPointAtBinding.Yes range0,
DebugPointAtBinding.Yes range0, SynBinding.triviaZero true
SynExpr.synBindingTriviaZero true
),
range0
) )
|> SynBinding.withAccessibility info.Accessibility
|> fun b -> SynMemberDefn.Member (b, range0)
let getHttpAttributes (attrs : SynAttribute list) : HttpAttribute list = let getHttpAttributes (attrs : SynAttribute list) : HttpAttribute list =
attrs attrs
|> List.choose (fun attr -> |> List.choose (fun attr ->
match attr.TypeName.AsString with match attr.TypeName.AsString with
| "RestEase.Query"
| "RestEase.QueryAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Query"
| "WoofWare.Myriad.Plugins.RestEase.QueryAttribute"
| "Query" | "Query"
| "QueryAttribute" -> | "QueryAttribute" ->
match attr.ArgExpr with match attr.ArgExpr with
@@ -717,14 +674,22 @@ module internal HttpClientGenerator =
Some (HttpAttribute.Query (Some s)) Some (HttpAttribute.Query (Some s))
| SynExpr.Const (a, _) -> failwith $"unrecognised constant arg to the Query attribute: %+A{a}" | SynExpr.Const (a, _) -> failwith $"unrecognised constant arg to the Query attribute: %+A{a}"
| _ -> None | _ -> None
| "RestEase.Path"
| "RestEase.PathAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Path"
| "WoofWare.Myriad.Plugins.RestEase.PathAttribute"
| "Path" | "Path"
| "PathAttribute" -> | "PathAttribute" ->
match attr.ArgExpr with match attr.ArgExpr |> SynExpr.stripOptionalParen with
| SynExpr.Const (SynConst.String (s, SynStringKind.Regular, _), _) -> | SynExpr.Const (SynConst.String (s, SynStringKind.Regular, _), _) ->
Some (HttpAttribute.Path (PathSpec.Verbatim s)) Some (HttpAttribute.Path (PathSpec.Verbatim s))
| SynExpr.Const (SynConst.Unit, _) -> Some (HttpAttribute.Path PathSpec.MatchArgName) | SynExpr.Const (SynConst.Unit, _) -> Some (HttpAttribute.Path PathSpec.MatchArgName)
| SynExpr.Const (a, _) -> failwith $"unrecognised constant arg to the Path attribute: %+A{a}" | SynExpr.Const (a, _) -> failwith $"unrecognised constant arg to the Path attribute: %+A{a}"
| _ -> None | _ -> None
| "RestEase.Body"
| "RestEase.BodyAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Body"
| "WoofWare.Myriad.Plugins.RestEase.BodyAttribute"
| "Body" | "Body"
| "BodyAttribute" -> | "BodyAttribute" ->
match attr.ArgExpr with match attr.ArgExpr with
@@ -740,8 +705,10 @@ module internal HttpClientGenerator =
match attr.TypeName.AsString with match attr.TypeName.AsString with
| "BasePath" | "BasePath"
| "RestEase.BasePath" | "RestEase.BasePath"
| "WoofWare.Myriad.Plugins.RestEase.BasePath"
| "BasePathAttribute" | "BasePathAttribute"
| "RestEase.BasePathAttribute" -> Some attr.ArgExpr | "RestEase.BasePathAttribute"
| "WoofWare.Myriad.Plugins.RestEase.BasePathAttribute" -> Some attr.ArgExpr
| _ -> None | _ -> None
) )
@@ -751,19 +718,25 @@ module internal HttpClientGenerator =
match attr.TypeName.AsString with match attr.TypeName.AsString with
| "BaseAddress" | "BaseAddress"
| "RestEase.BaseAddress" | "RestEase.BaseAddress"
| "WoofWare.Myriad.Plugins.RestEase.BaseAddress"
| "BaseAddressAttribute" | "BaseAddressAttribute"
| "RestEase.BaseAddressAttribute" -> Some attr.ArgExpr | "RestEase.BaseAddressAttribute"
| "WoofWare.Myriad.Plugins.RestEase.BaseAddressAttribute" -> Some attr.ArgExpr
| _ -> None | _ -> None
) )
let createModule let createModule
(opens : SynOpenDeclTarget list) (opens : SynOpenDeclTarget list)
(ns : LongIdent) (ns : LongIdent)
(interfaceType : SynTypeDefn) (interfaceType : SynTypeDefn, spec : HttpClientGeneratorOutputSpec)
: SynModuleOrNamespace : SynModuleOrNamespace
= =
let interfaceType = AstHelper.parseInterface interfaceType let interfaceType = AstHelper.parseInterface interfaceType
if not (List.isEmpty interfaceType.Inherits) then
failwith
"HttpClientGenerator does not support inheritance. Remove the `inherit` keyword if you want to use this generator."
let constantHeaders = let constantHeaders =
interfaceType.Attributes interfaceType.Attributes
|> extractHeaderInformation |> extractHeaderInformation
@@ -878,17 +851,11 @@ module internal HttpClientGenerator =
SynValInfo.SynValInfo ([ [ SynArgInfo.Empty ] ; [] ], SynArgInfo.Empty), SynValInfo.SynValInfo ([ [ SynArgInfo.Empty ] ; [] ], SynArgInfo.Empty),
None None
), ),
SynPat.CreateLongIdent ( SynPat.CreateLongIdent (SynLongIdent.create [ Ident.create "_" ; pi.Identifier ], []),
SynLongIdent.CreateFromLongIdent [ Ident.Create "_" ; pi.Identifier ],
[]
),
Some (SynBindingReturnInfo.Create pi.Type), Some (SynBindingReturnInfo.Create pi.Type),
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent' [ Ident.lowerFirstLetter pi.Identifier ])
SynLongIdent.CreateFromLongIdent [ Ident.lowerFirstLetter pi.Identifier ] (SynExpr.CreateConst ()),
),
SynExpr.CreateConst SynConst.Unit
),
range0, range0,
DebugPointAtBinding.Yes range0, DebugPointAtBinding.Yes range0,
{ {
@@ -903,11 +870,17 @@ module internal HttpClientGenerator =
let members = propertyMembers @ nonPropertyMembers let members = propertyMembers @ nonPropertyMembers
let docString = PreXmlDoc.Create " Module for constructing a REST client." let docString =
(if spec.ExtensionMethods then
"Extension methods"
else
"Module")
|> sprintf "%s for constructing a REST client."
|> PreXmlDoc.create
let interfaceImpl = let interfaceImpl =
SynExpr.ObjExpr ( SynExpr.ObjExpr (
SynType.LongIdent (SynLongIdent.CreateFromLongIdent interfaceType.Name), SynType.createLongIdent interfaceType.Name,
None, None,
Some range0, Some range0,
[], [],
@@ -920,80 +893,103 @@ module internal HttpClientGenerator =
let headerArgs = let headerArgs =
properties properties
|> List.map (fun (_, pi) -> |> List.map (fun (_, pi) ->
SynPat.CreateTyped ( SynPat.namedI (Ident.lowerFirstLetter pi.Identifier)
SynPat.CreateNamed (Ident.lowerFirstLetter pi.Identifier), |> SynPat.annotateType (SynType.funFromDomain (SynType.named "unit") pi.Type)
SynType.CreateFun (SynType.CreateLongIdent "unit", pi.Type)
)
|> SynPat.CreateParen
) )
let clientCreationArg = let clientCreationArg =
SynPat.CreateTyped ( SynPat.named "client"
SynPat.CreateNamed (Ident.Create "client"), |> SynPat.annotateType (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "HttpClient" ])
SynType.CreateLongIdent (SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "HttpClient" ])
)
|> SynPat.CreateParen
let xmlDoc = let xmlDoc =
if properties.IsEmpty then if properties.IsEmpty then
" Create a REST client." "Create a REST client."
else else
" Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties." "Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties."
|> PreXmlDoc.create
let createFunc = let functionName = Ident.create "client"
SynBinding.SynBinding (
None, let valData =
SynBindingKind.Normal, let memberFlags =
false, if spec.ExtensionMethods then
false, {
[], SynMemberFlags.IsInstance = false
PreXmlDoc.Create xmlDoc, SynMemberFlags.IsDispatchSlot = false
SynValData.SynValData ( SynMemberFlags.IsOverrideOrExplicitImpl = false
None, SynMemberFlags.IsFinal = false
SynValInfo.SynValInfo ( SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
[ [ SynArgInfo.SynArgInfo ([], false, Some (Ident.Create "client")) ] ], SynMemberFlags.MemberKind = SynMemberKind.Member
SynArgInfo.Empty }
), |> Some
else
None None
),
SynPat.CreateLongIdent (SynLongIdent.CreateString "make", headerArgs @ [ clientCreationArg ]),
Some (
SynBindingReturnInfo.Create (
SynType.LongIdent (SynLongIdent.CreateFromLongIdent interfaceType.Name)
)
),
interfaceImpl,
range0,
DebugPointAtBinding.NoneAtLet,
SynExpr.synBindingTriviaZero false
)
|> List.singleton
|> SynModuleDecl.CreateLet
let moduleName : LongIdent = SynValData.SynValData (
memberFlags,
SynValInfo.SynValInfo ([ [ SynArgInfo.SynArgInfo ([], false, Some functionName) ] ], SynArgInfo.Empty),
None
)
let pattern = SynLongIdent.createS "make"
let returnInfo = SynType.createLongIdent interfaceType.Name
let nameWithoutLeadingI =
List.last interfaceType.Name List.last interfaceType.Name
|> _.idText |> _.idText
|> fun s -> |> fun s ->
if s.StartsWith 'I' then if s.StartsWith 'I' then
s.[1..] s.Substring 1
else else
failwith $"Expected interface type to start with 'I', but was: %s{s}" failwith $"Expected interface type to start with 'I', but was: %s{s}"
|> Ident.Create
|> List.singleton let createFunc =
if spec.ExtensionMethods then
let binding =
SynBinding.basic (SynLongIdent.createS "make") (headerArgs @ [ clientCreationArg ]) interfaceImpl
|> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo
|> SynMemberDefn.staticMember
let componentInfo =
SynComponentInfo.create (Ident.create nameWithoutLeadingI)
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for HTTP clients")
let containingType =
SynTypeDefnRepr.augmentation ()
|> SynTypeDefn.create componentInfo
|> SynTypeDefn.withMemberDefns [ binding ]
SynModuleDecl.Types ([ containingType ], range0)
else
SynBinding.basic (SynLongIdent.createS "make") (headerArgs @ [ clientCreationArg ]) interfaceImpl
|> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo
|> List.singleton
|> SynModuleDecl.CreateLet
let moduleName =
if spec.ExtensionMethods then
Ident.create (nameWithoutLeadingI + "HttpClientExtension")
else
Ident.create nameWithoutLeadingI
let attribs = let attribs =
[ if spec.ExtensionMethods then
SynAttributeList.Create SynAttribute.compilationRepresentation [ SynAttribute.autoOpen ]
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ()) else
] [
SynAttribute.compilationRepresentation
SynAttribute.RequireQualifiedAccess ()
]
let modInfo = let modInfo =
SynComponentInfo.Create ( SynComponentInfo.create moduleName
moduleName, |> SynComponentInfo.withDocString docString
attributes = attribs, |> SynComponentInfo.addAttributes attribs
xmldoc = docString, |> SynComponentInfo.setAccessibility interfaceType.Accessibility
access = interfaceType.Accessibility
)
SynModuleOrNamespace.CreateNamespace ( SynModuleOrNamespace.CreateNamespace (
ns, ns,
@@ -1023,9 +1019,29 @@ type HttpClientGenerator () =
let namespaceAndTypes = let namespaceAndTypes =
types types
|> List.choose (fun (ns, types) -> |> List.choose (fun (ns, types) ->
match types |> List.filter Ast.hasAttribute<HttpClientAttribute> with types
| [] -> None |> List.choose (fun typeDef ->
| types -> Some (ns, types) match Ast.getAttribute<HttpClientAttribute> typeDef with
| None -> None
| Some attr ->
let arg =
match SynExpr.stripOptionalParen attr.ArgExpr with
| SynExpr.Const (SynConst.Bool value, _) -> value
| SynExpr.Const (SynConst.Unit, _) -> JsonParseAttribute.DefaultIsExtensionMethod
| arg ->
failwith
$"Unrecognised argument %+A{arg} to [<%s{nameof HttpClientAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
let spec =
{
ExtensionMethods = arg
}
Some (typeDef, spec)
)
|> function
| [] -> None
| ty -> Some (ns, ty)
) )
let modules = let modules =

View File

@@ -21,6 +21,9 @@ module internal InterfaceMockGenerator =
| None -> failwith "Expected record field to have a name, but it was somehow anonymous" | None -> failwith "Expected record field to have a name, but it was somehow anonymous"
| Some id -> id | Some id -> id
[<RequireQualifiedAccess>]
type private KnownInheritance = | IDisposable
let createType let createType
(spec : GenerateMockOutputSpec) (spec : GenerateMockOutputSpec)
(name : string) (name : string)
@@ -29,93 +32,80 @@ module internal InterfaceMockGenerator =
(fields : SynField list) (fields : SynField list)
: SynModuleDecl : SynModuleDecl
= =
let synValData = let inherits =
{ interfaceType.Inherits
SynMemberFlags.IsInstance = false |> Seq.map (fun ty ->
SynMemberFlags.IsDispatchSlot = false match ty with
SynMemberFlags.IsOverrideOrExplicitImpl = false | SynType.LongIdent (SynLongIdent.SynLongIdent (name, _, _)) ->
SynMemberFlags.IsFinal = false match name |> List.map _.idText with
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false | [] -> failwith "Unexpected empty identifier in inheritance declaration"
SynMemberFlags.MemberKind = SynMemberKind.Member | [ "IDisposable" ]
} | [ "System" ; "IDisposable" ] -> KnownInheritance.IDisposable
| _ -> failwithf "Unrecognised inheritance identifier: %+A" name
| x -> failwithf "Unrecognised type in inheritance: %+A" x
)
|> Set.ofSeq
let failwithFun = let failwithFun =
SynExpr.createLambda SynExpr.createLongIdent [ "System" ; "NotImplementedException" ]
"x" |> SynExpr.applyTo (SynExpr.CreateConst "Unimplemented mock function")
(SynExpr.CreateApp ( |> SynExpr.CreateParen
SynExpr.CreateIdentString "raise", |> SynExpr.applyFunction (SynExpr.createIdent "raise")
SynExpr.CreateParen ( |> SynExpr.createLambda "_"
SynExpr.CreateApp (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "NotImplementedException" ]),
SynExpr.CreateConstString "Unimplemented mock function"
)
)
))
let constructorIdent =
let generics =
interfaceType.Generics
|> Option.map (fun generics -> SynValTyparDecls (Some generics, false))
SynPat.LongIdent (
SynLongIdent.CreateString "Empty",
None,
None, // no generics on the "Empty", only on the return type
SynArgPats.Pats (
if generics.IsNone then
[]
else
[ SynPat.CreateParen (SynPat.CreateConst SynConst.Unit) ]
),
None,
range0
)
let constructorReturnType = let constructorReturnType =
match interfaceType.Generics with match interfaceType.Generics with
| None -> SynType.CreateLongIdent name | None -> SynType.CreateLongIdent name
| Some generics -> | Some generics ->
let generics =
generics.TyparDecls
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.Var (typar, range0))
SynType.App ( let generics =
SynType.CreateLongIdent name, generics.TyparDecls
Some range0, |> List.map (fun (SynTyparDecl (_, typar)) -> SynType.var typar)
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.createLambda "_" SynExpr.CreateUnit
[ (SynLongIdent.createS "Dispose", true), Some unitFun ]
else
[]
let nonExtras =
fields
|> List.map (fun field -> (SynLongIdent.createI (getName field), true), Some failwithFun)
extras @ nonExtras
let constructor = let constructor =
SynMemberDefn.Member ( SynBinding.basic
SynBinding.SynBinding ( (SynLongIdent.createS "Empty")
None, (if interfaceType.Generics.IsNone then
SynBindingKind.Normal, []
false, else
false, [ SynPat.CreateConst SynConst.Unit ])
[], (AstHelper.instantiateRecord constructorFields)
PreXmlDoc.Create " An implementation where every method throws.", |> SynBinding.makeStaticMember
SynValData.SynValData (Some synValData, SynValInfo.Empty, None), |> SynBinding.withXmlDoc (PreXmlDoc.Create " An implementation where every method throws.")
constructorIdent, |> SynBinding.withReturnAnnotation constructorReturnType
Some constructorReturnType, |> fun m -> SynMemberDefn.Member (m, range0)
AstHelper.instantiateRecord (
fields let fields =
|> List.map (fun field -> let extras =
((SynLongIdent.CreateFromLongIdent [ getName field ], true), Some failwithFun) if inherits.Contains KnownInheritance.IDisposable then
[
SynField.Create (
SynType.CreateFun (SynType.CreateUnit, SynType.CreateUnit),
Ident.Create "Dispose",
xmldoc = PreXmlDoc.Create " Implementation of IDisposable.Dispose"
) )
), ]
range0, else
DebugPointAtBinding.Yes range0, []
{ SynExpr.synBindingTriviaZero true with
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0) extras @ fields
}
),
range0
)
let interfaceMembers = let interfaceMembers =
let members = let members =
@@ -150,7 +140,9 @@ module internal InterfaceMockGenerator =
|> List.mapi (fun i arg -> |> List.mapi (fun i arg ->
arg.Args arg.Args
|> List.mapi (fun j arg -> |> List.mapi (fun j arg ->
SynArgInfo.CreateIdString $"arg_%i{i}_%i{j}" match arg.Type with
| UnitType -> SynArgInfo.SynArgInfo ([], false, None)
| _ -> SynArgInfo.CreateIdString $"arg_%i{i}_%i{j}"
) )
) )
], ],
@@ -165,16 +157,24 @@ module internal InterfaceMockGenerator =
|> List.mapi (fun i tupledArgs -> |> List.mapi (fun i tupledArgs ->
let args = let args =
tupledArgs.Args tupledArgs.Args
|> List.mapi (fun j _ -> SynPat.CreateNamed (Ident.Create $"arg_%i{i}_%i{j}")) |> List.mapi (fun j ty ->
match ty.Type with
| UnitType -> SynPat.Const (SynConst.Unit, range0)
| _ -> SynPat.CreateNamed (Ident.Create $"arg_%i{i}_%i{j}")
)
SynPat.Tuple (false, args, List.replicate (args.Length - 1) range0, range0) match args with
|> SynPat.CreateParen | [] -> failwith "somehow got no args at all"
| [ arg ] -> arg
| args ->
SynPat.Tuple (false, args, List.replicate (args.Length - 1) range0, range0)
|> SynPat.CreateParen
|> fun i -> if tupledArgs.HasParen then SynPat.Paren (i, range0) else i |> fun i -> if tupledArgs.HasParen then SynPat.Paren (i, range0) else i
) )
let headPat = let headPat =
SynPat.LongIdent ( SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; memberInfo.Identifier ], SynLongIdent.create [ Ident.Create "this" ; memberInfo.Identifier ],
None, None,
None, None,
SynArgPats.Pats headArgs, SynArgPats.Pats headArgs,
@@ -187,7 +187,11 @@ module internal InterfaceMockGenerator =
memberInfo.Args memberInfo.Args
|> List.mapi (fun i args -> |> List.mapi (fun i args ->
args.Args args.Args
|> List.mapi (fun j args -> SynExpr.CreateIdentString $"arg_%i{i}_%i{j}") |> List.mapi (fun j arg ->
match arg.Type with
| UnitType -> SynExpr.CreateConst ()
| _ -> SynExpr.createIdent $"arg_%i{i}_%i{j}"
)
|> SynExpr.CreateParenedTuple |> SynExpr.CreateParenedTuple
) )
@@ -197,13 +201,9 @@ module internal InterfaceMockGenerator =
(last, rest) (last, rest)
||> List.fold (fun trail next -> SynExpr.CreateApp (next, trail)) ||> List.fold (fun trail next -> SynExpr.CreateApp (next, trail))
|> fun args -> |> SynExpr.applyFunction (
SynExpr.CreateApp ( SynExpr.createLongIdent' [ Ident.Create "this" ; memberInfo.Identifier ]
SynExpr.CreateLongIdent ( )
SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; memberInfo.Identifier ]
),
args
)
SynMemberDefn.Member ( SynMemberDefn.Member (
SynBinding.SynBinding ( SynBinding.SynBinding (
@@ -230,8 +230,7 @@ module internal InterfaceMockGenerator =
) )
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
@@ -241,17 +240,9 @@ 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,
Some range0,
generics,
List.replicate (generics.Length - 1) range0,
Some range0,
false,
range0
)
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0) SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
@@ -264,11 +255,35 @@ module internal InterfaceMockGenerator =
| Some (SynAccess.Internal _), _ -> SynAccess.Internal range0 | Some (SynAccess.Internal _), _ -> SynAccess.Internal range0
| Some (SynAccess.Private _), _ -> SynAccess.Private range0 | Some (SynAccess.Private _), _ -> SynAccess.Private range0
let extraInterfaces =
inherits
|> Seq.map (fun inheritance ->
match inheritance with
| KnownInheritance.IDisposable ->
let binding =
SynBinding.basic
(SynLongIdent.createS' [ "this" ; "Dispose" ])
[ SynPat.CreateConst SynConst.Unit ]
(SynExpr.CreateApp (SynExpr.createLongIdent [ "this" ; "Dispose" ], SynExpr.CreateUnit))
|> SynBinding.withReturnAnnotation (SynType.Unit ())
|> SynBinding.makeInstanceMember
let mem = SynMemberDefn.Member (binding, range0)
SynMemberDefn.Interface (
SynType.CreateLongIdent (SynLongIdent.createS' [ "System" ; "IDisposable" ]),
Some range0,
Some [ mem ],
range0
)
)
|> Seq.toList
let record = let record =
{ {
Name = Ident.Create name Name = Ident.Create name
Fields = fields Fields = fields
Members = Some [ constructor ; interfaceMembers ] Members = Some ([ constructor ; interfaceMembers ] @ extraInterfaces)
XmlDoc = Some xmlDoc XmlDoc = Some xmlDoc
Generics = interfaceType.Generics Generics = interfaceType.Generics
Accessibility = Some access Accessibility = Some access
@@ -280,7 +295,7 @@ 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
@@ -326,14 +341,13 @@ module internal InterfaceMockGenerator =
|> _.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 spec name interfaceType docString fields let typeDecl = createType spec name interfaceType docString fields
SynModuleOrNamespace.CreateNamespace ( SynModuleOrNamespace.CreateNamespace (
namespaceId, namespaceId,
decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ typeDecl ] decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ typeDecl ]

View File

@@ -30,38 +30,34 @@ module internal JsonParseGenerator =
/// (match {indexed} with | null -> raise (System.Collections.Generic.KeyNotFoundException ()) | v -> v) /// (match {indexed} with | null -> raise (System.Collections.Generic.KeyNotFoundException ()) | v -> v)
let assertNotNull (propertyName : SynExpr) (indexed : SynExpr) = let assertNotNull (propertyName : SynExpr) (indexed : SynExpr) =
let raiseExpr = let raiseExpr =
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateIdentString "raise", (SynExpr.createIdent "sprintf")
SynExpr.CreateParen ( (SynExpr.CreateConst "Required key '%s' not found on JSON object")
SynExpr.CreateApp ( |> SynExpr.applyTo (SynExpr.paren propertyName)
SynExpr.CreateLongIdent ( |> SynExpr.paren
SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "KeyNotFoundException" ] |> SynExpr.applyFunction (
), SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "KeyNotFoundException" ]
SynExpr.CreateParen (
SynExpr.CreateApp (
SynExpr.CreateApp (
SynExpr.CreateIdentString "sprintf",
SynExpr.CreateConstString "Required key '%s' not found on JSON object"
),
SynExpr.CreateParen propertyName
)
)
)
)
) )
|> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createIdent "raise")
SynExpr.CreateMatch ( [
indexed, SynMatchClause.create SynPat.CreateNull raiseExpr
[ SynMatchClause.create (SynPat.named "v") (SynExpr.createIdent "v")
SynMatchClause.Create (SynPat.CreateNull, None, raiseExpr) ]
SynMatchClause.Create (SynPat.CreateNamed (Ident.Create "v"), None, SynExpr.CreateIdentString "v") |> SynExpr.createMatch indexed
] |> SynExpr.paren
)
|> SynExpr.CreateParen
/// {node}.AsValue().GetValue<{typeName}> () /// {node}.AsValue().GetValue<{typeName}> ()
/// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`. /// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`.
let asValueGetValue (propertyName : SynExpr option) (typeName : string) (node : SynExpr) : SynExpr = let asValueGetValue (propertyName : SynExpr option) (typeName : string) (node : SynExpr) : SynExpr =
match propertyName with
| None -> node
| Some propertyName -> assertNotNull propertyName node
|> SynExpr.callMethod "AsValue"
|> SynExpr.callGenericMethod' "GetValue" typeName
let asValueGetValueIdent (propertyName : SynExpr option) (typeName : LongIdent) (node : SynExpr) : SynExpr =
match propertyName with match propertyName with
| None -> node | None -> node
| Some propertyName -> assertNotNull propertyName node | Some propertyName -> assertNotNull propertyName node
@@ -78,10 +74,8 @@ module internal JsonParseGenerator =
/// {type}.jsonParse {node} /// {type}.jsonParse {node}
let typeJsonParse (typeName : LongIdent) (node : SynExpr) : SynExpr = let typeJsonParse (typeName : LongIdent) (node : SynExpr) : SynExpr =
SynExpr.CreateApp ( node
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent (typeName @ [ Ident.Create "jsonParse" ])), |> SynExpr.applyFunction (SynExpr.createLongIdent' (typeName @ [ Ident.create "jsonParse" ]))
node
)
/// collectionType is e.g. "List"; we'll be calling `ofSeq` on it. /// collectionType is e.g. "List"; we'll be calling `ofSeq` on it.
/// body is the body of a lambda which takes a parameter `elt`. /// body is the body of a lambda which takes a parameter `elt`.
@@ -100,64 +94,40 @@ module internal JsonParseGenerator =
| Some propertyName -> assertNotNull propertyName node | Some propertyName -> assertNotNull propertyName node
|> SynExpr.callMethod "AsArray" |> SynExpr.callMethod "AsArray"
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction (SynExpr.createLongIdent [ "Seq" ; "map" ]) (SynExpr.createLambda "elt" body)
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]),
SynExpr.createLambda "elt" body
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ collectionType ; "ofSeq" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ collectionType ; "ofSeq" ])
/// match {node} with | null -> None | v -> {body} |> Some /// match {node} with | null -> None | v -> {body} |> Some
/// Use the variable `v` to get access to the `Some`. /// Use the variable `v` to get access to the `Some`.
let createParseLineOption (node : SynExpr) (body : SynExpr) : SynExpr = let createParseLineOption (node : SynExpr) (body : SynExpr) : SynExpr =
let body = SynExpr.pipeThroughFunction (SynExpr.CreateIdentString "Some") body let body = SynExpr.pipeThroughFunction (SynExpr.createIdent "Some") body
SynExpr.CreateMatch ( [
node, SynMatchClause.create SynPat.CreateNull (SynExpr.createIdent "None")
[ SynMatchClause.create (SynPat.named "v") body
SynMatchClause.Create (SynPat.CreateNull, None, SynExpr.CreateIdent (Ident.Create "None")) ]
SynMatchClause.Create (SynPat.CreateNamed (Ident.Create "v"), None, body) |> SynExpr.createMatch node
]
)
/// Given e.g. "float", returns "System.Double.Parse" /// Given e.g. "float", returns "System.Double.Parse"
let parseFunction (typeName : string) : LongIdent = let parseFunction (typeName : string) : LongIdent =
List.append (SynExpr.qualifyPrimitiveType typeName) [ Ident.Create "Parse" ] let qualified =
match Primitives.qualifyType typeName with
| Some x -> x
| None -> failwith $"Could not recognise type %s{typeName} as a primitive."
List.append qualified [ Ident.create "Parse" ]
/// fun kvp -> let key = {key(kvp)} in let value = {value(kvp)} in (key, value)) /// fun kvp -> let key = {key(kvp)} in let value = {value(kvp)} in (key, value))
/// The inputs will be fed with appropriate SynExprs to apply them to the `kvp.Key` and `kvp.Value` args. /// The inputs will be fed with appropriate SynExprs to apply them to the `kvp.Key` and `kvp.Value` args.
let dictionaryMapper (key : SynExpr -> SynExpr) (value : SynExpr -> SynExpr) : SynExpr = let dictionaryMapper (key : SynExpr -> SynExpr) (value : SynExpr -> SynExpr) : SynExpr =
let keyArg = let keyArg = SynExpr.createLongIdent [ "kvp" ; "Key" ] |> SynExpr.paren
SynExpr.CreateLongIdent (SynLongIdent.Create [ "kvp" ; "Key" ])
|> SynExpr.CreateParen
let valueArg = let valueArg = SynExpr.createLongIdent [ "kvp" ; "Value" ] |> SynExpr.paren
SynExpr.CreateLongIdent (SynLongIdent.Create [ "kvp" ; "Value" ])
|> SynExpr.CreateParen
SynExpr.LetOrUse ( SynExpr.CreateTuple [ SynExpr.createIdent "key" ; SynExpr.createIdent "value" ]
false, |> SynExpr.createLet [ SynBinding.Let (pattern = SynPat.named "value", expr = value valueArg) ]
false, |> SynExpr.createLet [ SynBinding.Let (pattern = SynPat.named "key", expr = key keyArg) ]
[
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "key"), expr = key keyArg)
],
SynExpr.LetOrUse (
false,
false,
[
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "value"), expr = value valueArg)
],
SynExpr.CreateTuple [ SynExpr.CreateIdentString "key" ; SynExpr.CreateIdentString "value" ],
range0,
{
InKeyword = None
}
),
range0,
{
InKeyword = None
}
)
|> SynExpr.createLambda "kvp" |> SynExpr.createLambda "kvp"
/// A conforming JSON object has only strings as keys. But it would be reasonable to allow the user /// A conforming JSON object has only strings as keys. But it would be reasonable to allow the user
@@ -167,7 +137,7 @@ module internal JsonParseGenerator =
| String -> key | String -> key
| Uri -> | Uri ->
key key
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Uri" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Uri" ])
| _ -> | _ ->
failwithf failwithf
$"Unable to parse the key type %+A{desiredType} of a JSON object. Keys are strings, and this plugin does not know how to convert to that from a string." $"Unable to parse the key type %+A{desiredType} of a JSON object. Keys are strings, and this plugin does not know how to convert to that from a string."
@@ -187,25 +157,19 @@ module internal JsonParseGenerator =
| DateOnly -> | DateOnly ->
node node
|> asValueGetValue propertyName "string" |> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateOnly" ; "Parse" ])
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "DateOnly" ; "Parse" ])
)
| Uri -> | Uri ->
node node
|> asValueGetValue propertyName "string" |> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Uri" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Uri" ])
| Guid -> | Guid ->
node node
|> asValueGetValue propertyName "string" |> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Guid" ; "Parse" ])
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Guid" ; "Parse" ])
)
| DateTime -> | DateTime ->
node node
|> asValueGetValue propertyName "string" |> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ])
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "DateTime" ; "Parse" ])
)
| NumberType typeName -> | NumberType typeName ->
let basic = asValueGetValue propertyName typeName node let basic = asValueGetValue propertyName typeName node
@@ -213,105 +177,92 @@ module internal JsonParseGenerator =
| None -> basic | None -> basic
| Some option -> | Some option ->
let cond = let cond =
SynExpr.DotGet ( SynExpr.DotGet (SynExpr.createIdent "exc", range0, SynLongIdent.createS "Message", range0)
SynExpr.CreateIdentString "exc", |> SynExpr.callMethodArg "Contains" (SynExpr.CreateConst "cannot be converted to")
range0,
SynLongIdent.CreateString "Message",
range0
)
|> SynExpr.callMethodArg
"Contains"
(SynExpr.CreateConst (SynConst.CreateString "cannot be converted to"))
let handler = let handler =
asValueGetValue propertyName "string" node asValueGetValue propertyName "string" node
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (parseFunction typeName))
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent (parseFunction typeName))
)
|> SynExpr.ifThenElse |> SynExpr.ifThenElse
(SynExpr.equals (SynExpr.equals
option option
(SynExpr.CreateLongIdent ( (SynExpr.createLongIdent
SynLongIdent.Create [
[ "System"
"System" "Text"
"Text" "Json"
"Json" "Serialization"
"Serialization" "JsonNumberHandling"
"JsonNumberHandling" "AllowReadingFromString"
"AllowReadingFromString" ]))
]
)))
SynExpr.reraise SynExpr.reraise
|> SynExpr.ifThenElse cond SynExpr.reraise |> SynExpr.ifThenElse cond SynExpr.reraise
basic basic
|> SynExpr.pipeThroughTryWith |> SynExpr.pipeThroughTryWith
(SynPat.IsInst ( (SynPat.IsInst (
SynType.LongIdent (SynLongIdent.Create [ "System" ; "InvalidOperationException" ]), SynType.LongIdent (SynLongIdent.createS' [ "System" ; "InvalidOperationException" ]),
range0 range0
)) ))
handler handler
| PrimitiveType typeName -> asValueGetValue propertyName typeName node | PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node
| OptionType ty -> | OptionType ty ->
parseNode None options ty (SynExpr.CreateIdentString "v") parseNode None options ty (SynExpr.createIdent "v")
|> createParseLineOption node |> createParseLineOption node
| ListType ty -> | ListType ty ->
parseNode None options ty (SynExpr.CreateLongIdent (SynLongIdent.CreateString "elt")) parseNode None options ty (SynExpr.createIdent "elt")
|> asArrayMapped propertyName "List" node |> asArrayMapped propertyName "List" node
| ArrayType ty -> | ArrayType ty ->
parseNode None options ty (SynExpr.CreateLongIdent (SynLongIdent.CreateString "elt")) parseNode None options ty (SynExpr.createIdent "elt")
|> asArrayMapped propertyName "Array" node |> asArrayMapped propertyName "Array" node
| IDictionaryType (keyType, valueType) -> | IDictionaryType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "dict" ])) |> SynExpr.pipeThroughFunction (SynExpr.createIdent "dict")
| DictionaryType (keyType, valueType) -> | DictionaryType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "KeyValuePair" ])
SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "KeyValuePair" ]
)
)
) )
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "Dictionary" ]) SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "Dictionary" ]
) )
| IReadOnlyDictionaryType (keyType, valueType) -> | IReadOnlyDictionaryType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "readOnlyDict" ])) |> SynExpr.pipeThroughFunction (SynExpr.createIdent "readOnlyDict")
| MapType (keyType, valueType) -> | MapType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Map" ; "ofSeq" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "Map" ; "ofSeq" ])
| BigInt ->
node
|> SynExpr.callMethod "ToJsonString"
|> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Numerics" ; "BigInteger" ; "Parse" ])
| _ -> | _ ->
// Let's just hope that we've also got our own type annotation! // Let's just hope that we've also got our own type annotation!
let typeName = let typeName =
@@ -327,9 +278,8 @@ module internal JsonParseGenerator =
/// propertyName is probably a string literal, but it could be a [<Literal>] variable /// propertyName is probably a string literal, but it could be a [<Literal>] variable
/// The result of this function is the body of a let-binding (not including the LHS of that let-binding). /// The result of this function is the body of a let-binding (not including the LHS of that let-binding).
let createParseRhs (options : JsonParseOption) (propertyName : SynExpr) (fieldType : SynType) : SynExpr = let createParseRhs (options : JsonParseOption) (propertyName : SynExpr) (fieldType : SynType) : SynExpr =
SynExpr.CreateIdentString "node" let objectToParse = SynExpr.createIdent "node" |> SynExpr.index propertyName
|> SynExpr.index propertyName parseNode (Some propertyName) options fieldType objectToParse
|> parseNode (Some propertyName) options fieldType
let isJsonNumberHandling (literal : LongIdent) : bool = let isJsonNumberHandling (literal : LongIdent) : bool =
match List.rev literal |> List.map (fun ident -> ident.idText) with match List.rev literal |> List.map (fun ident -> ident.idText) with
@@ -340,270 +290,262 @@ module internal JsonParseGenerator =
| [ _ ; "JsonNumberHandling" ; "Serialization" ; "Json" ; "Text" ; "System" ] -> true | [ _ ; "JsonNumberHandling" ; "Serialization" ; "Json" ; "Text" ; "System" ] -> true
| _ -> false | _ -> false
let createMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : SynField list) = /// `populateNode` will be inserted before we return the `node` variable.
let xmlDoc = PreXmlDoc.Create " Parse from a JSON node." ///
/// That is, we give you access to a `JsonNode` called `node`,
/// and you must return a `typeName`.
let scaffolding (spec : JsonParseOutputSpec) (typeName : LongIdent) (functionBody : SynExpr) : SynModuleDecl =
let xmlDoc = PreXmlDoc.create "Parse from a JSON node."
let returnInfo = let returnInfo = SynType.createLongIdent typeName
SynBindingReturnInfo.Create (SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName))
let inputArg = Ident.Create "node" let inputArg = "node"
let functionName = Ident.Create "jsonParse" let functionName = Ident.create "jsonParse"
let inputVal = let arg =
let memberFlags = SynPat.named inputArg
if spec.ExtensionMethods then |> SynPat.annotateType (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
{
SynMemberFlags.IsInstance = false
SynMemberFlags.IsDispatchSlot = false
SynMemberFlags.IsOverrideOrExplicitImpl = false
SynMemberFlags.IsFinal = false
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
SynMemberFlags.MemberKind = SynMemberKind.Member
}
|> Some
else
None
let thisIdOpt = if spec.ExtensionMethods then None else Some inputArg if spec.ExtensionMethods then
let binding =
SynBinding.basic (SynLongIdent.createI functionName) [ arg ] functionBody
|> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo
|> SynMemberDefn.staticMember
SynValData.SynValData ( let componentInfo =
memberFlags, SynComponentInfo.createLong typeName
SynValInfo.SynValInfo ([ [ SynArgInfo.CreateId functionName ] ], SynArgInfo.Empty), |> SynComponentInfo.withDocString (PreXmlDoc.Create " Extension methods for JSON parsing")
thisIdOpt
)
let containingType =
SynTypeDefnRepr.augmentation ()
|> SynTypeDefn.create componentInfo
|> SynTypeDefn.withMemberDefns [ binding ]
SynModuleDecl.Types ([ containingType ], range0)
else
SynBinding.basic (SynLongIdent.createI functionName) [ arg ] functionBody
|> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo
|> List.singleton
|> SynModuleDecl.CreateLet
let getParseOptions (fieldAttrs : SynAttribute list) =
(JsonParseOption.None, fieldAttrs)
||> List.fold (fun options attr ->
if attr.TypeName.AsString.EndsWith ("JsonNumberHandling", StringComparison.Ordinal) then
let qualifiedEnumValue =
match SynExpr.stripOptionalParen attr.ArgExpr with
| SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when isJsonNumberHandling ident ->
// Make sure it's fully qualified
SynExpr.createLongIdent
[
"System"
"Text"
"Json"
"Serialization"
"JsonNumberHandling"
"AllowReadingFromString"
]
| _ -> attr.ArgExpr
{
JsonNumberHandlingArg = Some qualifiedEnumValue
}
else
options
)
let createRecordMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : SynFieldData<Ident> list) =
let assignments = let assignments =
fields fields
|> List.map (fun (SynField (attrs, _, id, fieldType, _, _, _, _, _)) -> |> List.mapi (fun i fieldData ->
let id =
match id with
| None -> failwith "didn't get an ID on field"
| Some id -> id
let attrs = attrs |> List.collect (fun l -> l.Attributes)
let propertyNameAttr = let propertyNameAttr =
attrs fieldData.Attrs
|> List.tryFind (fun attr -> |> List.tryFind (fun attr ->
attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal) attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
) )
let options = let options = getParseOptions fieldData.Attrs
(JsonParseOption.None, attrs)
||> List.fold (fun options attr ->
if attr.TypeName.AsString.EndsWith ("JsonNumberHandling", StringComparison.Ordinal) then
let qualifiedEnumValue =
match SynExpr.stripOptionalParen attr.ArgExpr with
| SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when
isJsonNumberHandling ident
->
// Make sure it's fully qualified
SynExpr.CreateLongIdent (
SynLongIdent.Create
[
"System"
"Text"
"Json"
"Serialization"
"JsonNumberHandling"
"AllowReadingFromString"
]
)
| _ -> attr.ArgExpr
{
JsonNumberHandlingArg = Some qualifiedEnumValue
}
else
options
)
let propertyName = let propertyName =
match propertyNameAttr with match propertyNameAttr with
| None -> | None ->
let sb = StringBuilder id.idText.Length let sb = StringBuilder fieldData.Ident.idText.Length
sb.Append (Char.ToLowerInvariant id.idText.[0]) |> ignore
if id.idText.Length > 1 then sb.Append (Char.ToLowerInvariant fieldData.Ident.idText.[0])
sb.Append id.idText.[1..] |> ignore |> ignore<StringBuilder>
sb.ToString () |> SynConst.CreateString |> SynExpr.CreateConst if fieldData.Ident.idText.Length > 1 then
sb.Append (fieldData.Ident.idText.Substring 1) |> ignore<StringBuilder>
sb.ToString () |> SynExpr.CreateConst
| Some name -> name.ArgExpr | Some name -> name.ArgExpr
let pattern = createParseRhs options propertyName fieldData.Type
SynPat.LongIdent ( |> SynBinding.basic (SynLongIdent.createS $"arg_%i{i}") []
SynLongIdent.CreateFromLongIdent [ id ],
None,
None,
SynArgPats.Empty,
None,
range0
)
SynBinding.Let (
isInline = false,
isMutable = false,
expr = createParseRhs options propertyName fieldType,
valData = inputVal,
pattern = pattern
)
) )
let finalConstruction = let finalConstruction =
fields fields
|> List.map (fun (SynField (_, _, id, _, _, _, _, _, _)) -> |> List.mapi (fun i fieldData ->
let id = (SynLongIdent.createI fieldData.Ident, true), Some (SynExpr.createIdent $"arg_%i{i}")
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 |> AstHelper.instantiateRecord
let assignments = (finalConstruction, assignments)
(finalConstruction, assignments) ||> List.fold (fun final assignment -> SynExpr.createLet [ assignment ] final)
||> List.fold (fun final assignment ->
SynExpr.LetOrUse (
false,
false,
[ assignment ],
final,
range0,
{
InKeyword = None
}
)
)
let pattern = let createUnionMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : UnionCase<Ident> list) =
SynPat.LongIdent ( fields
SynLongIdent.CreateFromLongIdent [ functionName ], |> List.map (fun case ->
None, let propertyName = JsonSerializeGenerator.getPropertyName case.Ident case.Attrs
None,
SynArgPats.Pats
[
SynPat.CreateTyped (
SynPat.CreateNamed inputArg,
SynType.LongIdent (
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
)
)
|> SynPat.CreateParen
],
None,
range0
)
if spec.ExtensionMethods then let body =
let binding = if case.Fields.IsEmpty then
SynBinding.SynBinding ( SynExpr.createLongIdent' (typeName @ [ case.Ident ])
else
case.Fields
|> List.map (fun field ->
let propertyName = JsonSerializeGenerator.getPropertyName field.Ident field.Attrs
let options = getParseOptions field.Attrs
createParseRhs options propertyName field.Type
)
|> SynExpr.CreateParenedTuple
|> SynExpr.applyFunction (SynExpr.createLongIdent' (typeName @ [ case.Ident ]))
|> SynExpr.createLet
[
SynExpr.index (SynExpr.CreateConst "data") (SynExpr.createIdent "node")
|> assertNotNull (SynExpr.CreateConst "data")
|> SynBinding.basic (SynLongIdent.createS "node") []
]
match propertyName with
| SynExpr.Const (synConst, _) ->
SynMatchClause.SynMatchClause (
SynPat.CreateConst synConst,
None, None,
SynBindingKind.Normal, body,
false,
false,
[],
xmlDoc,
inputVal,
pattern,
Some returnInfo,
assignments,
range0, range0,
DebugPointAtBinding.NoneAtInvisible, DebugPointAtTarget.Yes,
{ {
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0) ArrowRange = Some range0
InlineKeyword = None BarRange = Some range0
EqualsRange = Some range0
} }
) )
| _ ->
SynMatchClause.create (SynPat.named "x") body
|> SynMatchClause.withWhere (SynExpr.equals (SynExpr.createIdent "x") propertyName)
)
|> fun l ->
l
@ [
let fail =
SynExpr.plus (SynExpr.CreateConst "Unrecognised 'type' field value: ") (SynExpr.createIdent "v")
|> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createIdent "failwith")
let mem = SynMemberDefn.Member (binding, range0) SynMatchClause.SynMatchClause (
SynPat.named "v",
let containingType =
SynTypeDefn.SynTypeDefn (
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create " Extension methods for JSON parsing"),
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0),
[ mem ],
None, None,
fail,
range0, range0,
DebugPointAtTarget.Yes,
{ {
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0 ArrowRange = Some range0
EqualsRange = None BarRange = Some range0
WithKeyword = None
} }
) )
]
|> SynExpr.createMatch (SynExpr.createIdent "ty")
|> SynExpr.createLet
[
let property = SynExpr.CreateConst "type"
SynModuleDecl.Types ([ containingType ], range0) SynExpr.createIdent "node"
else |> SynExpr.index property
let binding = |> assertNotNull property
SynBinding.Let ( |> SynExpr.pipeThroughFunction (
isInline = false, SynExpr.createLambda
isMutable = false, "v"
xmldoc = xmlDoc, (SynExpr.callGenericMethod "GetValue" [ Ident.create "string" ] (SynExpr.createIdent "v"))
returnInfo = returnInfo,
expr = assignments,
valData = inputVal,
pattern = pattern
) )
|> SynBinding.basic (SynLongIdent.createS "ty") []
]
SynModuleDecl.CreateLet [ binding ] let createModule (namespaceId : LongIdent) (spec : JsonParseOutputSpec) (typeDefn : SynTypeDefn) =
let createRecordModule (namespaceId : LongIdent) (spec : JsonParseOutputSpec) (typeDefn : SynTypeDefn) =
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) = let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
typeDefn typeDefn
let (SynComponentInfo (_attributes, _typeParams, _constraints, recordId, _, _preferPostfix, _access, _)) = let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, _access, _)) =
synComponentInfo synComponentInfo
match synTypeDefnRepr with let attributes =
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _recordRange), _) -> if spec.ExtensionMethods then
[ 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 parsing %s{description} for the %s{fullyQualified} type"
let fullyQualified = recordId |> Seq.map (fun i -> i.idText) |> String.concat "." |> PreXmlDoc.Create
let description = let moduleName =
if spec.ExtensionMethods then if spec.ExtensionMethods then
"extension members" match ident with
else | [] -> failwith "unexpectedly got an empty identifier for record name"
"methods" | ident ->
let expanded =
List.last ident
|> fun i -> i.idText
|> fun s -> s + "JsonParseExtension"
|> Ident.create
$" Module containing JSON parsing %s{description} for the %s{fullyQualified} type" List.take (List.length ident - 1) ident @ [ expanded ]
|> PreXmlDoc.Create else
ident
let moduleName = let info =
if spec.ExtensionMethods then SynComponentInfo.createLong moduleName
match recordId with |> SynComponentInfo.withDocString xmlDoc
| [] -> failwith "unexpectedly got an empty identifier for record name" |> SynComponentInfo.addAttributes attributes
| recordId ->
let expanded =
List.last recordId
|> fun i -> i.idText
|> fun s -> s + "JsonParseExtension"
|> Ident.Create
List.take (List.length recordId - 1) recordId @ [ expanded ] let decl =
else match synTypeDefnRepr with
recordId | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, fields, _range), _) ->
let fields = fields |> List.map SynField.extractWithIdent
createRecordMaker spec ident fields
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, cases, _range), _) ->
let optionGet (i : Ident option) =
match i with
| None -> failwith "WoofWare.Myriad requires union cases to have identifiers on each field."
| Some i -> i
let info = let cases =
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc) cases
|> List.map SynUnionCase.extract
|> List.map (UnionCase.mapIdentFields optionGet)
let mdl = SynModuleDecl.CreateNestedModule (info, decls) createUnionMaker spec ident cases
| _ -> failwithf "Not a record or union type"
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ]) let mdl =
| _ -> failwithf "Not a record type" [ scaffolding spec ident decl ]
|> fun d -> SynModuleDecl.CreateNestedModule (info, d)
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ])
/// Myriad generator that provides a method (possibly an extension method) for a record type, /// Myriad generator that provides a method (possibly an extension method) for a record type,
/// containing a JSON parse function. /// containing a JSON parse function.
@@ -617,10 +559,20 @@ type JsonParseGenerator () =
let ast, _ = let ast, _ =
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
let records = Ast.extractRecords ast let recordsAndUnions =
Ast.extractTypeDefn ast
|> List.map (fun (name, defns) ->
defns
|> List.choose (fun defn ->
if Ast.isRecord defn then Some defn
elif Ast.isDu defn then Some defn
else None
)
|> fun defns -> name, defns
)
let namespaceAndRecords = let namespaceAndTypes =
records recordsAndUnions
|> List.choose (fun (ns, types) -> |> List.choose (fun (ns, types) ->
types types
|> List.choose (fun typeDef -> |> List.choose (fun typeDef ->
@@ -648,13 +600,9 @@ type JsonParseGenerator () =
) )
let modules = let modules =
namespaceAndRecords namespaceAndTypes
|> List.collect (fun (ns, records) -> |> List.collect (fun (ns, types) ->
records types |> List.map (fun (ty, spec) -> JsonParseGenerator.createModule ns spec ty)
|> List.map (fun (record, spec) ->
let recordModule = JsonParseGenerator.createRecordModule ns spec record
recordModule
)
) )
Output.Ast modules Output.Ast modules

View File

@@ -30,9 +30,7 @@ module internal JsonSerializeGenerator =
| Uri -> | Uri ->
// JsonValue.Create<type> // JsonValue.Create<type>
SynExpr.TypeApp ( SynExpr.TypeApp (
SynExpr.CreateLongIdent ( SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ],
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
),
range0, range0,
[ fieldType ], [ fieldType ],
[], [],
@@ -42,39 +40,29 @@ module internal JsonSerializeGenerator =
) )
| 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 ( [
SynExpr.CreateIdentString "field", SynMatchClause.Create (
[ SynPat.CreateLongIdent (SynLongIdent.createS "None", []),
SynMatchClause.Create ( None,
SynPat.CreateLongIdent (SynLongIdent.CreateString "None", []), // The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
None, // identically equal to null. We have to work around this later, but we might as well just
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"` // be efficient here and whip up the null directly.
// identically equal to null. We have to work around this later, but we might as well just SynExpr.CreateNull
// be efficient here and whip up the null directly. |> SynExpr.upcast' (
SynExpr.CreateNull SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|> SynExpr.upcast' (
SynType.CreateLongIdent (
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
)
)
)
SynMatchClause.Create (
SynPat.CreateLongIdent (
SynLongIdent.CreateString "Some",
[ SynPat.CreateNamed (Ident.Create "field") ]
),
None,
SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "field")
|> SynExpr.CreateParen
|> SynExpr.upcast' (
SynType.CreateLongIdent (
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
)
)
) )
] )
)
SynMatchClause.Create (
SynPat.CreateLongIdent (SynLongIdent.createS "Some", [ SynPat.named "field" ]),
None,
SynExpr.applyFunction (serializeNode ty) (SynExpr.createIdent "field")
|> SynExpr.paren
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
)
]
|> SynExpr.createMatch (SynExpr.createIdent "field")
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
| ArrayType ty | ArrayType ty
| ListType ty -> | ListType ty ->
@@ -82,116 +70,85 @@ module internal JsonSerializeGenerator =
// 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 (SynLongIdent.createS' [ "arr" ; "Add" ]))
(SynExpr.CreateParen (SynExpr.CreateApp (serializeNode ty, SynExpr.createIdent "mem"))),
range0
)
SynExpr.createIdent "arr"
]
|> SynExpr.CreateSequential
|> SynExpr.createLet
[ [
SynBinding.Let ( SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonArray" ]
pattern = SynPat.CreateNamed (Ident.Create "arr"), |> SynExpr.applyTo (SynExpr.CreateConst ())
expr = |> SynBinding.basic (SynLongIdent.createS "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) | IDictionaryType (_keyType, valueType)
| DictionaryType (keyType, valueType) | DictionaryType (_keyType, valueType)
| IReadOnlyDictionaryType (keyType, valueType) | IReadOnlyDictionaryType (_keyType, valueType)
| MapType (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,
SynBinding.Let ( SeqExprOnly.SeqExprOnly false,
pattern = SynPat.CreateNamed (Ident.Create "ret"), true,
expr = SynPat.CreateParen (
SynExpr.CreateApp ( SynPat.CreateLongIdent (
SynExpr.CreateLongIdent ( SynLongIdent.createS "KeyValue",
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ] [
), SynPat.CreateParen (
SynExpr.CreateConst SynConst.Unit SynPat.Tuple (
) false,
) [
], SynPat.CreateNamed (Ident.Create "key")
SynExpr.CreateSequential SynPat.CreateNamed (Ident.Create "value")
[ ],
SynExpr.ForEach ( [ range0 ],
DebugPointAtFor.Yes range0, 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" ),
], SynExpr.CreateIdent (Ident.Create "field"),
range0, SynExpr.CreateApp (
{ SynExpr.createLongIdent [ "ret" ; "Add" ],
InKeyword = None SynExpr.CreateParenedTuple
} [
) SynExpr.CreateApp (
SynExpr.createLongIdent [ "key" ; "ToString" ],
SynExpr.CreateConst ()
)
SynExpr.CreateApp (serializeNode valueType, SynExpr.createIdent "value")
]
),
range0
)
SynExpr.createIdent "ret"
]
|> SynExpr.CreateSequential
|> SynExpr.createLet
[
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic (SynLongIdent.createS "ret") []
]
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
| _ -> | _ ->
// {type}.toJsonNode // {type}.toJsonNode
@@ -200,213 +157,192 @@ 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" ])
/// 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.applyFunction
(serializeNode fieldType)
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ])
]
|> SynExpr.CreateParenedTuple
|> 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 -> attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal))
SynExpr.CreateApp (
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
/// `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.CreateSequential
| 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 (SynLongIdent.createS "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.CreateNamed 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 (SynLongIdent.createI 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 = let binding =
SynBinding.Let ( assignments
isInline = false, |> SynBinding.basic (SynLongIdent.createI functionName) [ pattern ]
isMutable = false, |> SynBinding.withReturnAnnotation returnInfo
xmldoc = xmlDoc, |> SynBinding.withXmlDoc xmlDoc
returnInfo = returnInfo,
expr = assignments,
valData = inputVal,
pattern = pattern
)
SynModuleDecl.CreateLet [ binding ] SynModuleDecl.CreateLet [ binding ]
let createRecordModule let recordModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (fields : SynField list) =
let inputArg = Ident.create "input"
let fields = fields |> List.map SynField.extractWithIdent
fields
|> List.map (fun fieldData ->
let propertyName = getPropertyName fieldData.Ident fieldData.Attrs
createSerializeRhsRecord propertyName fieldData.Ident fieldData.Type
)
|> SynExpr.CreateSequential
|> fun expr -> SynExpr.Do (expr, range0)
|> scaffolding spec typeName inputArg
let unionModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (cases : SynUnionCase list) =
let inputArg = Ident.create "input"
let fields = cases |> List.map SynUnionCase.extract
fields
|> List.map (fun unionCase ->
let propertyName = getPropertyName unionCase.Ident unionCase.Attrs
let caseNames = unionCase.Fields |> List.mapi (fun i _ -> Ident.create $"arg%i{i}")
let argPats = SynArgPats.create caseNames
let pattern =
SynPat.LongIdent (
SynLongIdent.create (typeName @ [ unionCase.Ident ]),
None,
None,
argPats,
None,
range0
)
let typeLine =
[
SynExpr.CreateConst "type"
SynExpr.applyFunction
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ])
propertyName
]
|> SynExpr.CreateParenedTuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
let dataNode =
SynBinding.Let (
pattern = SynPat.named "dataNode",
expr =
SynExpr.applyFunction
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ])
(SynExpr.CreateConst ())
)
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 (serializeNode fieldData.Type) (SynExpr.createIdent' caseName)
[ propertyName ; node ]
|> SynExpr.CreateParenedTuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "dataNode" ; "Add" ])
)
let assignToNode =
[ SynExpr.CreateConst "data" ; SynExpr.createIdent "dataNode" ]
|> SynExpr.CreateParenedTuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
let dataNode =
SynExpr.CreateSequential (dataBindings @ [ assignToNode ])
|> SynExpr.createLet [ dataNode ]
let action =
[
yield typeLine
if not dataBindings.IsEmpty then
yield dataNode
]
|> SynExpr.CreateSequential
SynMatchClause.create pattern action
)
|> SynExpr.createMatch (SynExpr.createIdent' inputArg)
|> scaffolding spec typeName inputArg
let createModule
(namespaceId : LongIdent) (namespaceId : LongIdent)
(opens : SynOpenDeclTarget list) (opens : SynOpenDeclTarget list)
(spec : JsonSerializeOutputSpec) (spec : JsonSerializeOutputSpec)
@@ -415,60 +351,64 @@ 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.withDocString xmlDoc
| recordId ->
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 ]
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, unionFields, _range), _) ->
[ unionModule spec ident unionFields ]
| _ -> failwithf "Only record types currently supported."
let info = let mdl = SynModuleDecl.CreateNestedModule (info, decls)
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc)
let mdl = SynModuleDecl.CreateNestedModule (info, decls) SynModuleOrNamespace.CreateNamespace (
namespaceId,
SynModuleOrNamespace.CreateNamespace ( decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ mdl ]
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.
@@ -482,10 +422,20 @@ type JsonSerializeGenerator () =
let ast, _ = let ast, _ =
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
let records = Ast.extractRecords ast let recordsAndUnions =
Ast.extractTypeDefn ast
|> List.map (fun (name, defns) ->
defns
|> List.choose (fun defn ->
if Ast.isRecord defn then Some defn
elif Ast.isDu defn then Some defn
else None
)
|> fun defns -> name, defns
)
let namespaceAndRecords = let namespaceAndTypes =
records recordsAndUnions
|> List.choose (fun (ns, types) -> |> List.choose (fun (ns, types) ->
types types
|> List.choose (fun typeDef -> |> List.choose (fun typeDef ->
@@ -515,13 +465,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

View File

@@ -0,0 +1,30 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal Primitives =
/// Given e.g. "byte", returns "System.Byte".
let qualifyType (typeName : string) : LongIdent option =
match typeName with
| "float32"
| "single" -> [ "System" ; "Single" ] |> Some
| "float"
| "double" -> [ "System" ; "Double" ] |> Some
| "byte"
| "uint8" -> [ "System" ; "Byte" ] |> Some
| "sbyte"
| "int8" -> [ "System" ; "SByte" ] |> Some
| "int16" -> [ "System" ; "Int16" ] |> Some
| "int"
| "int32" -> [ "System" ; "Int32" ] |> Some
| "int64" -> [ "System" ; "Int64" ] |> Some
| "uint16" -> [ "System" ; "UInt16" ] |> Some
| "uint"
| "uint32" -> [ "System" ; "UInt32" ] |> Some
| "uint64" -> [ "System" ; "UInt64" ] |> Some
| "char" -> [ "System" ; "Char" ] |> Some
| "decimal" -> [ "System" ; "Decimal" ] |> Some
| _ -> None
|> Option.map (List.map (fun i -> (Ident (i, range0))))

View File

@@ -47,7 +47,7 @@ module internal RemoveOptionsGenerator =
(fields : SynField list) (fields : SynField list)
= =
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,38 +63,28 @@ module internal RemoveOptionsGenerator =
SynModuleDecl.Types ([ typeDecl ], range0) SynModuleDecl.Types ([ typeDecl ], range0)
let createMaker (withOptionsType : LongIdent) (withoutOptionsType : LongIdent) (fields : SynField list) = let createMaker (withOptionsType : LongIdent) (withoutOptionsType : LongIdent) (fields : SynFieldData<Ident> list) =
let xmlDoc = PreXmlDoc.Create " Remove the optional members of the input." let xmlDoc = PreXmlDoc.create "Remove the optional members of the input."
let returnInfo = let 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 ( SynExpr.applyFunction
SynExpr.CreateAppInfix ( (SynExpr.CreateAppInfix (
SynExpr.LongIdent ( SynExpr.LongIdent (
false, false,
SynLongIdent.SynLongIdent ( SynLongIdent.SynLongIdent (
@@ -106,49 +96,29 @@ module internal RemoveOptionsGenerator =
range0 range0
), ),
accessor accessor
), ))
SynExpr.CreateApp ( (SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.CreateString "Option.defaultWith"), (SynExpr.createLongIdent [ "Option" ; "defaultWith" ])
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent' (
SynLongIdent.CreateFromLongIdent ( withoutOptionsType
withoutOptionsType @ [ Ident.Create (sprintf "Default%s" id.idText) ] @ [ Ident.create (sprintf "Default%s" fieldData.Ident.idText) ]
) )))
)
)
)
| _ -> accessor | _ -> accessor
(SynLongIdent.CreateFromLongIdent [ id ], true), Some body (SynLongIdent.createI fieldData.Ident, true), Some body
) )
|> AstHelper.instantiateRecord |> AstHelper.instantiateRecord
let pattern =
SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent [ functionName ],
None,
None,
SynArgPats.Pats
[
SynPat.CreateTyped (
SynPat.CreateNamed inputArg,
SynType.LongIdent (SynLongIdent.CreateFromLongIdent withoutOptionsType)
)
|> SynPat.CreateParen
],
None,
range0
)
let binding = let binding =
SynBinding.Let ( SynBinding.basic
isInline = false, (SynLongIdent.createI functionName)
isMutable = false, [
xmldoc = xmlDoc, SynPat.named inputArg.idText
returnInfo = returnInfo, |> SynPat.annotateType (SynType.LongIdent (SynLongIdent.create withoutOptionsType))
expr = body, ]
valData = inputVal, body
pattern = pattern |> SynBinding.withXmlDoc xmlDoc
) |> SynBinding.withReturnAnnotation (SynType.LongIdent (SynLongIdent.create withOptionsType))
SynModuleDecl.CreateLet [ binding ] SynModuleDecl.CreateLet [ binding ]
@@ -160,29 +130,27 @@ module internal RemoveOptionsGenerator =
synComponentInfo synComponentInfo
match synTypeDefnRepr with match synTypeDefnRepr with
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (accessibility, recordFields, _recordRange), _) -> | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (accessibility, fields, _range), _) ->
let fieldData = fields |> List.map SynField.extractWithIdent
let decls = let decls =
[ [
createType (Some doc) accessibility typeParams recordFields createType (Some doc) accessibility typeParams fields
createMaker [ Ident.Create "Short" ] recordId recordFields createMaker [ Ident.create "Short" ] recordId fieldData
]
let attributes =
[
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
SynAttributeList.Create SynAttribute.compilationRepresentation
] ]
let xmlDoc = let xmlDoc =
recordId recordId
|> Seq.map (fun i -> i.idText) |> Seq.map (fun i -> i.idText)
|> String.concat "." |> String.concat "."
|> sprintf " Module containing an option-truncated version of the %s type" |> sprintf "Module containing an option-truncated version of the %s type"
|> PreXmlDoc.Create |> PreXmlDoc.create
let info = let info =
SynComponentInfo.Create (recordId, attributes = attributes, xmldoc = xmlDoc) SynComponentInfo.createLong recordId
|> SynComponentInfo.withDocString xmlDoc
|> SynComponentInfo.addAttributes [ SynAttribute.compilationRepresentation ]
|> SynComponentInfo.addAttributes [ SynAttribute.RequireQualifiedAccess () ]
let mdl = SynModuleDecl.CreateNestedModule (info, decls) let mdl = SynModuleDecl.CreateNestedModule (info, decls)

View File

@@ -0,0 +1,49 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
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
(*
Potential API!
type internal CompExprBindings =
private
{
/// These are stored in reverse.
Bindings : CompExprBinding list
CompExprName : string
}
[<RequireQualifiedAccess>]
module internal CompExprBindings =
let make (name : string) : CompExprBindings =
{
Bindings = []
CompExprName = name
}
let thenDo (body : SynExpr) (bindings : CompExprBindings) =
{ bindings with
Bindings = (Do body :: bindings.Bindings)
}
let thenLet (varName : string) (value : SynExpr) (bindings : CompExprBindings) =
{ bindings with
Bindings = (Let (varName, value) :: bindings.Bindings)
}
let thenLetBang (varName : string) (value : SynExpr) (bindings : CompExprBindings) =
{ bindings with
Bindings = (LetBang (varName, value) :: bindings.Bindings)
}
let thenUse (varName : string) (value : SynExpr) (bindings : CompExprBindings) =
{ bindings with
Bindings = (LetBang (varName, value) :: bindings.Bindings)
}
*)

View File

@@ -3,12 +3,14 @@ namespace WoofWare.Myriad.Plugins
open System open System
open System.Text open System.Text
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Myriad.Core open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal Ident = module internal Ident =
let inline create (s : string) = Ident (s, range0)
let lowerFirstLetter (x : Ident) : Ident = let lowerFirstLetter (x : Ident) : Ident =
let result = StringBuilder x.idText.Length let result = StringBuilder x.idText.Length
result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore
result.Append x.idText.[1..] |> ignore result.Append x.idText.[1..] |> ignore
Ident.Create ((result : StringBuilder).ToString ()) create ((result : StringBuilder).ToString ())

View File

@@ -0,0 +1,9 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Xml
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal PreXmlDoc =
let create (s : string) : PreXmlDoc =
PreXmlDoc.Create ([| " " + s |], range0)

View File

@@ -0,0 +1,18 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynArgPats =
let create (caseNames : Ident list) : SynArgPats =
if caseNames.IsEmpty then
SynArgPats.Pats []
else
caseNames
|> List.map (fun ident -> SynPat.Named (SynIdent.SynIdent (ident, None), false, None, range0))
|> fun ps -> SynPat.Tuple (false, ps, List.replicate (ps.Length - 1) range0, range0)
|> fun p -> SynPat.Paren (p, range0)
|> List.singleton
|> SynArgPats.Pats

View File

@@ -8,11 +8,11 @@ open Myriad.Core
module internal SynAttribute = module internal SynAttribute =
let internal compilationRepresentation : SynAttribute = let internal compilationRepresentation : SynAttribute =
{ {
TypeName = SynLongIdent.CreateString "CompilationRepresentation" TypeName = SynLongIdent.createS "CompilationRepresentation"
ArgExpr = ArgExpr =
SynExpr.CreateLongIdent ( SynExpr.CreateLongIdent (
false, false,
SynLongIdent.Create [ "CompilationRepresentationFlags" ; "ModuleSuffix" ], SynLongIdent.createS' [ "CompilationRepresentationFlags" ; "ModuleSuffix" ],
None None
) )
|> SynExpr.CreateParen |> SynExpr.CreateParen
@@ -23,7 +23,7 @@ module internal SynAttribute =
let internal autoOpen : SynAttribute = let internal autoOpen : SynAttribute =
{ {
TypeName = SynLongIdent.CreateString "AutoOpen" TypeName = SynLongIdent.createS "AutoOpen"
ArgExpr = SynExpr.CreateConst SynConst.Unit ArgExpr = SynExpr.CreateConst SynConst.Unit
Target = None Target = None
AppliesToGetterAndSetter = false AppliesToGetterAndSetter = false

View File

@@ -0,0 +1,173 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynBinding =
let rec private stripParen (pat : SynPat) =
match pat with
| SynPat.Paren (p, _) -> stripParen p
| _ -> pat
let rec private getName (pat : SynPat) : Ident option =
match stripParen pat with
| SynPat.Named (SynIdent.SynIdent (name, _), _, _, _) -> Some name
| SynPat.Wild _ -> None
| SynPat.Typed (pat, _, _) -> getName pat
| SynPat.Const _ -> None
| SynPat.LongIdent (SynLongIdent.SynLongIdent (longIdent, _, _), _, _, _, _, _) ->
match longIdent with
| [ x ] -> Some x
| _ -> failwithf "got long ident %O ; can only get the name of a long ident with one component" longIdent
| _ -> failwithf "unrecognised pattern: %+A" pat
let triviaZero (isMember : bool) =
{
SynBindingTrivia.EqualsRange = Some range0
InlineKeyword = None
LeadingKeyword =
if isMember then
SynLeadingKeyword.Member range0
else
SynLeadingKeyword.Let range0
}
let basic (name : SynLongIdent) (args : SynPat list) (body : SynExpr) : SynBinding =
let valInfo : SynValInfo =
args
|> List.map (fun pat -> [ SynArgInfo.SynArgInfo (SynAttributes.Empty, false, getName pat) ])
|> fun x -> SynValInfo.SynValInfo (x, SynArgInfo.SynArgInfo ([], false, None))
SynBinding.SynBinding (
None,
SynBindingKind.Normal,
false,
false,
[],
PreXmlDoc.Empty,
SynValData.SynValData (None, valInfo, None),
SynPat.LongIdent (name, None, None, SynArgPats.Pats args, None, range0),
None,
body,
range0,
DebugPointAtBinding.Yes range0,
triviaZero false
)
let withAccessibility (acc : SynAccess option) (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (_, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
let headPat =
match headPat with
| SynPat.LongIdent (ident, extra, options, argPats, _, range) ->
SynPat.LongIdent (ident, extra, options, argPats, acc, range)
| _ -> failwithf "unrecognised head pattern: %O" headPat
SynBinding (acc, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
let withXmlDoc (doc : PreXmlDoc) (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (acc, kind, inl, mut, attrs, _, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
let withReturnAnnotation (ty : SynType) (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, _, expr, range, debugPoint, trivia) ->
let retInfo =
SynBindingReturnInfo.SynBindingReturnInfo (
ty,
range0,
[],
{
ColonRange = Some range0
}
)
SynBinding (
acc,
kind,
inl,
mut,
attrs,
doc,
valData,
headPat,
Some retInfo,
expr,
range,
debugPoint,
trivia
)
let makeInline (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
SynBinding (
acc,
kind,
true,
mut,
attrs,
doc,
valData,
headPat,
ret,
expr,
range,
debugPoint,
{ trivia with
InlineKeyword = Some range0
}
)
let makeStaticMember (binding : SynBinding) : SynBinding =
let memberFlags =
{
SynMemberFlags.IsInstance = false
SynMemberFlags.IsDispatchSlot = false
SynMemberFlags.IsOverrideOrExplicitImpl = false
SynMemberFlags.IsFinal = false
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
SynMemberFlags.MemberKind = SynMemberKind.Member
}
match binding with
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
let valData =
match valData with
| SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None)
let trivia =
{ trivia with
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0)
}
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia)
let makeInstanceMember (binding : SynBinding) : SynBinding =
let memberFlags =
{
SynMemberFlags.IsInstance = true
SynMemberFlags.IsDispatchSlot = false
SynMemberFlags.IsOverrideOrExplicitImpl = true
SynMemberFlags.IsFinal = false
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
SynMemberFlags.MemberKind = SynMemberKind.Member
}
match binding with
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
let valData =
match valData with
| SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None)
let trivia =
{ trivia with
LeadingKeyword = SynLeadingKeyword.Member range0
}
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia)

View File

@@ -0,0 +1,50 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Xml
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynComponentInfo =
let inline createLong (name : LongIdent) =
SynComponentInfo.SynComponentInfo ([], None, [], name, PreXmlDoc.Empty, false, None, range0)
let inline create (name : Ident) = createLong [ name ]
let inline withDocString (doc : PreXmlDoc) (i : SynComponentInfo) : SynComponentInfo =
match i with
| SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, _, postfix, access, range) ->
SynComponentInfo (attrs, typars, constraints, name, doc, postfix, access, range)
let inline setGenerics (typars : SynTyparDecls option) (i : SynComponentInfo) : SynComponentInfo =
match i with
| SynComponentInfo.SynComponentInfo (attrs, _, constraints, name, doc, postfix, access, range) ->
SynComponentInfo (attrs, typars, constraints, name, doc, postfix, access, range)
let inline withGenerics (typars : SynTyparDecl list) (i : SynComponentInfo) : SynComponentInfo =
let inner =
if typars.IsEmpty then
None
else
Some (SynTyparDecls.PostfixList (typars, [], range0))
setGenerics inner i
let inline setAccessibility (acc : SynAccess option) (i : SynComponentInfo) : SynComponentInfo =
match i with
| SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, doc, postfix, _, range) ->
SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, doc, postfix, acc, range)
let inline withAccessibility (acc : SynAccess) (i : SynComponentInfo) : SynComponentInfo =
setAccessibility (Some acc) i
let inline addAttributes (attrs : SynAttribute list) (i : SynComponentInfo) : SynComponentInfo =
match i with
| SynComponentInfo.SynComponentInfo (oldAttrs, typars, constraints, name, doc, postfix, acc, range) ->
let attrs =
{
SynAttributeList.Attributes = attrs
SynAttributeList.Range = range0
}
SynComponentInfo.SynComponentInfo ((attrs :: oldAttrs), typars, constraints, name, doc, postfix, acc, range)

View File

@@ -3,33 +3,41 @@ namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia open Fantomas.FCS.SyntaxTrivia
open Myriad.Core open Myriad.Core
open Myriad.Core.Ast
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
type internal CompExprBinding = [<AutoOpen>]
| LetBang of varName : string * rhs : SynExpr module internal SynExprExtensions =
| Let of varName : string * rhs : SynExpr type SynExpr with
| Use of varName : string * rhs : SynExpr static member CreateConst (s : string) : SynExpr =
| Do of body : SynExpr SynExpr.Const (SynConst.String (s, SynStringKind.Regular, range0), range0)
static member CreateConst () : SynExpr = SynExpr.Const (SynConst.Unit, range0)
static member CreateConst (i : int32) : SynExpr =
SynExpr.Const (SynConst.Int32 i, range0)
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynExpr = module internal SynExpr =
/// {f} {x}
let applyFunction (f : SynExpr) (x : SynExpr) : SynExpr = SynExpr.CreateApp (f, x)
/// {f} {x}
let applyTo (x : SynExpr) (f : SynExpr) : SynExpr = SynExpr.CreateApp (f, x)
/// {expr} |> {func} /// {expr} |> {func}
let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr = let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr =
SynExpr.CreateApp ( SynExpr.CreateAppInfix (
SynExpr.CreateAppInfix ( SynExpr.CreateLongIdent (
SynExpr.CreateLongIdent ( SynLongIdent.SynLongIdent (
SynLongIdent.SynLongIdent ( [ Ident.Create "op_PipeRight" ],
[ Ident.Create "op_PipeRight" ], [],
[], [ Some (IdentTrivia.OriginalNotation "|>") ]
[ Some (IdentTrivia.OriginalNotation "|>") ] )
)
),
expr
), ),
func expr
) )
|> applyTo func
/// if {cond} then {trueBranch} else {falseBranch} /// if {cond} then {trueBranch} else {falseBranch}
/// Note that this function puts the trueBranch last, for pipelining convenience: /// Note that this function puts the trueBranch last, for pipelining convenience:
@@ -54,7 +62,7 @@ module internal SynExpr =
/// try {body} with | {exc} as exc -> {handler} /// try {body} with | {exc} as exc -> {handler}
let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr = let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr =
let clause = let clause =
SynMatchClause.Create (SynPat.As (exc, SynPat.CreateNamed (Ident.Create "exc"), range0), None, handler) SynMatchClause.create (SynPat.As (exc, SynPat.named "exc", range0)) handler
SynExpr.TryWith ( SynExpr.TryWith (
body, body,
@@ -72,96 +80,82 @@ module internal SynExpr =
/// {a} = {b} /// {a} = {b}
let equals (a : SynExpr) (b : SynExpr) = let equals (a : SynExpr) (b : SynExpr) =
SynExpr.CreateApp ( SynExpr.CreateAppInfix (
SynExpr.CreateAppInfix ( SynExpr.CreateLongIdent (
SynExpr.CreateLongIdent ( SynLongIdent.SynLongIdent (
SynLongIdent.SynLongIdent ( Ident.CreateLong "op_Equality",
Ident.CreateLong "op_Equality", [],
[], [ Some (IdentTrivia.OriginalNotation "=") ]
[ Some (IdentTrivia.OriginalNotation "=") ] )
)
),
a
), ),
b a
) )
|> applyTo b
/// {a} + {b} /// {a} + {b}
let plus (a : SynExpr) (b : SynExpr) = let plus (a : SynExpr) (b : SynExpr) =
SynExpr.CreateApp ( SynExpr.CreateAppInfix (
SynExpr.CreateAppInfix ( SynExpr.CreateLongIdent (
SynExpr.CreateLongIdent ( SynLongIdent.SynLongIdent (
SynLongIdent.SynLongIdent ( Ident.CreateLong "op_Addition",
Ident.CreateLong "op_Addition", [],
[], [ Some (IdentTrivia.OriginalNotation "+") ]
[ Some (IdentTrivia.OriginalNotation "+") ] )
)
),
a
), ),
b a
) )
|> applyTo b
let rec stripOptionalParen (expr : SynExpr) : SynExpr = let rec stripOptionalParen (expr : SynExpr) : SynExpr =
match expr with match expr with
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr | SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr
| expr -> expr | expr -> expr
/// Given e.g. "byte", returns "System.Byte".
let qualifyPrimitiveType (typeName : string) : LongIdent =
match typeName with
| "float32" -> [ "System" ; "Single" ]
| "float" -> [ "System" ; "Double" ]
| "byte"
| "uint8" -> [ "System" ; "Byte" ]
| "sbyte" -> [ "System" ; "SByte" ]
| "int16" -> [ "System" ; "Int16" ]
| "int" -> [ "System" ; "Int32" ]
| "int64" -> [ "System" ; "Int64" ]
| "uint16" -> [ "System" ; "UInt16" ]
| "uint"
| "uint32" -> [ "System" ; "UInt32" ]
| "uint64" -> [ "System" ; "UInt64" ]
| _ -> failwith $"Unable to identify a parsing function `string -> %s{typeName}`"
|> List.map Ident.Create
/// {obj}.{meth} {arg} /// {obj}.{meth} {arg}
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr = let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr =
SynExpr.CreateApp ( SynExpr.DotGet (
SynExpr.DotGet ( obj,
obj, range0,
range0, SynLongIdent.SynLongIdent (id = [ Ident.Create meth ], dotRanges = [], trivia = [ None ]),
SynLongIdent.SynLongIdent (id = [ Ident.Create meth ], dotRanges = [], trivia = [ None ]), range0
range0
),
arg
) )
|> applyTo arg
/// {obj}.{meth}() /// {obj}.{meth}()
let callMethod (meth : string) (obj : SynExpr) : SynExpr = let callMethod (meth : string) (obj : SynExpr) : SynExpr =
callMethodArg meth (SynExpr.CreateConst SynConst.Unit) obj callMethodArg meth (SynExpr.CreateConst ()) obj
let callGenericMethod (meth : string) (ty : LongIdent) (obj : SynExpr) : SynExpr =
SynExpr.TypeApp (
SynExpr.DotGet (obj, range0, SynLongIdent.createS meth, range0),
range0,
[ SynType.LongIdent (SynLongIdent.create ty) ],
[],
Some range0,
range0,
range0
)
|> applyTo (SynExpr.CreateConst ())
/// {obj}.{meth}<ty>() /// {obj}.{meth}<ty>()
let callGenericMethod (meth : string) (ty : string) (obj : SynExpr) : SynExpr = let callGenericMethod' (meth : string) (ty : string) (obj : SynExpr) : SynExpr =
SynExpr.CreateApp ( SynExpr.TypeApp (
SynExpr.TypeApp ( SynExpr.DotGet (obj, range0, SynLongIdent.createS meth, range0),
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0), range0,
range0, [ SynType.CreateLongIdent ty ],
[ SynType.CreateLongIdent ty ], [],
[], Some range0,
Some range0, range0,
range0, range0
range0
),
SynExpr.CreateConst SynConst.Unit
) )
|> applyTo (SynExpr.CreateConst ())
let index (property : SynExpr) (obj : SynExpr) : SynExpr = let index (property : SynExpr) (obj : SynExpr) : SynExpr =
SynExpr.DotIndexedGet (obj, property, range0, range0) SynExpr.DotIndexedGet (obj, property, range0, range0)
/// (fun {varName} -> {body}) /// (fun {varName} -> {body})
let createLambda (varName : string) (body : SynExpr) : SynExpr = let createLambda (varName : string) (body : SynExpr) : SynExpr =
let parsedDataPat = [ SynPat.CreateNamed (Ident.Create varName) ] let parsedDataPat = [ SynPat.named varName ]
SynExpr.Lambda ( SynExpr.Lambda (
false, false,
@@ -176,26 +170,66 @@ module internal SynExpr =
) )
|> SynExpr.CreateParen |> SynExpr.CreateParen
let reraise : SynExpr = let createThunk (body : SynExpr) : SynExpr =
SynExpr.CreateApp (SynExpr.CreateIdent (Ident.Create "reraise"), SynExpr.CreateConst SynConst.Unit) let parsedDataPat = [ SynPat.Const (SynConst.Unit, range0) ]
SynExpr.Lambda (
false,
false,
SynSimplePats.Create [],
body,
Some (parsedDataPat, body),
range0,
{
ArrowRange = Some range0
}
)
|> SynExpr.CreateParen
/// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct) /// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct)
let startAsTask (ct : SynLongIdent) (body : SynExpr) = let startAsTask (ct : SynLongIdent) (body : SynExpr) =
let lambda = let lambda =
SynExpr.CreateApp ( [
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "StartAsTask" ]), SynExpr.CreateLongIdent (SynLongIdent.createS "a")
SynExpr.CreateParenedTuple equals
[ (SynExpr.LongIdent (true, SynLongIdent.createS "cancellationToken", None, range0))
SynExpr.CreateLongIdent (SynLongIdent.CreateString "a") (SynExpr.CreateLongIdent ct)
equals ]
(SynExpr.LongIdent (true, SynLongIdent.CreateString "cancellationToken", None, range0)) |> SynExpr.CreateParenedTuple
(SynExpr.CreateLongIdent ct) |> applyFunction (SynExpr.CreateLongIdent (SynLongIdent.createS' [ "Async" ; "StartAsTask" ]))
]
)
|> createLambda "a" |> createLambda "a"
pipeThroughFunction lambda body pipeThroughFunction lambda body
let inline createIdent (s : string) : SynExpr = SynExpr.Ident (Ident (s, range0))
let inline createIdent' (i : Ident) : SynExpr = SynExpr.Ident i
let inline createLongIdent (ident : string list) : SynExpr =
SynExpr.CreateLongIdent (SynLongIdent.createS' ident)
let inline createLongIdent' (ident : Ident list) : SynExpr =
SynExpr.CreateLongIdent (SynLongIdent.create ident)
let inline createLet (bindings : SynBinding list) (body : SynExpr) : SynExpr =
SynExpr.LetOrUse (false, false, bindings, body, range0, SynExprLetOrUseTrivia.empty)
let inline createMatch (matchOn : SynExpr) (cases : SynMatchClause list) : SynExpr =
SynExpr.CreateMatch (matchOn, cases)
let typeAnnotate (ty : SynType) (expr : SynExpr) : SynExpr = SynExpr.CreateTyped (expr, ty)
let inline paren (e : SynExpr) : SynExpr =
SynExpr.Paren (e, range0, Some range0, range0)
let inline createNew (ty : SynType) (args : SynExpr) : SynExpr =
SynExpr.New (false, ty, paren args, range0)
let inline createWhile (cond : SynExpr) (body : SynExpr) : SynExpr =
SynExpr.While (DebugPointAtWhile.Yes range0, cond, body, range0)
let reraise : SynExpr = createIdent "reraise" |> applyTo (SynExpr.CreateConst ())
/// {compExpr} { {lets} ; return {ret} } /// {compExpr} { {lets} ; return {ret} }
let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr = let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr =
let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0) let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0)
@@ -209,7 +243,7 @@ module internal SynExpr =
DebugPointAtBinding.Yes range0, DebugPointAtBinding.Yes range0,
false, false,
true, true,
SynPat.CreateNamed (Ident.Create lhs), SynPat.named lhs,
rhs, rhs,
[], [],
state, state,
@@ -218,22 +252,12 @@ module internal SynExpr =
EqualsRange = Some range0 EqualsRange = Some range0
} }
) )
| Let (lhs, rhs) -> | Let (lhs, rhs) -> createLet [ SynBinding.basic (SynLongIdent.createS lhs) [] rhs ] state
SynExpr.LetOrUse (
false,
false,
[ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ],
state,
range0,
{
SynExprLetOrUseTrivia.InKeyword = None
}
)
| Use (lhs, rhs) -> | Use (lhs, rhs) ->
SynExpr.LetOrUse ( SynExpr.LetOrUse (
false, false,
true, true,
[ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ], [ SynBinding.basic (SynLongIdent.createS lhs) [] rhs ],
state, state,
range0, range0,
{ {
@@ -250,32 +274,18 @@ module internal SynExpr =
/// {expr} |> Async.AwaitTask /// {expr} |> Async.AwaitTask
let awaitTask (expr : SynExpr) : SynExpr = let awaitTask (expr : SynExpr) : SynExpr =
expr expr |> pipeThroughFunction (createLongIdent [ "Async" ; "AwaitTask" ])
|> pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "AwaitTask" ]))
/// {ident}.ToString () /// {ident}.ToString ()
/// with special casing for some types like DateTime /// with special casing for some types like DateTime
let toString (ty : SynType) (ident : SynExpr) = let toString (ty : SynType) (ident : SynExpr) =
match ty with match ty with
| DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-dd") | DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-dd")
| DateTime -> | DateTime -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-ddTHH:mm:ss")
ident
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
| _ -> callMethod "ToString" ident | _ -> callMethod "ToString" ident
let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0) let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0)
let synBindingTriviaZero (isMember : bool) =
{
SynBindingTrivia.EqualsRange = Some range0
InlineKeyword = None
LeadingKeyword =
if isMember then
SynLeadingKeyword.Member range0
else
SynLeadingKeyword.Let range0
}
/// {ident} - {rhs} /// {ident} - {rhs}
let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr = let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr =
SynExpr.CreateApp ( SynExpr.CreateApp (
@@ -293,8 +303,7 @@ module internal SynExpr =
) )
/// {ident} - {n} /// {ident} - {n}
let minusN (ident : SynLongIdent) (n : int) : SynExpr = let minusN (ident : SynLongIdent) (n : int) : SynExpr = minus ident (SynExpr.CreateConst n)
minus ident (SynExpr.CreateConst (SynConst.Int32 n))
/// {y} > {x} /// {y} > {x}
let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr = let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr =
@@ -311,3 +320,17 @@ module internal SynExpr =
), ),
x x
) )
/// {y} >= {x}
let greaterThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr =
SynExpr.CreateAppInfix (
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
[ Ident.Create "op_GreaterThanOrEqual" ],
[],
[ Some (IdentTrivia.OriginalNotation ">=") ]
)
),
y
)
|> applyTo x

View File

@@ -0,0 +1,10 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.SyntaxTrivia
[<RequireQualifiedAccess>]
module internal SynExprLetOrUseTrivia =
let empty : SynExprLetOrUseTrivia =
{
InKeyword = None
}

View File

@@ -0,0 +1,39 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
type internal SynFieldData<'Ident> =
{
Attrs : SynAttribute list
Ident : 'Ident
Type : SynType
}
[<RequireQualifiedAccess>]
module internal SynField =
/// Get the useful information out of a SynField.
let extract (SynField (attrs, _, id, fieldType, _, _, _, _, _)) : SynFieldData<Ident option> =
{
Attrs = attrs |> List.collect (fun l -> l.Attributes)
Ident = id
Type = fieldType
}
let mapIdent<'a, 'b> (f : 'a -> 'b) (x : SynFieldData<'a>) : SynFieldData<'b> =
let ident = f x.Ident
{
Attrs = x.Attrs
Ident = ident
Type = x.Type
}
/// Throws if the field has no identifier.
let extractWithIdent (f : SynField) : SynFieldData<Ident> =
f
|> extract
|> mapIdent (fun ident ->
match ident with
| None -> failwith "expected field identifier to have a value, but it did not"
| Some i -> i
)

View File

@@ -0,0 +1,83 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Text.Range
open Fantomas.FCS.Syntax
[<RequireQualifiedAccess>]
module internal SynLongIdent =
let create (ident : LongIdent) : SynLongIdent =
let commas =
match ident with
| [] -> []
| _ :: commas -> commas |> List.map (fun _ -> range0)
SynLongIdent.SynLongIdent (ident, commas, List.replicate ident.Length None)
let inline createI (i : Ident) : SynLongIdent = create [ i ]
let inline createS (s : string) : SynLongIdent = createI (Ident (s, range0))
let inline createS' (s : string list) : SynLongIdent =
create (s |> List.map (fun i -> Ident (i, range0)))
let isUnit (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "unit", System.StringComparison.OrdinalIgnoreCase) -> true
| _ -> false
let isList (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 isArray (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 isOption (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 isResponse (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Response" ]
| [ "RestEase" ; "Response" ] -> true
| _ -> false
let isMap (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Map" ] -> true
| _ -> false
let isReadOnlyDictionary (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "IReadOnlyDictionary" ]
| [ "Generic" ; "IReadOnlyDictionary" ]
| [ "Collections" ; "Generic" ; "IReadOnlyDictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "IReadOnlyDictionary" ] -> true
| _ -> false
let isDictionary (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Dictionary" ]
| [ "Generic" ; "Dictionary" ]
| [ "Collections" ; "Generic" ; "Dictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "Dictionary" ] -> true
| _ -> false
let isIDictionary (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "IDictionary" ]
| [ "Generic" ; "IDictionary" ]
| [ "Collections" ; "Generic" ; "IDictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "IDictionary" ] -> true
| _ -> false

View File

@@ -0,0 +1,24 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynMatchClause =
let create (lhs : SynPat) (rhs : SynExpr) : SynMatchClause =
SynMatchClause.SynMatchClause (
lhs,
None,
rhs,
range0,
DebugPointAtTarget.Yes,
{
ArrowRange = Some range0
BarRange = Some range0
}
)
let withWhere (where : SynExpr) (m : SynMatchClause) : SynMatchClause =
match m with
| SynMatchClause (synPat, _, resultExpr, range, debugPointAtTarget, synMatchClauseTrivia) ->
SynMatchClause (synPat, Some where, resultExpr, range, debugPointAtTarget, synMatchClauseTrivia)

View File

@@ -0,0 +1,61 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Text.Range
open Fantomas.FCS.Xml
[<RequireQualifiedAccess>]
module internal SynMemberDefn =
let private interfaceMemberSlotFlags =
{
SynMemberFlags.IsInstance = true
SynMemberFlags.IsDispatchSlot = true
SynMemberFlags.IsOverrideOrExplicitImpl = false
SynMemberFlags.IsFinal = false
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
SynMemberFlags.MemberKind = SynMemberKind.Member
}
let abstractMember
(ident : SynIdent)
(typars : SynTyparDecls option)
(arity : SynValInfo)
(xmlDoc : PreXmlDoc)
(returnType : SynType)
: SynMemberDefn
=
let slot =
SynValSig.SynValSig (
[],
ident,
SynValTyparDecls.SynValTyparDecls (typars, true),
returnType,
arity,
false,
false,
xmlDoc,
None,
None,
range0,
{
EqualsRange = None
WithKeyword = None
InlineKeyword = None
LeadingKeyword = SynLeadingKeyword.Abstract range0
}
)
SynMemberDefn.AbstractSlot (
slot,
interfaceMemberSlotFlags,
range0,
{
GetSetKeywords = None
}
)
let staticMember (binding : SynBinding) : SynMemberDefn =
let binding = SynBinding.makeStaticMember binding
SynMemberDefn.Member (binding, range0)

View File

@@ -0,0 +1,16 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynPat =
let annotateType (ty : SynType) (pat : SynPat) =
SynPat.Paren (SynPat.Typed (pat, ty, range0), range0)
let named (s : string) : SynPat =
SynPat.Named (SynIdent.SynIdent (Ident (s, range0), None), false, None, range0)
let namedI (i : Ident) : SynPat =
SynPat.Named (SynIdent.SynIdent (i, None), false, None, range0)

View File

@@ -0,0 +1,232 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynType =
let rec stripOptionalParen (ty : SynType) : SynType =
match ty with
| SynType.Paren (ty, _) -> stripOptionalParen ty
| ty -> ty
let inline createLongIdent (ident : LongIdent) : SynType =
SynType.LongIdent (SynLongIdent.create ident)
let inline createLongIdent' (ident : string list) : SynType =
SynType.LongIdent (SynLongIdent.createS' ident)
let inline named (name : string) = createLongIdent' [ name ]
let inline app' (name : SynType) (args : SynType list) : SynType =
if args.IsEmpty then
failwith "Type cannot be applied to no arguments"
SynType.App (name, Some range0, args, List.replicate (args.Length - 1) range0, Some range0, false, range0)
let inline app (name : string) (args : SynType list) : SynType = app' (named name) args
let inline appPostfix (name : string) (arg : SynType) : SynType =
SynType.App (named name, None, [ arg ], [], None, true, range0)
let inline funFromDomain (domain : SynType) (range : SynType) : SynType =
SynType.Fun (
domain,
range,
range0,
{
ArrowRange = range0
}
)
let inline signatureParamOfType (ty : SynType) (name : Ident option) : SynType =
SynType.SignatureParameter ([], false, name, ty, range0)
let inline var (ty : SynTypar) : SynType = SynType.Var (ty, range0)
[<AutoOpen>]
module internal SynTypePatterns =
let (|OptionType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isOption ident ->
Some innerType
| _ -> None
let (|UnitType|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident when SynLongIdent.isUnit ident -> Some ()
| _ -> None
let (|ListType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isList ident ->
Some innerType
| _ -> None
let (|ArrayType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isArray ident ->
Some innerType
| SynType.Array (1, innerType, _) -> Some innerType
| _ -> None
let (|RestEaseResponseType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isResponse ident ->
Some innerType
| _ -> None
let (|DictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isDictionary ident ->
Some (key, value)
| _ -> None
let (|IDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isIDictionary ident ->
Some (key, value)
| _ -> None
let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when
SynLongIdent.isReadOnlyDictionary ident
->
Some (key, value)
| _ -> None
let (|MapType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isMap ident ->
Some (key, value)
| _ -> None
let (|BigInt|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map _.idText with
| [ "bigint" ]
| [ "BigInteger" ]
| [ "Numerics" ; "BigInteger" ]
| [ "System" ; "Numerics" ; "BigInteger" ] -> Some ()
| _ -> None
| _ -> None
/// Returns the type, qualified as in e.g. `System.Boolean`.
let (|PrimitiveType|_|) (fieldType : SynType) : LongIdent option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] -> Primitives.qualifyType i.idText
| _ -> None
| _ -> None
let (|String|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
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 (|Guid|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Guid" ]
| [ "Guid" ] -> Some ()
| _ -> None
| _ -> None
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
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

View File

@@ -0,0 +1,27 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynTypeDefn =
let inline create (componentInfo : SynComponentInfo) (repr : SynTypeDefnRepr) : SynTypeDefn =
SynTypeDefn.SynTypeDefn (
componentInfo,
repr,
[],
None,
range0,
{
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
EqualsRange = Some range0
WithKeyword = None
}
)
let inline withMemberDefns (members : SynMemberDefn list) (r : SynTypeDefn) : SynTypeDefn =
match r with
| SynTypeDefn (typeInfo, typeRepr, _, ctor, range, trivia) ->
SynTypeDefn.SynTypeDefn (typeInfo, typeRepr, members, ctor, range, trivia)

View File

@@ -0,0 +1,20 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynTypeDefnRepr =
let inline interfaceType (mems : SynMemberDefns) : SynTypeDefnRepr =
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Unspecified, mems, range0)
/// Indicates the body of a `type Foo with {body}` extension type declaration.
let inline augmentation () : SynTypeDefnRepr =
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0)
let inline union (cases : SynUnionCase list) : SynTypeDefnRepr =
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (None, cases, range0), range0)
let inline record (fields : SynField list) : SynTypeDefnRepr =
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (None, fields, range0), range0)

View File

@@ -0,0 +1,41 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
type internal UnionCase<'Ident> =
{
Fields : SynFieldData<'Ident> list
Attrs : SynAttribute list
Ident : Ident
}
[<RequireQualifiedAccess>]
module internal UnionCase =
let mapIdentFields<'a, 'b> (f : 'a -> 'b) (unionCase : UnionCase<'a>) : UnionCase<'b> =
{
Fields = unionCase.Fields |> List.map (SynField.mapIdent f)
Attrs = unionCase.Attrs
Ident = unionCase.Ident
}
[<RequireQualifiedAccess>]
module internal SynUnionCase =
let extract (SynUnionCase (attrs, id, caseType, _, _, _, _)) : UnionCase<Ident option> =
match caseType with
| SynUnionCaseKind.FullType _ -> failwith "WoofWare.Myriad does not support FullType union cases."
| SynUnionCaseKind.Fields fields ->
let fields = fields |> List.map SynField.extract
let id =
match id with
| SynIdent.SynIdent (ident, _) -> ident
// As far as I can tell, there's no way to get any attributes here? :shrug:
let attrs = attrs |> List.collect (fun l -> l.Attributes)
{
Fields = fields
Attrs = attrs
Ident = id
}

View File

@@ -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

View File

@@ -25,11 +25,26 @@
<ItemGroup> <ItemGroup>
<Compile Include="List.fs"/> <Compile Include="List.fs"/>
<Compile Include="Ident.fs" /> <Compile Include="Primitives.fs" />
<Compile Include="AstHelper.fs"/> <Compile Include="SynExpr\PreXmlDoc.fs" />
<Compile Include="SynExpr.fs"/> <Compile Include="SynExpr\Ident.fs" />
<Compile Include="SynType.fs"/> <Compile Include="SynExpr\SynLongIdent.fs" />
<Compile Include="SynAttribute.fs"/> <Compile Include="SynExpr\SynExprLetOrUseTrivia.fs" />
<Compile Include="SynExpr\SynBinding.fs" />
<Compile Include="SynExpr\SynType.fs" />
<Compile Include="SynExpr\SynMatchClause.fs" />
<Compile Include="SynExpr\SynPat.fs" />
<Compile Include="SynExpr\CompExpr.fs" />
<Compile Include="SynExpr\SynExpr.fs" />
<Compile Include="SynExpr\SynAttribute.fs" />
<Compile Include="SynExpr\SynArgPats.fs" />
<Compile Include="SynExpr\SynField.fs" />
<Compile Include="SynExpr\SynUnionCase.fs" />
<Compile Include="SynExpr\SynTypeDefnRepr.fs" />
<Compile Include="SynExpr\SynTypeDefn.fs" />
<Compile Include="SynExpr\SynComponentInfo.fs" />
<Compile Include="SynExpr\SynMemberDefn.fs" />
<Compile Include="AstHelper.fs" />
<Compile Include="RemoveOptionsGenerator.fs"/> <Compile Include="RemoveOptionsGenerator.fs"/>
<Compile Include="InterfaceMockGenerator.fs"/> <Compile Include="InterfaceMockGenerator.fs"/>
<Compile Include="JsonSerializeGenerator.fs"/> <Compile Include="JsonSerializeGenerator.fs"/>

View File

@@ -3,5 +3,11 @@
"publicReleaseRefSpec": [ "publicReleaseRefSpec": [
"^refs/heads/main$" "^refs/heads/main$"
], ],
"pathFilters": null "pathFilters": [
} ":/",
":^WoofWare.Myriad.Plugins.Test/",
":^WoofWare.Myriad.Plugins.Attributes/Test/",
":^/.github/",
":^/CHANGELOG.md"
]
}

View File

@@ -10,7 +10,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageDownload Include="G-Research.FSharp.Analyzers" Version="[0.9.3]" /> <PackageDownload Include="G-Research.FSharp.Analyzers" Version="[0.10.0]" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -7,7 +7,6 @@
}; };
outputs = { outputs = {
self,
nixpkgs, nixpkgs,
flake-utils, flake-utils,
... ...

View File

@@ -3,18 +3,18 @@
{fetchNuGet}: [ {fetchNuGet}: [
(fetchNuGet { (fetchNuGet {
pname = "fsharp-analyzers"; pname = "fsharp-analyzers";
version = "0.25.0"; version = "0.26.0";
sha256 = "sha256-njfJYi40jNvrD+mgu9LtQw2Omh8P1SSDThesozH0KQY="; sha256 = "sha256-60Bl36LOb/zVNdH2SBSuQ5O41lP9dKTNZbs5vvYs+3U=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "fantomas"; pname = "fantomas";
version = "6.3.0-alpha-008"; version = "6.3.4";
sha256 = "sha256-rI/4upuj8JBy2C9gl2lwI/JXmBD7UHKxCoSpd+bstRw="; sha256 = "sha256-1aWqZynBkQoznenGoP0sbf1PcUXAbcHiWyECuv89xa0=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "ApiSurface"; pname = "ApiSurface";
version = "4.0.33"; version = "4.0.40";
sha256 = "0mmsa5gxfd3bbgacip0c1hljwd958zcx1012qdh033sx6nfz3v36"; sha256 = "1c9z0b6minlripwrjmv4yd5w8zj4lcpak4x41izh7ygx8kgmbvx0";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Fantomas.Core"; pname = "Fantomas.Core";
@@ -118,13 +118,13 @@
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.CodeCoverage"; pname = "Microsoft.CodeCoverage";
version = "17.9.0"; version = "17.10.0";
sha256 = "1gljgi69k0fz8vy8bn6xlyxabj6q4vls2zza9wz7ng6ix3irm89r"; sha256 = "0s0v7jmrq85n356xv7zixvwa4z94fszjcr5vll8x4im1a2lp00f9";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NET.Test.Sdk"; pname = "Microsoft.NET.Test.Sdk";
version = "17.9.0"; version = "17.10.0";
sha256 = "1lls1fly2gr1n9n1xyl9k33l2v4pwfmylyzkq8v4v5ldnwkl1zdb"; sha256 = "13g8fwl09li8fc71nk13dgkb7gahd4qhamyg2xby7am63nlchhdf";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-arm64"; pname = "Microsoft.NETCore.App.Host.linux-arm64";
@@ -268,13 +268,13 @@
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.TestPlatform.ObjectModel"; pname = "Microsoft.TestPlatform.ObjectModel";
version = "17.9.0"; version = "17.10.0";
sha256 = "1kgsl9w9fganbm9wvlkqgk0ag9hfi58z88rkfybc6kvg78bx89ca"; sha256 = "07j69cw8r39533w4p39mnj00kahazz38760in3jfc45kmlcdb26x";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.TestPlatform.TestHost"; pname = "Microsoft.TestPlatform.TestHost";
version = "17.9.0"; version = "17.10.0";
sha256 = "19ffh31a1jxzn8j69m1vnk5hyfz3dbxmflq77b8x82zybiilh5nl"; sha256 = "1bl471s7fx9jycr0cc8rylwf34mrvlg9qn1an6l86nisavfcyb7v";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Myriad.Core"; pname = "Myriad.Core";
@@ -308,33 +308,33 @@
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Common"; pname = "NuGet.Common";
version = "6.9.1"; version = "6.10.0";
sha256 = "0ic3d46r9v05pkczpmskw86yzixm6iwshbw0ya8i2957nhhlymw8"; sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Configuration"; pname = "NuGet.Configuration";
version = "6.9.1"; version = "6.10.0";
sha256 = "07z4qgbibpg59j2r05ifnqdyqf2xinm33rx7gjyr1f73kzg01m33"; sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Frameworks"; pname = "NuGet.Frameworks";
version = "6.9.1"; version = "6.10.0";
sha256 = "0s3az3ac53icjnmb14hfjcmkvzscvrkm62jgqf48yvsbysyhqm5s"; sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Packaging"; pname = "NuGet.Packaging";
version = "6.9.1"; version = "6.10.0";
sha256 = "0w0arkmzg3qh1brq4vm10zrsjm7nw706ld4y5kqcmvjpd16f4b4y"; sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Protocol"; pname = "NuGet.Protocol";
version = "6.7.0"; version = "6.10.0";
sha256 = "1v5ibnq2mp801vw68zyj169hkj3xm7h55824i33n1jxxj2vs3vbk"; sha256 = "0hmv4q0ks9i34mfgpb13l01la9v3jjllfh1qd3aqv105xrqrdxac";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Versioning"; pname = "NuGet.Versioning";
version = "6.9.1"; version = "6.10.0";
sha256 = "0xrs82dydy9cgxf0qypr01wawwnq1nf6fc7rwisb4y5v4r259fdm"; sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NUnit"; pname = "NUnit";
@@ -433,12 +433,12 @@
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Text.Encodings.Web"; pname = "System.Text.Encodings.Web";
version = "6.0.0"; version = "7.0.0";
sha256 = "06n9ql3fmhpjl32g3492sj181zjml5dlcc5l76xq2h38c4f87sai"; sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Text.Json"; pname = "System.Text.Json";
version = "6.0.0"; version = "7.0.3";
sha256 = "1si2my1g0q0qv1hiqnji4xh9wd05qavxnzj9dwgs23iqvgjky0gl"; sha256 = "0zjrnc9lshagm6kdb9bdh45dmlnkpwcpyssa896sda93ngbmj8k9";
}) })
] ]