Compare commits

..

14 Commits

Author SHA1 Message Date
dependabot[bot]
e96803e303 Bump ApiSurface from 4.0.42 to 4.0.43 (#180)
* Bump ApiSurface from 4.0.42 to 4.0.43

Bumps ApiSurface from 4.0.42 to 4.0.43.

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

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

* 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-07-08 19:06:18 +01:00
dependabot[bot]
b53b410feb Bump ApiSurface from 4.0.41 to 4.0.42 (#176)
* Bump ApiSurface from 4.0.41 to 4.0.42

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

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

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

* 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-07-01 18:21:26 +01:00
Patrick Stevens
398cd04a2a Support DateTimeOffset in JSON generators (#179) 2024-07-01 18:08:09 +01:00
Patrick Stevens
434c042510 Omit upcasts where possible (#178) 2024-07-01 17:45:36 +01:00
Patrick Stevens
c590db2a65 JSON enums (#175) 2024-06-27 21:23:06 +01:00
Patrick Stevens
6a81513a93 Add nullable support to JSON generators (#174) 2024-06-27 08:40:58 +01:00
Patrick Stevens
ba31689145 Also allow serialising units of measure (#171) 2024-06-25 00:04:56 +01:00
Patrick Stevens
85929d49d5 Support units of measure in JsonParse (#170) 2024-06-24 23:23:23 +01:00
dependabot[bot]
db4694f6e7 Bump actions/attest-build-provenance from 1.0.0 to 1.3.2 (#169)
Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 1.0.0 to 1.3.2.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](897ed5eab6...bdd51370e0)

---
updated-dependencies:
- dependency-name: actions/attest-build-provenance
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-06-24 18:55:05 +01:00
Patrick Stevens
669eccbdef Nudge README to bump the pipeline (#168) 2024-06-17 23:17:34 +01:00
Patrick Stevens
1bb87e55da Attest contents of packages (#167) 2024-06-17 23:08:36 +01:00
Patrick Stevens
4901e7cdf4 Add visibility modifiers in JsonParse/Serialize (#165) 2024-06-15 21:03:59 +01:00
dependabot[bot]
68bd4bc1fd Bump fantomas from 6.3.7 to 6.3.9 (#162)
* Bump fantomas from 6.3.7 to 6.3.9

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.7 to 6.3.9.
- [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.7...v6.3.9)

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

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

* 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-06-10 18:04:02 +01:00
dependabot[bot]
8da0fd01fe Bump Nerdbank.GitVersioning from 3.6.133 to 3.6.139 (#164)
* Bump ApiSurface from 4.0.40 to 4.0.41

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

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

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

* Bump Nerdbank.GitVersioning from 3.6.133 to 3.6.139

Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.6.133 to 3.6.139.
- [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases)
- [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/compare/v3.6.133...v3.6.139)

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

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

* 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-06-10 12:25:03 +01:00
34 changed files with 1406 additions and 393 deletions

View File

@@ -3,7 +3,7 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"fantomas": { "fantomas": {
"version": "6.3.7", "version": "6.3.9",
"commands": [ "commands": [
"fantomas" "fantomas"
] ]

14
.github/workflows/assert-contents.sh vendored Normal file
View File

@@ -0,0 +1,14 @@
#!/bin/bash
echo "Unzipping version from NuGet"
ls from-nuget.nupkg
mkdir from-nuget && cp from-nuget.nupkg from-nuget/zip.zip && cd from-nuget && unzip zip.zip && rm zip.zip && cd - || exit 1
echo "Unzipping version from local build"
ls packed/
mkdir from-local && cp packed/*.nupkg from-local/zip.zip && cd from-local && unzip zip.zip && rm zip.zip && cd - || exit 1
cd from-local && find . -type f -exec sha256sum {} \; | sort > ../from-local.txt && cd .. || exit 1
cd from-nuget && find . -type f -and -not -name '.signature.p7s' -exec sha256sum {} \; | sort > ../from-nuget.txt && cd .. || exit 1
diff from-local.txt from-nuget.txt

View File

@@ -221,11 +221,53 @@ jobs:
steps: steps:
- run: echo "All required checks complete." - run: echo "All required checks complete."
nuget-publish: attestation-attribute:
runs-on: ubuntu-latest
needs: [all-required-checks-complete]
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
permissions:
id-token: write
attestations: write
contents: read
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-attribute
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@bdd51370e0416ac948727f861e03c2f05d32d78e # v1.3.2
with:
subject-path: "packed/*.nupkg"
attestation-plugin:
runs-on: ubuntu-latest
needs: [all-required-checks-complete]
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
permissions:
id-token: write
attestations: write
contents: read
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@bdd51370e0416ac948727f861e03c2f05d32d78e # v1.3.2
with:
subject-path: "packed/*.nupkg"
nuget-publish-attribute:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }} if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete] needs: [all-required-checks-complete]
environment: main-deploy environment: main-deploy
permissions:
id-token: write
attestations: write
contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
@@ -233,20 +275,73 @@ jobs:
with: with:
extra_nix_config: | extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Download NuGet artifact (plugin) - name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
path: packed-plugin
- name: Publish to NuGet (plugin)
run: nix develop --command dotnet nuget push "packed-plugin/WoofWare.Myriad.Plugins.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate
- name: Download NuGet artifact (attribute)
uses: actions/download-artifact@v4 uses: actions/download-artifact@v4
with: with:
name: nuget-package-attribute name: nuget-package-attribute
path: packed-attribute path: packed
- name: Publish to NuGet (attribute) - name: Publish to NuGet
run: nix develop --command dotnet nuget push "packed-attribute/WoofWare.Myriad.Plugins.Attributes.*.nupkg" --api-key ${{ secrets.NUGET_API_KEY }} --source https://api.nuget.org/v3/index.json --skip-duplicate id: publish-success
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: 'nix develop --command bash ./.github/workflows/nuget-push.sh "packed/WoofWare.Myriad.Plugins.Attributes.*.nupkg"'
- name: Wait for availability
if: steps.publish-success.outputs.result == 'published'
env:
PACKAGE_VERSION: ${{ steps.publish-success.outputs.version }}
run: 'echo "$PACKAGE_VERSION" && while ! curl -L --fail -o from-nuget.nupkg "https://www.nuget.org/api/v2/package/WoofWare.Myriad.Plugins.Attributes/$PACKAGE_VERSION" ; do sleep 10; done'
# Astonishingly, NuGet.org considers it to be "more secure" to tamper with my package after upload (https://devblogs.microsoft.com/nuget/introducing-repository-signatures/).
# So we have to *re-attest* it after it's uploaded. Mind-blowing.
- name: Assert package contents
if: steps.publish-success.outputs.result == 'published'
run: 'bash ./.github/workflows/assert-contents.sh'
- name: Attest Build Provenance
if: steps.publish-success.outputs.result == 'published'
uses: actions/attest-build-provenance@bdd51370e0416ac948727f861e03c2f05d32d78e # v1.3.2
with:
subject-path: "from-nuget.nupkg"
nuget-publish-plugin:
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete]
environment: main-deploy
permissions:
id-token: write
attestations: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@V27
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
path: packed
- name: Publish to NuGet
id: publish-success
env:
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
run: 'nix develop --command bash ./.github/workflows/nuget-push.sh "packed/WoofWare.Myriad.Plugins.*.nupkg"'
- name: Wait for availability
if: steps.publish-success.outputs.result == 'published'
env:
PACKAGE_VERSION: ${{ steps.publish-success.outputs.version }}
run: 'echo "$PACKAGE_VERSION" && while ! curl -L --fail -o from-nuget.nupkg "https://www.nuget.org/api/v2/package/WoofWare.Myriad.Plugins/$PACKAGE_VERSION" ; do sleep 10; done'
# Astonishingly, NuGet.org considers it to be "more secure" to tamper with my package after upload (https://devblogs.microsoft.com/nuget/introducing-repository-signatures/).
# So we have to *re-attest* it after it's uploaded. Mind-blowing.
- name: Assert package contents
if: steps.publish-success.outputs.result == 'published'
run: 'bash ./.github/workflows/assert-contents.sh'
- name: Attest Build Provenance
if: steps.publish-success.outputs.result == 'published'
uses: actions/attest-build-provenance@bdd51370e0416ac948727f861e03c2f05d32d78e # v1.3.2
with:
subject-path: "from-nuget.nupkg"
github-release-plugin: github-release-plugin:
runs-on: ubuntu-latest runs-on: ubuntu-latest

24
.github/workflows/nuget-push.sh vendored Normal file
View File

@@ -0,0 +1,24 @@
#!/bin/bash
SOURCE_NUPKG=$(find . -type f -name '*.nupkg')
PACKAGE_VERSION=$(basename "$SOURCE_NUPKG" | rev | cut -d '.' -f 2-4 | rev)
echo "version=$PACKAGE_VERSION" >> "$GITHUB_OUTPUT"
tmp=$(mktemp)
if ! dotnet nuget push "$SOURCE_NUPKG" --api-key "$NUGET_API_KEY" --source https://api.nuget.org/v3/index.json > "$tmp" ; then
cat "$tmp"
if grep 'already exists and cannot be modified' "$tmp" ; then
echo "result=skipped" >> "$GITHUB_OUTPUT"
exit 0
else
echo "Unexpected failure to upload"
exit 1
fi
fi
cat "$tmp"
echo "result=published" >> "$GITHUB_OUTPUT"

View File

@@ -1,5 +1,10 @@
Notable changes are recorded here. Notable changes are recorded here.
# WoofWare.Myriad.Plugins 2.1.45, WoofWare.Myriad.Plugins.Attributes 3.1.7
The NuGet packages are now attested to through [GitHub Attestations](https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/).
You can run `gh attestation verify ~/.nuget/packages/woofware.myriad.plugins/2.1.45/woofware.myriad.plugins.2.1.45.nupkg -o Smaug123`, for example, to verify with GitHub that the GitHub Actions pipeline on this repository produced a nupkg file with the same hash as the one you were served from NuGet.
# WoofWare.Myriad.Plugins 2.1.33 # WoofWare.Myriad.Plugins 2.1.33
`JsonParse` can now deserialize the discriminated unions which `JsonSerialize` wrote out. `JsonParse` can now deserialize the discriminated unions which `JsonSerialize` wrote out.

View File

@@ -4,6 +4,33 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace ConsumePlugin
open System.Text.Json.Serialization
/// Module containing JSON serializing methods for the InternalTypeNotExtensionSerial type
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module internal InternalTypeNotExtensionSerial =
/// Serialize to a JSON node
let toJsonNode (input : InternalTypeNotExtensionSerial) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do node.Add ((Literals.something), (input.InternalThing2 |> System.Text.Json.Nodes.JsonValue.Create<string>))
node :> _
namespace ConsumePlugin
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the InternalTypeExtension type
[<AutoOpen>]
module internal InternalTypeExtensionJsonSerializeExtension =
/// Extension methods for JSON parsing
type InternalTypeExtension with
/// Serialize to a JSON node
static member toJsonNode (input : InternalTypeExtension) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do node.Add ((Literals.something), (input.ExternalThing |> System.Text.Json.Nodes.JsonValue.Create<string>))
node :> _
namespace ConsumePlugin namespace ConsumePlugin
@@ -22,7 +49,7 @@ module InnerType =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Thing = arg_0 Thing = arg_0
@@ -44,7 +71,7 @@ module JsonRecordType =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|> Array.ofSeq |> Array.ofSeq
let arg_4 = let arg_4 =
@@ -57,7 +84,7 @@ module JsonRecordType =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|> Array.ofSeq |> Array.ofSeq
let arg_3 = let arg_3 =
@@ -82,7 +109,7 @@ module JsonRecordType =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|> List.ofSeq |> List.ofSeq
let arg_1 = let arg_1 =
@@ -95,7 +122,7 @@ module JsonRecordType =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["a"] with (match node.["a"] with
@@ -107,7 +134,7 @@ module JsonRecordType =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
A = arg_0 A = arg_0
@@ -119,6 +146,53 @@ module JsonRecordType =
} }
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing methods for the InternalTypeNotExtension type
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module internal InternalTypeNotExtension =
/// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InternalTypeNotExtension =
let arg_0 =
(match node.[(Literals.something)] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ((Literals.something))
)
)
| v -> v)
.AsValue()
.GetValue<System.String> ()
{
InternalThing = arg_0
}
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the InternalTypeExtension type
[<AutoOpen>]
module internal InternalTypeExtensionJsonParseExtension =
/// Extension methods for JSON parsing
type InternalTypeExtension with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : InternalTypeExtension =
let arg_0 =
(match node.[(Literals.something)] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ((Literals.something))
)
)
| v -> v)
.AsValue()
.GetValue<System.String> ()
{
ExternalThing = arg_0
}
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the ToGetExtensionMethod type /// Module containing JSON parsing extension members for the ToGetExtensionMethod type
[<AutoOpen>] [<AutoOpen>]
module ToGetExtensionMethodJsonParseExtension = module ToGetExtensionMethodJsonParseExtension =
@@ -271,7 +345,7 @@ module ToGetExtensionMethodJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_7 = let arg_7 =
(match node.["hotel"] with (match node.["hotel"] with
@@ -343,7 +417,7 @@ module ToGetExtensionMethodJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<float> () .GetValue<System.Double> ()
let arg_1 = let arg_1 =
(match node.["bravo"] with (match node.["bravo"] with
@@ -368,7 +442,7 @@ module ToGetExtensionMethodJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Alpha = arg_0 Alpha = arg_0

View File

@@ -20,21 +20,26 @@ module MemberJsonSerializeExtension =
let node = System.Text.Json.Nodes.JsonObject () let node = System.Text.Json.Nodes.JsonObject ()
do do
node.Add ("id", System.Text.Json.Nodes.JsonValue.Create<int> input.Id) node.Add ("id", (input.Id |> System.Text.Json.Nodes.JsonValue.Create<int>))
node.Add ("compoundMemberId", System.Text.Json.Nodes.JsonValue.Create<string> input.CompoundMemberId)
node.Add ("firstName", System.Text.Json.Nodes.JsonValue.Create<string> input.FirstName) node.Add (
node.Add ("lastName", System.Text.Json.Nodes.JsonValue.Create<string> input.LastName) "compoundMemberId",
node.Add ("homeGymId", System.Text.Json.Nodes.JsonValue.Create<int> input.HomeGymId) (input.CompoundMemberId |> System.Text.Json.Nodes.JsonValue.Create<string>)
node.Add ("homeGymName", System.Text.Json.Nodes.JsonValue.Create<string> input.HomeGymName) )
node.Add ("emailAddress", System.Text.Json.Nodes.JsonValue.Create<string> input.EmailAddress)
node.Add ("gymAccessPin", System.Text.Json.Nodes.JsonValue.Create<string> input.GymAccessPin) node.Add ("firstName", (input.FirstName |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("dateofBirth", System.Text.Json.Nodes.JsonValue.Create<DateOnly> input.DateOfBirth) node.Add ("lastName", (input.LastName |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("mobileNumber", System.Text.Json.Nodes.JsonValue.Create<string> input.MobileNumber) node.Add ("homeGymId", (input.HomeGymId |> System.Text.Json.Nodes.JsonValue.Create<int>))
node.Add ("postCode", System.Text.Json.Nodes.JsonValue.Create<string> input.Postcode) node.Add ("homeGymName", (input.HomeGymName |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("membershipName", System.Text.Json.Nodes.JsonValue.Create<string> input.MembershipName) node.Add ("emailAddress", (input.EmailAddress |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("membershipLevel", System.Text.Json.Nodes.JsonValue.Create<int> input.MembershipLevel) node.Add ("gymAccessPin", (input.GymAccessPin |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("suspendedReason", System.Text.Json.Nodes.JsonValue.Create<int> input.SuspendedReason) node.Add ("dateofBirth", (input.DateOfBirth |> System.Text.Json.Nodes.JsonValue.Create<DateOnly>))
node.Add ("memberStatus", System.Text.Json.Nodes.JsonValue.Create<int> input.MemberStatus) node.Add ("mobileNumber", (input.MobileNumber |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("postCode", (input.Postcode |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("membershipName", (input.MembershipName |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("membershipLevel", (input.MembershipLevel |> System.Text.Json.Nodes.JsonValue.Create<int>))
node.Add ("suspendedReason", (input.SuspendedReason |> System.Text.Json.Nodes.JsonValue.Create<int>))
node.Add ("memberStatus", (input.MemberStatus |> System.Text.Json.Nodes.JsonValue.Create<int>))
node :> _ node :> _
@@ -55,7 +60,7 @@ module GymOpeningHours =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|> List.ofSeq |> List.ofSeq
let arg_0 = let arg_0 =
@@ -68,7 +73,7 @@ module GymOpeningHours =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
{ {
IsAlwaysOpen = arg_0 IsAlwaysOpen = arg_0
@@ -91,7 +96,7 @@ module GymAccessOptions =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_0 = let arg_0 =
(match node.["pinAccess"] with (match node.["pinAccess"] with
@@ -103,7 +108,7 @@ module GymAccessOptions =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
{ {
PinAccess = arg_0 PinAccess = arg_0
@@ -127,7 +132,7 @@ module GymLocation =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<float> () .GetValue<System.Double> ()
with :? System.InvalidOperationException as exc -> with :? System.InvalidOperationException as exc ->
if exc.Message.Contains "cannot be converted to" then if exc.Message.Contains "cannot be converted to" then
if if
@@ -148,6 +153,7 @@ module GymLocation =
reraise () reraise ()
else else
reraise () reraise ()
|> LanguagePrimitives.FloatWithMeasure
let arg_0 = let arg_0 =
try try
@@ -160,7 +166,7 @@ module GymLocation =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<float> () .GetValue<System.Double> ()
with :? System.InvalidOperationException as exc -> with :? System.InvalidOperationException as exc ->
if exc.Message.Contains "cannot be converted to" then if exc.Message.Contains "cannot be converted to" then
if if
@@ -203,12 +209,12 @@ module GymAddress =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_4 = 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<System.String> () |> Some
let arg_3 = let arg_3 =
(match node.["town"] with (match node.["town"] with
@@ -220,17 +226,17 @@ module GymAddress =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_2 = 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<System.String> () |> Some
let arg_1 = 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<System.String> () |> Some
let arg_0 = let arg_0 =
(match node.["addressLine1"] with (match node.["addressLine1"] with
@@ -242,7 +248,7 @@ module GymAddress =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
AddressLine1 = arg_0 AddressLine1 = arg_0
@@ -269,7 +275,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_9 = let arg_9 =
(match node.["timeZone"] with (match node.["timeZone"] with
@@ -281,7 +287,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_8 = let arg_8 =
GymLocation.jsonParse ( GymLocation.jsonParse (
@@ -329,7 +335,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_4 = let arg_4 =
(match node.["phoneNumber"] with (match node.["phoneNumber"] with
@@ -341,7 +347,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_3 = let arg_3 =
GymAddress.jsonParse ( GymAddress.jsonParse (
@@ -365,7 +371,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_1 = let arg_1 =
(match node.["id"] with (match node.["id"] with
@@ -377,7 +383,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_0 = let arg_0 =
(match node.["name"] with (match node.["name"] with
@@ -389,7 +395,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Name = arg_0 Name = arg_0
@@ -424,7 +430,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_13 = let arg_13 =
(match node.["suspendedReason"] with (match node.["suspendedReason"] with
@@ -436,7 +442,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_12 = let arg_12 =
(match node.["membershipLevel"] with (match node.["membershipLevel"] with
@@ -448,7 +454,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_11 = let arg_11 =
(match node.["membershipName"] with (match node.["membershipName"] with
@@ -460,7 +466,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_10 = let arg_10 =
(match node.["postCode"] with (match node.["postCode"] with
@@ -472,7 +478,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_9 = let arg_9 =
(match node.["mobileNumber"] with (match node.["mobileNumber"] with
@@ -484,7 +490,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_8 = let arg_8 =
(match node.["dateofBirth"] with (match node.["dateofBirth"] with
@@ -509,7 +515,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_6 = let arg_6 =
(match node.["emailAddress"] with (match node.["emailAddress"] with
@@ -521,7 +527,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_5 = let arg_5 =
(match node.["homeGymName"] with (match node.["homeGymName"] with
@@ -533,7 +539,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_4 = let arg_4 =
(match node.["homeGymId"] with (match node.["homeGymId"] with
@@ -545,7 +551,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_3 = let arg_3 =
(match node.["lastName"] with (match node.["lastName"] with
@@ -557,7 +563,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_2 = let arg_2 =
(match node.["firstName"] with (match node.["firstName"] with
@@ -569,7 +575,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_1 = let arg_1 =
(match node.["compoundMemberId"] with (match node.["compoundMemberId"] with
@@ -581,7 +587,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["id"] with (match node.["id"] with
@@ -593,7 +599,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
Id = arg_0 Id = arg_0
@@ -629,7 +635,7 @@ module GymAttendance =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_7 = let arg_7 =
(match node.["lastRefreshedPeopleInClasses"] with (match node.["lastRefreshedPeopleInClasses"] with
@@ -680,12 +686,12 @@ module GymAttendance =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_3 = 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<System.String> () |> Some
let arg_2 = let arg_2 =
(match node.["totalPeopleInClasses"] with (match node.["totalPeopleInClasses"] with
@@ -697,7 +703,7 @@ module GymAttendance =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_1 = let arg_1 =
(match node.["totalPeopleInGym"] with (match node.["totalPeopleInGym"] with
@@ -709,7 +715,7 @@ module GymAttendance =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_0 = let arg_0 =
(match node.["description"] with (match node.["description"] with
@@ -721,7 +727,7 @@ module GymAttendance =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Description = arg_0 Description = arg_0
@@ -764,7 +770,7 @@ module MemberActivityDto =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_3 = let arg_3 =
(match node.["totalClasses"] with (match node.["totalClasses"] with
@@ -776,7 +782,7 @@ module MemberActivityDto =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_2 = let arg_2 =
(match node.["totalVisits"] with (match node.["totalVisits"] with
@@ -788,7 +794,7 @@ module MemberActivityDto =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_1 = let arg_1 =
(match node.["averageDuration"] with (match node.["averageDuration"] with
@@ -800,7 +806,7 @@ module MemberActivityDto =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_0 = let arg_0 =
(match node.["totalDuration"] with (match node.["totalDuration"] with
@@ -812,7 +818,7 @@ module MemberActivityDto =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
TotalDuration = arg_0 TotalDuration = arg_0
@@ -839,7 +845,7 @@ module SessionsAggregate =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_1 = let arg_1 =
(match node.["Visits"] with (match node.["Visits"] with
@@ -851,7 +857,7 @@ module SessionsAggregate =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_0 = let arg_0 =
(match node.["Activities"] with (match node.["Activities"] with
@@ -863,7 +869,7 @@ module SessionsAggregate =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
Activities = arg_0 Activities = arg_0
@@ -887,7 +893,7 @@ module VisitGym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_1 = let arg_1 =
(match node.["Name"] with (match node.["Name"] with
@@ -899,7 +905,7 @@ module VisitGym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["Id"] with (match node.["Id"] with
@@ -911,7 +917,7 @@ module VisitGym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
Id = arg_0 Id = arg_0
@@ -947,7 +953,7 @@ module Visit =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_1 = let arg_1 =
(match node.["StartTime"] with (match node.["StartTime"] with
@@ -972,7 +978,7 @@ module Visit =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
{ {
IsDurationEstimated = arg_0 IsDurationEstimated = arg_0

View File

@@ -302,7 +302,7 @@ module PureGymApi =
v.AsObject () v.AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) let key = (kvp.Key)
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> Map.ofSeq |> Map.ofSeq

View File

@@ -21,69 +21,69 @@ module InnerTypeWithBothJsonSerializeExtension =
let node = System.Text.Json.Nodes.JsonObject () let node = System.Text.Json.Nodes.JsonObject ()
do do
node.Add (("it's-a-me"), System.Text.Json.Nodes.JsonValue.Create<Guid> input.Thing) node.Add (("it's-a-me"), (input.Thing |> System.Text.Json.Nodes.JsonValue.Create<Guid>))
node.Add ( node.Add (
"map", "map",
(fun field -> (input.Map
let ret = System.Text.Json.Nodes.JsonObject () |> (fun field ->
let ret = System.Text.Json.Nodes.JsonObject ()
for (KeyValue (key, value)) in field do for (KeyValue (key, value)) in field do
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value) ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<Uri> value)
ret ret
) ))
input.Map
) )
node.Add ( node.Add (
"readOnlyDict", "readOnlyDict",
(fun field -> (input.ReadOnlyDict
let ret = System.Text.Json.Nodes.JsonObject () |> (fun field ->
let ret = System.Text.Json.Nodes.JsonObject ()
for (KeyValue (key, value)) in field do for (KeyValue (key, value)) in field do
ret.Add ( ret.Add (
key.ToString (), key.ToString (),
(fun field -> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray () let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<char> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<char> mem)
arr arr
) )
value value
) )
ret ret
) ))
input.ReadOnlyDict
) )
node.Add ( node.Add (
"dict", "dict",
(fun field -> (input.Dict
let ret = System.Text.Json.Nodes.JsonObject () |> (fun field ->
let ret = System.Text.Json.Nodes.JsonObject ()
for (KeyValue (key, value)) in field do for (KeyValue (key, value)) in field do
ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<bool> value) ret.Add (key.ToString (), System.Text.Json.Nodes.JsonValue.Create<bool> value)
ret ret
) ))
input.Dict
) )
node.Add ( node.Add (
"concreteDict", "concreteDict",
(fun field -> (input.ConcreteDict
let ret = System.Text.Json.Nodes.JsonObject () |> (fun field ->
let ret = System.Text.Json.Nodes.JsonObject ()
for (KeyValue (key, value)) in field do for (KeyValue (key, value)) in field do
ret.Add (key.ToString (), InnerTypeWithBoth.toJsonNode value) ret.Add (key.ToString (), InnerTypeWithBoth.toJsonNode value)
ret ret
) ))
input.ConcreteDict
) )
node :> _ node :> _
@@ -93,6 +93,24 @@ open System
open System.Collections.Generic open System.Collections.Generic
open System.Text.Json.Serialization open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the SomeEnum type
[<AutoOpen>]
module SomeEnumJsonSerializeExtension =
/// Extension methods for JSON parsing
type SomeEnum with
/// Serialize to a JSON node
static member toJsonNode (input : SomeEnum) : System.Text.Json.Nodes.JsonNode =
match input with
| SomeEnum.Blah -> System.Text.Json.Nodes.JsonValue.Create 1
| SomeEnum.Thing -> System.Text.Json.Nodes.JsonValue.Create 0
| v -> failwith (sprintf "Unrecognised value for enum: %O" v)
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the JsonRecordTypeWithBoth type /// Module containing JSON serializing extension members for the JsonRecordTypeWithBoth type
[<AutoOpen>] [<AutoOpen>]
module JsonRecordTypeWithBothJsonSerializeExtension = module JsonRecordTypeWithBothJsonSerializeExtension =
@@ -104,48 +122,92 @@ module JsonRecordTypeWithBothJsonSerializeExtension =
let node = System.Text.Json.Nodes.JsonObject () let node = System.Text.Json.Nodes.JsonObject ()
do do
node.Add ("a", System.Text.Json.Nodes.JsonValue.Create<int> input.A) node.Add ("a", (input.A |> System.Text.Json.Nodes.JsonValue.Create<int>))
node.Add ("b", System.Text.Json.Nodes.JsonValue.Create<string> input.B) node.Add ("b", (input.B |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ( node.Add (
"c", "c",
(fun field -> (input.C
let arr = System.Text.Json.Nodes.JsonArray () |> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
arr arr
) ))
input.C
) )
node.Add ("d", InnerTypeWithBoth.toJsonNode input.D) node.Add ("d", (input.D |> InnerTypeWithBoth.toJsonNode))
node.Add ( node.Add (
"e", "e",
(fun field -> (input.E
let arr = System.Text.Json.Nodes.JsonArray () |> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem)
arr arr
) ))
input.E
) )
node.Add ( node.Add (
"f", "arr",
(fun field -> (input.Arr
let arr = System.Text.Json.Nodes.JsonArray () |> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
arr arr
) ))
input.F )
node.Add ("byte", (input.Byte |> System.Text.Json.Nodes.JsonValue.Create<byte<measure>>))
node.Add ("sbyte", (input.Sbyte |> System.Text.Json.Nodes.JsonValue.Create<sbyte<measure>>))
node.Add ("i", (input.I |> System.Text.Json.Nodes.JsonValue.Create<int<measure>>))
node.Add ("i32", (input.I32 |> System.Text.Json.Nodes.JsonValue.Create<int32<measure>>))
node.Add ("i64", (input.I64 |> System.Text.Json.Nodes.JsonValue.Create<int64<measure>>))
node.Add ("u", (input.U |> System.Text.Json.Nodes.JsonValue.Create<uint<measure>>))
node.Add ("u32", (input.U32 |> System.Text.Json.Nodes.JsonValue.Create<uint32<measure>>))
node.Add ("u64", (input.U64 |> System.Text.Json.Nodes.JsonValue.Create<uint64<measure>>))
node.Add ("f", (input.F |> System.Text.Json.Nodes.JsonValue.Create<float<measure>>))
node.Add ("f32", (input.F32 |> System.Text.Json.Nodes.JsonValue.Create<float32<measure>>))
node.Add ("single", (input.Single |> System.Text.Json.Nodes.JsonValue.Create<single<measure>>))
node.Add (
"intMeasureOption",
(input.IntMeasureOption
|> (fun field ->
match field with
| None -> null :> System.Text.Json.Nodes.JsonNode
| Some field ->
(System.Text.Json.Nodes.JsonValue.Create<int<measure>> field)
:> System.Text.Json.Nodes.JsonNode
))
)
node.Add (
"intMeasureNullable",
(input.IntMeasureNullable
|> (fun field ->
if field.HasValue then
System.Text.Json.Nodes.JsonValue.Create<int<measure>> field.Value
:> System.Text.Json.Nodes.JsonNode
else
null :> System.Text.Json.Nodes.JsonNode
))
)
node.Add ("enum", (input.Enum |> SomeEnum.toJsonNode))
node.Add (
"timestamp",
(input.Timestamp
|> (fun field -> field.ToString "o" |> System.Text.Json.Nodes.JsonValue.Create<string>))
) )
node :> _ node :> _
@@ -180,6 +242,55 @@ module FirstDuJsonSerializeExtension =
node.Add ("data", dataNode) node.Add ("data", dataNode)
node :> _ node :> _
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the HeaderAndValue type
[<AutoOpen>]
module HeaderAndValueJsonSerializeExtension =
/// Extension methods for JSON parsing
type HeaderAndValue with
/// Serialize to a JSON node
static member toJsonNode (input : HeaderAndValue) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do
node.Add ("header", (input.Header |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ("value", (input.Value |> System.Text.Json.Nodes.JsonValue.Create<string>))
node :> _
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the Foo type
[<AutoOpen>]
module FooJsonSerializeExtension =
/// Extension methods for JSON parsing
type Foo with
/// Serialize to a JSON node
static member toJsonNode (input : Foo) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do
node.Add (
"message",
(input.Message
|> (fun field ->
match field with
| None -> null :> System.Text.Json.Nodes.JsonNode
| Some field -> HeaderAndValue.toJsonNode field
))
)
node :> _
namespace ConsumePlugin namespace ConsumePlugin
@@ -221,7 +332,7 @@ module InnerTypeWithBothJsonParseExtension =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) |> System.Uri let key = (kvp.Key) |> System.Uri
let value = (kvp.Value).AsValue().GetValue<bool> () let value = (kvp.Value).AsValue().GetValue<System.Boolean> ()
key, value key, value
) )
|> dict |> dict
@@ -287,6 +398,24 @@ module InnerTypeWithBothJsonParseExtension =
} }
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing extension members for the SomeEnum type
[<AutoOpen>]
module SomeEnumJsonParseExtension =
/// Extension methods for JSON parsing
type SomeEnum with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : SomeEnum =
match node.GetValueKind () with
| System.Text.Json.JsonValueKind.Number -> node.AsValue().GetValue<int> () |> enum<SomeEnum>
| System.Text.Json.JsonValueKind.String ->
match node.AsValue().GetValue<string>().ToLowerInvariant () with
| "blah" -> SomeEnum.Blah
| "thing" -> SomeEnum.Thing
| v -> failwith ("Unrecognised value for enum: %i" + v)
| _ -> failwith ("Unrecognised kind for enum of type: " + "SomeEnum")
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the JsonRecordTypeWithBoth type /// Module containing JSON parsing extension members for the JsonRecordTypeWithBoth type
[<AutoOpen>] [<AutoOpen>]
module JsonRecordTypeWithBothJsonParseExtension = module JsonRecordTypeWithBothJsonParseExtension =
@@ -295,7 +424,74 @@ 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 arg_5 = let arg_20 =
(match node.["timestamp"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("timestamp")
)
)
| v -> v)
.AsValue()
.GetValue<string> ()
|> System.DateTimeOffset.Parse
let arg_19 =
SomeEnum.jsonParse (
match node.["enum"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("enum")
)
)
| v -> v
)
let arg_18 =
match node.["intMeasureNullable"] with
| null -> System.Nullable ()
| v ->
v.AsValue().GetValue<System.Int32> ()
|> LanguagePrimitives.Int32WithMeasure
|> System.Nullable
let arg_17 =
match node.["intMeasureOption"] with
| null -> None
| v ->
v.AsValue().GetValue<System.Int32> ()
|> LanguagePrimitives.Int32WithMeasure
|> Some
let arg_16 =
(match node.["single"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("single")
)
)
| v -> v)
.AsValue()
.GetValue<System.Single> ()
|> LanguagePrimitives.Float32WithMeasure
let arg_15 =
(match node.["f32"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("f32")
)
)
| v -> v)
.AsValue()
.GetValue<System.Single> ()
|> LanguagePrimitives.Float32WithMeasure
let arg_14 =
(match node.["f"] with (match node.["f"] with
| null -> | null ->
raise ( raise (
@@ -303,9 +499,126 @@ module JsonRecordTypeWithBothJsonParseExtension =
sprintf "Required key '%s' not found on JSON object" ("f") sprintf "Required key '%s' not found on JSON object" ("f")
) )
) )
| v -> v)
.AsValue()
.GetValue<System.Double> ()
|> LanguagePrimitives.FloatWithMeasure
let arg_13 =
(match node.["u64"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("u64")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt64> ()
|> LanguagePrimitives.UInt64WithMeasure
let arg_12 =
(match node.["u32"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("u32")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt32> ()
|> LanguagePrimitives.UInt32WithMeasure
let arg_11 =
(match node.["u"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("u")
)
)
| v -> v)
.AsValue()
.GetValue<System.UInt32> ()
|> LanguagePrimitives.UInt32WithMeasure
let arg_10 =
(match node.["i64"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("i64")
)
)
| v -> v)
.AsValue()
.GetValue<System.Int64> ()
|> LanguagePrimitives.Int64WithMeasure
let arg_9 =
(match node.["i32"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("i32")
)
)
| v -> v)
.AsValue()
.GetValue<System.Int32> ()
|> LanguagePrimitives.Int32WithMeasure
let arg_8 =
(match node.["i"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("i")
)
)
| v -> v)
.AsValue()
.GetValue<System.Int32> ()
|> LanguagePrimitives.Int32WithMeasure
let arg_7 =
(match node.["sbyte"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("sbyte")
)
)
| v -> v)
.AsValue()
.GetValue<System.SByte> ()
|> LanguagePrimitives.SByteWithMeasure
let arg_6 =
(match node.["byte"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("byte")
)
)
| v -> v)
.AsValue()
.GetValue<System.Byte> ()
|> LanguagePrimitives.ByteWithMeasure
let arg_5 =
(match node.["arr"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("arr")
)
)
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|> Array.ofSeq |> Array.ofSeq
let arg_4 = let arg_4 =
@@ -318,7 +631,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|> Array.ofSeq |> Array.ofSeq
let arg_3 = let arg_3 =
@@ -343,7 +656,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<int> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.Int32> ())
|> List.ofSeq |> List.ofSeq
let arg_1 = let arg_1 =
@@ -356,7 +669,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["a"] with (match node.["a"] with
@@ -368,7 +681,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
A = arg_0 A = arg_0
@@ -376,7 +689,22 @@ module JsonRecordTypeWithBothJsonParseExtension =
C = arg_2 C = arg_2
D = arg_3 D = arg_3
E = arg_4 E = arg_4
F = arg_5 Arr = arg_5
Byte = arg_6
Sbyte = arg_7
I = arg_8
I32 = arg_9
I64 = arg_10
U = arg_11
U32 = arg_12
U64 = arg_13
F = arg_14
F32 = arg_15
Single = arg_16
IntMeasureOption = arg_17
IntMeasureNullable = arg_18
Enum = arg_19
Timestamp = arg_20
} }
namespace ConsumePlugin namespace ConsumePlugin
@@ -422,7 +750,7 @@ module FirstDuJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
) )
| "case2" -> | "case2" ->
let node = let node =
@@ -455,6 +783,62 @@ module FirstDuJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
) )
| v -> failwith ("Unrecognised 'type' field value: " + v) | v -> failwith ("Unrecognised 'type' field value: " + v)
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the HeaderAndValue type
[<AutoOpen>]
module HeaderAndValueJsonParseExtension =
/// Extension methods for JSON parsing
type HeaderAndValue with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : HeaderAndValue =
let arg_1 =
(match node.["value"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("value")
)
)
| v -> v)
.AsValue()
.GetValue<System.String> ()
let arg_0 =
(match node.["header"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("header")
)
)
| v -> v)
.AsValue()
.GetValue<System.String> ()
{
Header = arg_0
Value = arg_1
}
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the Foo type
[<AutoOpen>]
module FooJsonParseExtension =
/// Extension methods for JSON parsing
type Foo with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : Foo =
let arg_0 =
match node.["message"] with
| null -> None
| v -> HeaderAndValue.jsonParse v |> Some
{
Message = arg_0
}

View File

@@ -22,7 +22,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_9 = let arg_9 =
(match node.["orphan"] with (match node.["orphan"] with
@@ -34,7 +34,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_8 = let arg_8 =
(match node.["entity_id"] with (match node.["entity_id"] with
@@ -46,7 +46,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_7 = let arg_7 =
(match node.["token_type"] with (match node.["token_type"] with
@@ -58,7 +58,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_6 = let arg_6 =
(match node.["renewable"] with (match node.["renewable"] with
@@ -70,7 +70,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_5 = let arg_5 =
(match node.["lease_duration"] with (match node.["lease_duration"] with
@@ -82,7 +82,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_4 = let arg_4 =
(match node.["identity_policies"] with (match node.["identity_policies"] with
@@ -94,7 +94,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|> List.ofSeq |> List.ofSeq
let arg_3 = let arg_3 =
@@ -107,7 +107,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|> List.ofSeq |> List.ofSeq
let arg_2 = let arg_2 =
@@ -120,7 +120,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsArray () .AsArray ()
|> Seq.map (fun elt -> elt.AsValue().GetValue<string> ()) |> Seq.map (fun elt -> elt.AsValue().GetValue<System.String> ())
|> List.ofSeq |> List.ofSeq
let arg_1 = let arg_1 =
@@ -133,7 +133,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["client_token"] with (match node.["client_token"] with
@@ -145,7 +145,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
ClientToken = arg_0 ClientToken = arg_0
@@ -189,7 +189,7 @@ module JwtVaultResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_2 = let arg_2 =
(match node.["renewable"] with (match node.["renewable"] with
@@ -201,7 +201,7 @@ module JwtVaultResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_1 = let arg_1 =
(match node.["lease_id"] with (match node.["lease_id"] with
@@ -213,7 +213,7 @@ module JwtVaultResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["request_id"] with (match node.["request_id"] with
@@ -225,7 +225,7 @@ module JwtVaultResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
RequestId = arg_0 RequestId = arg_0
@@ -271,7 +271,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) let key = (kvp.Key)
let value = (kvp.Value).AsValue().GetValue<int> () let value = (kvp.Value).AsValue().GetValue<System.Int32> ()
key, value key, value
) )
|> Map.ofSeq |> Map.ofSeq
@@ -288,7 +288,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) |> System.Uri let key = (kvp.Key) |> System.Uri
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> dict |> dict
@@ -305,7 +305,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) |> System.Uri let key = (kvp.Key) |> System.Uri
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> readOnlyDict |> readOnlyDict
@@ -322,7 +322,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) let key = (kvp.Key)
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> Map.ofSeq |> Map.ofSeq
@@ -339,7 +339,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) let key = (kvp.Key)
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> Seq.map System.Collections.Generic.KeyValuePair |> Seq.map System.Collections.Generic.KeyValuePair
@@ -357,7 +357,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) let key = (kvp.Key)
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> dict |> dict
@@ -374,7 +374,7 @@ module JwtSecretResponse =
.AsObject () .AsObject ()
|> Seq.map (fun kvp -> |> Seq.map (fun kvp ->
let key = (kvp.Key) let key = (kvp.Key)
let value = (kvp.Value).AsValue().GetValue<string> () let value = (kvp.Value).AsValue().GetValue<System.String> ()
key, value key, value
) )
|> readOnlyDict |> readOnlyDict
@@ -389,7 +389,7 @@ module JwtSecretResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
let arg_2 = let arg_2 =
(match node.["renewable"] with (match node.["renewable"] with
@@ -401,7 +401,7 @@ module JwtSecretResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
let arg_1 = let arg_1 =
(match node.["lease_id"] with (match node.["lease_id"] with
@@ -413,7 +413,7 @@ module JwtSecretResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
let arg_0 = let arg_0 =
(match node.["request_id"] with (match node.["request_id"] with
@@ -425,7 +425,7 @@ module JwtSecretResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
RequestId = arg_0 RequestId = arg_0

View File

@@ -29,6 +29,28 @@ type JsonRecordType =
F : int[] F : int[]
} }
[<WoofWare.Myriad.Plugins.JsonParse>]
type internal InternalTypeNotExtension =
{
[<JsonPropertyName(Literals.something)>]
InternalThing : string
}
[<WoofWare.Myriad.Plugins.JsonSerialize>]
type internal InternalTypeNotExtensionSerial =
{
[<JsonPropertyName(Literals.something)>]
InternalThing2 : string
}
[<WoofWare.Myriad.Plugins.JsonParse true>]
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
type internal InternalTypeExtension =
{
[<JsonPropertyName(Literals.something)>]
ExternalThing : string
}
[<WoofWare.Myriad.Plugins.JsonParse true>] [<WoofWare.Myriad.Plugins.JsonParse true>]
type ToGetExtensionMethod = type ToGetExtensionMethod =
{ {

View File

@@ -19,13 +19,16 @@ type GymAccessOptions =
QrCodeAccess : bool QrCodeAccess : bool
} }
[<Measure>]
type measure
[<WoofWare.Myriad.Plugins.JsonParse>] [<WoofWare.Myriad.Plugins.JsonParse>]
type GymLocation = type GymLocation =
{ {
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>] [<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
Longitude : float Longitude : float
[<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>] [<JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)>]
Latitude : float Latitude : float<measure>
} }
[<WoofWare.Myriad.Plugins.JsonParse>] [<WoofWare.Myriad.Plugins.JsonParse>]

View File

@@ -16,6 +16,15 @@ type InnerTypeWithBoth =
ConcreteDict : Dictionary<string, InnerTypeWithBoth> ConcreteDict : Dictionary<string, InnerTypeWithBoth>
} }
[<WoofWare.Myriad.Plugins.JsonParse true>]
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
type SomeEnum =
| Blah = 1
| Thing = 0
[<Measure>]
type measure
[<WoofWare.Myriad.Plugins.JsonParse true>] [<WoofWare.Myriad.Plugins.JsonParse true>]
[<WoofWare.Myriad.Plugins.JsonSerialize true>] [<WoofWare.Myriad.Plugins.JsonSerialize true>]
type JsonRecordTypeWithBoth = type JsonRecordTypeWithBoth =
@@ -25,7 +34,22 @@ type JsonRecordTypeWithBoth =
C : int list C : int list
D : InnerTypeWithBoth D : InnerTypeWithBoth
E : string array E : string array
F : int[] Arr : int[]
Byte : byte<measure>
Sbyte : sbyte<measure>
I : int<measure>
I32 : int32<measure>
I64 : int64<measure>
U : uint<measure>
U32 : uint32<measure>
U64 : uint64<measure>
F : float<measure>
F32 : float32<measure>
Single : single<measure>
IntMeasureOption : int<measure> option
IntMeasureNullable : int<measure> Nullable
Enum : SomeEnum
Timestamp : DateTimeOffset
} }
[<WoofWare.Myriad.Plugins.JsonSerialize true>] [<WoofWare.Myriad.Plugins.JsonSerialize true>]
@@ -34,3 +58,18 @@ type FirstDu =
| EmptyCase | EmptyCase
| Case1 of data : string | Case1 of data : string
| Case2 of record : JsonRecordTypeWithBoth * i : int | Case2 of record : JsonRecordTypeWithBoth * i : int
[<WoofWare.Myriad.Plugins.JsonParse true>]
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
type HeaderAndValue =
{
Header : string
Value : string
}
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
[<WoofWare.Myriad.Plugins.JsonParse true>]
type Foo =
{
Message : HeaderAndValue option
}

View File

@@ -10,7 +10,7 @@
<WarnOn>FS3388,FS3559</WarnOn> <WarnOn>FS3388,FS3559</WarnOn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all"/> <PackageReference Include="Nerdbank.GitVersioning" Version="3.6.139" PrivateAssets="all"/>
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/> <SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="'$(GITHUB_ACTION)' != ''"> <PropertyGroup Condition="'$(GITHUB_ACTION)' != ''">

View File

@@ -8,23 +8,20 @@
Some helpers in [Myriad](https://github.com/MoiraeSoftware/myriad/) which might be useful. Some helpers in [Myriad](https://github.com/MoiraeSoftware/myriad/) which might be useful.
These are currently somewhat experimental, and I personally am their primary customer. Currently implemented:
The `RemoveOptions` generator in particular is extremely half-baked.
* `JsonParse` (to stamp out `jsonParse : JsonNode -> 'T` methods).
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods).
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
* `GenerateMock` (to stamp out a record type corresponding to an interface, like a compile-time [Foq](https://github.com/fsprojects/Foq)).
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
* `RemoveOptions` (to strip `option` modifiers from a type) - this one is particularly half-baked!
If you would like to ensure that your particular use-case remains unbroken, please do contribute tests to this repository. If you would like to ensure that your particular use-case remains unbroken, please do contribute tests to this repository.
The `ConsumePlugin` assembly contains a number of invocations of these source generators, The `ConsumePlugin` assembly contains a number of invocations of these source generators,
so you just need to add copies of your types to that assembly to ensure that I will at least notice if I break the build; so you just need to add copies of your types to that assembly to ensure that I will at least notice if I break the build;
and if you add tests to `WoofWare.Myriad.Plugins.Test` then I will also notice if I break the runtime semantics of the generated code. and if you add tests to `WoofWare.Myriad.Plugins.Test` then I will also notice if I break the runtime semantics of the generated code.
Currently implemented:
* `JsonParse` (to stamp out `jsonParse : JsonNode -> 'T` methods);
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods);
* `RemoveOptions` (to strip `option` modifiers from a type).
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
* `GenerateMock` (to stamp out a record type corresponding to an interface).
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
## `JsonParse` ## `JsonParse`
Takes records like this: Takes records like this:

View File

@@ -12,7 +12,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.40" /> <PackageReference Include="ApiSurface" Version="4.0.43" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.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"/>

View File

@@ -58,7 +58,7 @@ module PureGymDtos =
[ [
"""{"latitude": 1.0, "longitude": 3.0}""", """{"latitude": 1.0, "longitude": 3.0}""",
{ {
GymLocation.Latitude = 1.0 GymLocation.Latitude = 1.0<measure>
Longitude = 3.0 Longitude = 3.0
} }
] ]
@@ -96,7 +96,7 @@ module PureGymDtos =
Location = Location =
{ {
Longitude = -0.110252 Longitude = -0.110252
Latitude = 51.480401 Latitude = 51.480401<measure>
} }
TimeZone = "Europe/London" TimeZone = "Europe/London"
ReopenDate = "2021-04-12T00:00:00+01 Europe/London" ReopenDate = "2021-04-12T00:00:00+01 Europe/London"

View File

@@ -49,3 +49,15 @@ module TestJsonParse =
let actual = s |> JsonNode.Parse |> InnerType.jsonParse let actual = s |> JsonNode.Parse |> InnerType.jsonParse
actual |> shouldEqual expected actual |> shouldEqual expected
[<TestCase("thing", SomeEnum.Thing)>]
[<TestCase("Thing", SomeEnum.Thing)>]
[<TestCase("THING", SomeEnum.Thing)>]
[<TestCase("blah", SomeEnum.Blah)>]
[<TestCase("Blah", SomeEnum.Blah)>]
[<TestCase("BLAH", SomeEnum.Blah)>]
let ``Can deserialise enum`` (str : string, expected : SomeEnum) =
sprintf "\"%s\"" str
|> JsonNode.Parse
|> SomeEnum.jsonParse
|> shouldEqual expected

View File

@@ -77,7 +77,22 @@ module TestJsonSerde =
let! depth = Gen.choose (0, 2) let! depth = Gen.choose (0, 2)
let! d = innerGen depth let! d = innerGen depth
let! e = Gen.arrayOf Arb.generate<NonNull<string>> let! e = Gen.arrayOf Arb.generate<NonNull<string>>
let! f = Gen.arrayOf Arb.generate<int> let! arr = Gen.arrayOf Arb.generate<int>
let! byte = Arb.generate
let! sbyte = Arb.generate
let! i = Arb.generate
let! i32 = Arb.generate
let! i64 = Arb.generate
let! u = Arb.generate
let! u32 = Arb.generate
let! u64 = Arb.generate
let! f = Arb.generate |> Gen.filter (fun s -> Double.IsFinite (s / 1.0<measure>))
let! f32 = Arb.generate |> Gen.filter (fun s -> Single.IsFinite (s / 1.0f<measure>))
let! single = Arb.generate |> Gen.filter (fun s -> Single.IsFinite (s / 1.0f<measure>))
let! intMeasureOption = Arb.generate
let! intMeasureNullable = Arb.generate
let! someEnum = Gen.choose (0, 1)
let! timestamp = Arb.generate
return return
{ {
@@ -86,7 +101,22 @@ module TestJsonSerde =
C = c C = c
D = d D = d
E = e |> Array.map _.Get E = e |> Array.map _.Get
Arr = arr
Byte = byte
Sbyte = sbyte
I = i
I32 = i32
I64 = i64
U = u
U32 = u32
U64 = u64
F = f F = f
F32 = f32
Single = single
IntMeasureOption = intMeasureOption
IntMeasureNullable = intMeasureNullable
Enum = enum<SomeEnum> someEnum
Timestamp = timestamp
} }
} }
@@ -104,6 +134,80 @@ module TestJsonSerde =
property |> Prop.forAll (Arb.fromGen outerGen) |> Check.QuickThrowOnFailure property |> Prop.forAll (Arb.fromGen outerGen) |> Check.QuickThrowOnFailure
[<Test>]
let ``Single example of big record`` () =
let guid = Guid.Parse "dfe24db5-9f8d-447b-8463-4c0bcf1166d5"
let data =
{
A = 3
B = "hello!"
C = [ 1 ; -9 ]
D =
{
Thing = guid
Map = Map.ofList []
ReadOnlyDict = readOnlyDict []
Dict = dict []
ConcreteDict = Dictionary ()
}
E = [| "I'm-a-string" |]
Arr = [| -18883 ; 9100 |]
Byte = 87uy<measure>
Sbyte = 89y<measure>
I = 199993345<measure>
I32 = -485832<measure>
I64 = -13458625689L<measure>
U = 458582u<measure>
U32 = 857362147u<measure>
U64 = 1234567892123414596UL<measure>
F = 8833345667.1<measure>
F32 = 1000.98f<measure>
Single = 0.334f<measure>
IntMeasureOption = Some 981<measure>
IntMeasureNullable = Nullable -883<measure>
Enum = enum<SomeEnum> 1
Timestamp = DateTimeOffset (2024, 07, 01, 17, 54, 00, TimeSpan.FromHours 1.0)
}
let expected =
"""{
"a": 3,
"b": "hello!",
"c": [1, -9],
"d": {
"it\u0027s-a-me": "dfe24db5-9f8d-447b-8463-4c0bcf1166d5",
"map": {},
"readOnlyDict": {},
"dict": {},
"concreteDict": {}
},
"e": ["I\u0027m-a-string"],
"arr": [-18883, 9100],
"byte": 87,
"sbyte": 89,
"i": 199993345,
"i32": -485832,
"i64": -13458625689,
"u": 458582,
"u32": 857362147,
"u64": 1234567892123414596,
"f": 8833345667.1,
"f32": 1000.98,
"single": 0.334,
"intMeasureOption": 981,
"intMeasureNullable": -883,
"enum": 1,
"timestamp": "2024-07-01T17:54:00.0000000\u002B01:00"
}
"""
|> fun s -> s.ToCharArray ()
|> Array.filter (fun c -> not (Char.IsWhiteSpace c))
|> fun s -> new String (s)
JsonRecordTypeWithBoth.toJsonNode(data).ToJsonString () |> shouldEqual expected
JsonRecordTypeWithBoth.jsonParse (JsonNode.Parse expected) |> shouldEqual data
[<Test>] [<Test>]
let ``Guids are treated just like strings`` () = let ``Guids are treated just like strings`` () =
let guidStr = "b1e7496e-6e79-4158-8579-a01de355d3b2" let guidStr = "b1e7496e-6e79-4158-8579-a01de355d3b2"
@@ -140,8 +244,7 @@ module TestJsonSerde =
} }
let sanitiseRec (r : JsonRecordTypeWithBoth) : JsonRecordTypeWithBoth = let sanitiseRec (r : JsonRecordTypeWithBoth) : JsonRecordTypeWithBoth =
{ { r with
A = r.A
B = if isNull r.B then "<null>" else r.B B = if isNull r.B then "<null>" else r.B
C = C =
if Object.ReferenceEquals (r.C, (null : obj)) then if Object.ReferenceEquals (r.C, (null : obj)) then
@@ -150,11 +253,11 @@ module TestJsonSerde =
r.C r.C
D = sanitiseInner r.D D = sanitiseInner r.D
E = if isNull r.E then [||] else r.E E = if isNull r.E then [||] else r.E
F = Arr =
if Object.ReferenceEquals (r.F, (null : obj)) then if Object.ReferenceEquals (r.Arr, (null : obj)) then
[||] [||]
else else
r.F r.Arr
} }
let duGen = let duGen =

View File

@@ -33,7 +33,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.40"/> <PackageReference Include="ApiSurface" Version="4.0.43"/>
<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.10.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>

View File

@@ -96,6 +96,11 @@ type internal AdtProduct =
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal AstHelper = module internal AstHelper =
let isEnum (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) : bool =
match repr with
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum _, _) -> true
| _ -> false
let instantiateRecord (fields : (RecordFieldName * SynExpr option) list) : SynExpr = let instantiateRecord (fields : (RecordFieldName * SynExpr option) list) : SynExpr =
let fields = let fields =
fields fields

View File

@@ -1070,17 +1070,11 @@ module internal CataGenerator =
body body
|> SynExpr.createLet |> SynExpr.createLet
[ [
SynExpr.TypeApp ( (SynExpr.createIdent "ResizeArray")
SynExpr.createIdent "ResizeArray", |> SynExpr.typeApp
range0,
[ [
SynType.var (SynTypar.SynTypar (unionCase.GenericName, TyparStaticReq.None, false)) SynType.var (SynTypar.SynTypar (unionCase.GenericName, TyparStaticReq.None, false))
], ]
[],
Some range0,
range0,
range0
)
|> SynExpr.applyTo (SynExpr.CreateConst ()) |> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic [ unionCase.StackName ] [] |> SynBinding.basic [ unionCase.StackName ] []
] ]

View File

@@ -449,7 +449,7 @@ module internal HttpClientGenerator =
SynExpr.createNew SynExpr.createNew
(SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ]) (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ])
(SynExpr.createIdent' bodyParamName (SynExpr.createIdent' bodyParamName
|> SynExpr.pipeThroughFunction (JsonSerializeGenerator.serializeNode ty) |> SynExpr.pipeThroughFunction (fst (JsonSerializeGenerator.serializeNode ty))
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.createLambda SynExpr.createLambda
"node" "node"

View File

@@ -24,7 +24,7 @@ module internal JsonParseGenerator =
JsonNumberHandlingArg = None JsonNumberHandlingArg = None
} }
/// (match {indexed} with | null -> raise (System.Collections.Generic.KeyNotFoundException ()) | v -> v) /// (match {indexed} with | null -> raise (System.Collections.Generic.KeyNotFoundException ({propertyName} not found)) | v -> v)
let assertNotNull (propertyName : SynExpr) (indexed : SynExpr) = let assertNotNull (propertyName : SynExpr) (indexed : SynExpr) =
let raiseExpr = let raiseExpr =
SynExpr.applyFunction SynExpr.applyFunction
@@ -95,25 +95,8 @@ module internal JsonParseGenerator =
) )
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ collectionType ; "ofSeq" ]) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ collectionType ; "ofSeq" ])
/// match {node} with | null -> None | v -> {body} |> Some let dotParse (typeName : LongIdent) : LongIdent =
/// Use the variable `v` to get access to the `Some`. List.append typeName [ Ident.create "Parse" ]
let createParseLineOption (node : SynExpr) (body : SynExpr) : SynExpr =
let body = SynExpr.pipeThroughFunction (SynExpr.createIdent "Some") body
[
SynMatchClause.create SynPat.createNull (SynExpr.createIdent "None")
SynMatchClause.create (SynPat.named "v") body
]
|> SynExpr.createMatch node
/// Given e.g. "float", returns "System.Double.Parse"
let parseFunction (typeName : string) : LongIdent =
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.
@@ -140,6 +123,47 @@ module internal JsonParseGenerator =
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."
let private parseNumberType
(options : JsonParseOption)
(propertyName : SynExpr option)
(node : SynExpr)
(typeName : LongIdent)
=
let basic = asValueGetValueIdent propertyName typeName node
match options.JsonNumberHandlingArg with
| None -> basic
| Some option ->
let cond =
SynExpr.DotGet (SynExpr.createIdent "exc", range0, SynLongIdent.createS "Message", range0)
|> SynExpr.callMethodArg "Contains" (SynExpr.CreateConst "cannot be converted to")
let handler =
asValueGetValue propertyName "string" node
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (typeName |> dotParse))
|> SynExpr.ifThenElse
(SynExpr.equals
option
(SynExpr.createLongIdent
[
"System"
"Text"
"Json"
"Serialization"
"JsonNumberHandling"
"AllowReadingFromString"
]))
SynExpr.reraise
|> SynExpr.ifThenElse cond SynExpr.reraise
basic
|> SynExpr.pipeThroughTryWith
(SynPat.IsInst (
SynType.LongIdent (SynLongIdent.createS' [ "System" ; "InvalidOperationException" ]),
range0
))
handler
/// Given `node.["town"]`, for example, choose how to obtain a JSON value from it. /// Given `node.["town"]`, for example, choose how to obtain a JSON value from it.
/// The property name is used in error messages at runtime to show where a JSON /// The property name is used in error messages at runtime to show where a JSON
/// parse error occurred; supply `None` to indicate "don't validate". /// parse error occurred; supply `None` to indicate "don't validate".
@@ -168,45 +192,36 @@ module internal JsonParseGenerator =
node node
|> asValueGetValue propertyName "string" |> asValueGetValue propertyName "string"
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ]) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTime" ; "Parse" ])
| NumberType typeName -> | DateTimeOffset ->
let basic = asValueGetValue propertyName typeName node node
|> asValueGetValue propertyName "string"
match options.JsonNumberHandlingArg with |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "DateTimeOffset" ; "Parse" ])
| None -> basic | NumberType typeName -> parseNumberType options propertyName node typeName
| Some option ->
let cond =
SynExpr.DotGet (SynExpr.createIdent "exc", range0, SynLongIdent.createS "Message", range0)
|> SynExpr.callMethodArg "Contains" (SynExpr.CreateConst "cannot be converted to")
let handler =
asValueGetValue propertyName "string" node
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (parseFunction typeName))
|> SynExpr.ifThenElse
(SynExpr.equals
option
(SynExpr.createLongIdent
[
"System"
"Text"
"Json"
"Serialization"
"JsonNumberHandling"
"AllowReadingFromString"
]))
SynExpr.reraise
|> SynExpr.ifThenElse cond SynExpr.reraise
basic
|> SynExpr.pipeThroughTryWith
(SynPat.IsInst (
SynType.LongIdent (SynLongIdent.createS' [ "System" ; "InvalidOperationException" ]),
range0
))
handler
| PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node | PrimitiveType typeName -> asValueGetValueIdent propertyName typeName node
| OptionType ty -> | OptionType ty ->
parseNode None options ty (SynExpr.createIdent "v") let someClause =
|> createParseLineOption node parseNode None options ty (SynExpr.createIdent "v")
|> SynExpr.pipeThroughFunction (SynExpr.createIdent "Some")
|> SynMatchClause.create (SynPat.named "v")
[
SynMatchClause.create SynPat.createNull (SynExpr.createIdent "None")
someClause
]
|> SynExpr.createMatch node
| NullableType ty ->
let someClause =
parseNode None options ty (SynExpr.createIdent "v")
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Nullable" ])
|> SynMatchClause.create (SynPat.named "v")
[
SynMatchClause.create
SynPat.createNull
(SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Nullable" ]) (SynExpr.CreateConst ()))
someClause
]
|> SynExpr.createMatch node
| ListType ty -> | ListType ty ->
parseNode None options ty (SynExpr.createIdent "elt") parseNode None options ty (SynExpr.createIdent "elt")
|> asArrayMapped propertyName "List" node |> asArrayMapped propertyName "List" node
@@ -261,6 +276,9 @@ module internal JsonParseGenerator =
|> SynExpr.callMethod "ToJsonString" |> SynExpr.callMethod "ToJsonString"
|> SynExpr.paren |> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Numerics" ; "BigInteger" ; "Parse" ]) |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "Numerics" ; "BigInteger" ; "Parse" ])
| Measure (_measure, primType) ->
parseNumberType options propertyName node primType
|> SynExpr.pipeThroughFunction (Measure.getLanguagePrimitivesMeasure primType)
| _ -> | _ ->
// 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 =
@@ -474,11 +492,64 @@ module internal JsonParseGenerator =
|> SynBinding.basic [ Ident.create "ty" ] [] |> SynBinding.basic [ Ident.create "ty" ] []
] ]
let createEnumMaker
(spec : JsonParseOutputSpec)
(typeName : LongIdent)
(fields : (Ident * SynExpr) list)
: SynExpr
=
let numberKind =
[ "System" ; "Text" ; "Json" ; "JsonValueKind" ; "Number" ]
|> List.map Ident.create
let stringKind =
[ "System" ; "Text" ; "Json" ; "JsonValueKind" ; "String" ]
|> List.map Ident.create
let fail =
SynExpr.plus
(SynExpr.CreateConst "Unrecognised kind for enum of type: ")
(SynExpr.CreateConst (typeName |> List.map _.idText |> String.concat "."))
|> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createIdent "failwith")
let failString =
SynExpr.plus (SynExpr.CreateConst "Unrecognised value for enum: %i") (SynExpr.createIdent "v")
|> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createIdent "failwith")
let parseString =
fields
|> List.map (fun (ident, _) ->
SynMatchClause.create
(SynPat.createConst (
SynConst.String (ident.idText.ToLowerInvariant (), SynStringKind.Regular, range0)
))
(SynExpr.createLongIdent' (typeName @ [ ident ]))
)
|> fun l -> l @ [ SynMatchClause.create (SynPat.named "v") failString ]
|> SynExpr.createMatch (
asValueGetValue None "string" (SynExpr.createIdent "node")
|> SynExpr.callMethod "ToLowerInvariant"
)
[
SynMatchClause.create
(SynPat.identWithArgs numberKind (SynArgPats.create []))
(asValueGetValue None "int" (SynExpr.createIdent "node")
|> SynExpr.pipeThroughFunction (
SynExpr.typeApp [ SynType.createLongIdent typeName ] (SynExpr.createIdent "enum")
))
SynMatchClause.create (SynPat.identWithArgs stringKind (SynArgPats.create [])) parseString
SynMatchClause.create (SynPat.named "_") fail
]
|> SynExpr.createMatch (SynExpr.callMethod "GetValueKind" (SynExpr.createIdent "node"))
let createModule (namespaceId : LongIdent) (spec : JsonParseOutputSpec) (typeDefn : SynTypeDefn) = let createModule (namespaceId : LongIdent) (spec : JsonParseOutputSpec) (typeDefn : SynTypeDefn) =
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) = let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
typeDefn typeDefn
let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, _access, _)) = let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, access, _)) =
synComponentInfo synComponentInfo
let attributes = let attributes =
@@ -517,6 +588,7 @@ module internal JsonParseGenerator =
let info = let info =
SynComponentInfo.createLong moduleName SynComponentInfo.createLong moduleName
|> SynComponentInfo.withDocString xmlDoc |> SynComponentInfo.withDocString xmlDoc
|> SynComponentInfo.setAccessibility access
|> SynComponentInfo.addAttributes attributes |> SynComponentInfo.addAttributes attributes
let decl = let decl =
@@ -533,6 +605,13 @@ module internal JsonParseGenerator =
|> List.map SynUnionCase.extract |> List.map SynUnionCase.extract
|> List.map (UnionCase.mapIdentFields optionGet) |> List.map (UnionCase.mapIdentFields optionGet)
|> createUnionMaker spec ident |> createUnionMaker spec ident
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum (cases, _range), _) ->
cases
|> List.map (fun c ->
match c with
| SynEnumCase.SynEnumCase (_, SynIdent.SynIdent (ident, _), value, _, _, _) -> ident, value
)
|> createEnumMaker spec ident
| _ -> failwithf "Not a record or union type" | _ -> failwithf "Not a record or union type"
[ scaffolding spec ident decl ] [ scaffolding spec ident decl ]
@@ -554,20 +633,21 @@ type JsonParseGenerator () =
let ast, _ = let ast, _ =
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
let recordsAndUnions = let relevantTypes =
Ast.extractTypeDefn ast Ast.extractTypeDefn ast
|> List.map (fun (name, defns) -> |> List.map (fun (name, defns) ->
defns defns
|> List.choose (fun defn -> |> List.choose (fun defn ->
if Ast.isRecord defn then Some defn if Ast.isRecord defn then Some defn
elif Ast.isDu defn then Some defn elif Ast.isDu defn then Some defn
elif AstHelper.isEnum defn then Some defn
else None else None
) )
|> fun defns -> name, defns |> fun defns -> name, defns
) )
let namespaceAndTypes = let namespaceAndTypes =
recordsAndUnions relevantTypes
|> List.choose (fun (ns, types) -> |> List.choose (fun (ns, types) ->
types types
|> List.choose (fun typeDef -> |> List.choose (fun typeDef ->

View File

@@ -13,41 +13,65 @@ type internal JsonSerializeOutputSpec =
module internal JsonSerializeGenerator = module internal JsonSerializeGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
// identically equal to null. We have to work around this later, but we might as well just
// be efficient here and whip up the null directly.
let private jsonNull () =
SynExpr.createNull ()
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
/// Given `input.Ident`, for example, choose how to add it to the ambient `node`. /// Given `input.Ident`, for example, choose how to add it to the ambient `node`.
/// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`. /// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`.
let rec serializeNode (fieldType : SynType) : SynExpr = /// Returns also a bool which is true if the resulting SynExpr represents something of type JsonNode.
let rec serializeNode (fieldType : SynType) : SynExpr * bool =
// TODO: serialization format for DateTime etc // TODO: serialization format for DateTime etc
match fieldType with match fieldType with
| DateOnly | DateOnly
| DateTime | DateTime
| NumberType _ | NumberType _
| Measure _
| PrimitiveType _ | PrimitiveType _
| Guid | Guid
| Uri -> | Uri ->
// JsonValue.Create<type> // JsonValue.Create<type>
SynExpr.TypeApp ( SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ], |> SynExpr.typeApp [ fieldType ]
range0, |> fun e -> e, false
[ fieldType ], | DateTimeOffset ->
[], // fun field -> field.ToString("o") |> JsonValue.Create<string>
Some range0, let create =
range0, SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
range0 |> SynExpr.typeApp [ SynType.named "string" ]
)
SynExpr.createIdent "field"
|> SynExpr.callMethodArg "ToString" (SynExpr.CreateConst "o")
|> SynExpr.pipeThroughFunction create
|> SynExpr.createLambda "field"
|> fun e -> e, false
| NullableType ty ->
// fun field -> if field.HasValue then {serializeNode ty} field.Value else JsonValue.Create null
let inner, innerIsJsonNode = serializeNode ty
SynExpr.applyFunction inner (SynExpr.createLongIdent [ "field" ; "Value" ])
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|> SynExpr.ifThenElse (SynExpr.createLongIdent [ "field" ; "HasValue" ]) (jsonNull ())
|> SynExpr.createLambda "field"
|> fun e -> e, innerIsJsonNode
| OptionType ty -> | OptionType ty ->
// fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field // fun field -> match field with | None -> JsonValue.Create null | Some v -> {serializeNode ty} field
let noneClause = let noneClause = jsonNull () |> SynMatchClause.create (SynPat.named "None")
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
// identically equal to null. We have to work around this later, but we might as well just
// be efficient here and whip up the null directly.
SynExpr.createNull ()
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|> SynMatchClause.create (SynPat.named "None")
let someClause = let someClause =
SynExpr.applyFunction (serializeNode ty) (SynExpr.createIdent "field") let inner, innerIsJsonNode = serializeNode ty
|> SynExpr.paren let target = SynExpr.applyFunction inner (SynExpr.createIdent "field")
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
if innerIsJsonNode then
target
else
target
|> SynExpr.paren
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
|> SynMatchClause.create ( |> SynMatchClause.create (
SynPat.identWithArgs [ Ident.create "Some" ] (SynArgPats.create [ Ident.create "field" ]) SynPat.identWithArgs [ Ident.create "Some" ] (SynArgPats.create [ Ident.create "field" ])
) )
@@ -55,6 +79,7 @@ module internal JsonSerializeGenerator =
[ noneClause ; someClause ] [ noneClause ; someClause ]
|> SynExpr.createMatch (SynExpr.createIdent "field") |> SynExpr.createMatch (SynExpr.createIdent "field")
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, true
| ArrayType ty | ArrayType ty
| ListType ty -> | ListType ty ->
// fun field -> // fun field ->
@@ -71,7 +96,7 @@ module internal JsonSerializeGenerator =
SynExpr.createIdent "field", SynExpr.createIdent "field",
SynExpr.applyFunction SynExpr.applyFunction
(SynExpr.createLongIdent [ "arr" ; "Add" ]) (SynExpr.createLongIdent [ "arr" ; "Add" ])
(SynExpr.paren (SynExpr.applyFunction (serializeNode ty) (SynExpr.createIdent "mem"))), (SynExpr.paren (SynExpr.applyFunction (fst (serializeNode ty)) (SynExpr.createIdent "mem"))),
range0 range0
) )
SynExpr.createIdent "arr" SynExpr.createIdent "arr"
@@ -84,6 +109,7 @@ module internal JsonSerializeGenerator =
|> SynBinding.basic [ Ident.create "arr" ] [] |> SynBinding.basic [ Ident.create "arr" ] []
] ]
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, false
| IDictionaryType (_keyType, valueType) | IDictionaryType (_keyType, valueType)
| DictionaryType (_keyType, valueType) | DictionaryType (_keyType, valueType)
| IReadOnlyDictionaryType (_keyType, valueType) | IReadOnlyDictionaryType (_keyType, valueType)
@@ -111,7 +137,7 @@ module internal JsonSerializeGenerator =
[ [
SynExpr.createLongIdent [ "key" ; "ToString" ] SynExpr.createLongIdent [ "key" ; "ToString" ]
|> SynExpr.applyTo (SynExpr.CreateConst ()) |> SynExpr.applyTo (SynExpr.CreateConst ())
SynExpr.applyFunction (serializeNode valueType) (SynExpr.createIdent "value") SynExpr.applyFunction (fst (serializeNode valueType)) (SynExpr.createIdent "value")
]), ]),
range0 range0
) )
@@ -125,6 +151,7 @@ module internal JsonSerializeGenerator =
|> SynBinding.basic [ Ident.create "ret" ] [] |> SynBinding.basic [ Ident.create "ret" ] []
] ]
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, false
| _ -> | _ ->
// {type}.toJsonNode // {type}.toJsonNode
let typeName = let typeName =
@@ -132,16 +159,17 @@ 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' (typeName @ [ Ident.create "toJsonNode" ]) SynExpr.createLongIdent' (typeName @ [ Ident.create "toJsonNode" ]), true
/// propertyName is probably a string literal, but it could be a [<Literal>] variable /// propertyName is probably a string literal, but it could be a [<Literal>] variable
/// `node.Add ({propertyName}, {toJsonNode})` /// `node.Add ({propertyName}, {toJsonNode})`
let createSerializeRhsRecord (propertyName : SynExpr) (fieldId : Ident) (fieldType : SynType) : SynExpr = let createSerializeRhsRecord (propertyName : SynExpr) (fieldId : Ident) (fieldType : SynType) : SynExpr =
[ [
propertyName propertyName
SynExpr.applyFunction SynExpr.pipeThroughFunction
(serializeNode fieldType) (fst (serializeNode fieldType))
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ]) (SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ])
|> SynExpr.paren
] ]
|> SynExpr.tuple |> SynExpr.tuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ]) |> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
@@ -228,8 +256,7 @@ module internal JsonSerializeGenerator =
|> SynBinding.withXmlDoc xmlDoc |> SynBinding.withXmlDoc xmlDoc
|> SynModuleDecl.createLet |> SynModuleDecl.createLet
let recordModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (fields : SynField list) = let recordModule (spec : JsonSerializeOutputSpec) (_typeName : LongIdent) (fields : SynField list) =
let inputArg = Ident.create "input"
let fields = fields |> List.map SynField.extractWithIdent let fields = fields |> List.map SynField.extractWithIdent
fields fields
@@ -239,7 +266,6 @@ module internal JsonSerializeGenerator =
) )
|> SynExpr.sequential |> SynExpr.sequential
|> fun expr -> SynExpr.Do (expr, range0) |> fun expr -> SynExpr.Do (expr, range0)
|> scaffolding spec typeName inputArg
let unionModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (cases : SynUnionCase list) = let unionModule (spec : JsonSerializeOutputSpec) (typeName : LongIdent) (cases : SynUnionCase list) =
let inputArg = Ident.create "input" let inputArg = Ident.create "input"
@@ -285,7 +311,7 @@ module internal JsonSerializeGenerator =
let propertyName = getPropertyName (Option.get fieldData.Ident) fieldData.Attrs let propertyName = getPropertyName (Option.get fieldData.Ident) fieldData.Attrs
let node = let node =
SynExpr.applyFunction (serializeNode fieldData.Type) (SynExpr.createIdent' caseName) SynExpr.applyFunction (fst (serializeNode fieldData.Type)) (SynExpr.createIdent' caseName)
[ propertyName ; node ] [ propertyName ; node ]
|> SynExpr.tuple |> SynExpr.tuple
@@ -312,7 +338,68 @@ module internal JsonSerializeGenerator =
SynMatchClause.create pattern action SynMatchClause.create pattern action
) )
|> SynExpr.createMatch (SynExpr.createIdent' inputArg) |> SynExpr.createMatch (SynExpr.createIdent' inputArg)
|> scaffolding spec typeName inputArg
let enumModule
(spec : JsonSerializeOutputSpec)
(typeName : LongIdent)
(cases : (Ident * SynExpr) list)
: SynModuleDecl
=
let fail =
SynExpr.CreateConst "Unrecognised value for enum: %O"
|> SynExpr.applyFunction (SynExpr.createIdent "sprintf")
|> SynExpr.applyTo (SynExpr.createIdent "v")
|> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.createIdent "failwith")
let body =
cases
|> List.map (fun (caseName, value) ->
value
|> SynExpr.applyFunction (
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ]
)
|> SynMatchClause.create (SynPat.identWithArgs (typeName @ [ caseName ]) (SynArgPats.create []))
)
|> fun l -> l @ [ SynMatchClause.create (SynPat.named "v") fail ]
|> SynExpr.createMatch (SynExpr.createIdent "input")
let xmlDoc = PreXmlDoc.create "Serialize to a JSON node"
let returnInfo =
SynLongIdent.createS' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|> SynType.LongIdent
let functionName = Ident.create "toJsonNode"
let pattern =
SynPat.named "input"
|> SynPat.annotateType (SynType.LongIdent (SynLongIdent.create typeName))
if spec.ExtensionMethods then
let componentInfo =
SynComponentInfo.createLong typeName
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for JSON parsing")
let memberDef =
body
|> SynBinding.basic [ functionName ] [ pattern ]
|> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo
|> SynMemberDefn.staticMember
let containingType =
SynTypeDefnRepr.augmentation ()
|> SynTypeDefn.create componentInfo
|> SynTypeDefn.withMemberDefns [ memberDef ]
SynModuleDecl.Types ([ containingType ], range0)
else
body
|> SynBinding.basic [ functionName ] [ pattern ]
|> SynBinding.withReturnAnnotation returnInfo
|> SynBinding.withXmlDoc xmlDoc
|> SynModuleDecl.createLet
let createModule let createModule
(namespaceId : LongIdent) (namespaceId : LongIdent)
@@ -323,7 +410,7 @@ module internal JsonSerializeGenerator =
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) = let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) =
typeDefn typeDefn
let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, _access, _)) = let (SynComponentInfo (_attributes, _typeParams, _constraints, ident, _, _preferPostfix, access, _)) =
synComponentInfo synComponentInfo
let attributes = let attributes =
@@ -362,19 +449,29 @@ module internal JsonSerializeGenerator =
let info = let info =
SynComponentInfo.createLong moduleName SynComponentInfo.createLong moduleName
|> SynComponentInfo.addAttributes attributes |> SynComponentInfo.addAttributes attributes
|> SynComponentInfo.setAccessibility access
|> SynComponentInfo.withDocString xmlDoc |> SynComponentInfo.withDocString xmlDoc
let decls = let decls =
match synTypeDefnRepr with match synTypeDefnRepr with
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _range), _) -> | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, recordFields, _range), _) ->
[ recordModule spec ident recordFields ] recordModule spec ident recordFields
|> scaffolding spec ident (Ident.create "input")
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, unionFields, _range), _) -> | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, unionFields, _range), _) ->
[ unionModule spec ident unionFields ] unionModule spec ident unionFields
| _ -> failwithf "Only record types currently supported." |> scaffolding spec ident (Ident.create "input")
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum (cases, _range), _) ->
cases
|> List.map (fun c ->
match c with
| SynEnumCase.SynEnumCase (_, SynIdent.SynIdent (ident, _), value, _, _, _) -> ident, value
)
|> enumModule spec ident
| ty -> failwithf "Unsupported type: got %O" ty
[ [
yield! opens |> List.map SynModuleDecl.openAny yield! opens |> List.map SynModuleDecl.openAny
yield SynModuleDecl.nestedModule info decls yield decls |> List.singleton |> SynModuleDecl.nestedModule info
] ]
|> SynModuleOrNamespace.createNamespace namespaceId |> SynModuleOrNamespace.createNamespace namespaceId
@@ -392,20 +489,21 @@ type JsonSerializeGenerator () =
let ast, _ = let ast, _ =
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
let recordsAndUnions = let relevantTypes =
Ast.extractTypeDefn ast Ast.extractTypeDefn ast
|> List.map (fun (name, defns) -> |> List.map (fun (name, defns) ->
defns defns
|> List.choose (fun defn -> |> List.choose (fun defn ->
if Ast.isRecord defn then Some defn if Ast.isRecord defn then Some defn
elif Ast.isDu defn then Some defn elif Ast.isDu defn then Some defn
elif AstHelper.isEnum defn then Some defn
else None else None
) )
|> fun defns -> name, defns |> fun defns -> name, defns
) )
let namespaceAndTypes = let namespaceAndTypes =
recordsAndUnions relevantTypes
|> List.choose (fun (ns, types) -> |> List.choose (fun (ns, types) ->
types types
|> List.choose (fun typeDef -> |> List.choose (fun typeDef ->

View File

@@ -0,0 +1,24 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
[<RequireQualifiedAccess>]
module internal Measure =
let getLanguagePrimitivesMeasure (typeName : LongIdent) : SynExpr =
match typeName |> List.map _.idText with
| [ "System" ; "Single" ] -> [ "LanguagePrimitives" ; "Float32WithMeasure" ]
| [ "System" ; "Double" ] -> [ "LanguagePrimitives" ; "FloatWithMeasure" ]
| [ "System" ; "Byte" ] -> [ "LanguagePrimitives" ; "ByteWithMeasure" ]
| [ "System" ; "SByte" ] -> [ "LanguagePrimitives" ; "SByteWithMeasure" ]
| [ "System" ; "Int16" ] -> [ "LanguagePrimitives" ; "Int16WithMeasure" ]
| [ "System" ; "Int32" ] -> [ "LanguagePrimitives" ; "Int32WithMeasure" ]
| [ "System" ; "Int64" ] -> [ "LanguagePrimitives" ; "Int64WithMeasure" ]
| [ "System" ; "UInt16" ] -> [ "LanguagePrimitives" ; "UInt16WithMeasure" ]
| [ "System" ; "UInt32" ] -> [ "LanguagePrimitives" ; "UInt32WithMeasure" ]
| [ "System" ; "UInt64" ] -> [ "LanguagePrimitives" ; "UInt64WithMeasure" ]
| l ->
let l = String.concat "." l
failwith $"unrecognised type for measure: %s{l}"
|> SynExpr.createLongIdent

View File

@@ -26,5 +26,7 @@ module internal Primitives =
| "uint64" -> [ "System" ; "UInt64" ] |> Some | "uint64" -> [ "System" ; "UInt64" ] |> Some
| "char" -> [ "System" ; "Char" ] |> Some | "char" -> [ "System" ; "Char" ] |> Some
| "decimal" -> [ "System" ; "Decimal" ] |> Some | "decimal" -> [ "System" ; "Decimal" ] |> Some
| "string" -> [ "System" ; "String" ] |> Some
| "bool" -> [ "System" ; "Boolean" ] |> Some
| _ -> None | _ -> None
|> Option.map (List.map (fun i -> (Ident (i, range0)))) |> Option.map (List.map (fun i -> (Ident (i, range0))))

View File

@@ -87,48 +87,52 @@ module internal SynExpr =
) )
|> applyTo b |> applyTo b
/// {a} * {b}
let times (a : SynExpr) (b : SynExpr) =
SynExpr.CreateAppInfix (
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
Ident.CreateLong "op_Multiply",
[],
[ Some (IdentTrivia.OriginalNotation "*") ]
)
),
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
/// {obj}.{meth} {arg} let dotGet (field : string) (obj : SynExpr) : SynExpr =
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr =
SynExpr.DotGet ( SynExpr.DotGet (
obj, obj,
range0, range0,
SynLongIdent.SynLongIdent (id = [ Ident.create meth ], dotRanges = [], trivia = [ None ]), SynLongIdent.SynLongIdent (id = [ Ident.create field ], dotRanges = [], trivia = [ None ]),
range0 range0
) )
|> applyTo arg
/// {obj}.{meth} {arg}
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr = dotGet meth obj |> applyTo arg
/// {obj}.{meth}() /// {obj}.{meth}()
let callMethod (meth : string) (obj : SynExpr) : SynExpr = let callMethod (meth : string) (obj : SynExpr) : SynExpr =
callMethodArg meth (SynExpr.CreateConst ()) obj callMethodArg meth (SynExpr.CreateConst ()) obj
let typeApp (types : SynType list) (operand : SynExpr) =
SynExpr.TypeApp (operand, range0, types, List.replicate (types.Length - 1) range0, Some range0, range0, range0)
let callGenericMethod (meth : string) (ty : LongIdent) (obj : SynExpr) : SynExpr = let callGenericMethod (meth : string) (ty : LongIdent) (obj : SynExpr) : SynExpr =
SynExpr.TypeApp ( SynExpr.DotGet (obj, range0, SynLongIdent.createS meth, range0)
SynExpr.DotGet (obj, range0, SynLongIdent.createS meth, range0), |> typeApp [ SynType.LongIdent (SynLongIdent.create ty) ]
range0,
[ SynType.LongIdent (SynLongIdent.create ty) ],
[],
Some range0,
range0,
range0
)
|> applyTo (SynExpr.CreateConst ()) |> 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.TypeApp ( SynExpr.DotGet (obj, range0, SynLongIdent.createS meth, range0)
SynExpr.DotGet (obj, range0, SynLongIdent.createS meth, range0), |> typeApp [ SynType.createLongIdent' [ ty ] ]
range0,
[ SynType.createLongIdent' [ ty ] ],
[],
Some range0,
range0,
range0
)
|> applyTo (SynExpr.CreateConst ()) |> applyTo (SynExpr.CreateConst ())
let inline index (property : SynExpr) (obj : SynExpr) : SynExpr = let inline index (property : SynExpr) (obj : SynExpr) : SynExpr =

View File

@@ -70,6 +70,12 @@ module internal SynLongIdent =
// TODO: consider Microsoft.FSharp.Option or whatever it is // TODO: consider Microsoft.FSharp.Option or whatever it is
| _ -> false | _ -> false
let isNullable (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "System" ; "Nullable" ]
| [ "Nullable" ] -> true
| _ -> false
let isResponse (ident : SynLongIdent) : bool = let isResponse (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with match ident.LongIdent |> List.map _.idText with
| [ "Response" ] | [ "Response" ]

View File

@@ -59,6 +59,12 @@ module internal SynTypePatterns =
Some innerType Some innerType
| _ -> None | _ -> None
let (|NullableType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isNullable ident ->
Some innerType
| _ -> None
let (|UnitType|_|) (fieldType : SynType) : unit option = let (|UnitType|_|) (fieldType : SynType) : unit option =
match fieldType with match fieldType with
| SynType.LongIdent ident when SynLongIdent.isUnit ident -> Some () | SynType.LongIdent ident when SynLongIdent.isUnit ident -> Some ()
@@ -193,10 +199,30 @@ module internal SynTypePatterns =
match fieldType with match fieldType with
| SynType.LongIdent ident -> | SynType.LongIdent ident ->
match ident.LongIdent with match ident.LongIdent with
| [ i ] -> [ "string" ; "float" ; "int" ; "bool" ] |> List.tryFind (fun s -> s = i.idText) | [ i ] ->
// We won't bother with the case that the user has done e.g. `Single` (relying on `System` being open).
match Primitives.qualifyType i.idText with
| Some qualified ->
match i.idText with
| "char"
| "string" -> None
| _ -> Some qualified
| None -> None
| _ -> None | _ -> None
| _ -> None | _ -> None
/// Returns the name of the measure, and the outer type.
let (|Measure|_|) (fieldType : SynType) : (Ident * LongIdent) option =
match fieldType with
| SynType.App (NumberType outer,
_,
[ SynType.LongIdent (SynLongIdent.SynLongIdent ([ ident ], _, _)) ],
_,
_,
_,
_) -> Some (ident, outer)
| _ -> None
let (|DateOnly|_|) (fieldType : SynType) = let (|DateOnly|_|) (fieldType : SynType) =
match fieldType with match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
@@ -215,6 +241,15 @@ module internal SynTypePatterns =
| _ -> None | _ -> None
| _ -> None | _ -> None
let (|DateTimeOffset|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "DateTimeOffset" ]
| [ "DateTimeOffset" ] -> Some ()
| _ -> None
| _ -> None
let (|Uri|_|) (fieldType : SynType) = let (|Uri|_|) (fieldType : SynType) =
match fieldType with match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->

View File

@@ -46,6 +46,7 @@
<Compile Include="SynExpr\SynAttribute.fs" /> <Compile Include="SynExpr\SynAttribute.fs" />
<Compile Include="SynExpr\SynModuleDecl.fs" /> <Compile Include="SynExpr\SynModuleDecl.fs" />
<Compile Include="SynExpr\SynModuleOrNamespace.fs" /> <Compile Include="SynExpr\SynModuleOrNamespace.fs" />
<Compile Include="Measure.fs" />
<Compile Include="AstHelper.fs" /> <Compile Include="AstHelper.fs" />
<Compile Include="RemoveOptionsGenerator.fs"/> <Compile Include="RemoveOptionsGenerator.fs"/>
<Compile Include="InterfaceMockGenerator.fs"/> <Compile Include="InterfaceMockGenerator.fs"/>

View File

@@ -4,10 +4,11 @@
"^refs/heads/main$" "^refs/heads/main$"
], ],
"pathFilters": [ "pathFilters": [
":/", "./",
":^WoofWare.Myriad.Plugins.Test/", ":/WoofWare.Myriad.Plugins.Attributes",
":^WoofWare.Myriad.Plugins.Attributes/Test/", "^:/WoofWare.Myriad.Plugins.Attributes/Test",
":^/.github/", ":/global.json",
":^/CHANGELOG.md" ":/README.md",
":/Directory.Build.props"
] ]
} }

View File

@@ -3,13 +3,13 @@
{fetchNuGet}: [ {fetchNuGet}: [
(fetchNuGet { (fetchNuGet {
pname = "ApiSurface"; pname = "ApiSurface";
version = "4.0.40"; version = "4.0.43";
sha256 = "1c9z0b6minlripwrjmv4yd5w8zj4lcpak4x41izh7ygx8kgmbvx0"; sha256 = "0bx09n3c751vz7v39pcdm6zb8k5p01fzc2vlclgkxg4nj38mmvh8";
}) })
(fetchNuGet { (fetchNuGet {
pname = "fantomas"; pname = "fantomas";
version = "6.3.7"; version = "6.3.9";
sha256 = "1z1a5bw7vwz6g8nvfgkvx66jnm4hmvn62vbyq0as60nw0jlvaidl"; sha256 = "1b34iiiff02bbzjv03zyna8xmrgs6y87zdvp5i5k58fcqpjw44sx";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Fantomas.Core"; pname = "Fantomas.Core";
@@ -76,11 +76,6 @@
version = "6.0.26"; version = "6.0.26";
sha256 = "02src68hd3213sd1a2ms1my7i92knfmdxclvv90il9cky2zsq8kw"; sha256 = "02src68hd3213sd1a2ms1my7i92knfmdxclvv90il9cky2zsq8kw";
}) })
(fetchNuGet {
pname = "Microsoft.Build.Tasks.Git";
version = "8.0.0";
sha256 = "0055f69q3hbagqp8gl3nk0vfn4qyqyxsxyy7pd0g7wm3z28byzmx";
})
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.CodeCoverage"; pname = "Microsoft.CodeCoverage";
version = "17.10.0"; version = "17.10.0";
@@ -156,16 +151,6 @@
version = "1.1.3"; version = "1.1.3";
sha256 = "05smkcyxir59rgrmp7d6327vvrlacdgldfxhmyr1azclvga1zfsq"; sha256 = "05smkcyxir59rgrmp7d6327vvrlacdgldfxhmyr1azclvga1zfsq";
}) })
(fetchNuGet {
pname = "Microsoft.SourceLink.Common";
version = "8.0.0";
sha256 = "0xrr8yd34ij7dqnyddkp2awfmf9qn3c89xmw2f3npaa4wnajmx81";
})
(fetchNuGet {
pname = "Microsoft.SourceLink.GitHub";
version = "8.0.0";
sha256 = "1gdx7n45wwia3yvang3ls92sk3wrymqcx9p349j8wba2lyjf9m44";
})
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.TestPlatform.ObjectModel"; pname = "Microsoft.TestPlatform.ObjectModel";
version = "17.10.0"; version = "17.10.0";
@@ -188,8 +173,8 @@
}) })
(fetchNuGet { (fetchNuGet {
pname = "Nerdbank.GitVersioning"; pname = "Nerdbank.GitVersioning";
version = "3.6.133"; version = "3.6.139";
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80"; sha256 = "0npcryhq3r0c2zi940jk39h13mzc4hyg7z8gm6jdmxi1aqv1vh8c";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NETStandard.Library"; pname = "NETStandard.Library";
@@ -208,33 +193,33 @@
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Common"; pname = "NuGet.Common";
version = "6.10.0"; version = "6.10.1";
sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh"; sha256 = "1z69k0j727jcwrxzmvnixdac84lb9706iabqs8mrns8j7kbmw1ns";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Configuration"; pname = "NuGet.Configuration";
version = "6.10.0"; version = "6.10.1";
sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz"; sha256 = "0qy2bdi3dz6fdw7qbv77fg956idm9d9733j8b1pcrcj9pfayys26";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Frameworks"; pname = "NuGet.Frameworks";
version = "6.10.0"; version = "6.10.1";
sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk"; sha256 = "1p8d701fhbqv2r8vqmj948af9xvz2fd3273803cdrjy3a2wykmq1";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Packaging"; pname = "NuGet.Packaging";
version = "6.10.0"; version = "6.10.1";
sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4"; sha256 = "0zl8xfzvd1yij2ln6iwy6cz8qfwlbyyqlin872ab5y58ws61a2x2";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Protocol"; pname = "NuGet.Protocol";
version = "6.10.0"; version = "6.10.1";
sha256 = "0hmv4q0ks9i34mfgpb13l01la9v3jjllfh1qd3aqv105xrqrdxac"; sha256 = "0zw5q1pn74qa1mcxx5268nklx18442s76x70dlqan4pm9kbvzr2i";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Versioning"; pname = "NuGet.Versioning";
version = "6.10.0"; version = "6.10.1";
sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g"; sha256 = "0lji7g6abnpmhzlgvni8wlb7l62n4180v3sphp4494wi0gn7ds4c";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NUnit"; pname = "NUnit";