Compare commits

..

75 Commits

Author SHA1 Message Date
Patrick Stevens
0a1783d6ed Support [<BasePath>] (#263) 2024-09-15 17:38:03 +00:00
Patrick Stevens
9a3ebbf28f Cope with unit type in JSON (#262) 2024-09-15 14:37:50 +00:00
Patrick Stevens
e22525c200 Interpret JsonExtensionData (#261) 2024-09-15 11:13:22 +01:00
Patrick Stevens
09b7109c84 Extract some utilities from http-client branch (#260) 2024-09-14 22:02:32 +00:00
Patrick Stevens
693b95106a Also pipe through parser in PositionalArgs true (#259) 2024-09-13 16:11:53 +00:00
Patrick Stevens
49ecfbf5e5 Fix includeFlagLike when arg doesn't have an equals (#257) 2024-09-12 22:10:08 +00:00
Patrick Stevens
5748ac3d5b Allow consuming *all* args as positionals, not just ones which look like --foo (#255) 2024-09-11 19:00:04 +00:00
Patrick Stevens
913959a740 Make arg parser more AOT-friendly (#253) 2024-09-10 22:16:43 +01:00
dependabot[bot]
93ffc065cd Bump Microsoft.NET.Test.Sdk from 17.11.0 to 17.11.1 (#248)
* Bump Microsoft.NET.Test.Sdk from 17.11.0 to 17.11.1

Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.11.0 to 17.11.1.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.11.0...v17.11.1)

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

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

* Bump fantomas from 6.3.11 to 6.3.12

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.11 to 6.3.12.
- [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.11...v6.3.12)

---
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-09-09 19:33:39 +00:00
dependabot[bot]
d14efba7e7 Bump actions/attest-build-provenance from 1.4.2 to 1.4.3 (#247) 2024-09-09 12:42:12 +01:00
patrick-conscriptus[bot]
f5cf0b79dd Automated commit (#246)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-09-08 01:20:34 +00:00
Patrick Stevens
029e3746bb Fix record/union impl accessibility (#245) 2024-09-07 08:49:28 +00:00
Patrick Stevens
8ae749c529 Add ArgumentLongForm (#244) 2024-09-05 21:26:52 +01:00
Patrick Stevens
e4cbab3209 Implement [<ArgumentFlag>] for two-case DUs (#242) 2024-09-04 21:48:36 +00:00
Patrick Stevens
bdce82fb7a Handle env vars with value 0 and 1 for bools (#241) 2024-09-04 21:06:22 +01:00
Patrick Stevens
8f9f933971 Handle URI in arg parser (#240) 2024-09-04 20:20:51 +01:00
Patrick Stevens
3a55ba1242 Allow positional args to be Choice<'a, 'a> to indicate whether they came before any positional marker (#238) 2024-09-04 20:09:40 +01:00
Patrick Stevens
047b2eda99 Throw on duplicated args (#237) 2024-09-04 08:39:19 +01:00
Patrick Stevens
2220f88053 Compose records in arg parser (#234) 2024-09-03 23:04:23 +00:00
dependabot[bot]
86b938c81e Bump Nerdbank.GitVersioning from 3.6.141 to 3.6.143 (#231) 2024-09-02 20:05:18 +01:00
patrick-conscriptus[bot]
1832a57bdf Automated commit (#230)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-09-01 01:25:31 +00:00
Patrick Stevens
38f4821fa4 Switch to centrally managed actions (#229) 2024-08-28 16:51:36 +00:00
dependabot[bot]
70aaf8c408 Bump actions/attest-build-provenance from 1.4.1 to 1.4.2 (#221)
* Bump actions/attest-build-provenance from 1.4.1 to 1.4.2

Bumps [actions/attest-build-provenance](https://github.com/actions/attest-build-provenance) from 1.4.1 to 1.4.2.
- [Release notes](https://github.com/actions/attest-build-provenance/releases)
- [Changelog](https://github.com/actions/attest-build-provenance/blob/main/RELEASE.md)
- [Commits](310b0a4a3b...6149ea5740)

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

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

* Bump Microsoft.NET.Test.Sdk from 17.10.0 to 17.11.0

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

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

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

* Bump ApiSurface from 4.0.44 to 4.1.2

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

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

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

* Bump NUnit from 4.1.0 to 4.2.1

Bumps [NUnit](https://github.com/nunit/nunit) from 4.1.0 to 4.2.1.
- [Release notes](https://github.com/nunit/nunit/releases)
- [Changelog](https://github.com/nunit/nunit/blob/main/CHANGES.md)
- [Commits](https://github.com/nunit/nunit/compare/4.1.0...4.2.1)

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

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

* Bump fsharp-analyzers from 0.26.1 to 0.27.0

Bumps [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK) from 0.26.1 to 0.27.0.
- [Release notes](https://github.com/ionide/FSharp.Analyzers.SDK/releases)
- [Changelog](https://github.com/ionide/FSharp.Analyzers.SDK/blob/main/CHANGELOG.md)
- [Commits](https://github.com/ionide/FSharp.Analyzers.SDK/compare/v0.26.1...v0.27.0)

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

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

* Bump fantomas from 6.3.10 to 6.3.11

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.10 to 6.3.11.
- [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.10...v6.3.11)

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

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

* Deps

* Bump analyzers too

---------

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-08-26 11:07:27 +00:00
Patrick Stevens
417ca45c37 Basic arg parser (#216) 2024-08-26 11:54:35 +01:00
Patrick Stevens
569b3cc553 Pull out general changes from ArgParser PR (#217) 2024-08-25 19:23:23 +00:00
patrick-conscriptus[bot]
20226b9da9 Automated commit (#215)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-08-25 01:17:27 +00:00
dependabot[bot]
f800e53bff Bump ApiSurface from 4.0.43 to 4.0.44 (#214)
* Bump ApiSurface from 4.0.43 to 4.0.44

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

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

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

* Fix deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-08-19 20:22:38 +01:00
patrick-conscriptus[bot]
5358f5da0e Automated commit (#213)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-08-18 08:29:45 +00:00
Patrick Stevens
a868b8c08e No-op change to test the pipeline (#212) 2024-08-13 22:06:54 +00:00
Patrick Stevens
a4f945a3ee Upgrade all-required-checks-complete (#211) 2024-08-13 23:03:27 +01:00
Patrick Stevens
8434730ba7 Migrate to dedicated publish action (#210) 2024-08-13 21:54:14 +00:00
dependabot[bot]
811026996c Bump fsharp-analyzers from 0.26.0 to 0.26.1 (#208)
* Bump Nerdbank.GitVersioning from 3.6.139 to 3.6.141

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

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

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

* Bump fsharp-analyzers from 0.26.0 to 0.26.1

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

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

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

* Bump deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-08-12 11:35:03 +00:00
dependabot[bot]
25b2b160bb Bump actions/attest-build-provenance from 1.4.0 to 1.4.1 (#206) 2024-08-12 11:27:09 +01:00
patrick-conscriptus[bot]
4679474604 Automated commit (#205)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-08-11 01:16:52 +00:00
dependabot[bot]
e16e241785 Bump actions/attest-build-provenance from 1.3.3 to 1.4.0 (#204) 2024-08-05 13:01:06 +01:00
Patrick Stevens
a52e4a46b0 Properly exclude test project (#203) 2024-08-04 19:42:15 +01:00
dependabot[bot]
f40a368948 Bump NUnit3TestAdapter from 4.5.0 to 4.6.0 (#201)
* Bump NUnit3TestAdapter from 4.5.0 to 4.6.0

Bumps [NUnit3TestAdapter](https://github.com/nunit/nunit3-vs-adapter) from 4.5.0 to 4.6.0.
- [Release notes](https://github.com/nunit/nunit3-vs-adapter/releases)
- [Commits](https://github.com/nunit/nunit3-vs-adapter/compare/V4.5.0...V4.6.0)

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

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

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-08-04 19:07:03 +01:00
patrick-conscriptus[bot]
adaee61fbf Automated commit (#202)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-08-04 01:15:45 +00:00
patrick-conscriptus[bot]
d388660bfe Automated commit (#200)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-07-28 01:14:36 +00:00
patrick-conscriptus[bot]
d0e9ba0efd Automated commit (#199)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-07-21 01:14:26 +00:00
dependabot[bot]
d7d6c57910 Bump fantomas from 6.3.9 to 6.3.10 (#198)
Bumps fantomas from 6.3.9 to 6.3.10.

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

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-07-15 17:44:58 +00:00
dependabot[bot]
98e52743f5 Bump actions/attest-build-provenance from 1.3.2 to 1.3.3 (#197) 2024-07-15 11:45:54 +01:00
patrick-conscriptus[bot]
896696e002 Automated commit (#196)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-07-14 01:14:38 +00:00
Patrick Stevens
654f760f3a Abstract away the required-checks feature (#194) 2024-07-12 20:02:04 +00:00
Patrick Stevens
31bd9e22f2 Shrink flake-updating action (#193) 2024-07-12 19:44:13 +01:00
Patrick Stevens
b7a240bbb9 Early out of flake update if there are no changes (#192) 2024-07-12 16:46:04 +00:00
Patrick Stevens
ebbe10ad81 Fix PR-raising logic (#191) 2024-07-12 16:36:56 +00:00
patrick-conscriptus[bot]
8f9af9af67 Upgrade Nix flake and deps (#190)
* Automated commit

---------

Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2024-07-12 16:32:21 +00:00
Patrick Stevens
2c7cd91cbc Fix default branch name bit of update workflow (#189) 2024-07-12 16:22:07 +00:00
Patrick Stevens
ffaa373da9 Fix flake update workflow (#184) 2024-07-12 16:07:31 +00:00
Patrick Stevens
9f8459a7d3 Add flake update workflow (#183) 2024-07-12 12:12:44 +01:00
Patrick Stevens
362542d5ee Make required status check actually required (#182) 2024-07-12 11:59:42 +01:00
Patrick Stevens
18309becbd Add reproducibility check (#181) 2024-07-11 00:39:31 +01:00
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
Patrick Stevens
18c7a2e920 Continuous integration to true (#161) 2024-06-09 10:56:11 +01:00
Patrick Stevens
f371ee59fe Say which mock function wasn't implemented (#160) 2024-06-04 18:36:49 +01:00
dependabot[bot]
f8296e54bc Bump fantomas from 6.3.4 to 6.3.7 (#158)
* Bump fantomas from 6.3.4 to 6.3.7

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

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

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

* Upgrade Fantomas

---------

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-03 18:31:24 +01:00
Patrick Stevens
adf497c5db Tidy up a bit more (#156) 2024-06-01 15:57:53 +01:00
Patrick Stevens
04ecbe6002 Simplify flake (#155) 2024-05-31 21:58:33 +01:00
Patrick Stevens
7b14e52e9d Use our DSLs a bit more (#154) 2024-05-31 19:20:28 +01:00
Patrick Stevens
8e47f39efc Make more extensive use of our own DSLs (#153) 2024-05-31 16:54:05 +00:00
Patrick Stevens
6942ba42b9 Update changelog (#152) 2024-05-30 22:37:05 +01:00
83 changed files with 12593 additions and 2931 deletions

View File

@@ -3,13 +3,13 @@
"isRoot": true, "isRoot": true,
"tools": { "tools": {
"fantomas": { "fantomas": {
"version": "6.3.4", "version": "6.3.12",
"commands": [ "commands": [
"fantomas" "fantomas"
] ]
}, },
"fsharp-analyzers": { "fsharp-analyzers": {
"version": "0.26.0", "version": "0.27.0",
"commands": [ "commands": [
"fsharp-analyzers" "fsharp-analyzers"
] ]

1
.fantomasignore Normal file
View File

@@ -0,0 +1 @@
.direnv/

View File

@@ -73,6 +73,8 @@ jobs:
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }} access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
- name: Build - name: Build
run: nix build run: nix build
- name: Reproducibility check
run: nix build --rebuild
check-dotnet-format: check-dotnet-format:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@@ -217,15 +219,60 @@ jobs:
all-required-checks-complete: all-required-checks-complete:
needs: [check-dotnet-format, check-nix-format, check-accurate-generations, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack, github-release-plugin-dry-run] needs: [check-dotnet-format, check-nix-format, check-accurate-generations, build, build-nix, linkcheck, flake-check, analyzers, nuget-pack, expected-pack, github-release-plugin-dry-run]
if: ${{ always() }}
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- run: echo "All required checks complete." - uses: G-Research/common-actions/check-required-lite@2b7dc49cb14f3344fbe6019c14a31165e258c059
with:
needs-context: ${{ toJSON(needs) }}
nuget-publish: attestation-attribute:
runs-on: ubuntu-latest
needs: [all-required-checks-complete]
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
permissions:
id-token: write
attestations: write
contents: read
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-attribute
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-path: "packed/*.nupkg"
attestation-plugin:
runs-on: ubuntu-latest
needs: [all-required-checks-complete]
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
permissions:
id-token: write
attestations: write
contents: read
steps:
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@1c608d11d69870c2092266b3f9a6f3abbf17002c # v1.4.3
with:
subject-path: "packed/*.nupkg"
nuget-publish-attribute:
runs-on: ubuntu-latest runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }} if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete] needs: [all-required-checks-complete]
environment: main-deploy environment: main-deploy
permissions:
id-token: write
attestations: write
contents: read
steps: steps:
- uses: actions/checkout@v4 - uses: actions/checkout@v4
- name: Install Nix - name: Install Nix
@@ -233,20 +280,55 @@ 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: Identify `dotnet`
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: dotnet-identify
run: nix develop --command bash -c 'echo "dotnet=$(which dotnet)" >> $GITHUB_OUTPUT'
- name: Publish to NuGet
id: publish-success
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
with:
package-name: WoofWare.Myriad.Plugins.Attributes
nuget-key: ${{ secrets.NUGET_API_KEY }}
nupkg-dir: packed/
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
nuget-publish-plugin:
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete]
environment: main-deploy
permissions:
id-token: write
attestations: write
contents: read
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@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: Identify `dotnet`
id: dotnet-identify
run: nix develop --command bash -c 'echo "dotnet=$(which dotnet)" >> $GITHUB_OUTPUT'
- name: Publish to NuGet
id: publish-success
uses: G-Research/common-actions/publish-nuget@2b7dc49cb14f3344fbe6019c14a31165e258c059
with:
package-name: WoofWare.Myriad.Plugins
nuget-key: ${{ secrets.NUGET_API_KEY }}
nupkg-dir: packed/
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
github-release-plugin: github-release-plugin:
runs-on: ubuntu-latest runs-on: ubuntu-latest

57
.github/workflows/flake_update.yaml vendored Normal file
View File

@@ -0,0 +1,57 @@
# yaml-language-server: $schema=https://raw.githubusercontent.com/SchemaStore/schemastore/master/src/schemas/json/github-workflow.json
name: Weekly Nix Flake Update
on:
schedule:
- cron: '0 0 * * 0' # Runs at 00:00 every Sunday
workflow_dispatch: # Allows manual triggering
jobs:
update-nix-flake:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Update Nix flake
run: 'nix flake update'
- name: Build passthru
run: 'nix build ".#default.passthru.fetch-deps"'
- name: Run passthru
run: |
set -o pipefail
./result | tee /tmp/passthru.txt
cp /"$(cat /tmp/passthru.txt | grep " wrote lockfile to " | cut -d / -f 2-)" nix/deps.nix
- name: Format
run: 'nix develop --command alejandra .'
- name: Create token
id: generate-token
uses: actions/create-github-app-token@v1
with:
# https://github.com/actions/create-github-app-token/issues/136
app-id: ${{ secrets.APP_ID }}
private-key: ${{ secrets.APP_PRIVATE_KEY }}
- name: Raise pull request
uses: Smaug123/commit-action@cc25e6d80a796c49669dda4a0aa36c54c573983d
id: cpr
with:
bearer-token: ${{ steps.generate-token.outputs.token }}
pr-title: "Upgrade Nix flake and deps"
- name: Enable Pull Request Automerge
if: ${{ steps.cpr.outputs.pull-request-number }}
uses: peter-evans/enable-pull-request-automerge@v3
with:
token: ${{ steps.generate-token.outputs.token }}
pull-request-number: ${{ steps.cpr.outputs.pull-request-number }}
merge-method: squash

26
.gitignore vendored
View File

@@ -1,12 +1,14 @@
bin/ bin/
obj/ obj/
/packages/ /packages/
riderModule.iml riderModule.iml
/_ReSharper.Caches/ /_ReSharper.Caches/
.idea/ .idea/
*.sln.DotSettings.user *.sln.DotSettings.user
.DS_Store .DS_Store
result result
.analyzerpackages/ .analyzerpackages/
analysis.sarif analysis.sarif
.direnv/ .direnv/
.venv/
.vs/

View File

@@ -1,5 +1,32 @@
Notable changes are recorded here. Notable changes are recorded here.
# WoofWare.Myriad.Plugins 3.0.1
Semantics of `HttpClient`'s URI component composition changed:
we now implicitly insert `/` characters after `[<BaseAddress>]` and `[<BasePath>]`, so that URI composition doesn't silently drop the last component if you didn't put a slash there.
# WoofWare.Myriad.Plugins 2.3.9
`JsonParse` and `JsonSerialize` now interpret `[<JsonExtensionData>]`, which must be on a `Dictionary<string, _>`; this collects any extra components that were present on the JSON object.
# WoofWare.Myriad.Plugins 2.2.1, WoofWare.Myriad.Plugins.Attributes 3.2.1
New generator: `ArgParser`, a basic reflection-free argument parser.
# WoofWare.Myriad.Plugins 2.1.45, WoofWare.Myriad.Plugins.Attributes 3.1.7
The NuGet packages are now attested to through [GitHub Attestations](https://github.blog/2024-05-02-introducing-artifact-attestations-now-in-public-beta/).
You can run `gh attestation verify ~/.nuget/packages/woofware.myriad.plugins/2.1.45/woofware.myriad.plugins.2.1.45.nupkg -o Smaug123`, for example, to verify with GitHub that the GitHub Actions pipeline on this repository produced a nupkg file with the same hash as the one you were served from NuGet.
# WoofWare.Myriad.Plugins 2.1.33
`JsonParse` can now deserialize the discriminated unions which `JsonSerialize` wrote out.
# WoofWare.Myriad.Plugins 2.1.32, WoofWare.Myriad.Plugins.Attributes 3.1.4
`JsonSerialize` can now serialize many discriminated unions.
(This operation is inherently opinionated, because JSON does not model discriminated unions.)
# WoofWare.Myriad.Plugins 2.1.20, WoofWare.Myriad.Plugins.Attributes 3.0.1 # WoofWare.Myriad.Plugins 2.1.20, WoofWare.Myriad.Plugins.Attributes 3.0.1
We now bundle copies of the RestEase attributes in `WoofWare.Myriad.Plugins.Attributes`, in case you don't want to take a dependency on RestEase. We now bundle copies of the RestEase attributes in `WoofWare.Myriad.Plugins.Attributes`, in case you don't want to take a dependency on RestEase.

237
ConsumePlugin/Args.fs Normal file
View File

@@ -0,0 +1,237 @@
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
[<ArgParser>]
type BasicNoPositionals =
{
Foo : int
Bar : string
Baz : bool
Rest : int list
}
[<ArgParser>]
type Basic =
{
[<ArgumentHelpText "This is a foo!">]
Foo : int
Bar : string
Baz : bool
[<ArgumentHelpText "Here's where the rest of the args go">]
[<PositionalArgs>]
Rest : string list
}
[<ArgParser>]
type BasicWithIntPositionals =
{
Foo : int
Bar : string
Baz : bool
[<PositionalArgs>]
Rest : int list
}
[<ArgParser>]
type LoadsOfTypes =
{
Foo : int
Bar : string
Baz : bool
SomeFile : FileInfo
SomeDirectory : DirectoryInfo
SomeList : DirectoryInfo list
OptionalThingWithNoDefault : int option
[<PositionalArgs>]
Positionals : int list
[<ArgumentDefaultFunction>]
OptionalThing : Choice<bool, bool>
[<ArgumentDefaultFunction>]
AnotherOptionalThing : Choice<int, int>
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
YetAnotherOptionalThing : Choice<string, string>
}
static member DefaultOptionalThing () = true
static member DefaultAnotherOptionalThing () = 3
[<ArgParser>]
type LoadsOfTypesNoPositionals =
{
Foo : int
Bar : string
Baz : bool
SomeFile : FileInfo
SomeDirectory : DirectoryInfo
SomeList : DirectoryInfo list
OptionalThingWithNoDefault : int option
[<ArgumentDefaultFunction>]
OptionalThing : Choice<bool, bool>
[<ArgumentDefaultFunction>]
AnotherOptionalThing : Choice<int, int>
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
YetAnotherOptionalThing : Choice<string, string>
}
static member DefaultOptionalThing () = false
static member DefaultAnotherOptionalThing () = 3
[<ArgParser true>]
type DatesAndTimes =
{
Plain : TimeSpan
[<InvariantCulture>]
Invariant : TimeSpan
[<ParseExact @"hh\:mm\:ss">]
[<ArgumentHelpText "An exact time please">]
Exact : TimeSpan
[<InvariantCulture ; ParseExact @"hh\:mm\:ss">]
InvariantExact : TimeSpan
}
type ChildRecord =
{
Thing1 : int
Thing2 : string
}
[<ArgParser true>]
type ParentRecord =
{
Child : ChildRecord
AndAnother : bool
}
type ChildRecordWithPositional =
{
Thing1 : int
[<PositionalArgs>]
Thing2 : Uri list
}
[<ArgParser true>]
type ParentRecordChildPos =
{
Child : ChildRecordWithPositional
AndAnother : bool
}
[<ArgParser true>]
type ParentRecordSelfPos =
{
Child : ChildRecord
[<PositionalArgs>]
AndAnother : bool list
}
[<ArgParser true>]
type ChoicePositionals =
{
[<PositionalArgs>]
Args : Choice<string, string> list
}
[<ArgParser true>]
type ContainsBoolEnvVar =
{
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
BoolVar : Choice<bool, bool>
}
[<RequireQualifiedAccess>]
module Consts =
[<Literal>]
let FALSE = false
[<Literal>]
let TRUE = true
type DryRunMode =
| [<ArgumentFlag(Consts.FALSE)>] Wet
| [<ArgumentFlag true>] Dry
[<ArgParser true>]
type WithFlagDu =
{
DryRun : DryRunMode
}
[<ArgParser true>]
type ContainsFlagEnvVar =
{
// This phrasing is odd, but it's for a test. Nobody's really going to have `--dry-run`
// controlled by an env var!
[<ArgumentDefaultEnvironmentVariable "CONSUMEPLUGIN_THINGS">]
DryRun : Choice<DryRunMode, DryRunMode>
}
[<ArgParser true>]
type ContainsFlagDefaultValue =
{
[<ArgumentDefaultFunction>]
DryRun : Choice<DryRunMode, DryRunMode>
}
static member DefaultDryRun () = DryRunMode.Wet
[<ArgParser true>]
type ManyLongForms =
{
[<ArgumentLongForm "do-something-else">]
[<ArgumentLongForm "anotherarg">]
DoTheThing : string
[<ArgumentLongForm "turn-it-on">]
[<ArgumentLongForm "dont-turn-it-off">]
SomeFlag : bool
}
[<RequireQualifiedAccess>]
type private IrrelevantDu =
| Foo
| Bar
[<ArgParser true>]
type FlagsIntoPositionalArgs =
{
A : string
[<PositionalArgs true>]
GrabEverything : string list
}
[<ArgParser true>]
type FlagsIntoPositionalArgsChoice =
{
A : string
[<PositionalArgs true>]
GrabEverything : Choice<string, string> list
}
[<ArgParser true>]
type FlagsIntoPositionalArgsInt =
{
A : string
[<PositionalArgs true>]
GrabEverything : int list
}
[<ArgParser true>]
type FlagsIntoPositionalArgsIntChoice =
{
A : string
[<PositionalArgs true>]
GrabEverything : Choice<int, int> list
}
[<ArgParser true>]
type FlagsIntoPositionalArgs' =
{
A : string
[<PositionalArgs false>]
DontGrabEverything : string list
}

View File

@@ -3,6 +3,7 @@
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<OtherFlags>--reflectionfree $(OtherFlags)</OtherFlags>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<MyriadSdkGenerator Include="$(MSBuildThisFileDirectory)..\WoofWare.Myriad.Plugins\bin\$(Configuration)\net6.0\WoofWare.Myriad.Plugins.dll"/> <MyriadSdkGenerator Include="$(MSBuildThisFileDirectory)..\WoofWare.Myriad.Plugins\bin\$(Configuration)\net6.0\WoofWare.Myriad.Plugins.dll"/>
@@ -51,14 +52,17 @@
<Compile Include="ListCata.fs"> <Compile Include="ListCata.fs">
<MyriadFile>List.fs</MyriadFile> <MyriadFile>List.fs</MyriadFile>
</Compile> </Compile>
<Compile Include="Args.fs" />
<Compile Include="GeneratedArgs.fs">
<MyriadFile>Args.fs</MyriadFile>
</Compile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="RestEase" Version="1.6.4"/> <PackageReference Include="RestEase" Version="1.6.4"/>
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj" /> <ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj" />
<ProjectReference Include="..\WoofWare.Myriad.Plugins\WoofWare.Myriad.Plugins.fsproj"/> <ProjectReference Include="..\WoofWare.Myriad.Plugins\WoofWare.Myriad.Plugins.fsproj" PrivateAssets="all" />
<PackageReference Include="Myriad.Sdk" Version="0.8.3"/> <PackageReference Include="Myriad.Sdk" Version="0.8.3" PrivateAssets="all" />
<PackageReference Include="Myriad.Core" Version="0.8.3"/>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -30,6 +30,12 @@ type ChocolateType =
| Milk | Milk
| SeventyPercent | SeventyPercent
override this.ToString () =
match this with
| ChocolateType.Dark -> "Dark"
| ChocolateType.Milk -> "Milk"
| ChocolateType.SeventyPercent -> "SeventyPercent"
type Chocolate = type Chocolate =
{ {
chocType : ChocolateType chocType : ChocolateType
@@ -43,6 +49,12 @@ type WrappingPaperStyle =
| HappyHolidays | HappyHolidays
| SolidColor | SolidColor
override this.ToString () =
match this with
| WrappingPaperStyle.HappyBirthday -> "HappyBirthday"
| WrappingPaperStyle.HappyHolidays -> "HappyHolidays"
| WrappingPaperStyle.SolidColor -> "SolidColor"
[<CreateCatamorphism "GiftCata">] [<CreateCatamorphism "GiftCata">]
type Gift = type Gift =
| Book of Book | Book of Book

File diff suppressed because it is too large Load Diff

View File

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

View File

@@ -41,7 +41,7 @@ module FileSystemItemCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__FileSystemItem (x) -> | Instruction.Process__FileSystemItem x ->
match x with match x with
| FileSystemItem.Directory ({ | FileSystemItem.Directory ({
Name = name Name = name
@@ -116,7 +116,7 @@ module GiftCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__Gift (x) -> | Instruction.Process__Gift x ->
match x with match x with
| Gift.Book (arg0_0) -> cata.Gift.Book arg0_0 |> giftStack.Add | Gift.Book (arg0_0) -> cata.Gift.Book arg0_0 |> giftStack.Add
| Gift.Chocolate (arg0_0) -> cata.Gift.Chocolate arg0_0 |> giftStack.Add | Gift.Chocolate (arg0_0) -> cata.Gift.Chocolate arg0_0 |> giftStack.Add
@@ -129,7 +129,7 @@ module GiftCata =
| Gift.WithACard (arg0_0, message) -> | Gift.WithACard (arg0_0, message) ->
instructions.Add (Instruction.Gift_WithACard (message)) instructions.Add (Instruction.Gift_WithACard (message))
instructions.Add (Instruction.Process__Gift arg0_0) instructions.Add (Instruction.Process__Gift arg0_0)
| Instruction.Gift_Wrapped (arg1_0) -> | Instruction.Gift_Wrapped arg1_0 ->
let arg0_0 = giftStack.[giftStack.Count - 1] let arg0_0 = giftStack.[giftStack.Count - 1]
giftStack.RemoveAt (giftStack.Count - 1) giftStack.RemoveAt (giftStack.Count - 1)
cata.Gift.Wrapped arg0_0 arg1_0 |> giftStack.Add cata.Gift.Wrapped arg0_0 arg1_0 |> giftStack.Add
@@ -137,7 +137,7 @@ module GiftCata =
let arg0_0 = giftStack.[giftStack.Count - 1] let arg0_0 = giftStack.[giftStack.Count - 1]
giftStack.RemoveAt (giftStack.Count - 1) giftStack.RemoveAt (giftStack.Count - 1)
cata.Gift.Boxed arg0_0 |> giftStack.Add cata.Gift.Boxed arg0_0 |> giftStack.Add
| Instruction.Gift_WithACard (message) -> | Instruction.Gift_WithACard message ->
let arg0_0 = giftStack.[giftStack.Count - 1] let arg0_0 = giftStack.[giftStack.Count - 1]
giftStack.RemoveAt (giftStack.Count - 1) giftStack.RemoveAt (giftStack.Count - 1)
cata.Gift.WithACard arg0_0 message |> giftStack.Add cata.Gift.WithACard arg0_0 message |> giftStack.Add

View File

@@ -4,12 +4,38 @@
//------------------------------------------------------------------------------ //------------------------------------------------------------------------------
namespace ConsumePlugin
open System.Text.Json.Serialization
/// Module containing JSON serializing methods for the InternalTypeNotExtensionSerial type
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module internal InternalTypeNotExtensionSerial =
/// Serialize to a JSON node
let toJsonNode (input : InternalTypeNotExtensionSerial) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do node.Add ((Literals.something), (input.InternalThing2 |> System.Text.Json.Nodes.JsonValue.Create<string>))
node :> _
namespace ConsumePlugin
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the InternalTypeExtension type
[<AutoOpen>]
module internal InternalTypeExtensionJsonSerializeExtension =
/// Extension methods for JSON parsing
type InternalTypeExtension with
/// Serialize to a JSON node
static member toJsonNode (input : InternalTypeExtension) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do node.Add ((Literals.something), (input.ExternalThing |> System.Text.Json.Nodes.JsonValue.Create<string>))
node :> _
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing methods for the InnerType type /// Module containing JSON parsing methods for the InnerType type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module InnerType = module InnerType =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerType = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : InnerType =
@@ -23,7 +49,7 @@ module InnerType =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Thing = arg_0 Thing = arg_0
@@ -31,8 +57,7 @@ module InnerType =
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing methods for the JsonRecordType type /// Module containing JSON parsing methods for the JsonRecordType type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module JsonRecordType = module JsonRecordType =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordType = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordType =
@@ -46,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 =
@@ -59,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 =
@@ -84,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 =
@@ -97,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
@@ -109,7 +134,7 @@ module JsonRecordType =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
A = arg_0 A = arg_0
@@ -121,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 =
@@ -273,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
@@ -345,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
@@ -370,7 +442,7 @@ module ToGetExtensionMethodJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Alpha = arg_0 Alpha = arg_0

View File

@@ -19,9 +19,9 @@ type internal PublicTypeMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PublicTypeMock = static member Empty : PublicTypeMock =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
} }
interface IPublicType with interface IPublicType with
@@ -44,9 +44,9 @@ type public PublicTypeInternalFalseMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PublicTypeInternalFalseMock = static member Empty : PublicTypeInternalFalseMock =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
} }
interface IPublicTypeInternalFalse with interface IPublicTypeInternalFalse with
@@ -68,8 +68,8 @@ type internal InternalTypeMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : InternalTypeMock = static member Empty : InternalTypeMock =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
} }
interface InternalType with interface InternalType with
@@ -90,8 +90,8 @@ type private PrivateTypeMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PrivateTypeMock = static member Empty : PrivateTypeMock =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
} }
interface PrivateType with interface PrivateType with
@@ -112,8 +112,8 @@ type private PrivateTypeInternalFalseMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : PrivateTypeInternalFalseMock = static member Empty : PrivateTypeInternalFalseMock =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
} }
interface PrivateTypeInternalFalse with interface PrivateTypeInternalFalse with
@@ -133,7 +133,7 @@ type internal VeryPublicTypeMock<'a, 'b> =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty () : VeryPublicTypeMock<'a, 'b> = static member Empty () : VeryPublicTypeMock<'a, 'b> =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
} }
interface VeryPublicType<'a, 'b> with interface VeryPublicType<'a, 'b> with
@@ -157,12 +157,12 @@ type internal CurriedMock<'a> =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty () : CurriedMock<'a> = static member Empty () : CurriedMock<'a> =
{ {
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem4"))
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem5"))
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem6"))
} }
interface Curried<'a> with interface Curried<'a> with
@@ -195,9 +195,9 @@ type internal TypeWithInterfaceMock =
/// An implementation where every method throws. /// An implementation where every method throws.
static member Empty : TypeWithInterfaceMock = static member Empty : TypeWithInterfaceMock =
{ {
Dispose = (fun _ -> ()) Dispose = (fun () -> ())
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function")) Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
} }
interface TypeWithInterface with interface TypeWithInterface with

View File

@@ -20,29 +20,33 @@ 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 :> _
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymOpeningHours type /// Module containing JSON parsing methods for the GymOpeningHours type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymOpeningHours = module GymOpeningHours =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymOpeningHours = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymOpeningHours =
@@ -56,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 =
@@ -69,7 +73,7 @@ module GymOpeningHours =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
{ {
IsAlwaysOpen = arg_0 IsAlwaysOpen = arg_0
@@ -78,8 +82,7 @@ module GymOpeningHours =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymAccessOptions type /// Module containing JSON parsing methods for the GymAccessOptions type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAccessOptions = module GymAccessOptions =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAccessOptions = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAccessOptions =
@@ -93,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
@@ -105,7 +108,7 @@ module GymAccessOptions =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
{ {
PinAccess = arg_0 PinAccess = arg_0
@@ -114,8 +117,7 @@ module GymAccessOptions =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymLocation type /// Module containing JSON parsing methods for the GymLocation type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymLocation = module GymLocation =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymLocation = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymLocation =
@@ -130,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
@@ -151,6 +153,7 @@ module GymLocation =
reraise () reraise ()
else else
reraise () reraise ()
|> LanguagePrimitives.FloatWithMeasure
let arg_0 = let arg_0 =
try try
@@ -163,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
@@ -192,8 +195,7 @@ module GymLocation =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymAddress type /// Module containing JSON parsing methods for the GymAddress type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAddress = module GymAddress =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAddress = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAddress =
@@ -207,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
@@ -224,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
@@ -246,7 +248,7 @@ module GymAddress =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
AddressLine1 = arg_0 AddressLine1 = arg_0
@@ -259,8 +261,7 @@ module GymAddress =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the Gym type /// Module containing JSON parsing methods for the Gym type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Gym = module Gym =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Gym = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Gym =
@@ -274,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
@@ -286,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 (
@@ -334,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
@@ -346,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 (
@@ -370,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
@@ -382,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
@@ -394,7 +395,7 @@ module Gym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Name = arg_0 Name = arg_0
@@ -429,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
@@ -441,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
@@ -453,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
@@ -465,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
@@ -477,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
@@ -489,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
@@ -514,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
@@ -526,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
@@ -538,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
@@ -550,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
@@ -562,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
@@ -574,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
@@ -586,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
@@ -598,7 +599,7 @@ module MemberJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
Id = arg_0 Id = arg_0
@@ -620,8 +621,7 @@ module MemberJsonParseExtension =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the GymAttendance type /// Module containing JSON parsing methods for the GymAttendance type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module GymAttendance = module GymAttendance =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAttendance = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : GymAttendance =
@@ -635,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
@@ -686,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
@@ -703,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
@@ -715,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
@@ -727,7 +727,7 @@ module GymAttendance =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
Description = arg_0 Description = arg_0
@@ -743,8 +743,7 @@ module GymAttendance =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the MemberActivityDto type /// Module containing JSON parsing methods for the MemberActivityDto type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module MemberActivityDto = module MemberActivityDto =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : MemberActivityDto = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : MemberActivityDto =
@@ -771,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
@@ -783,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
@@ -795,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
@@ -807,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
@@ -819,7 +818,7 @@ module MemberActivityDto =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
TotalDuration = arg_0 TotalDuration = arg_0
@@ -832,8 +831,7 @@ module MemberActivityDto =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the SessionsAggregate type /// Module containing JSON parsing methods for the SessionsAggregate type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module SessionsAggregate = module SessionsAggregate =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsAggregate = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsAggregate =
@@ -847,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
@@ -859,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
@@ -871,7 +869,7 @@ module SessionsAggregate =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
Activities = arg_0 Activities = arg_0
@@ -881,8 +879,7 @@ module SessionsAggregate =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the VisitGym type /// Module containing JSON parsing methods for the VisitGym type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module VisitGym = module VisitGym =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : VisitGym = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : VisitGym =
@@ -896,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
@@ -908,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
@@ -920,7 +917,7 @@ module VisitGym =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
Id = arg_0 Id = arg_0
@@ -930,8 +927,7 @@ module VisitGym =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the Visit type /// Module containing JSON parsing methods for the Visit type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Visit = module Visit =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Visit = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Visit =
@@ -957,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
@@ -982,7 +978,7 @@ module Visit =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<bool> () .GetValue<System.Boolean> ()
{ {
IsDurationEstimated = arg_0 IsDurationEstimated = arg_0
@@ -993,8 +989,7 @@ module Visit =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the SessionsSummary type /// Module containing JSON parsing methods for the SessionsSummary type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module SessionsSummary = module SessionsSummary =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsSummary = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : SessionsSummary =
@@ -1029,8 +1024,7 @@ module SessionsSummary =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the Sessions type /// Module containing JSON parsing methods for the Sessions type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Sessions = module Sessions =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Sessions = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : Sessions =
@@ -1066,8 +1060,7 @@ module Sessions =
namespace PureGym namespace PureGym
/// Module containing JSON parsing methods for the UriThing type /// Module containing JSON parsing methods for the UriThing type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module UriThing = module UriThing =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : UriThing = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : UriThing =

View File

@@ -17,8 +17,7 @@ open System.Net.Http
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module PureGymApi = module PureGymApi =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IPureGymApi = let make (client : System.Net.Http.HttpClient) : IPureGymApi =
@@ -30,7 +29,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri (("v1/gyms/"), System.UriKind.Relative) System.Uri (("v1/gyms/"), System.UriKind.Relative)
) )
@@ -60,7 +59,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ( System.Uri (
"v1/gyms/{gym_id}/attendance" "v1/gyms/{gym_id}/attendance"
@@ -94,7 +93,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ( System.Uri (
"v1/gyms/{gym_id}/attendance" "v1/gyms/{gym_id}/attendance"
@@ -128,7 +127,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("v1/member", System.UriKind.Relative) System.Uri ("v1/member", System.UriKind.Relative)
) )
@@ -158,7 +157,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ( System.Uri (
"v1/gyms/{gym}" "v1/gyms/{gym}"
@@ -192,7 +191,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("v1/member/activity", System.UriKind.Relative) System.Uri ("v1/member/activity", System.UriKind.Relative)
) )
@@ -222,7 +221,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("some/url", System.UriKind.Relative) System.Uri ("some/url", System.UriKind.Relative)
) )
@@ -252,7 +251,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("some/url", System.UriKind.Relative) System.Uri ("some/url", System.UriKind.Relative)
) )
@@ -303,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
@@ -318,7 +317,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ( System.Uri (
("/v2/gymSessions/member" ("/v2/gymSessions/member"
@@ -359,7 +358,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ( System.Uri (
("/v2/gymSessions/member?foo=1" ("/v2/gymSessions/member?foo=1"
@@ -400,7 +399,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -427,7 +426,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -454,7 +453,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -481,7 +480,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -508,7 +507,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -535,7 +534,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -568,7 +567,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -601,7 +600,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -634,7 +633,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("users/new", System.UriKind.Relative) System.Uri ("users/new", System.UriKind.Relative)
) )
@@ -660,7 +659,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ( System.Uri (
"endpoint/{param}" "endpoint/{param}"
@@ -689,7 +688,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -714,7 +713,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -739,7 +738,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -764,7 +763,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -788,7 +787,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -812,7 +811,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -836,7 +835,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -860,7 +859,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -896,7 +895,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -932,7 +931,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -968,7 +967,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -1004,7 +1003,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -1027,7 +1026,7 @@ module PureGymApi =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with (match client.BaseAddress with
| null -> System.Uri "https://whatnot.com" | null -> System.Uri "https://whatnot.com/"
| v -> v), | v -> v),
System.Uri ("endpoint", System.UriKind.Relative) System.Uri ("endpoint", System.UriKind.Relative)
) )
@@ -1055,8 +1054,7 @@ open System.Net.Http
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module internal ApiWithoutBaseAddress = module internal ApiWithoutBaseAddress =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithoutBaseAddress = let make (client : System.Net.Http.HttpClient) : IApiWithoutBaseAddress =
@@ -1107,8 +1105,7 @@ open System.Net.Http
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module ApiWithBasePath = module ApiWithBasePath =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithBasePath = let make (client : System.Net.Http.HttpClient) : IApiWithBasePath =
@@ -1119,15 +1116,18 @@ module ApiWithBasePath =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with System.Uri (
| null -> (match client.BaseAddress with
raise ( | null ->
System.ArgumentNullException ( raise (
nameof (client.BaseAddress), System.ArgumentNullException (
"No base address was supplied on the type, and no BaseAddress was on the HttpClient." nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
) )
) | v -> v),
| v -> v), System.Uri ("foo/", System.UriKind.Relative)
),
System.Uri ( System.Uri (
"endpoint/{param}" "endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode), .Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
@@ -1159,8 +1159,7 @@ open System.Net.Http
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module ApiWithBasePathAndAddress = module ApiWithBasePathAndAddress =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAddress = let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAddress =
@@ -1171,9 +1170,12 @@ module ApiWithBasePathAndAddress =
let uri = let uri =
System.Uri ( System.Uri (
(match client.BaseAddress with System.Uri (
| null -> System.Uri "https://whatnot.com" (match client.BaseAddress with
| v -> v), | null -> System.Uri "https://whatnot.com/thing/"
| v -> v),
System.Uri ("foo/", System.UriKind.Relative)
),
System.Uri ( System.Uri (
"endpoint/{param}" "endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode), .Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
@@ -1205,8 +1207,313 @@ open System.Net.Http
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>] module ApiWithAbsoluteBasePath =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePath =
{ new IApiWithAbsoluteBasePath with
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri ("/foo/", System.UriKind.Relative)
),
System.Uri (
"endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithAbsoluteBasePathAndAddress =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePathAndAddress =
{ new IApiWithAbsoluteBasePathAndAddress with
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
System.Uri (
(match client.BaseAddress with
| null -> System.Uri "https://whatnot.com/thing/"
| v -> v),
System.Uri ("/foo/", System.UriKind.Relative)
),
System.Uri (
"endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithBasePathAndAbsoluteEndpoint =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAbsoluteEndpoint =
{ new IApiWithBasePathAndAbsoluteEndpoint with
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri ("foo/", System.UriKind.Relative)
),
System.Uri (
"/endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithBasePathAndAddressAndAbsoluteEndpoint =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAddressAndAbsoluteEndpoint =
{ new IApiWithBasePathAndAddressAndAbsoluteEndpoint with
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
System.Uri (
(match client.BaseAddress with
| null -> System.Uri "https://whatnot.com/thing/"
| v -> v),
System.Uri ("foo/", System.UriKind.Relative)
),
System.Uri (
"/endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithAbsoluteBasePathAndAbsoluteEndpoint =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePathAndAbsoluteEndpoint =
{ new IApiWithAbsoluteBasePathAndAbsoluteEndpoint with
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
System.Uri (
(match client.BaseAddress with
| null ->
raise (
System.ArgumentNullException (
nameof (client.BaseAddress),
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
)
)
| v -> v),
System.Uri ("/foo/", System.UriKind.Relative)
),
System.Uri (
"/endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
/// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
{ new IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint with
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
async {
let! ct = Async.CancellationToken
let uri =
System.Uri (
System.Uri (
(match client.BaseAddress with
| null -> System.Uri "https://whatnot.com/thing/"
| v -> v),
System.Uri ("/foo/", System.UriKind.Relative)
),
System.Uri (
"/endpoint/{param}"
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
System.UriKind.Relative
)
)
let httpMessage =
new System.Net.Http.HttpRequestMessage (
Method = System.Net.Http.HttpMethod.Get,
RequestUri = uri
)
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
let response = response.EnsureSuccessStatusCode ()
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
return responseString
}
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
}
namespace PureGym
open System
open System.Threading
open System.Threading.Tasks
open System.IO
open System.Net
open System.Net.Http
open RestEase
/// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
module ApiWithHeaders = module ApiWithHeaders =
/// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties. /// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties.
let make let make
@@ -1268,8 +1575,7 @@ open System.Net.Http
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module ApiWithHeaders2 = module ApiWithHeaders2 =
/// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties. /// Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties.
let make let make

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,50 +122,96 @@ module JsonRecordTypeWithBothJsonSerializeExtension =
let node = System.Text.Json.Nodes.JsonObject () let node = System.Text.Json.Nodes.JsonObject ()
do do
node.Add ("a", System.Text.Json.Nodes.JsonValue.Create<int> input.A) node.Add ("a", (input.A |> System.Text.Json.Nodes.JsonValue.Create<int>))
node.Add ("b", System.Text.Json.Nodes.JsonValue.Create<string> input.B) node.Add ("b", (input.B |> System.Text.Json.Nodes.JsonValue.Create<string>))
node.Add ( node.Add (
"c", "c",
(fun field -> (input.C
let arr = System.Text.Json.Nodes.JsonArray () |> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
arr arr
) ))
input.C
) )
node.Add ("d", InnerTypeWithBoth.toJsonNode input.D) node.Add ("d", (input.D |> InnerTypeWithBoth.toJsonNode))
node.Add ( node.Add (
"e", "e",
(fun field -> (input.E
let arr = System.Text.Json.Nodes.JsonArray () |> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<string> mem)
arr arr
) ))
input.E
) )
node.Add ( node.Add (
"f", "arr",
(fun field -> (input.Arr
let arr = System.Text.Json.Nodes.JsonArray () |> (fun field ->
let arr = System.Text.Json.Nodes.JsonArray ()
for mem in field do for mem in field do
arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem) arr.Add (System.Text.Json.Nodes.JsonValue.Create<int> mem)
arr arr
) ))
input.F
) )
node.Add ("byte", (input.Byte |> System.Text.Json.Nodes.JsonValue.Create<byte<measure>>))
node.Add ("sbyte", (input.Sbyte |> System.Text.Json.Nodes.JsonValue.Create<sbyte<measure>>))
node.Add ("i", (input.I |> System.Text.Json.Nodes.JsonValue.Create<int<measure>>))
node.Add ("i32", (input.I32 |> System.Text.Json.Nodes.JsonValue.Create<int32<measure>>))
node.Add ("i64", (input.I64 |> System.Text.Json.Nodes.JsonValue.Create<int64<measure>>))
node.Add ("u", (input.U |> System.Text.Json.Nodes.JsonValue.Create<uint<measure>>))
node.Add ("u32", (input.U32 |> System.Text.Json.Nodes.JsonValue.Create<uint32<measure>>))
node.Add ("u64", (input.U64 |> System.Text.Json.Nodes.JsonValue.Create<uint64<measure>>))
node.Add ("f", (input.F |> System.Text.Json.Nodes.JsonValue.Create<float<measure>>))
node.Add ("f32", (input.F32 |> System.Text.Json.Nodes.JsonValue.Create<float32<measure>>))
node.Add ("single", (input.Single |> System.Text.Json.Nodes.JsonValue.Create<single<measure>>))
node.Add (
"intMeasureOption",
(input.IntMeasureOption
|> (fun field ->
match field with
| None -> null :> System.Text.Json.Nodes.JsonNode
| Some field ->
(System.Text.Json.Nodes.JsonValue.Create<int<measure>> field)
:> System.Text.Json.Nodes.JsonNode
))
)
node.Add (
"intMeasureNullable",
(input.IntMeasureNullable
|> (fun field ->
if field.HasValue then
System.Text.Json.Nodes.JsonValue.Create<int<measure>> field.Value
:> System.Text.Json.Nodes.JsonNode
else
null :> System.Text.Json.Nodes.JsonNode
))
)
node.Add ("enum", (input.Enum |> SomeEnum.toJsonNode))
node.Add (
"timestamp",
(input.Timestamp
|> (fun field -> field.ToString "o" |> System.Text.Json.Nodes.JsonValue.Create<string>))
)
node.Add ("unit", (input.Unit |> (fun value -> System.Text.Json.Nodes.JsonObject ())))
node :> _ node :> _
namespace ConsumePlugin namespace ConsumePlugin
@@ -167,7 +231,7 @@ module FirstDuJsonSerializeExtension =
match input with match input with
| FirstDu.EmptyCase -> node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "emptyCase") | FirstDu.EmptyCase -> node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "emptyCase")
| FirstDu.Case1 (arg0) -> | FirstDu.Case1 arg0 ->
node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "case1") node.Add ("type", System.Text.Json.Nodes.JsonValue.Create "case1")
let dataNode = System.Text.Json.Nodes.JsonObject () let dataNode = System.Text.Json.Nodes.JsonObject ()
dataNode.Add ("data", System.Text.Json.Nodes.JsonValue.Create<string> arg0) dataNode.Add ("data", System.Text.Json.Nodes.JsonValue.Create<string> arg0)
@@ -180,6 +244,109 @@ 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
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the CollectRemaining type
[<AutoOpen>]
module CollectRemainingJsonSerializeExtension =
/// Extension methods for JSON parsing
type CollectRemaining with
/// Serialize to a JSON node
static member toJsonNode (input : CollectRemaining) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do
node.Add (
"message",
(input.Message
|> (fun field ->
match field with
| None -> null :> System.Text.Json.Nodes.JsonNode
| Some field -> HeaderAndValue.toJsonNode field
))
)
for KeyValue (key, value) in input.Rest do
node.Add (key, id value)
node :> _
namespace ConsumePlugin
open System
open System.Collections.Generic
open System.Text.Json.Serialization
/// Module containing JSON serializing extension members for the OuterCollectRemaining type
[<AutoOpen>]
module OuterCollectRemainingJsonSerializeExtension =
/// Extension methods for JSON parsing
type OuterCollectRemaining with
/// Serialize to a JSON node
static member toJsonNode (input : OuterCollectRemaining) : System.Text.Json.Nodes.JsonNode =
let node = System.Text.Json.Nodes.JsonObject ()
do
for KeyValue (key, value) in input.Others do
node.Add (key, System.Text.Json.Nodes.JsonValue.Create<int> value)
node.Add ("remaining", (input.Remaining |> CollectRemaining.toJsonNode))
node :> _
namespace ConsumePlugin namespace ConsumePlugin
@@ -221,7 +388,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 +454,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 +480,76 @@ module JsonRecordTypeWithBothJsonParseExtension =
/// Parse from a JSON node. /// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordTypeWithBoth = static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : JsonRecordTypeWithBoth =
let arg_5 = let arg_21 = ()
let arg_20 =
(match node.["timestamp"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("timestamp")
)
)
| v -> v)
.AsValue()
.GetValue<string> ()
|> System.DateTimeOffset.Parse
let arg_19 =
SomeEnum.jsonParse (
match node.["enum"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("enum")
)
)
| v -> v
)
let arg_18 =
match node.["intMeasureNullable"] with
| null -> System.Nullable ()
| v ->
v.AsValue().GetValue<System.Int32> ()
|> LanguagePrimitives.Int32WithMeasure
|> System.Nullable
let arg_17 =
match node.["intMeasureOption"] with
| null -> None
| v ->
v.AsValue().GetValue<System.Int32> ()
|> LanguagePrimitives.Int32WithMeasure
|> Some
let arg_16 =
(match node.["single"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("single")
)
)
| v -> v)
.AsValue()
.GetValue<System.Single> ()
|> LanguagePrimitives.Float32WithMeasure
let arg_15 =
(match node.["f32"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("f32")
)
)
| v -> v)
.AsValue()
.GetValue<System.Single> ()
|> LanguagePrimitives.Float32WithMeasure
let arg_14 =
(match node.["f"] with (match node.["f"] with
| null -> | null ->
raise ( raise (
@@ -303,9 +557,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 +689,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 +714,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 +727,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 +739,7 @@ module JsonRecordTypeWithBothJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<int> () .GetValue<System.Int32> ()
{ {
A = arg_0 A = arg_0
@@ -376,7 +747,23 @@ 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
Unit = arg_21
} }
namespace ConsumePlugin namespace ConsumePlugin
@@ -422,7 +809,7 @@ module FirstDuJsonParseExtension =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
) )
| "case2" -> | "case2" ->
let node = let node =
@@ -455,6 +842,142 @@ 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
}
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the CollectRemaining type
[<AutoOpen>]
module CollectRemainingJsonParseExtension =
/// Extension methods for JSON parsing
type CollectRemaining with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : CollectRemaining =
let arg_1 =
let result =
System.Collections.Generic.Dictionary<string, System.Text.Json.Nodes.JsonNode> ()
let node = node.AsObject ()
for KeyValue (key, value) in node do
if key = "message" then () else result.Add (key, node.[key])
result
let arg_0 =
match node.["message"] with
| null -> None
| v -> HeaderAndValue.jsonParse v |> Some
{
Message = arg_0
Rest = arg_1
}
namespace ConsumePlugin
/// Module containing JSON parsing extension members for the OuterCollectRemaining type
[<AutoOpen>]
module OuterCollectRemainingJsonParseExtension =
/// Extension methods for JSON parsing
type OuterCollectRemaining with
/// Parse from a JSON node.
static member jsonParse (node : System.Text.Json.Nodes.JsonNode) : OuterCollectRemaining =
let arg_1 =
CollectRemaining.jsonParse (
match node.["remaining"] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" ("remaining")
)
)
| v -> v
)
let arg_0 =
let result = System.Collections.Generic.Dictionary<string, int> ()
let node = node.AsObject ()
for KeyValue (key, value) in node do
if key = "remaining" then
()
else
result.Add (
key,
(match node.[key] with
| null ->
raise (
System.Collections.Generic.KeyNotFoundException (
sprintf "Required key '%s' not found on JSON object" (key)
)
)
| v -> v)
.AsValue()
.GetValue<System.Int32> ()
)
result
{
Others = arg_0
Remaining = arg_1
}

View File

@@ -8,8 +8,7 @@
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing methods for the JwtVaultAuthResponse type /// Module containing JSON parsing methods for the JwtVaultAuthResponse type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module JwtVaultAuthResponse = module JwtVaultAuthResponse =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultAuthResponse = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultAuthResponse =
@@ -23,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
@@ -35,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
@@ -47,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
@@ -59,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
@@ -71,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
@@ -83,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
@@ -95,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 =
@@ -108,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 =
@@ -121,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 =
@@ -134,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
@@ -146,7 +145,7 @@ module JwtVaultAuthResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
ClientToken = arg_0 ClientToken = arg_0
@@ -164,8 +163,7 @@ module JwtVaultAuthResponse =
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing methods for the JwtVaultResponse type /// Module containing JSON parsing methods for the JwtVaultResponse type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module JwtVaultResponse = module JwtVaultResponse =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultResponse = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtVaultResponse =
@@ -191,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
@@ -203,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
@@ -215,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
@@ -227,7 +225,7 @@ module JwtVaultResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
RequestId = arg_0 RequestId = arg_0
@@ -239,8 +237,7 @@ module JwtVaultResponse =
namespace ConsumePlugin namespace ConsumePlugin
/// Module containing JSON parsing methods for the JwtSecretResponse type /// Module containing JSON parsing methods for the JwtSecretResponse type
[<RequireQualifiedAccess>] [<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module JwtSecretResponse = module JwtSecretResponse =
/// Parse from a JSON node. /// Parse from a JSON node.
let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtSecretResponse = let jsonParse (node : System.Text.Json.Nodes.JsonNode) : JwtSecretResponse =
@@ -274,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
@@ -291,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
@@ -308,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
@@ -325,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
@@ -342,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
@@ -360,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
@@ -377,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
@@ -392,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
@@ -404,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
@@ -416,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
@@ -428,7 +425,7 @@ module JwtSecretResponse =
) )
| v -> v) | v -> v)
.AsValue() .AsValue()
.GetValue<string> () .GetValue<System.String> ()
{ {
RequestId = arg_0 RequestId = arg_0
@@ -455,8 +452,7 @@ open System.Threading.Tasks
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module VaultClient = module VaultClient =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IVaultClient = let make (client : System.Net.Http.HttpClient) : IVaultClient =
@@ -553,8 +549,7 @@ open System.Threading.Tasks
open RestEase open RestEase
/// Module for constructing a REST client. /// Module for constructing a REST client.
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>] [<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
[<RequireQualifiedAccess>]
module VaultClientNonExtensionMethod = module VaultClientNonExtensionMethod =
/// Create a REST client. /// Create a REST client.
let make (client : System.Net.Http.HttpClient) : IVaultClientNonExtensionMethod = let make (client : System.Net.Http.HttpClient) : IVaultClientNonExtensionMethod =

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

@@ -41,7 +41,7 @@ module MyListCata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__MyList (x) -> | Instruction.Process__MyList x ->
match x with match x with
| MyList.Nil -> cata.MyList.Nil |> myListStack.Add | MyList.Nil -> cata.MyList.Nil |> myListStack.Add
| MyList.Cons ({ | MyList.Cons ({
@@ -50,7 +50,7 @@ module MyListCata =
}) -> }) ->
instructions.Add (Instruction.MyList_Cons (head)) instructions.Add (Instruction.MyList_Cons (head))
instructions.Add (Instruction.Process__MyList tail) instructions.Add (Instruction.Process__MyList tail)
| Instruction.MyList_Cons (head) -> | Instruction.MyList_Cons head ->
let tail = myListStack.[myListStack.Count - 1] let tail = myListStack.[myListStack.Count - 1]
myListStack.RemoveAt (myListStack.Count - 1) myListStack.RemoveAt (myListStack.Count - 1)
cata.MyList.Cons head tail |> myListStack.Add cata.MyList.Cons head tail |> myListStack.Add
@@ -97,13 +97,13 @@ module MyList2Cata =
instructions.RemoveAt (instructions.Count - 1) instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with match currentInstruction with
| Instruction.Process__MyList2 (x) -> | Instruction.Process__MyList2 x ->
match x with match x with
| MyList2.Nil -> cata.MyList2.Nil |> myList2Stack.Add | MyList2.Nil -> cata.MyList2.Nil |> myList2Stack.Add
| MyList2.Cons (arg0_0, arg1_0) -> | MyList2.Cons (arg0_0, arg1_0) ->
instructions.Add (Instruction.MyList2_Cons (arg0_0)) instructions.Add (Instruction.MyList2_Cons (arg0_0))
instructions.Add (Instruction.Process__MyList2 arg1_0) instructions.Add (Instruction.Process__MyList2 arg1_0)
| Instruction.MyList2_Cons (arg0_0) -> | Instruction.MyList2_Cons arg0_0 ->
let arg1_0 = myList2Stack.[myList2Stack.Count - 1] let arg1_0 = myList2Stack.[myList2Stack.Count - 1]
myList2Stack.RemoveAt (myList2Stack.Count - 1) myList2Stack.RemoveAt (myList2Stack.Count - 1)
cata.MyList2.Cons arg0_0 arg1_0 |> myList2Stack.Add cata.MyList2.Cons arg0_0 arg1_0 |> myList2Stack.Add

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

@@ -1,9 +1,5 @@
namespace ConsumePlugin namespace ConsumePlugin
type ParseState =
| AwaitingKey
| AwaitingValue of string
/// My whatnot /// My whatnot
[<WoofWare.Myriad.Plugins.RemoveOptions>] [<WoofWare.Myriad.Plugins.RemoveOptions>]
type RecordType = type RecordType =

View File

@@ -122,8 +122,6 @@ type internal IApiWithoutBaseAddress =
[<Get "endpoint/{param}">] [<Get "endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string> abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
// TODO: implement BasePath support
[<WoofWare.Myriad.Plugins.HttpClient>] [<WoofWare.Myriad.Plugins.HttpClient>]
[<BasePath "foo">] [<BasePath "foo">]
type IApiWithBasePath = type IApiWithBasePath =
@@ -132,12 +130,54 @@ type IApiWithBasePath =
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string> abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>] [<WoofWare.Myriad.Plugins.HttpClient>]
[<BaseAddress "https://whatnot.com">] [<BaseAddress "https://whatnot.com/thing">]
[<BasePath "foo">] [<BasePath "foo">]
type IApiWithBasePathAndAddress = type IApiWithBasePathAndAddress =
[<Get "endpoint/{param}">] [<Get "endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string> abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>]
[<BasePath "/foo">]
type IApiWithAbsoluteBasePath =
// Example where we use the bundled attributes rather than RestEase's
[<WoofWare.Myriad.Plugins.RestEase.Get "endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>]
[<BaseAddress "https://whatnot.com/thing">]
[<BasePath "/foo">]
type IApiWithAbsoluteBasePathAndAddress =
[<Get "endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>]
[<BasePath "foo">]
type IApiWithBasePathAndAbsoluteEndpoint =
// Example where we use the bundled attributes rather than RestEase's
[<WoofWare.Myriad.Plugins.RestEase.Get "/endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>]
[<BaseAddress "https://whatnot.com/thing">]
[<BasePath "foo">]
type IApiWithBasePathAndAddressAndAbsoluteEndpoint =
[<Get "/endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>]
[<BasePath "/foo">]
type IApiWithAbsoluteBasePathAndAbsoluteEndpoint =
// Example where we use the bundled attributes rather than RestEase's
[<WoofWare.Myriad.Plugins.RestEase.Get "/endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>]
[<BaseAddress "https://whatnot.com/thing">]
[<BasePath "/foo">]
type IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
[<Get "/endpoint/{param}">]
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
[<WoofWare.Myriad.Plugins.HttpClient>] [<WoofWare.Myriad.Plugins.HttpClient>]
[<Header("Header-Name", "Header-Value")>] [<Header("Header-Name", "Header-Value")>]
type IApiWithHeaders = type IApiWithHeaders =

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,23 @@ type JsonRecordTypeWithBoth =
C : int list C : int list
D : InnerTypeWithBoth D : InnerTypeWithBoth
E : string array E : string array
F : int[] Arr : int[]
Byte : byte<measure>
Sbyte : sbyte<measure>
I : int<measure>
I32 : int32<measure>
I64 : int64<measure>
U : uint<measure>
U32 : uint32<measure>
U64 : uint64<measure>
F : float<measure>
F32 : float32<measure>
Single : single<measure>
IntMeasureOption : int<measure> option
IntMeasureNullable : int<measure> Nullable
Enum : SomeEnum
Timestamp : DateTimeOffset
Unit : unit
} }
[<WoofWare.Myriad.Plugins.JsonSerialize true>] [<WoofWare.Myriad.Plugins.JsonSerialize true>]
@@ -34,3 +59,36 @@ 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
}
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
[<WoofWare.Myriad.Plugins.JsonParse true>]
type CollectRemaining =
{
Message : HeaderAndValue option
[<JsonExtensionData>]
Rest : Dictionary<string, System.Text.Json.Nodes.JsonNode>
}
[<WoofWare.Myriad.Plugins.JsonSerialize true>]
[<WoofWare.Myriad.Plugins.JsonParse true>]
type OuterCollectRemaining =
{
[<JsonExtensionData>]
Others : Dictionary<string, int>
Remaining : CollectRemaining
}

View File

@@ -10,19 +10,10 @@
<WarnOn>FS3388,FS3559</WarnOn> <WarnOn>FS3388,FS3559</WarnOn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Nerdbank.GitVersioning" Version="3.6.133" PrivateAssets="all"/> <PackageReference Include="Nerdbank.GitVersioning" Version="3.6.143" PrivateAssets="all"/>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0" PrivateAssets="All"/>
<SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/> <SourceLinkGitHubHost Include="github.com" ContentUrl="https://raw.githubusercontent.com"/>
</ItemGroup> </ItemGroup>
<!-- <PropertyGroup Condition="'$(GITHUB_ACTION)' != ''">
SourceLink doesn't support F# deterministic builds out of the box, <ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
so tell SourceLink that our source root is going to be remapped. </PropertyGroup>
-->
<Target Name="MapSourceRoot" BeforeTargets="_GenerateSourceLinkFile" Condition="'$(SourceRootMappedPathsFeatureSupported)' != 'true'">
<ItemGroup>
<SourceRoot Update="@(SourceRoot)">
<MappedPath>Z:\CheckoutRoot\WoofWare.Myriad\</MappedPath>
</SourceRoot>
</ItemGroup>
</Target>
</Project> </Project>

View File

@@ -8,23 +8,21 @@
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)).
* `ArgParser` (to stamp out a basic argument parser)
* `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:
@@ -153,6 +151,73 @@ The same limitations generally apply to `JsonSerialize` as do to `JsonParse`.
For an example of using both `JsonParse` and `JsonSerialize` together with complex types, see [the type definitions](./ConsumePlugin/SerializationAndDeserialization.fs) and [tests](./WoofWare.Myriad.Plugins.Test/TestJsonSerialize/TestJsonSerde.fs). For an example of using both `JsonParse` and `JsonSerialize` together with complex types, see [the type definitions](./ConsumePlugin/SerializationAndDeserialization.fs) and [tests](./WoofWare.Myriad.Plugins.Test/TestJsonSerialize/TestJsonSerde.fs).
## `ArgParser`
Takes a record like this:
```fsharp
[<ArgParser>]
type Foo =
{
[<ArgumentHelpText "Enable the frobnicator">]
SomeFlag : bool
A : int option
[<ArgumentDefaultFunction>]
B : Choice<int, int>
[<ArgumentDefaultEnvironmentVariable "MY_ENV_VAR">]
BWithEnv : Choice<int, int>
C : float list
// optionally:
[<PositionalArgs>]
Rest : string list // or e.g. `int list` if you want them parsed into a type too
}
static member DefaultB () = 4
```
and stamps out a basic `parse` method of this signature:
```fsharp
[<RequireQualifiedAccess>]
module Foo =
// in case you want to test it
let parse' (getEnvVar : string -> string) (args : string list) : Foo = ...
// the one we expect you actually want to use
let parse (args : string list) : Foo = ...
```
Default arguments are handled as `Choice<'a, 'a>`:
you get a `Choice1Of2` if the user provided the input, or a `Choice2Of2` if the parser filled in your specified default value.
You can control `TimeSpan` and friends with the `[<InvariantCulture>]` and `[<ParseExact @"hh\:mm\:ss">]` attributes.
You can generate extension methods for the type, instead of a module with the type's name, using `[<ArgParser (* isExtensionMethod = *) true>]`.
If `--help` appears in a position where the parser is expecting a key (e.g. in the first position, or after a `--foo=bar`), the parser fails with help text.
The parser also makes a limited effort to supply help text when encountering an invalid parse.
### What's the point?
I got fed up of waiting for us to find time to rewrite the in-house one at work.
That one has a bunch of nice compositional properties, which my version lacks:
I can basically only deal with primitive types, and e.g. you can't stack records and discriminated unions inside each other.
But I *do* want an F#-native argument parser suitable for AOT-compilation.
Why not [Argu](https://fsprojects.github.io/Argu/)?
Answer: I got annoyed with having to construct my records by hand even after Argu returned and said the parsing was all "done".
### Limitations
This is very bare-bones, but do raise GitHub issues if you like (or if you find cases where the parser does the wrong thing).
* Help is signalled by throwing an exception, so you'll get an unsightly stack trace and a nonzero exit code.
* Help doesn't take into account any arguments the user has entered. Ideally you'd get contextual information like an identification of which args the user has supplied at the point where the parse failed or help was requested.
* I don't handle very many types, and in particular a real arg parser would handle DUs and records with nesting.
* I don't try very hard to find a valid parse. It may well be possible to find a case where I fail to parse despite there existing a valid parse.
* There's no subcommand support (you'll have to do that yourself).
It should work fine if you just want to compose a few primitive types, though.
## `RemoveOptions` ## `RemoveOptions`
Takes a record like this: Takes a record like this:

View File

@@ -0,0 +1,97 @@
namespace WoofWare.Myriad.Plugins
open System
/// Attribute indicating a record type to which the "build arg parser" Myriad
/// generator should apply during build.
///
/// If you supply isExtensionMethod = true, you will get extension methods.
/// These can only be consumed from F#, but the benefit is that they don't use up the module name
/// (since by default we create a module called "{TypeName}").
type ArgParserAttribute (isExtensionMethod : bool) =
inherit Attribute ()
/// The default value of `isExtensionMethod`, the optional argument to the ArgParserAttribute constructor.
static member DefaultIsExtensionMethod = false
/// Shorthand for the "isExtensionMethod = false" constructor; see documentation there for details.
new () = ArgParserAttribute ArgParserAttribute.DefaultIsExtensionMethod
/// Attribute indicating that this field shall accumulate all unmatched args,
/// as well as any that appear after a bare `--`.
///
/// Set `includeFlagLike = true` to include args that begin `--` in the
/// positional args.
/// (By default, `includeFlagLike = false` and we throw when encountering
/// an argument which looks like a flag but which we don't recognise.)
/// We will still interpret `--help` as requesting help, unless it comes after
/// a standalone `--` separator.
type PositionalArgsAttribute (includeFlagLike : bool) =
inherit Attribute ()
/// The default value of `isExtensionMethod`, the optional argument to the ArgParserAttribute constructor.
static member DefaultIncludeFlagLike = false
/// Shorthand for the "includeFlagLike = false" constructor; see documentation there for details.
new () = PositionalArgsAttribute PositionalArgsAttribute.DefaultIncludeFlagLike
/// Attribute indicating that this field shall have a default value derived
/// from calling an appropriately named static method on the type.
///
/// This attribute can only be placed on fields of type `Choice<_, _>` where both type parameters
/// are the same.
/// After a successful parse, the value is Choice1Of2 if the user supplied an input,
/// or Choice2Of2 if the input was obtained by calling the default function.
///
/// The static method we call for field `FieldName : 'a` is `DefaultFieldName : unit -> 'a`.
type ArgumentDefaultFunctionAttribute () =
inherit Attribute ()
/// Attribute indicating that this field shall have a default value derived
/// from an environment variable (whose name you give in the attribute constructor).
///
/// This attribute can only be placed on fields of type `Choice<_, _>` where both type parameters
/// are the same.
/// After a successful parse, the value is Choice1Of2 if the user supplied an input,
/// or Choice2Of2 if the input was obtained by pulling a value from `Environment.GetEnvironmentVariable`.
type ArgumentDefaultEnvironmentVariableAttribute (envVar : string) =
inherit Attribute ()
/// Attribute indicating that this field shall have the given help text, when `--help` is invoked
/// or when a parse error causes us to print help text.
type ArgumentHelpTextAttribute (helpText : string) =
inherit Attribute ()
/// Attribute indicating that this field should be parsed with a ParseExact method on its type.
/// For example, on a TimeSpan field, with [<ArgumentParseExact @"hh\:mm\:ss">], we will call
/// `TimeSpan.ParseExact (s, @"hh\:mm\:ss", CultureInfo.CurrentCulture).
type ParseExactAttribute (format : string) =
inherit Attribute ()
/// Attribute indicating that this field should be parsed in the invariant culture, rather than the
/// default current culture.
/// For example, on a TimeSpan field, with [<InvariantCulture>] and [<ArgumentParseExact @"hh\:mm\:ss">], we will call
/// `TimeSpan.ParseExact (s, @"hh\:mm\:ss", CultureInfo.InvariantCulture).
type InvariantCultureAttribute () =
inherit Attribute ()
/// Attribute placed on a field of a two-case no-data discriminated union, indicating that this is "basically a bool".
/// For example: `type DryRun = | [<ArgumentFlag true>] Dry | [<ArgumentFlag false>] Wet`
/// A record with `{ DryRun : DryRun }` will then be parsed like `{ DryRun : bool }` (so the user supplies `--dry-run`),
/// but that you get this strongly-typed value directly in the code (so you `match args.DryRun with | DryRun.Dry ...`).
///
/// You must put this attribute on both cases of the discriminated union, with opposite values in each case.
type ArgumentFlagAttribute (flagValue : bool) =
inherit Attribute ()
/// Attribute placed on a field of a record to specify a different long form from the default. If you place this
/// attribute, you won't get the default: ArgFoo would normally be expressed as `--arg-foo`, but if you instead
/// say `[<ArgumentLongForm "thingy-blah">]` or `[<ArgumentLongForm "thingy">]`, you instead use `--thingy-blah`
/// or `--thingy` respectively.
///
/// You can place this argument multiple times.
///
/// Omit the initial `--` that you expect the user to type.
[<AttributeUsage(AttributeTargets.Field, AllowMultiple = true)>]
type ArgumentLongForm (s : string) =
inherit Attribute ()

View File

@@ -45,6 +45,9 @@ module RestEase =
/// Indicates that this interface represents a REST client which accesses an API whose paths are /// Indicates that this interface represents a REST client which accesses an API whose paths are
/// all relative to the given address. /// all relative to the given address.
///
/// We will essentially unconditionally append a slash to this for you, on the grounds that you probably don't
/// intend the base path *itself* to be an endpoint.
type BaseAddressAttribute (addr : string) = type BaseAddressAttribute (addr : string) =
inherit Attribute () inherit Attribute ()
@@ -61,3 +64,21 @@ module RestEase =
inherit Attribute () inherit Attribute ()
new (path : string) = PathAttribute (Some path) new (path : string) = PathAttribute (Some path)
new () = PathAttribute None new () = PathAttribute None
/// Indicates that this argument to a method is passed to the remote API by being serialised into the request
/// body.
type BodyAttribute () =
inherit Attribute ()
/// This is interpolated into every URL, between the BaseAddress and the path specified by e.g. [<Get>].
/// Note that if the [<Get>]-specified path starts with a slash, the BasePath is ignored, because then [<Get>]
/// is considered to be relative to the URL root (i.e. the host part of the BaseAddress).
/// Similarly, if the [<BasePath>] starts with a slash, then any path component of the BaseAddress is ignored.
///
/// We will essentially unconditionally append a slash to this for you, on the grounds that you probably don't
/// intend the base path *itself* to be an endpoint.
///
/// Can contain {placeholders}; hopefully your methods define values for those placeholders with [<Path>]
/// attributes!
type BasePathAttribute (path : string) =
inherit Attribute ()

View File

@@ -1,3 +1,18 @@
WoofWare.Myriad.Plugins.ArgParserAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.ArgParserAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.ArgParserAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.ArgParserAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
WoofWare.Myriad.Plugins.ArgParserAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
WoofWare.Myriad.Plugins.ArgumentDefaultEnvironmentVariableAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.ArgumentDefaultEnvironmentVariableAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.ArgumentDefaultFunctionAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.ArgumentDefaultFunctionAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.ArgumentFlagAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.ArgumentFlagAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.ArgumentHelpTextAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.ArgumentHelpTextAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.ArgumentLongForm inherit System.Attribute
WoofWare.Myriad.Plugins.ArgumentLongForm..ctor [constructor]: string
WoofWare.Myriad.Plugins.CreateCatamorphismAttribute inherit System.Attribute WoofWare.Myriad.Plugins.CreateCatamorphismAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.CreateCatamorphismAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.CreateCatamorphismAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.GenerateMockAttribute inherit System.Attribute WoofWare.Myriad.Plugins.GenerateMockAttribute inherit System.Attribute
@@ -10,6 +25,8 @@ WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.HttpClientAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.HttpClientAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool WoofWare.Myriad.Plugins.HttpClientAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
WoofWare.Myriad.Plugins.HttpClientAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool WoofWare.Myriad.Plugins.HttpClientAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
WoofWare.Myriad.Plugins.InvariantCultureAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.InvariantCultureAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.JsonParseAttribute inherit System.Attribute WoofWare.Myriad.Plugins.JsonParseAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: bool WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.JsonParseAttribute..ctor [constructor]: unit
@@ -20,11 +37,22 @@ WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.JsonSerializeAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.JsonSerializeAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool WoofWare.Myriad.Plugins.JsonSerializeAttribute.DefaultIsExtensionMethod [static property]: [read-only] bool
WoofWare.Myriad.Plugins.JsonSerializeAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool WoofWare.Myriad.Plugins.JsonSerializeAttribute.get_DefaultIsExtensionMethod [static method]: unit -> bool
WoofWare.Myriad.Plugins.ParseExactAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.ParseExactAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.PositionalArgsAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.PositionalArgsAttribute..ctor [constructor]: bool
WoofWare.Myriad.Plugins.PositionalArgsAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.PositionalArgsAttribute.DefaultIncludeFlagLike [static property]: [read-only] bool
WoofWare.Myriad.Plugins.PositionalArgsAttribute.get_DefaultIncludeFlagLike [static method]: unit -> bool
WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RemoveOptionsAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.RestEase inherit obj WoofWare.Myriad.Plugins.RestEase inherit obj
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+BasePathAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+BasePathAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+BodyAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+BodyAttribute..ctor [constructor]: unit
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+DeleteAttribute inherit System.Attribute
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute..ctor [constructor]: string WoofWare.Myriad.Plugins.RestEase+DeleteAttribute..ctor [constructor]: string
WoofWare.Myriad.Plugins.RestEase+GetAttribute inherit System.Attribute WoofWare.Myriad.Plugins.RestEase+GetAttribute inherit System.Attribute

View File

@@ -1,10 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
<!--
Known high severity vulnerability
I have not yet seen a single instance where I care about this warning
-->
<NoWarn>$(NoWarn),NU1903</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -12,10 +17,10 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.40" /> <PackageReference Include="ApiSurface" Version="4.1.5" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
<PackageReference Include="NUnit" Version="4.1.0"/> <PackageReference Include="NUnit" Version="4.2.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

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

View File

@@ -1,5 +1,5 @@
{ {
"version": "3.1", "version": "3.6",
"publicReleaseRefSpec": [ "publicReleaseRefSpec": [
"^refs/heads/main$" "^refs/heads/main$"
], ],
@@ -10,6 +10,6 @@
":/Directory.Build.props", ":/Directory.Build.props",
":/global.json", ":/global.json",
"./", "./",
"^./Test" ":^Test"
] ]
} }

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

@@ -0,0 +1,706 @@
namespace WoofWare.Myriad.Plugins.Test
open System
open System.Threading
open NUnit.Framework
open FsUnitTyped
open ConsumePlugin
open FsCheck
[<TestFixture>]
module TestArgParser =
[<TestCase true>]
[<TestCase false>]
let ``Positionals get parsed: they don't have to be strings`` (sep : bool) =
let getEnvVar (_ : string) = failwith "should not call"
let property
(fooSep : bool)
(barSep : bool)
(bazSep : bool)
(pos0 : int list)
(pos1 : int list)
(pos2 : int list)
(pos3 : int list)
(pos4 : int list)
=
let args =
[
yield! pos0 |> List.map string<int>
if fooSep then
yield "--foo=3"
else
yield "--foo"
yield "3"
yield! pos1 |> List.map string<int>
if barSep then
yield "--bar=4"
else
yield "--bar"
yield "4"
yield! pos2 |> List.map string<int>
if bazSep then
yield "--baz=true"
else
yield "--baz"
yield "true"
yield! pos3 |> List.map string<int>
if sep then
yield "--"
yield! pos4 |> List.map string<int>
]
BasicWithIntPositionals.parse' getEnvVar args
|> shouldEqual
{
Foo = 3
Bar = "4"
Baz = true
Rest = pos0 @ pos1 @ pos2 @ pos3 @ pos4
}
Check.QuickThrowOnFailure property
[<Test>]
let ``Arg-like thing appearing before double dash`` () =
let envCalls = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment envCalls |> ignore<int>
""
let args = [ "--foo=3" ; "--non-existent" ; "--bar=4" ; "--baz=true" ]
let exc =
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar args |> ignore<Basic>)
envCalls.Value |> shouldEqual 0
exc.Message
|> shouldEqual
"""Unable to process supplied arg --non-existent. Help text follows.
--foo int32 : This is a foo!
--bar string
--baz bool
--rest string (positional args) (can be repeated) : Here's where the rest of the args go"""
[<Test>]
let ``Can supply positional args with key`` () =
let envCalls = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment envCalls |> ignore<int>
""
let property (args : (int * bool) list) (afterDoubleDash : int list option) =
let flatArgs =
args
|> List.collect (fun (value, sep) ->
if sep then
[ $"--rest=%i{value}" ]
else
[ "--rest" ; string<int> value ]
)
|> fun l -> l @ [ "--foo=3" ; "--bar=4" ; "--baz=true" ]
let flatArgs, expected =
match afterDoubleDash with
| None -> flatArgs, List.map fst args
| Some rest -> flatArgs @ [ "--" ] @ (List.map string<int> rest), List.map fst args @ rest
BasicWithIntPositionals.parse' getEnvVar flatArgs
|> shouldEqual
{
Foo = 3
Bar = "4"
Baz = true
Rest = expected
}
Check.QuickThrowOnFailure property
envCalls.Value |> shouldEqual 0
[<Test>]
let ``Consume multiple occurrences of required arg`` () =
let envCalls = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment envCalls |> ignore<int>
""
let args = [ "--foo=3" ; "--rest" ; "7" ; "--bar=4" ; "--baz=true" ; "--rest=8" ]
let result = BasicNoPositionals.parse' getEnvVar args
envCalls.Value |> shouldEqual 0
result
|> shouldEqual
{
Foo = 3
Bar = "4"
Baz = true
Rest = [ 7 ; 8 ]
}
[<Test>]
let ``Gracefully handle invalid multiple occurrences of required arg`` () =
let envCalls = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment envCalls |> ignore<int>
""
let args = [ "--foo=3" ; "--foo" ; "9" ; "--bar=4" ; "--baz=true" ; "--baz=false" ]
let exc =
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar args |> ignore<Basic>)
envCalls.Value |> shouldEqual 0
exc.Message
|> shouldEqual
"""Errors during parse!
Argument '--foo' was supplied multiple times: 3 and 9
Argument '--baz' was supplied multiple times: True and false"""
[<Test>]
let ``Args appearing after double dash are positional`` () =
let envCalls = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment envCalls |> ignore<int>
""
let args = [ "--" ; "--foo=3" ; "--bar=4" ; "--baz=true" ]
let exc =
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar args |> ignore<Basic>)
exc.Message
|> shouldEqual
"""Errors during parse!
Required argument '--foo' received no value
Required argument '--bar' received no value
Required argument '--baz' received no value"""
envCalls.Value |> shouldEqual 0
[<Test>]
let ``Help text`` () =
let getEnvVar (s : string) =
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
"hi!"
let exc =
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar [ "--help" ] |> ignore<Basic>)
exc.Message
|> shouldEqual
"""Help text requested.
--foo int32 : This is a foo!
--bar string
--baz bool
--rest string (positional args) (can be repeated) : Here's where the rest of the args go"""
[<Test>]
let ``Help text, with default values`` () =
let envVars = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment envVars |> ignore<int>
""
let exc =
Assert.Throws<exn> (fun () -> LoadsOfTypes.parse' getEnvVar [ "--help" ] |> ignore<LoadsOfTypes>)
exc.Message
|> shouldEqual
"""Help text requested.
--foo int32
--bar string
--baz bool
--some-file FileInfo
--some-directory DirectoryInfo
--some-list DirectoryInfo (can be repeated)
--optional-thing-with-no-default int32 (optional)
--optional-thing bool (default value: True)
--another-optional-thing int32 (default value: 3)
--yet-another-optional-thing string (default value populated from env var CONSUMEPLUGIN_THINGS)
--positionals int32 (positional args) (can be repeated)"""
envVars.Value |> shouldEqual 0
[<Test>]
let ``Default values`` () =
let getEnvVar (s : string) =
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
"hi!"
let args =
[
"--foo"
"3"
"--bar=some string"
"--baz"
"--some-file=/path/to/file"
"--some-directory"
"/a/dir"
"--another-optional-thing"
"3000"
]
let result = LoadsOfTypes.parse' getEnvVar args
result.OptionalThing |> shouldEqual (Choice2Of2 true)
result.OptionalThingWithNoDefault |> shouldEqual None
result.AnotherOptionalThing |> shouldEqual (Choice1Of2 3000)
result.YetAnotherOptionalThing |> shouldEqual (Choice2Of2 "hi!")
[<Test>]
let ``ParseExact and help`` () =
let count = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment count |> ignore<int>
""
let exc =
Assert.Throws<exn> (fun () -> DatesAndTimes.parse' getEnvVar [ "--help" ] |> ignore<DatesAndTimes>)
exc.Message
|> shouldEqual
@"Help text requested.
--plain TimeSpan
--invariant TimeSpan
--exact TimeSpan : An exact time please [Parse format (.NET): hh\:mm\:ss]
--invariant-exact TimeSpan : [Parse format (.NET): hh\:mm\:ss]"
count.Value |> shouldEqual 0
[<Test>]
let rec ``TimeSpans and their attributes`` () =
let count = ref 0
let getEnvVar (_ : string) =
Interlocked.Increment count |> ignore<int>
""
let parsed =
DatesAndTimes.parse'
getEnvVar
[
"--exact=11:34:00"
"--plain=1"
"--invariant=23:59"
"--invariant-exact=23:59:00"
]
parsed.Plain |> shouldEqual (TimeSpan (1, 0, 0, 0))
parsed.Invariant |> shouldEqual (TimeSpan (23, 59, 00))
parsed.Exact |> shouldEqual (TimeSpan (11, 34, 00))
parsed.InvariantExact |> shouldEqual (TimeSpan (23, 59, 00))
let exc =
Assert.Throws<exn> (fun () ->
DatesAndTimes.parse'
getEnvVar
[
"--exact=11:34:00"
"--plain=1"
"--invariant=23:59"
"--invariant-exact=23:59"
]
|> ignore<DatesAndTimes>
)
exc.Message
|> shouldEqual
"""Errors during parse!
Input string was not in a correct format. (at arg --invariant-exact=23:59)
Required argument '--invariant-exact' received no value"""
let exc =
Assert.Throws<exn> (fun () ->
DatesAndTimes.parse'
getEnvVar
[
"--exact=11:34"
"--plain=1"
"--invariant=23:59"
"--invariant-exact=23:59:00"
]
|> ignore<DatesAndTimes>
)
exc.Message
|> shouldEqual
"""Errors during parse!
Input string was not in a correct format. (at arg --exact=11:34)
Required argument '--exact' received no value"""
count.Value |> shouldEqual 0
[<Test>]
let ``Can consume stacked record without positionals`` () =
let getEnvVar (_ : string) = failwith "should not call"
let parsed =
ParentRecord.parse' getEnvVar [ "--and-another=true" ; "--thing1=9" ; "--thing2=a thing!" ]
parsed
|> shouldEqual
{
Child =
{
Thing1 = 9
Thing2 = "a thing!"
}
AndAnother = true
}
[<Test>]
let ``Can consume stacked record, child has positionals`` () =
let getEnvVar (_ : string) = failwith "should not call"
let parsed =
ParentRecordChildPos.parse'
getEnvVar
[
"--and-another=true"
"--thing1=9"
"--thing2=https://example.com"
"--thing2=http://example.com"
]
parsed.AndAnother |> shouldEqual true
parsed.Child.Thing1 |> shouldEqual 9
parsed.Child.Thing2
|> List.map (fun (x : Uri) -> x.ToString ())
|> shouldEqual [ "https://example.com/" ; "http://example.com/" ]
[<Test>]
let ``Can consume stacked record, child has no positionals, parent has positionals`` () =
let getEnvVar (_ : string) = failwith "should not call"
let parsed =
ParentRecordSelfPos.parse'
getEnvVar
[
"--and-another=true"
"--and-another=false"
"--and-another=true"
"--thing1=9"
"--thing2=some"
]
parsed
|> shouldEqual
{
Child =
{
Thing1 = 9
Thing2 = "some"
}
AndAnother = [ true ; false ; true ]
}
[<Test>]
let ``Help text for stacked records`` () =
let getEnvVar (_ : string) = failwith "should not call"
let exc =
Assert.Throws<exn> (fun () ->
ParentRecordSelfPos.parse' getEnvVar [ "--help" ] |> ignore<ParentRecordSelfPos>
)
exc.Message
|> shouldEqual
"""Help text requested.
--thing1 int32
--thing2 string
--and-another bool (positional args) (can be repeated)"""
[<Test>]
let ``Positionals are tagged with Choice`` () =
let getEnvVar (_ : string) = failwith "should not call"
ChoicePositionals.parse' getEnvVar [ "a" ; "b" ; "--" ; "--c" ; "--help" ]
|> shouldEqual
{
Args = [ Choice1Of2 "a" ; Choice1Of2 "b" ; Choice2Of2 "--c" ; Choice2Of2 "--help" ]
}
let boolCases =
[
"1", true
"0", false
"true", true
"false", false
"TRUE", true
"FALSE", false
]
|> List.map TestCaseData
[<TestCaseSource(nameof (boolCases))>]
let ``Bool env vars can be populated`` (envValue : string, boolValue : bool) =
let getEnvVar (s : string) =
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
envValue
ContainsBoolEnvVar.parse' getEnvVar []
|> shouldEqual
{
BoolVar = Choice2Of2 boolValue
}
[<Test>]
let ``Bools can be treated with arity 0`` () =
let getEnvVar (_ : string) = failwith "do not call"
ContainsBoolEnvVar.parse' getEnvVar [ "--bool-var" ]
|> shouldEqual
{
BoolVar = Choice1Of2 true
}
[<TestCaseSource(nameof boolCases)>]
let ``Flag DUs can be parsed from env var`` (envValue : string, boolValue : bool) =
let getEnvVar (s : string) =
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
envValue
let boolValue = if boolValue then DryRunMode.Dry else DryRunMode.Wet
ContainsFlagEnvVar.parse' getEnvVar []
|> shouldEqual
{
DryRun = Choice2Of2 boolValue
}
let dryRunData =
[
[ "--dry-run" ], DryRunMode.Dry
[ "--dry-run" ; "true" ], DryRunMode.Dry
[ "--dry-run=true" ], DryRunMode.Dry
[ "--dry-run" ; "True" ], DryRunMode.Dry
[ "--dry-run=True" ], DryRunMode.Dry
[ "--dry-run" ; "false" ], DryRunMode.Wet
[ "--dry-run=false" ], DryRunMode.Wet
[ "--dry-run" ; "False" ], DryRunMode.Wet
[ "--dry-run=False" ], DryRunMode.Wet
]
|> List.map TestCaseData
[<TestCaseSource(nameof dryRunData)>]
let ``Flag DUs can be parsed`` (args : string list, expected : DryRunMode) =
let getEnvVar (_ : string) = failwith "do not call"
ContainsFlagEnvVar.parse' getEnvVar args
|> shouldEqual
{
DryRun = Choice1Of2 expected
}
[<TestCaseSource(nameof dryRunData)>]
let ``Flag DUs can be parsed, ArgumentDefaultFunction`` (args : string list, expected : DryRunMode) =
let getEnvVar (_ : string) = failwith "do not call"
ContainsFlagDefaultValue.parse' getEnvVar args
|> shouldEqual
{
DryRun = Choice1Of2 expected
}
[<Test>]
let ``Flag DUs can be given a default value`` () =
let getEnvVar (_ : string) = failwith "do not call"
ContainsFlagDefaultValue.parse' getEnvVar []
|> shouldEqual
{
DryRun = Choice2Of2 DryRunMode.Wet
}
[<Test>]
let ``Help text for flag DU`` () =
let getEnvVar (_ : string) = failwith "do not call"
let exc =
Assert.Throws<exn> (fun () ->
ContainsFlagDefaultValue.parse' getEnvVar [ "--help" ]
|> ignore<ContainsFlagDefaultValue>
)
exc.Message
|> shouldEqual
"""Help text requested.
--dry-run bool (default value: false)"""
[<Test>]
let ``Help text for flag DU, non default`` () =
let getEnvVar (_ : string) = failwith "do not call"
let exc =
Assert.Throws<exn> (fun () -> WithFlagDu.parse' getEnvVar [ "--help" ] |> ignore<WithFlagDu>)
exc.Message
|> shouldEqual
"""Help text requested.
--dry-run bool"""
let longFormCases =
let doTheThing =
[
[ "--do-something-else=foo" ]
[ "--anotherarg=foo" ]
[ "--do-something-else" ; "foo" ]
[ "--anotherarg" ; "foo" ]
]
let someFlag =
[
[ "--turn-it-on" ], true
[ "--dont-turn-it-off" ], true
[ "--turn-it-on=true" ], true
[ "--dont-turn-it-off=true" ], true
[ "--turn-it-on=false" ], false
[ "--dont-turn-it-off=false" ], false
[ "--turn-it-on" ; "true" ], true
[ "--dont-turn-it-off" ; "true" ], true
[ "--turn-it-on" ; "false" ], false
[ "--dont-turn-it-off" ; "false" ], false
]
List.allPairs doTheThing someFlag
|> List.map (fun (doTheThing, (someFlag, someFlagResult)) ->
let args = doTheThing @ someFlag
let expected =
{
DoTheThing = "foo"
SomeFlag = someFlagResult
}
args, expected
)
|> List.map TestCaseData
[<TestCaseSource(nameof longFormCases)>]
let ``Long-form args`` (args : string list, expected : ManyLongForms) =
let getEnvVar (_ : string) = failwith "do not call"
ManyLongForms.parse' getEnvVar args |> shouldEqual expected
[<Test>]
let ``Long-form args can't be referred to by their original name`` () =
let getEnvVar (_ : string) = failwith "do not call"
let exc =
Assert.Throws<exn> (fun () ->
ManyLongForms.parse' getEnvVar [ "--do-the-thing=foo" ] |> ignore<ManyLongForms>
)
exc.Message
|> shouldEqual """Unable to process argument --do-the-thing=foo as key --do-the-thing and value foo"""
[<Test>]
let ``Long-form args help text`` () =
let getEnvVar (_ : string) = failwith "do not call"
let exc =
Assert.Throws<exn> (fun () -> ManyLongForms.parse' getEnvVar [ "--help" ] |> ignore<ManyLongForms>)
exc.Message
|> shouldEqual
"""Help text requested.
--do-something-else / --anotherarg string
--turn-it-on / --dont-turn-it-off bool"""
[<Test>]
let ``Can collect *all* non-help args into positional args with includeFlagLike`` () =
let getEnvVar (_ : string) = failwith "do not call"
FlagsIntoPositionalArgs.parse' getEnvVar [ "--a" ; "foo" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|> shouldEqual
{
A = "foo"
GrabEverything = [ "--b=false" ; "--c" ; "hi" ; "--help" ]
}
// Users might consider this eccentric!
// But we're only a simple arg parser; we don't look around to see whether this is "almost"
// a valid parse.
FlagsIntoPositionalArgs.parse' getEnvVar [ "--a" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|> shouldEqual
{
A = "--b=false"
GrabEverything = [ "--c" ; "hi" ; "--help" ]
}
[<Test>]
let ``Can collect non-help args into positional args with Choice`` () =
let getEnvVar (_ : string) = failwith "do not call"
FlagsIntoPositionalArgsChoice.parse' getEnvVar [ "--a" ; "foo" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|> shouldEqual
{
A = "foo"
GrabEverything =
[
Choice1Of2 "--b=false"
Choice1Of2 "--c"
Choice1Of2 "hi"
Choice2Of2 "--help"
]
}
[<Test>]
let ``Can collect non-help args into positional args, and we parse on the way`` () =
let getEnvVar (_ : string) = failwith "do not call"
FlagsIntoPositionalArgsInt.parse' getEnvVar [ "3" ; "--a" ; "foo" ; "5" ; "--" ; "98" ]
|> shouldEqual
{
A = "foo"
GrabEverything = [ 3 ; 5 ; 98 ]
}
[<Test>]
let ``Can collect non-help args into positional args with Choice, and we parse on the way`` () =
let getEnvVar (_ : string) = failwith "do not call"
FlagsIntoPositionalArgsIntChoice.parse' getEnvVar [ "3" ; "--a" ; "foo" ; "5" ; "--" ; "98" ]
|> shouldEqual
{
A = "foo"
GrabEverything = [ Choice1Of2 3 ; Choice1Of2 5 ; Choice2Of2 98 ]
}
[<Test>]
let ``Can refuse to collect non-help args with PositionalArgs false`` () =
let getEnvVar (_ : string) = failwith "do not call"
let exc =
Assert.Throws<exn> (fun () ->
FlagsIntoPositionalArgs'.parse'
getEnvVar
[ "--a" ; "foo" ; "--b=false" ; "--c" ; "hi" ; "--" ; "--help" ]
|> ignore<FlagsIntoPositionalArgs'>
)
exc.Message
|> shouldEqual """Unable to process argument --b=false as key --b and value false"""
let exc =
Assert.Throws<exn> (fun () ->
FlagsIntoPositionalArgs'.parse' getEnvVar [ "--a" ; "--b=false" ; "--c=hi" ; "--" ; "--help" ]
|> ignore<FlagsIntoPositionalArgs'>
)
// Again perhaps eccentric!
// Again, we don't try to detect that the user has missed out the desired argument to `--a`.
exc.Message
|> shouldEqual """Unable to process argument --c=hi as key --c and value hi"""

View File

@@ -43,7 +43,7 @@ module TestGift =
member _.WithACard g message = member _.WithACard g message =
$"%s{g} with a card saying '%s{message}'" $"%s{g} with a card saying '%s{message}'"
member _.Wrapped g paper = $"%s{g} wrapped in %A{paper} paper" member _.Wrapped g paper = $"%s{g} wrapped in %O{paper} paper"
} }
} }

View File

@@ -9,18 +9,18 @@ open FsUnitTyped
[<TestFixture>] [<TestFixture>]
module TestBasePath = module TestBasePath =
let replyWithUrl (message : HttpRequestMessage) : HttpResponseMessage Async =
async {
message.Method |> shouldEqual HttpMethod.Get
let content = new StringContent (message.RequestUri.ToString ())
let resp = new HttpResponseMessage (HttpStatusCode.OK)
resp.Content <- content
return resp
}
[<Test>] [<Test>]
let ``Base address is respected`` () = let ``Base address is respected`` () =
let proc (message : HttpRequestMessage) : HttpResponseMessage Async = use client = HttpClientMock.makeNoUri replyWithUrl
async {
message.Method |> shouldEqual HttpMethod.Get
let content = new StringContent (message.RequestUri.ToString ())
let resp = new HttpResponseMessage (HttpStatusCode.OK)
resp.Content <- content
return resp
}
use client = HttpClientMock.makeNoUri proc
let api = PureGymApi.make client let api = PureGymApi.make client
let observedUri = api.GetPathParam("param").Result let observedUri = api.GetPathParam("param").Result
@@ -28,38 +28,28 @@ module TestBasePath =
[<Test>] [<Test>]
let ``Without a base address attr but with BaseAddress on client, request goes through`` () = let ``Without a base address attr but with BaseAddress on client, request goes through`` () =
let proc (message : HttpRequestMessage) : HttpResponseMessage Async = use client = HttpClientMock.make (Uri "https://baseaddress.com") replyWithUrl
async {
message.Method |> shouldEqual HttpMethod.Get
let content = new StringContent (message.RequestUri.ToString ())
let resp = new HttpResponseMessage (HttpStatusCode.OK)
resp.Content <- content
return resp
}
use client = HttpClientMock.make (System.Uri "https://baseaddress.com") proc
let api = ApiWithoutBaseAddress.make client let api = ApiWithoutBaseAddress.make client
let observedUri = api.GetPathParam("param").Result let observedUri = api.GetPathParam("param").Result
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param" observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
[<Test>] [<Test>]
let ``Without a base address attr or BaseAddress on client, request throws`` () = let ``Base address on client takes precedence`` () =
let proc (message : HttpRequestMessage) : HttpResponseMessage Async = use client = HttpClientMock.make (Uri "https://baseaddress.com") replyWithUrl
async { let api = PureGymApi.make client
message.Method |> shouldEqual HttpMethod.Get
let content = new StringContent (message.RequestUri.ToString ())
let resp = new HttpResponseMessage (HttpStatusCode.OK)
resp.Content <- content
return resp
}
use client = HttpClientMock.makeNoUri proc let observedUri = api.GetPathParam("param").Result
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
[<Test>]
let ``Without a base address attr or BaseAddress on client, request throws`` () =
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithoutBaseAddress.make client let api = ApiWithoutBaseAddress.make client
let observedExc = let observedExc =
async { async {
let! result = api.GetPathParam ("param") |> Async.AwaitTask |> Async.Catch let! result = api.GetPathParam "param" |> Async.AwaitTask |> Async.Catch
match result with match result with
| Choice1Of2 _ -> return failwith "test failure" | Choice1Of2 _ -> return failwith "test failure"
@@ -78,3 +68,103 @@ module TestBasePath =
observedExc.Message observedExc.Message
|> shouldEqual |> shouldEqual
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')" "No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
[<Test>]
let ``Relative base path, no base address, relative attribute`` () : unit =
do
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithBasePath.make client
let exc =
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
exc.InnerException.Message
|> shouldEqual
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
let api = ApiWithBasePath.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/thing/foo/endpoint/hi"
[<Test>]
let ``Relative base path, base address, relative attribute`` () : unit =
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithBasePathAndAddress.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/thing/foo/endpoint/hi"
[<Test>]
let ``Absolute base path, no base address, relative attribute`` () : unit =
do
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithAbsoluteBasePath.make client
let exc =
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
exc.InnerException.Message
|> shouldEqual
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
let api = ApiWithAbsoluteBasePath.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/foo/endpoint/hi"
[<Test>]
let ``Absolute base path, base address, relative attribute`` () : unit =
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithAbsoluteBasePathAndAddress.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/foo/endpoint/hi"
[<Test>]
let ``Relative base path, no base address, absolute attribute`` () : unit =
do
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithBasePathAndAbsoluteEndpoint.make client
let exc =
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
exc.InnerException.Message
|> shouldEqual
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
let api = ApiWithBasePathAndAbsoluteEndpoint.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/endpoint/hi"
[<Test>]
let ``Relative base path, base address, absolute attribute`` () : unit =
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithBasePathAndAddressAndAbsoluteEndpoint.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/endpoint/hi"
[<Test>]
let ``Absolute base path, no base address, absolute attribute`` () : unit =
do
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithAbsoluteBasePathAndAbsoluteEndpoint.make client
let exc =
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
exc.InnerException.Message
|> shouldEqual
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
let api = ApiWithAbsoluteBasePathAndAbsoluteEndpoint.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/endpoint/hi"
[<Test>]
let ``Absolute base path, base address, absolute attribute`` () : unit =
use client = HttpClientMock.makeNoUri replyWithUrl
let api = ApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint.make client
let result = api.GetPathParam("hi").Result
result |> shouldEqual "https://whatnot.com/endpoint/hi"

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,23 @@ module TestJsonSerde =
C = c C = c
D = d D = d
E = e |> Array.map _.Get E = e |> Array.map _.Get
Arr = arr
Byte = byte
Sbyte = sbyte
I = i
I32 = i32
I64 = i64
U = u
U32 = u32
U64 = u64
F = f F = f
F32 = f32
Single = single
IntMeasureOption = intMeasureOption
IntMeasureNullable = intMeasureNullable
Enum = enum<SomeEnum> someEnum
Timestamp = timestamp
Unit = ()
} }
} }
@@ -104,6 +135,82 @@ 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)
Unit = ()
}
let expected =
"""{
"a": 3,
"b": "hello!",
"c": [1, -9],
"d": {
"it\u0027s-a-me": "dfe24db5-9f8d-447b-8463-4c0bcf1166d5",
"map": {},
"readOnlyDict": {},
"dict": {},
"concreteDict": {}
},
"e": ["I\u0027m-a-string"],
"arr": [-18883, 9100],
"byte": 87,
"sbyte": 89,
"i": 199993345,
"i32": -485832,
"i64": -13458625689,
"u": 458582,
"u32": 857362147,
"u64": 1234567892123414596,
"f": 8833345667.1,
"f32": 1000.98,
"single": 0.334,
"intMeasureOption": 981,
"intMeasureNullable": -883,
"enum": 1,
"timestamp": "2024-07-01T17:54:00.0000000\u002B01:00",
"unit": {}
}
"""
|> fun s -> s.ToCharArray ()
|> Array.filter (fun c -> not (Char.IsWhiteSpace c))
|> fun s -> new String (s)
JsonRecordTypeWithBoth.toJsonNode(data).ToJsonString () |> shouldEqual expected
JsonRecordTypeWithBoth.jsonParse (JsonNode.Parse expected) |> shouldEqual data
[<Test>] [<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 +247,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 +256,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 =
@@ -193,12 +299,176 @@ module TestJsonSerde =
let decompose = FSharpValue.PreComputeUnionTagReader typeof<FirstDu> let decompose = FSharpValue.PreComputeUnionTagReader typeof<FirstDu>
Gen.listOf duGen let mutable i = 0
|> Gen.eval 100 (StdGen.StdGen (rand.Next (), rand.Next ()))
|> List.iter (fun du -> while i < 10_000 && Array.exists (fun i -> i = 0) counts do
let du = Gen.eval 10 (StdGen.StdGen (rand.Next (), rand.Next ())) duGen
let tag = decompose du let tag = decompose du
counts.[tag] <- counts.[tag] + 1 counts.[tag] <- counts.[tag] + 1
) i <- i + 1
for i in counts do for i in counts do
i |> shouldBeGreaterThan 0 i |> shouldBeGreaterThan 0
let dict<'a, 'b when 'a : equality> (xs : ('a * 'b) seq) : Dictionary<'a, 'b> =
let result = Dictionary ()
for k, v in xs do
result.Add (k, v)
result
let inline makeJsonArr< ^t, ^u when ^u : (static member op_Implicit : ^t -> JsonNode) and ^u :> JsonNode>
(arr : ^t seq)
: JsonNode
=
let result = JsonArray ()
for a in arr do
result.Add a
result :> JsonNode
let normalise (d : Dictionary<'a, 'b>) : ('a * 'b) list =
d |> Seq.map (fun (KeyValue (a, b)) -> a, b) |> Seq.toList |> List.sortBy fst
[<Test>]
let ``Can collect extension data`` () =
let str =
"""{
"message": { "header": "hi", "value": "bye" },
"something": 3,
"arr": ["egg", "toast"],
"str": "whatnot"
}"""
|> JsonNode.Parse
let expected =
{
Rest =
[
"something", JsonNode.op_Implicit 3
"arr", makeJsonArr [| "egg" ; "toast" |]
"str", JsonNode.op_Implicit "whatnot"
]
|> dict
Message =
Some
{
Header = "hi"
Value = "bye"
}
}
let actual = CollectRemaining.jsonParse str
actual.Message |> shouldEqual expected.Message
normalise actual.Rest
|> List.map (fun (k, v) -> k, v.ToJsonString ())
|> shouldEqual (normalise expected.Rest |> List.map (fun (k, v) -> k, v.ToJsonString ()))
[<Test>]
let ``Can write out extension data`` () =
let expected =
"""{"message":{"header":"hi","value":"bye"},"something":3,"arr":["egg","toast"],"str":"whatnot"}"""
let toWrite =
{
Rest =
[
"something", JsonNode.op_Implicit 3
"arr", makeJsonArr [| "egg" ; "toast" |]
"str", JsonNode.op_Implicit "whatnot"
]
|> dict
Message =
Some
{
Header = "hi"
Value = "bye"
}
}
let actual = CollectRemaining.toJsonNode toWrite |> fun s -> s.ToJsonString ()
actual |> shouldEqual expected
[<Test>]
let ``Can collect extension data, nested`` () =
let str =
"""{
"thing": 99,
"baz": -123,
"remaining": {
"message": { "header": "hi", "value": "bye" },
"something": 3,
"arr": ["egg", "toast"],
"str": "whatnot"
}
}"""
|> JsonNode.Parse
let expected : OuterCollectRemaining =
{
Remaining =
{
Message =
Some
{
Header = "hi"
Value = "bye"
}
Rest =
[
"something", JsonNode.op_Implicit 3
"arr", makeJsonArr [| "egg" ; "toast" |]
"str", JsonNode.op_Implicit "whatnot"
]
|> dict
}
Others = [ "thing", 99 ; "baz", -123 ] |> dict
}
let actual = OuterCollectRemaining.jsonParse str
normalise actual.Others |> shouldEqual (normalise expected.Others)
let actual = actual.Remaining
let expected = expected.Remaining
actual.Message |> shouldEqual expected.Message
normalise actual.Rest
|> List.map (fun (k, v) -> k, v.ToJsonString ())
|> shouldEqual (normalise expected.Rest |> List.map (fun (k, v) -> k, v.ToJsonString ()))
[<Test>]
let ``Can write out extension data, nested`` () =
let expected =
"""{"thing":99,"baz":-123,"remaining":{"message":{"header":"hi","value":"bye"},"something":3,"arr":["egg","toast"],"str":"whatnot"}}"""
let toWrite : OuterCollectRemaining =
{
Others = [ "thing", 99 ; "baz", -123 ] |> dict
Remaining =
{
Rest =
[
"something", JsonNode.op_Implicit 3
"arr", makeJsonArr [| "egg" ; "toast" |]
"str", JsonNode.op_Implicit "whatnot"
]
|> dict
Message =
Some
{
Header = "hi"
Value = "bye"
}
}
}
let actual = OuterCollectRemaining.toJsonNode toWrite |> fun s -> s.ToJsonString ()
actual |> shouldEqual expected

View File

@@ -12,7 +12,8 @@ module TestSurface =
let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly let ``Ensure API surface has not been modified`` () = ApiSurface.assertIdentical assembly
[<Test>] [<Test>]
let ``Check version against remote`` () = // https://github.com/nunit/nunit3-vs-adapter/issues/876
let CheckVersionAgainstRemote () =
MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins" MonotonicVersion.validate assembly "WoofWare.Myriad.Plugins"
[<Test ; Explicit>] [<Test ; Explicit>]

View File

@@ -4,6 +4,11 @@
<TargetFramework>net8.0</TargetFramework> <TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
<!--
Known high severity vulnerability
I have not yet seen a single instance where I care about this warning
-->
<NoWarn>$(NoWarn),NU1903</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
@@ -27,18 +32,19 @@
<Compile Include="TestCataGenerator\TestGift.fs" /> <Compile Include="TestCataGenerator\TestGift.fs" />
<Compile Include="TestCataGenerator\TestMyList.fs" /> <Compile Include="TestCataGenerator\TestMyList.fs" />
<Compile Include="TestCataGenerator\TestMyList2.fs" /> <Compile Include="TestCataGenerator\TestMyList2.fs" />
<Compile Include="TestArgParser\TestArgParser.fs" />
<Compile Include="TestRemoveOptions.fs"/> <Compile Include="TestRemoveOptions.fs"/>
<Compile Include="TestSurface.fs"/> <Compile Include="TestSurface.fs"/>
<None Include="../.github/workflows/dotnet.yaml" /> <None Include="../.github/workflows/dotnet.yaml" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="ApiSurface" Version="4.0.40"/> <PackageReference Include="ApiSurface" Version="4.1.5"/>
<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.11.1"/>
<PackageReference Include="NUnit" Version="4.1.0"/> <PackageReference Include="NUnit" Version="4.2.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.5.0"/> <PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

File diff suppressed because it is too large Load Diff

View File

@@ -1,10 +1,8 @@
namespace WoofWare.Myriad.Plugins namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Fantomas.FCS.Xml open Fantomas.FCS.Xml
open Myriad.Core.AstExtensions
type internal ParameterInfo = type internal ParameterInfo =
{ {
@@ -64,13 +62,114 @@ type internal InterfaceType =
type internal RecordType = type internal RecordType =
{ {
Name : Ident Name : Ident
Fields : SynField seq Fields : SynField list
/// Any additional members which are not record fields.
Members : SynMemberDefns option Members : SynMemberDefns option
XmlDoc : PreXmlDoc option XmlDoc : PreXmlDoc option
Generics : SynTyparDecls option Generics : SynTyparDecls option
Accessibility : SynAccess option TypeAccessibility : SynAccess option
ImplAccessibility : SynAccess option
Attributes : SynAttribute list
} }
/// Parse from the AST.
static member OfRecord
(sci : SynComponentInfo)
(smd : SynMemberDefns)
(access : SynAccess option)
(recordFields : SynField list)
: RecordType
=
match sci with
| SynComponentInfo.SynComponentInfo (attrs, typars, _, longId, doc, _, implAccess, _) ->
{
Name = List.last longId
Fields = recordFields
Members = if smd.IsEmpty then None else Some smd
XmlDoc = if doc.IsEmpty then None else Some doc
Generics = typars
ImplAccessibility = implAccess
TypeAccessibility = access
Attributes = attrs |> List.collect (fun l -> l.Attributes)
}
/// Methods for manipulating UnionCase.
[<RequireQualifiedAccess>]
module UnionCase =
/// Construct our structured `UnionCase` from an FCS `SynUnionCase`: extract everything
/// we care about from the AST representation.
let ofSynUnionCase (case : SynUnionCase) : UnionCase<Ident option> =
match case with
| SynUnionCase.SynUnionCase (attributes, ident, caseType, xmlDoc, access, _, _) ->
let ident =
match ident with
| SynIdent.SynIdent (ident, _) -> ident
let fields =
match caseType with
| SynUnionCaseKind.Fields cases -> cases
| SynUnionCaseKind.FullType _ -> failwith "unexpected FullType union"
{
Name = ident
XmlDoc = if xmlDoc.IsEmpty then None else Some xmlDoc
Access = access
Attributes = attributes |> List.collect (fun t -> t.Attributes)
Fields = fields |> List.map SynField.extract
}
/// Functorial `map`.
let mapIdentFields<'a, 'b> (f : 'a -> 'b) (unionCase : UnionCase<'a>) : UnionCase<'b> =
{
Attributes = unionCase.Attributes
Name = unionCase.Name
Access = unionCase.Access
XmlDoc = unionCase.XmlDoc
Fields = unionCase.Fields |> List.map (SynField.mapIdent f)
}
/// Everything you need to know about a discriminated union definition.
type internal UnionType =
{
/// The name of the DU: for example, `type Foo = | Blah` has this being `Foo`.
Name : Ident
/// Any additional members which are not union cases.
Members : SynMemberDefns option
/// Any docstring associated with the DU itself (not its cases).
XmlDoc : PreXmlDoc option
/// Generic type parameters this DU takes: `type Foo<'a> = | ...`.
Generics : SynTyparDecls option
/// Attributes of the DU (not its cases): `[<Attr>] type Foo = | ...`
Attributes : SynAttribute list
/// Accessibility modifier of the DU: `type private Foo = ...`
TypeAccessibility : SynAccess option
/// Accessibility modifier of the DU's implementation: `type Foo = private | ...`
ImplAccessibility : SynAccess option
/// The actual DU cases themselves.
Cases : UnionCase<Ident option> list
}
static member OfUnion
(sci : SynComponentInfo)
(smd : SynMemberDefns)
(access : SynAccess option)
(cases : SynUnionCase list)
: UnionType
=
match sci with
| SynComponentInfo.SynComponentInfo (attrs, typars, _, longId, doc, _, implAccess, _) ->
{
Name = List.last longId
Members = if smd.IsEmpty then None else Some smd
XmlDoc = if doc.IsEmpty then None else Some doc
Generics = typars
Attributes = attrs |> List.collect (fun l -> l.Attributes)
TypeAccessibility = access
ImplAccessibility = implAccess
Cases = cases |> List.map UnionCase.ofSynUnionCase
}
/// Anything that is part of an ADT. /// Anything that is part of an ADT.
/// A record is a product of stuff; this type represents one of those stuffs. /// A record is a product of stuff; this type represents one of those stuffs.
type internal AdtNode = type internal AdtNode =
@@ -98,118 +197,30 @@ type internal AdtProduct =
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal AstHelper = module internal AstHelper =
/// Given e.g. "byte", returns "System.Byte". let isEnum (SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _)) : bool =
let qualifyPrimitiveType (typeName : string) : LongIdent option = match repr with
match typeName with | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum _, _) -> true
| "float32" | _ -> false
| "single" -> [ "System" ; "Single" ] |> Some
| "float"
| "double" -> [ "System" ; "Double" ] |> Some
| "byte"
| "uint8" -> [ "System" ; "Byte" ] |> Some
| "sbyte"
| "int8" -> [ "System" ; "SByte" ] |> Some
| "int16" -> [ "System" ; "Int16" ] |> Some
| "int"
| "int32" -> [ "System" ; "Int32" ] |> Some
| "int64" -> [ "System" ; "Int64" ] |> Some
| "uint16" -> [ "System" ; "UInt16" ] |> Some
| "uint"
| "uint32" -> [ "System" ; "UInt32" ] |> Some
| "uint64" -> [ "System" ; "UInt64" ] |> Some
| "char" -> [ "System" ; "Char" ] |> Some
| "decimal" -> [ "System" ; "Decimal" ] |> Some
| _ -> None
|> Option.map (List.map Ident.Create)
let instantiateRecord (fields : (RecordFieldName * SynExpr option) list) : SynExpr = let instantiateRecord (fields : (SynLongIdent * SynExpr) list) : SynExpr =
let fields = let fields =
fields fields
|> List.map (fun (rfn, synExpr) -> SynExprRecordField (rfn, Some range0, synExpr, None)) |> List.map (fun (rfn, synExpr) -> SynExprRecordField ((rfn, true), Some range0, Some synExpr, None))
SynExpr.Record (None, None, fields, range0) SynExpr.Record (None, None, fields, range0)
let defineRecordType (record : RecordType) : SynTypeDefn = let defineRecordType (record : RecordType) : SynTypeDefn =
let repr =
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (None, Seq.toList record.Fields, range0), range0)
let name = let name =
SynComponentInfo.Create ( SynComponentInfo.create record.Name
[ record.Name ], |> SynComponentInfo.setAccessibility record.TypeAccessibility
?xmldoc = record.XmlDoc, |> match record.XmlDoc with
?parameters = record.Generics, | None -> id
access = record.Accessibility | Some doc -> SynComponentInfo.withDocString doc
) |> SynComponentInfo.setGenerics record.Generics
let trivia : SynTypeDefnTrivia = SynTypeDefnRepr.recordWithAccess record.ImplAccessibility (Seq.toList record.Fields)
{ |> SynTypeDefn.create name
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0 |> SynTypeDefn.withMemberDefns (defaultArg record.Members SynMemberDefns.Empty)
EqualsRange = Some range0
WithKeyword = Some range0
}
SynTypeDefn (name, repr, defaultArg record.Members SynMemberDefns.Empty, None, range0, trivia)
let isOptionIdent (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "option", System.StringComparison.OrdinalIgnoreCase) -> true
// TODO: consider Microsoft.FSharp.Option or whatever it is
| _ -> false
let isUnitIdent (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "unit", System.StringComparison.OrdinalIgnoreCase) -> true
| _ -> false
let isListIdent (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true
// TODO: consider FSharpList or whatever it is
| _ -> false
let isArrayIdent (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when
System.String.Equals (i.idText, "array", System.StringComparison.OrdinalIgnoreCase)
|| System.String.Equals (i.idText, "[]", System.StringComparison.Ordinal)
->
true
| _ -> false
let isResponseIdent (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Response" ]
| [ "RestEase" ; "Response" ] -> true
| _ -> false
let isMapIdent (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Map" ] -> true
| _ -> false
let isReadOnlyDictionaryIdent (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "IReadOnlyDictionary" ]
| [ "Generic" ; "IReadOnlyDictionary" ]
| [ "Collections" ; "Generic" ; "IReadOnlyDictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "IReadOnlyDictionary" ] -> true
| _ -> false
let isDictionaryIdent (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Dictionary" ]
| [ "Generic" ; "Dictionary" ]
| [ "Collections" ; "Generic" ; "Dictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "Dictionary" ] -> true
| _ -> false
let isIDictionaryIdent (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "IDictionary" ]
| [ "Generic" ; "IDictionary" ]
| [ "Collections" ; "Generic" ; "IDictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "IDictionary" ] -> true
| _ -> false
let rec private extractOpensFromDecl (moduleDecls : SynModuleDecl list) : SynOpenDeclTarget list = let rec private extractOpensFromDecl (moduleDecls : SynModuleDecl list) : SynOpenDeclTarget list =
moduleDecls moduleDecls
@@ -231,12 +242,12 @@ module internal AstHelper =
| SynType.Paren (inner, _) -> | SynType.Paren (inner, _) ->
let result, _ = convertSigParam inner let result, _ = convertSigParam inner
result, true result, true
| SynType.LongIdent ident -> | SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
{ {
Attributes = [] Attributes = []
IsOptional = false IsOptional = false
Id = None Id = None
Type = SynType.CreateLongIdent ident Type = SynType.createLongIdent ident
}, },
false false
| SynType.SignatureParameter (attrs, opt, id, usedType, _) -> | SynType.SignatureParameter (attrs, opt, id, usedType, _) ->
@@ -254,7 +265,7 @@ module internal AstHelper =
Attributes = [] Attributes = []
IsOptional = false IsOptional = false
Id = None Id = None
Type = SynType.Var (typar, range0) Type = SynType.var typar
}, },
false false
| _ -> failwithf "expected SignatureParameter, got: %+A" ty | _ -> failwithf "expected SignatureParameter, got: %+A" ty
@@ -283,10 +294,6 @@ module internal AstHelper =
} }
| _ -> failwithf "Didn't have alternating type-and-star in interface member definition: %+A" tupleType | _ -> failwithf "Didn't have alternating type-and-star in interface member definition: %+A" tupleType
let toFun (inputs : SynType list) (ret : SynType) : SynType =
(ret, List.rev inputs)
||> List.fold (fun ty input -> SynType.CreateFun (input, ty))
/// Returns the args (where these are tuple types if curried) in order, and the return type. /// Returns the args (where these are tuple types if curried) in order, and the return type.
let rec getType (ty : SynType) : (SynType * bool) list * SynType = let rec getType (ty : SynType) : (SynType * bool) list * SynType =
match ty with match ty with
@@ -299,7 +306,7 @@ module internal AstHelper =
| SynType.Paren (argType, _) -> getType argType, true | SynType.Paren (argType, _) -> getType argType, true
| _ -> getType argType, false | _ -> getType argType, false
((toFun (List.map fst inputArgs) inputRet), hasParen) :: args, ret ((SynType.toFun (List.map fst inputArgs) inputRet), hasParen) :: args, ret
| _ -> [], ty | _ -> [], ty
let private parseMember (slotSig : SynValSig) (flags : SynMemberFlags) : Choice<MemberInfo, PropertyInfo> = let private parseMember (slotSig : SynValSig) (flags : SynMemberFlags) : Choice<MemberInfo, PropertyInfo> =
@@ -356,7 +363,7 @@ module internal AstHelper =
Attributes = [] Attributes = []
IsOptional = false IsOptional = false
Id = None Id = None
Type = SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent ident) Type = SynType.createLongIdent ident
} }
|> List.singleton |> List.singleton
} }
@@ -368,7 +375,7 @@ module internal AstHelper =
Attributes = [] Attributes = []
IsOptional = false IsOptional = false
Id = None Id = None
Type = SynType.Var (typar, range0) Type = SynType.var typar
} }
|> List.singleton |> List.singleton
} }
@@ -522,190 +529,3 @@ module internal AstHelper =
} }
) )
| _ -> failwithf "Failed to get record elements for type that was: %+A" repr | _ -> failwithf "Failed to get record elements for type that was: %+A" repr
[<AutoOpen>]
module internal SynTypePatterns =
let (|OptionType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isOptionIdent ident ->
Some innerType
| _ -> None
let (|UnitType|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident when AstHelper.isUnitIdent ident -> Some ()
| _ -> None
let (|ListType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isListIdent ident ->
Some innerType
| _ -> None
let (|ArrayType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isArrayIdent ident ->
Some innerType
| SynType.Array (1, innerType, _) -> Some innerType
| _ -> None
let (|RestEaseResponseType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when AstHelper.isResponseIdent ident ->
Some innerType
| _ -> None
let (|DictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isDictionaryIdent ident ->
Some (key, value)
| _ -> None
let (|IDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isIDictionaryIdent ident ->
Some (key, value)
| _ -> None
let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when
AstHelper.isReadOnlyDictionaryIdent ident
->
Some (key, value)
| _ -> None
let (|MapType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when AstHelper.isMapIdent ident ->
Some (key, value)
| _ -> None
let (|BigInt|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map _.idText with
| [ "bigint" ]
| [ "BigInteger" ]
| [ "Numerics" ; "BigInteger" ]
| [ "System" ; "Numerics" ; "BigInteger" ] -> Some ()
| _ -> None
| _ -> None
/// Returns the type, qualified as in e.g. `System.Boolean`.
let (|PrimitiveType|_|) (fieldType : SynType) : LongIdent option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] -> AstHelper.qualifyPrimitiveType i.idText
| _ -> None
| _ -> None
let (|String|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] ->
[ "string" ]
|> List.tryFind (fun s -> s = i.idText)
|> Option.map ignore<string>
| _ -> None
| _ -> None
let (|Byte|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] -> [ "byte" ] |> List.tryFind (fun s -> s = i.idText) |> Option.map ignore<string>
| _ -> None
| _ -> None
let (|Guid|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Guid" ]
| [ "Guid" ] -> Some ()
| _ -> None
| _ -> None
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Net" ; "Http" ; "HttpResponseMessage" ]
| [ "Net" ; "Http" ; "HttpResponseMessage" ]
| [ "Http" ; "HttpResponseMessage" ]
| [ "HttpResponseMessage" ] -> Some ()
| _ -> None
| _ -> None
let (|HttpContent|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Net" ; "Http" ; "HttpContent" ]
| [ "Net" ; "Http" ; "HttpContent" ]
| [ "Http" ; "HttpContent" ]
| [ "HttpContent" ] -> Some ()
| _ -> None
| _ -> None
let (|Stream|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "IO" ; "Stream" ]
| [ "IO" ; "Stream" ]
| [ "Stream" ] -> Some ()
| _ -> None
| _ -> None
let (|NumberType|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] -> [ "string" ; "float" ; "int" ; "bool" ] |> List.tryFind (fun s -> s = i.idText)
| _ -> None
| _ -> None
let (|DateOnly|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "DateOnly" ]
| [ "DateOnly" ] -> Some ()
| _ -> None
| _ -> None
let (|DateTime|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "DateTime" ]
| [ "DateTime" ] -> Some ()
| _ -> None
| _ -> None
let (|Uri|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "Uri" ]
| [ "Uri" ] -> Some ()
| _ -> None
| _ -> None
let (|Task|_|) (fieldType : SynType) : SynType option =
match fieldType with
| SynType.App (SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)), _, args, _, _, _, _) ->
match ident |> List.map (fun i -> i.idText) with
| [ "Task" ]
| [ "Tasks" ; "Task" ]
| [ "Threading" ; "Tasks" ; "Task" ]
| [ "System" ; "Threading" ; "Tasks" ; "Task" ] ->
match args with
| [ arg ] -> Some arg
| _ -> failwithf "Expected Task to be applied to exactly one arg, but got: %+A" args
| _ -> None
| _ -> None

File diff suppressed because it is too large Load Diff

View File

@@ -2,9 +2,6 @@ namespace WoofWare.Myriad.Plugins
open System.Net.Http open System.Net.Http
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
open Myriad.Core
type internal HttpClientGeneratorOutputSpec = type internal HttpClientGeneratorOutputSpec =
{ {
@@ -14,7 +11,6 @@ type internal HttpClientGeneratorOutputSpec =
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal HttpClientGenerator = module internal HttpClientGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Myriad.Core.Ast
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
type PathSpec = type PathSpec =
@@ -82,7 +78,7 @@ module internal HttpClientGenerator =
let matchingAttrs = let matchingAttrs =
attrs attrs
|> List.choose (fun attr -> |> List.choose (fun attr ->
match attr.TypeName.AsString with match SynLongIdent.toString attr.TypeName with
| "Get" | "Get"
| "GetAttribute" | "GetAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Get" | "WoofWare.Myriad.Plugins.RestEase.Get"
@@ -144,7 +140,7 @@ module internal HttpClientGenerator =
let extractHeaderInformation (attrs : SynAttribute list) : SynExpr list list = let extractHeaderInformation (attrs : SynAttribute list) : SynExpr list list =
attrs attrs
|> List.choose (fun attr -> |> List.choose (fun attr ->
match attr.TypeName.AsString with match SynLongIdent.toString attr.TypeName with
| "Header" | "Header"
| "RestEase.Header" | "RestEase.Header"
| "WoofWare.Myriad.Plugins.RestEase.Header" -> | "WoofWare.Myriad.Plugins.RestEase.Header" ->
@@ -158,7 +154,7 @@ module internal HttpClientGenerator =
let shouldAllowAnyStatusCode (attrs : SynAttribute list) : bool = let shouldAllowAnyStatusCode (attrs : SynAttribute list) : bool =
attrs attrs
|> List.exists (fun attr -> |> List.exists (fun attr ->
match attr.TypeName.AsString with match SynLongIdent.toString attr.TypeName with
| "AllowAnyStatusCode" | "AllowAnyStatusCode"
| "AllowAnyStatusCodeAttribute" | "AllowAnyStatusCodeAttribute"
| "RestEase.AllowAnyStatusCode" | "RestEase.AllowAnyStatusCode"
@@ -174,35 +170,6 @@ module internal HttpClientGenerator =
(info : MemberInfo) (info : MemberInfo)
: SynMemberDefn : SynMemberDefn
= =
let valInfo =
SynValInfo.SynValInfo (
[
[ SynArgInfo.Empty ]
[
for arg in info.Args do
match arg.Id with
| None -> yield SynArgInfo.CreateIdString (failwith "TODO: create an arg name")
| Some id -> yield SynArgInfo.CreateId id
]
],
SynArgInfo.Empty
)
let valData =
SynValData (
Some
{
IsInstance = true
IsDispatchSlot = false
IsOverrideOrExplicitImpl = true
IsFinal = false
GetterOrSetterIsCompilerGenerated = false
MemberKind = SynMemberKind.Member
},
valInfo,
None
)
let args = let args =
info.Args info.Args
|> List.map (fun arg -> |> List.map (fun arg ->
@@ -213,15 +180,13 @@ module internal HttpClientGenerator =
let argType = let argType =
if arg.IsOptional then if arg.IsOptional then
SynType.CreateApp ( SynType.appPostfix "option" arg.Type
SynType.CreateLongIdent (SynLongIdent.CreateString "option"),
[ arg.Type ],
isPostfix = true
)
else else
arg.Type arg.Type
argName, SynPat.CreateTyped (SynPat.CreateNamed argName, argType) // We'll be tupling these up anyway, so don't need the parens
// around the type annotations.
argName, SynPat.annotateTypeNoParen argType (SynPat.namedI argName)
) )
let cancellationTokenArg = let cancellationTokenArg =
@@ -229,26 +194,6 @@ module internal HttpClientGenerator =
| None -> failwith $"expected an optional cancellation token as final arg in %s{info.Identifier.idText}" | None -> failwith $"expected an optional cancellation token as final arg in %s{info.Identifier.idText}"
| Some (arg, _) -> arg | Some (arg, _) -> arg
let argPats =
let args = args |> List.map snd
SynPat.Tuple (false, args, List.replicate (args.Length - 1) range0, range0)
|> SynPat.CreateParen
|> List.singleton
|> SynArgPats.Pats
let headPat =
let thisIdent = if variableHeaders.IsEmpty then "_" else "this"
SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent [ Ident.Create thisIdent ; info.Identifier ],
None,
None,
argPats,
None,
range0
)
let requestUriTrailer = let requestUriTrailer =
(info.UrlTemplate, info.Args) (info.UrlTemplate, info.Args)
||> List.fold (fun template arg -> ||> List.fold (fun template arg ->
@@ -269,10 +214,10 @@ module internal HttpClientGenerator =
template template
|> SynExpr.callMethodArg |> SynExpr.callMethodArg
"Replace" "Replace"
(SynExpr.CreateParenedTuple (SynExpr.tuple
[ [
SynExpr.CreateConstString ("{" + substituteId + "}") SynExpr.CreateConst ("{" + substituteId + "}")
SynExpr.callMethod "ToString" (SynExpr.CreateIdent varName) SynExpr.callMethod "ToString" (SynExpr.createIdent' varName)
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ] SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
) )
@@ -312,32 +257,25 @@ module internal HttpClientGenerator =
| Some id -> id | Some id -> id
let urlSeparator = let urlSeparator =
// apparent Myriad bug: `IndexOf '?'` gets formatted as `IndexOf ?` which is clearly wrong let questionMark = SynExpr.CreateConst '?'
let questionMark =
SynExpr.CreateConst (SynConst.Int32 63)
|> SynExpr.applyFunction (SynExpr.CreateIdentString "char")
|> SynExpr.CreateParen
let containsQuestion = let containsQuestion =
info.UrlTemplate info.UrlTemplate
|> SynExpr.callMethodArg "IndexOf" questionMark |> SynExpr.callMethodArg "IndexOf" questionMark
|> SynExpr.greaterThanOrEqual (SynExpr.CreateConst (SynConst.Int32 0)) |> SynExpr.greaterThanOrEqual (SynExpr.CreateConst 0)
SynExpr.ifThenElse SynExpr.ifThenElse containsQuestion (SynExpr.CreateConst "?") (SynExpr.CreateConst "&")
containsQuestion |> SynExpr.paren
(SynExpr.CreateConst (SynConst.CreateString "?"))
(SynExpr.CreateConst (SynConst.CreateString "&"))
|> SynExpr.CreateParen
let prefix = let prefix =
SynExpr.CreateIdent firstValueId SynExpr.createIdent' firstValueId
|> SynExpr.toString firstValue.Type |> SynExpr.toString firstValue.Type
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]) SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
) )
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.plus (SynExpr.plus urlSeparator (SynExpr.CreateConstString (firstKey + "="))) |> SynExpr.plus (SynExpr.plus urlSeparator (SynExpr.CreateConst (firstKey + "=")))
(prefix, queryParams) (prefix, queryParams)
||> List.fold (fun uri (paramKey, paramValue) -> ||> List.fold (fun uri (paramKey, paramValue) ->
@@ -346,16 +284,16 @@ module internal HttpClientGenerator =
| None -> failwith "Unable to get parameter variable name from anonymous parameter" | None -> failwith "Unable to get parameter variable name from anonymous parameter"
| Some id -> id | Some id -> id
SynExpr.toString paramValue.Type (SynExpr.CreateIdent paramValueId) SynExpr.toString paramValue.Type (SynExpr.createIdent' paramValueId)
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ] SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
) )
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.plus (SynExpr.plus uri (SynExpr.CreateConstString ("&" + paramKey + "="))) |> SynExpr.plus (SynExpr.plus uri (SynExpr.CreateConst ("&" + paramKey + "=")))
) )
|> SynExpr.plus requestUriTrailer |> SynExpr.plus requestUriTrailer
|> SynExpr.CreateParen |> SynExpr.paren
let requestUri = let requestUri =
let uriIdent = SynExpr.createLongIdent [ "System" ; "Uri" ] let uriIdent = SynExpr.createLongIdent [ "System" ; "Uri" ]
@@ -364,45 +302,55 @@ module internal HttpClientGenerator =
let baseAddress = let baseAddress =
[ [
SynMatchClause.Create ( SynMatchClause.create
SynPat.CreateNull, SynPat.createNull
None, (match info.BaseAddress with
match info.BaseAddress with | None ->
| None -> [
[ SynExpr.applyFunction (SynExpr.createIdent "nameof") (SynExpr.paren baseAddress)
SynExpr.CreateApp (SynExpr.CreateIdentString "nameof", SynExpr.CreateParen baseAddress) SynExpr.CreateConst
SynExpr.CreateConstString "No base address was supplied on the type, and no BaseAddress was on the HttpClient."
"No base address was supplied on the type, and no BaseAddress was on the HttpClient." ]
] |> SynExpr.tuple
|> SynExpr.CreateParenedTuple |> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "ArgumentNullException" ])
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "System" ; "ArgumentNullException" ]) |> SynExpr.paren
|> SynExpr.CreateParen |> SynExpr.applyFunction (SynExpr.createIdent "raise")
|> SynExpr.applyFunction (SynExpr.CreateIdentString "raise") | Some expr -> SynExpr.applyFunction uriIdent expr)
| Some expr -> SynExpr.CreateApp (uriIdent, expr) SynMatchClause.create (SynPat.named "v") (SynExpr.createIdent "v")
)
SynMatchClause.Create (SynPat.CreateNamed (Ident.Create "v"), None, SynExpr.CreateIdentString "v")
] ]
|> SynExpr.createMatch baseAddress |> SynExpr.createMatch baseAddress
|> SynExpr.CreateParen |> SynExpr.paren
SynExpr.App ( let baseAddress =
ExprAtomicFlag.Atomic, match info.BasePath with
false, | None -> baseAddress
uriIdent, | Some basePath ->
SynExpr.CreateParenedTuple
[ [
baseAddress yield baseAddress
SynExpr.CreateApp (
uriIdent, yield
SynExpr.CreateParenedTuple SynExpr.applyFunction
[ uriIdent
requestUriTrailer (SynExpr.tuple
SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ] [ basePath ; SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ] ])
] ]
) |> SynExpr.tuple
], |> SynExpr.applyFunction uriIdent
range0
) [
yield baseAddress
yield
SynExpr.applyFunction
uriIdent
(SynExpr.tuple
[
requestUriTrailer
SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ]
])
]
|> SynExpr.tuple
|> SynExpr.applyFunction uriIdent
let bodyParams = let bodyParams =
info.Args info.Args
@@ -436,56 +384,43 @@ module internal HttpClientGenerator =
let httpReqMessageConstructor = let httpReqMessageConstructor =
[ [
SynExpr.equals SynExpr.equals
(SynExpr.CreateIdentString "Method") (SynExpr.createIdent "Method")
(SynExpr.createLongIdent (SynExpr.createLongIdent
[ "System" ; "Net" ; "Http" ; "HttpMethod" ; httpMethodString info.HttpMethod ]) [ "System" ; "Net" ; "Http" ; "HttpMethod" ; httpMethodString info.HttpMethod ])
SynExpr.equals (SynExpr.CreateIdentString "RequestUri") (SynExpr.CreateIdentString "uri") SynExpr.equals (SynExpr.createIdent "RequestUri") (SynExpr.createIdent "uri")
] ]
|> SynExpr.CreateParenedTuple |> SynExpr.tupleNoParen
let returnExpr = let returnExpr =
match info.TaskReturnType with match info.TaskReturnType with
| HttpResponseMessage -> SynExpr.CreateIdentString "response" | HttpResponseMessage -> SynExpr.createIdent "response"
| String -> SynExpr.CreateIdentString "responseString" | String -> SynExpr.createIdent "responseString"
| Stream -> SynExpr.CreateIdentString "responseStream" | Stream -> SynExpr.createIdent "responseStream"
| RestEaseResponseType contents -> | RestEaseResponseType contents ->
let deserialiser = let deserialiser =
SynExpr.CreateLambda ( JsonParseGenerator.parseNode
[ SynPat.CreateConst SynConst.Unit ], None
SynExpr.CreateParen ( JsonParseGenerator.JsonParseOption.None
JsonParseGenerator.parseNode contents
None (SynExpr.createIdent "jsonNode")
JsonParseGenerator.JsonParseOption.None |> SynExpr.paren
contents |> SynExpr.createThunk
(SynExpr.CreateIdentString "jsonNode")
)
)
// new RestEase.Response (content : string, response : HttpResponseMessage, deserialiser : unit -> 'T) // new RestEase.Response (content : string, response : HttpResponseMessage, deserialiser : unit -> 'T)
SynExpr.New ( SynExpr.createNew
false, (SynType.app' (SynType.createLongIdent' [ "RestEase" ; "Response" ]) [ SynType.Anon range0 ])
SynType.App ( (SynExpr.tupleNoParen
SynType.CreateLongIdent (SynLongIdent.Create [ "RestEase" ; "Response" ]),
Some range0,
[ SynType.Anon range0 ],
[],
Some range0,
false,
range0
),
SynExpr.CreateParenedTuple
[ [
SynExpr.CreateIdentString "responseString" SynExpr.createIdent "responseString"
SynExpr.CreateIdentString "response" SynExpr.createIdent "response"
SynExpr.CreateParen deserialiser deserialiser
], ])
range0
)
| retType -> | retType ->
JsonParseGenerator.parseNode JsonParseGenerator.parseNode
None None
JsonParseGenerator.JsonParseOption.None JsonParseGenerator.JsonParseOption.None
retType retType
(SynExpr.CreateIdentString "jsonNode") (SynExpr.createIdent "jsonNode")
let handleBodyParams = let handleBodyParams =
match bodyParam with match bodyParam with
@@ -498,72 +433,50 @@ module internal HttpClientGenerator =
[ [
Let ( Let (
"queryParams", "queryParams",
SynExpr.New ( SynExpr.createNew
false, (SynType.createLongIdent'
SynType.CreateLongIdent ( [ "System" ; "Net" ; "Http" ; (bodyParamType : BodyParamMethods).ToString () ])
SynLongIdent.Create (SynExpr.createIdent' bodyParamName)
[ "System" ; "Net" ; "Http" ; (bodyParamType : BodyParamMethods).ToString () ]
),
SynExpr.CreateParen (SynExpr.CreateIdent bodyParamName),
range0
)
) )
Do ( Do (
SynExpr.LongIdentSet ( SynExpr.assign
SynLongIdent.Create [ "httpMessage" ; "Content" ], (SynLongIdent.createS' [ "httpMessage" ; "Content" ])
SynExpr.CreateIdentString "queryParams", (SynExpr.createIdent "queryParams")
range0
)
) )
] ]
| BodyParamMethods.HttpContent -> | BodyParamMethods.HttpContent ->
[ [
Do ( Do (
SynExpr.LongIdentSet ( SynExpr.assign
SynLongIdent.Create [ "httpMessage" ; "Content" ], (SynLongIdent.createS' [ "httpMessage" ; "Content" ])
SynExpr.CreateIdent bodyParamName, (SynExpr.createIdent' bodyParamName)
range0
)
) )
] ]
| BodyParamMethods.Serialise ty -> | BodyParamMethods.Serialise ty ->
[ [
Let ( Let (
"queryParams", "queryParams",
SynExpr.New ( SynExpr.createNew
false, (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ])
SynType.CreateLongIdent ( (SynExpr.createIdent' bodyParamName
SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "StringContent" ] |> SynExpr.pipeThroughFunction (fst (JsonSerializeGenerator.serializeNode ty))
), |> SynExpr.pipeThroughFunction (
SynExpr.CreateParen ( SynExpr.createLambda
SynExpr.CreateIdent bodyParamName "node"
|> SynExpr.pipeThroughFunction (JsonSerializeGenerator.serializeNode ty) (SynExpr.ifThenElse
|> SynExpr.pipeThroughFunction ( (SynExpr.applyFunction
SynExpr.createLambda (SynExpr.createIdent "isNull")
"node" (SynExpr.createIdent "node"))
(SynExpr.ifThenElse (SynExpr.applyFunction
(SynExpr.CreateApp ( (SynExpr.createLongIdent [ "node" ; "ToJsonString" ])
SynExpr.CreateIdentString "isNull", (SynExpr.CreateConst ()))
SynExpr.CreateIdentString "node" (SynExpr.CreateConst "null"))
)) ))
(SynExpr.CreateApp (
SynExpr.CreateLongIdent (
SynLongIdent.Create [ "node" ; "ToJsonString" ]
),
SynExpr.CreateConst SynConst.Unit
))
(SynExpr.CreateConst (SynConst.CreateString "null")))
)
),
range0
)
) )
Do ( Do (
SynExpr.LongIdentSet ( SynExpr.assign
SynLongIdent.Create [ "httpMessage" ; "Content" ], (SynLongIdent.createS' [ "httpMessage" ; "Content" ])
SynExpr.CreateIdent (Ident.Create "queryParams"), (SynExpr.createIdent "queryParams")
range0
)
) )
] ]
@@ -572,10 +485,9 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"responseString", "responseString",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.createLongIdent [ "response" ; "Content" ; "ReadAsStringAsync" ], (SynExpr.createLongIdent [ "response" ; "Content" ; "ReadAsStringAsync" ])
SynExpr.CreateIdentString "ct" (SynExpr.createIdent "ct")
)
) )
) )
@@ -583,10 +495,9 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"responseStream", "responseStream",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.createLongIdent [ "response" ; "Content" ; "ReadAsStreamAsync" ], (SynExpr.createLongIdent [ "response" ; "Content" ; "ReadAsStreamAsync" ])
SynExpr.CreateIdentString "ct" (SynExpr.createIdent "ct")
)
) )
) )
@@ -594,47 +505,39 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"jsonNode", "jsonNode",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ; "ParseAsync" ], (SynExpr.createLongIdent
SynExpr.CreateParenedTuple [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ; "ParseAsync" ])
(SynExpr.tuple
[ [
SynExpr.CreateIdentString "responseStream" SynExpr.createIdent "responseStream"
SynExpr.equals SynExpr.equals (SynExpr.createIdent "cancellationToken") (SynExpr.createIdent "ct")
(SynExpr.CreateIdentString "cancellationToken") ])
(SynExpr.CreateIdentString "ct")
]
)
) )
) )
let setVariableHeaders = let setVariableHeaders =
variableHeaders variableHeaders
|> List.map (fun (headerName, callToGetValue) -> |> List.map (fun (headerName, callToGetValue) ->
Do ( [
SynExpr.CreateApp ( headerName
SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ], SynExpr.applyFunction
SynExpr.CreateParenedTuple (SynExpr.createLongIdent'
[ [ Ident.create "this" ; callToGetValue ; Ident.create "ToString" ])
headerName (SynExpr.CreateConst ())
SynExpr.CreateApp ( ]
SynExpr.createLongIdent' |> SynExpr.tuple
[ Ident.Create "this" ; callToGetValue ; Ident.Create "ToString" ], |> SynExpr.applyFunction (SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ])
SynExpr.CreateConst SynConst.Unit |> Do
)
]
)
)
) )
let setConstantHeaders = let setConstantHeaders =
constantHeaders constantHeaders
|> List.map (fun (headerName, headerValue) -> |> List.map (fun (headerName, headerValue) ->
Do ( SynExpr.applyFunction
SynExpr.CreateApp ( (SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ])
SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ], (SynExpr.tuple [ headerName ; headerValue ])
SynExpr.CreateParenedTuple [ headerName ; headerValue ] |> Do
)
)
) )
[ [
@@ -643,14 +546,9 @@ module internal HttpClientGenerator =
yield yield
Use ( Use (
"httpMessage", "httpMessage",
SynExpr.New ( SynExpr.createNew
false, (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "HttpRequestMessage" ])
SynType.CreateLongIdent ( httpReqMessageConstructor
SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "HttpRequestMessage" ]
),
httpReqMessageConstructor,
range0
)
) )
yield! handleBodyParams yield! handleBodyParams
@@ -662,21 +560,18 @@ module internal HttpClientGenerator =
LetBang ( LetBang (
"response", "response",
SynExpr.awaitTask ( SynExpr.awaitTask (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.createLongIdent [ "client" ; "SendAsync" ], (SynExpr.createLongIdent [ "client" ; "SendAsync" ])
SynExpr.CreateParenedTuple (SynExpr.tuple [ SynExpr.createIdent "httpMessage" ; SynExpr.createIdent "ct" ])
[ SynExpr.CreateIdentString "httpMessage" ; SynExpr.CreateIdentString "ct" ]
)
) )
) )
if info.EnsureSuccessHttpCode then if info.EnsureSuccessHttpCode then
yield yield
Let ( Let (
"response", "response",
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.createLongIdent [ "response" ; "EnsureSuccessStatusCode" ], (SynExpr.createLongIdent [ "response" ; "EnsureSuccessStatusCode" ])
SynExpr.CreateConst SynConst.Unit (SynExpr.CreateConst ())
)
) )
match info.TaskReturnType with match info.TaskReturnType with
| HttpResponseMessage -> () | HttpResponseMessage -> ()
@@ -691,30 +586,22 @@ module internal HttpClientGenerator =
yield jsonNode yield jsonNode
] ]
|> SynExpr.createCompExpr "async" returnExpr |> SynExpr.createCompExpr "async" returnExpr
|> SynExpr.startAsTask (SynLongIdent.CreateFromLongIdent [ cancellationTokenArg ]) |> SynExpr.startAsTask cancellationTokenArg
SynBinding.SynBinding ( let thisIdent =
None, if variableHeaders.IsEmpty then "_" else "this"
SynBindingKind.Normal, |> Ident.create
false,
false, let args = args |> List.map snd |> SynPat.tuple |> List.singleton
[],
PreXmlDoc.Empty, SynBinding.basic [ thisIdent ; info.Identifier ] args implementation
valData,
headPat,
None,
implementation,
range0,
DebugPointAtBinding.Yes range0,
SynBinding.triviaZero true
)
|> SynBinding.withAccessibility info.Accessibility |> SynBinding.withAccessibility info.Accessibility
|> fun b -> SynMemberDefn.Member (b, range0) |> SynMemberDefn.memberImplementation
let getHttpAttributes (attrs : SynAttribute list) : HttpAttribute list = let getHttpAttributes (attrs : SynAttribute list) : HttpAttribute list =
attrs attrs
|> List.choose (fun attr -> |> List.choose (fun attr ->
match attr.TypeName.AsString with match SynLongIdent.toString attr.TypeName with
| "RestEase.Query" | "RestEase.Query"
| "RestEase.QueryAttribute" | "RestEase.QueryAttribute"
| "WoofWare.Myriad.Plugins.RestEase.Query" | "WoofWare.Myriad.Plugins.RestEase.Query"
@@ -755,7 +642,7 @@ module internal HttpClientGenerator =
let extractBasePath (attrs : SynAttribute list) : SynExpr option = let extractBasePath (attrs : SynAttribute list) : SynExpr option =
attrs attrs
|> List.tryPick (fun attr -> |> List.tryPick (fun attr ->
match attr.TypeName.AsString with match SynLongIdent.toString attr.TypeName with
| "BasePath" | "BasePath"
| "RestEase.BasePath" | "RestEase.BasePath"
| "WoofWare.Myriad.Plugins.RestEase.BasePath" | "WoofWare.Myriad.Plugins.RestEase.BasePath"
@@ -768,7 +655,7 @@ module internal HttpClientGenerator =
let extractBaseAddress (attrs : SynAttribute list) : SynExpr option = let extractBaseAddress (attrs : SynAttribute list) : SynExpr option =
attrs attrs
|> List.tryPick (fun attr -> |> List.tryPick (fun attr ->
match attr.TypeName.AsString with match SynLongIdent.toString attr.TypeName with
| "BaseAddress" | "BaseAddress"
| "RestEase.BaseAddress" | "RestEase.BaseAddress"
| "WoofWare.Myriad.Plugins.RestEase.BaseAddress" | "WoofWare.Myriad.Plugins.RestEase.BaseAddress"
@@ -778,6 +665,15 @@ module internal HttpClientGenerator =
| _ -> None | _ -> None
) )
let insertTrailingSlash (path : SynExpr) : SynExpr =
match path |> SynExpr.stripOptionalParen with
| SynExpr.Const (SynConst.String (s, _, _), _) ->
if s.EndsWith '/' then
path
else
SynExpr.CreateConst (s + "/")
| _ -> SynExpr.plus (SynExpr.paren path) (SynExpr.CreateConst "/")
let createModule let createModule
(opens : SynOpenDeclTarget list) (opens : SynOpenDeclTarget list)
(ns : LongIdent) (ns : LongIdent)
@@ -807,8 +703,17 @@ module internal HttpClientGenerator =
"Expected constant header parameters to be of the form [<Header (key, value)>], but got more than two args" "Expected constant header parameters to be of the form [<Header (key, value)>], but got more than two args"
) )
let baseAddress = extractBaseAddress interfaceType.Attributes let baseAddress =
let basePath = extractBasePath interfaceType.Attributes extractBaseAddress interfaceType.Attributes
// We artificially insert a trailing slash because this is almost certainly
// not meant to be an endpoint itself.
|> Option.map insertTrailingSlash
let basePath =
extractBasePath interfaceType.Attributes
// We artificially insert a trailing slash because this is almost certainly
// not meant to be an endpoint itself.
|> Option.map insertTrailingSlash
let properties = let properties =
interfaceType.Properties interfaceType.Properties
@@ -816,7 +721,7 @@ module internal HttpClientGenerator =
let headerInfo = let headerInfo =
match extractHeaderInformation pi.Attributes with match extractHeaderInformation pi.Attributes with
| [ [ x ] ] -> x | [ [ x ] ] -> x
| [ xs ] -> | [ _ ] ->
failwith failwith
"Expected exactly one Header parameter on the member, with exactly one arg; got one Header parameter with non-1-many args" "Expected exactly one Header parameter on the member, with exactly one arg; got one Header parameter with non-1-many args"
| [] -> | [] ->
@@ -883,46 +788,13 @@ module internal HttpClientGenerator =
let propertyMembers = let propertyMembers =
properties properties
|> List.map (fun (_, pi) -> |> List.map (fun (_, pi) ->
SynMemberDefn.Member ( SynExpr.createLongIdent' [ Ident.lowerFirstLetter pi.Identifier ]
SynBinding.SynBinding ( |> SynExpr.applyTo (SynExpr.CreateConst ())
pi.Accessibility, |> SynBinding.basic [ Ident.create "_" ; pi.Identifier ] []
SynBindingKind.Normal, |> SynBinding.withReturnAnnotation pi.Type
pi.IsInline, |> SynBinding.setInline pi.IsInline
false, |> SynBinding.withAccessibility pi.Accessibility
[], |> SynMemberDefn.memberImplementation
PreXmlDoc.Empty,
SynValData.SynValData (
Some
{
IsInstance = true
IsDispatchSlot = false
IsOverrideOrExplicitImpl = true
IsFinal = false
GetterOrSetterIsCompilerGenerated = false
MemberKind = SynMemberKind.Member
},
SynValInfo.SynValInfo ([ [ SynArgInfo.Empty ] ; [] ], SynArgInfo.Empty),
None
),
SynPat.CreateLongIdent (
SynLongIdent.CreateFromLongIdent [ Ident.Create "_" ; pi.Identifier ],
[]
),
Some (SynBindingReturnInfo.Create pi.Type),
SynExpr.CreateApp (
SynExpr.createLongIdent' [ Ident.lowerFirstLetter pi.Identifier ],
SynExpr.CreateConst SynConst.Unit
),
range0,
DebugPointAtBinding.Yes range0,
{
LeadingKeyword = SynLeadingKeyword.Member range0
InlineKeyword = if pi.IsInline then Some range0 else None
EqualsRange = Some range0
}
),
range0
)
) )
let members = propertyMembers @ nonPropertyMembers let members = propertyMembers @ nonPropertyMembers
@@ -932,12 +804,12 @@ module internal HttpClientGenerator =
"Extension methods" "Extension methods"
else else
"Module") "Module")
|> sprintf " %s for constructing a REST client." |> sprintf "%s for constructing a REST client."
|> PreXmlDoc.Create |> PreXmlDoc.create
let interfaceImpl = let interfaceImpl =
SynExpr.ObjExpr ( SynExpr.ObjExpr (
SynType.LongIdent (SynLongIdent.CreateFromLongIdent interfaceType.Name), SynType.createLongIdent interfaceType.Name,
None, None,
Some range0, Some range0,
[], [],
@@ -950,54 +822,26 @@ module internal HttpClientGenerator =
let headerArgs = let headerArgs =
properties properties
|> List.map (fun (_, pi) -> |> List.map (fun (_, pi) ->
SynPat.CreateTyped ( SynPat.namedI (Ident.lowerFirstLetter pi.Identifier)
SynPat.CreateNamed (Ident.lowerFirstLetter pi.Identifier), |> SynPat.annotateType (SynType.funFromDomain (SynType.named "unit") pi.Type)
SynType.CreateFun (SynType.CreateLongIdent "unit", pi.Type)
)
|> SynPat.CreateParen
) )
let clientCreationArg = let clientCreationArg =
SynPat.CreateTyped ( SynPat.named "client"
SynPat.CreateNamed (Ident.Create "client"), |> SynPat.annotateType (SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "HttpClient" ])
SynType.CreateLongIdent (SynLongIdent.Create [ "System" ; "Net" ; "Http" ; "HttpClient" ])
)
|> SynPat.CreateParen
let xmlDoc = let xmlDoc =
if properties.IsEmpty then if properties.IsEmpty then
" Create a REST client." "Create a REST client."
else else
" Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties." "Create a REST client. The input functions will be re-evaluated on every HTTP request to obtain the required values for the corresponding header properties."
|> PreXmlDoc.Create |> PreXmlDoc.create
let functionName = Ident.Create "client" let functionName = Ident.create "client"
let valData = let pattern = SynLongIdent.createS "make"
let memberFlags =
if spec.ExtensionMethods then
{
SynMemberFlags.IsInstance = false
SynMemberFlags.IsDispatchSlot = false
SynMemberFlags.IsOverrideOrExplicitImpl = false
SynMemberFlags.IsFinal = false
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
SynMemberFlags.MemberKind = SynMemberKind.Member
}
|> Some
else
None
SynValData.SynValData ( let returnInfo = SynType.createLongIdent interfaceType.Name
memberFlags,
SynValInfo.SynValInfo ([ [ SynArgInfo.SynArgInfo ([], false, Some functionName) ] ], SynArgInfo.Empty),
None
)
let pattern = SynLongIdent.CreateString "make"
let returnInfo =
SynType.LongIdent (SynLongIdent.CreateFromLongIdent interfaceType.Name)
let nameWithoutLeadingI = let nameWithoutLeadingI =
List.last interfaceType.Name List.last interfaceType.Name
@@ -1011,74 +855,54 @@ module internal HttpClientGenerator =
let createFunc = let createFunc =
if spec.ExtensionMethods then if spec.ExtensionMethods then
let binding = let binding =
SynBinding.basic SynBinding.basic [ Ident.create "make" ] (headerArgs @ [ clientCreationArg ]) interfaceImpl
(SynLongIdent.CreateString "make")
(headerArgs @ [ clientCreationArg ])
interfaceImpl
|> SynBinding.withXmlDoc xmlDoc |> SynBinding.withXmlDoc xmlDoc
|> SynBinding.makeStaticMember
|> SynBinding.withReturnAnnotation returnInfo |> SynBinding.withReturnAnnotation returnInfo
|> SynMemberDefn.staticMember
let mem = SynMemberDefn.Member (binding, range0) let componentInfo =
SynComponentInfo.create (Ident.create nameWithoutLeadingI)
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for HTTP clients")
let containingType = let containingType =
SynTypeDefn.SynTypeDefn ( SynTypeDefnRepr.augmentation ()
SynComponentInfo.Create ( |> SynTypeDefn.create componentInfo
[ Ident.Create nameWithoutLeadingI ], |> SynTypeDefn.withMemberDefns [ binding ]
xmldoc = PreXmlDoc.Create " Extension methods for HTTP clients"
),
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0),
[ mem ],
None,
range0,
{
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
EqualsRange = None
WithKeyword = None
}
)
SynModuleDecl.Types ([ containingType ], range0) SynModuleDecl.createTypes [ containingType ]
else else
SynBinding.basic (SynLongIdent.CreateString "make") (headerArgs @ [ clientCreationArg ]) interfaceImpl SynBinding.basic [ Ident.create "make" ] (headerArgs @ [ clientCreationArg ]) interfaceImpl
|> SynBinding.withXmlDoc xmlDoc |> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo |> SynBinding.withReturnAnnotation returnInfo
|> List.singleton |> SynModuleDecl.createLet
|> SynModuleDecl.CreateLet
let moduleName : LongIdent = let moduleName =
if spec.ExtensionMethods then if spec.ExtensionMethods then
[ Ident.Create (nameWithoutLeadingI + "HttpClientExtension") ] Ident.create (nameWithoutLeadingI + "HttpClientExtension")
else else
[ Ident.Create nameWithoutLeadingI ] Ident.create nameWithoutLeadingI
let attribs = let attribs =
if spec.ExtensionMethods then if spec.ExtensionMethods then
[ SynAttributeList.Create SynAttribute.autoOpen ] [ SynAttribute.autoOpen ]
else else
[ [ SynAttribute.compilationRepresentation ; SynAttribute.requireQualifiedAccess ]
SynAttributeList.Create SynAttribute.compilationRepresentation
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
]
let modInfo = let modInfo =
SynComponentInfo.Create ( SynComponentInfo.create moduleName
moduleName, |> SynComponentInfo.withDocString docString
attributes = attribs, |> SynComponentInfo.addAttributes attribs
xmldoc = docString, |> SynComponentInfo.setAccessibility interfaceType.Accessibility
access = interfaceType.Accessibility
)
SynModuleOrNamespace.CreateNamespace ( [
ns, for openStatement in opens do
decls = yield SynModuleDecl.openAny openStatement
[ yield SynModuleDecl.nestedModule modInfo [ createFunc ]
for openStatement in opens do ]
yield SynModuleDecl.CreateOpen openStatement |> SynModuleOrNamespace.createNamespace ns
yield SynModuleDecl.CreateNestedModule (modInfo, [ createFunc ])
] open Myriad.Core
)
/// Myriad generator that provides an HTTP client for an interface type using RestEase annotations. /// Myriad generator that provides an HTTP client for an interface type using RestEase annotations.
[<MyriadGenerator("http-client")>] [<MyriadGenerator("http-client")>]

View File

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

View File

@@ -2,9 +2,7 @@ namespace WoofWare.Myriad.Plugins
open System open System
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml open Fantomas.FCS.Xml
open Myriad.Core
type internal GenerateMockOutputSpec = type internal GenerateMockOutputSpec =
{ {
@@ -14,7 +12,6 @@ type internal GenerateMockOutputSpec =
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal InterfaceMockGenerator = module internal InterfaceMockGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Myriad.Core.Ast
let private getName (SynField (_, _, id, _, _, _, _, _, _)) = let private getName (SynField (_, _, id, _, _, _, _, _, _)) =
match id with match id with
@@ -46,72 +43,67 @@ module internal InterfaceMockGenerator =
) )
|> Set.ofSeq |> Set.ofSeq
let failwithFun = let failwithFun (SynField (_, _, idOpt, _, _, _, _, _, _)) =
let failString =
match idOpt with
| None -> SynExpr.CreateConst "Unimplemented mock function"
| Some ident -> SynExpr.CreateConst $"Unimplemented mock function: %s{ident.idText}"
SynExpr.createLongIdent [ "System" ; "NotImplementedException" ] SynExpr.createLongIdent [ "System" ; "NotImplementedException" ]
|> SynExpr.applyTo (SynExpr.CreateConstString "Unimplemented mock function") |> SynExpr.applyTo failString
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.CreateIdentString "raise") |> SynExpr.applyFunction (SynExpr.createIdent "raise")
|> SynExpr.createLambda "_" |> SynExpr.createLambda "_"
let constructorReturnType = let constructorReturnType =
match interfaceType.Generics with match interfaceType.Generics with
| None -> SynType.CreateLongIdent name | None -> SynType.createLongIdent' [ name ]
| Some generics -> | Some generics ->
let generics = let generics =
generics.TyparDecls generics.TyparDecls
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.Var (typar, range0)) |> List.map (fun (SynTyparDecl (_, typar)) -> SynType.var typar)
SynType.App ( SynType.app name generics
SynType.CreateLongIdent name,
Some range0,
generics,
List.replicate (generics.Length - 1) range0,
Some range0,
false,
range0
)
let constructorFields = let constructorFields =
let extras = let extras =
if inherits.Contains KnownInheritance.IDisposable then if inherits.Contains KnownInheritance.IDisposable then
let unitFun = SynExpr.createLambda "_" SynExpr.CreateUnit let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
[ [ SynLongIdent.createS "Dispose", unitFun ]
(SynLongIdent.CreateFromLongIdent [ Ident.Create "Dispose" ], true), Some unitFun
]
else else
[] []
let nonExtras = let nonExtras =
fields fields
|> List.map (fun field -> (SynLongIdent.CreateFromLongIdent [ getName field ], true), Some failwithFun) |> List.map (fun field -> SynLongIdent.createI (getName field), failwithFun field)
extras @ nonExtras extras @ nonExtras
let constructor = let constructor =
SynBinding.basic SynBinding.basic
(SynLongIdent.CreateString "Empty") [ Ident.create "Empty" ]
(if interfaceType.Generics.IsNone then (if interfaceType.Generics.IsNone then
[] []
else else
[ SynPat.CreateConst SynConst.Unit ]) [ SynPat.unit ])
(AstHelper.instantiateRecord constructorFields) (AstHelper.instantiateRecord constructorFields)
|> SynBinding.makeStaticMember |> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every method throws.")
|> SynBinding.withXmlDoc (PreXmlDoc.Create " An implementation where every method throws.")
|> SynBinding.withReturnAnnotation constructorReturnType |> SynBinding.withReturnAnnotation constructorReturnType
|> fun m -> SynMemberDefn.Member (m, range0) |> SynMemberDefn.staticMember
let fields = let fields =
let extras = let extras =
if inherits.Contains KnownInheritance.IDisposable then if inherits.Contains KnownInheritance.IDisposable then
[ {
SynField.Create ( Attrs = []
SynType.CreateFun (SynType.CreateUnit, SynType.CreateUnit), Ident = Some (Ident.create "Dispose")
Ident.Create "Dispose", Type = SynType.funFromDomain SynType.unit SynType.unit
xmldoc = PreXmlDoc.Create " Implementation of IDisposable.Dispose" }
) |> SynField.make
] |> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|> List.singleton
else else
[] []
@@ -121,47 +113,6 @@ module internal InterfaceMockGenerator =
let members = let members =
interfaceType.Members interfaceType.Members
|> List.map (fun memberInfo -> |> List.map (fun memberInfo ->
let synValData =
SynValData.SynValData (
Some
{
IsInstance = true
IsDispatchSlot = false
IsOverrideOrExplicitImpl = true
IsFinal = false
GetterOrSetterIsCompilerGenerated = false
MemberKind = SynMemberKind.Member
},
valInfo =
SynValInfo.SynValInfo (
curriedArgInfos =
[
yield
[
SynArgInfo.SynArgInfo (
attributes = [],
optional = false,
ident = None
)
]
yield!
memberInfo.Args
|> List.mapi (fun i arg ->
arg.Args
|> List.mapi (fun j arg ->
match arg.Type with
| UnitType -> SynArgInfo.SynArgInfo ([], false, None)
| _ -> SynArgInfo.CreateIdString $"arg_%i{i}_%i{j}"
)
)
],
returnInfo =
SynArgInfo.SynArgInfo (attributes = [], optional = false, ident = None)
),
thisIdOpt = None
)
let headArgs = let headArgs =
memberInfo.Args memberInfo.Args
|> List.mapi (fun i tupledArgs -> |> List.mapi (fun i tupledArgs ->
@@ -169,27 +120,15 @@ module internal InterfaceMockGenerator =
tupledArgs.Args tupledArgs.Args
|> List.mapi (fun j ty -> |> List.mapi (fun j ty ->
match ty.Type with match ty.Type with
| UnitType -> SynPat.Const (SynConst.Unit, range0) | UnitType -> SynPat.unit
| _ -> SynPat.CreateNamed (Ident.Create $"arg_%i{i}_%i{j}") | _ -> SynPat.named $"arg_%i{i}_%i{j}"
) )
match args with match args with
| [] -> failwith "somehow got no args at all" | [] -> failwith "somehow got no args at all"
| [ arg ] -> arg | [ arg ] -> arg
| args -> | args -> SynPat.tuple args
SynPat.Tuple (false, args, List.replicate (args.Length - 1) range0, range0) |> fun i -> if tupledArgs.HasParen then SynPat.paren i else i
|> SynPat.CreateParen
|> fun i -> if tupledArgs.HasParen then SynPat.Paren (i, range0) else i
)
let headPat =
SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; memberInfo.Identifier ],
None,
None,
SynArgPats.Pats headArgs,
None,
range0
) )
let body = let body =
@@ -199,10 +138,10 @@ module internal InterfaceMockGenerator =
args.Args args.Args
|> List.mapi (fun j arg -> |> List.mapi (fun j arg ->
match arg.Type with match arg.Type with
| UnitType -> SynExpr.CreateConst SynConst.Unit | UnitType -> SynExpr.CreateConst ()
| _ -> SynExpr.CreateIdentString $"arg_%i{i}_%i{j}" | _ -> SynExpr.createIdent $"arg_%i{i}_%i{j}"
) )
|> SynExpr.CreateParenedTuple |> SynExpr.tuple
) )
match tuples |> List.rev with match tuples |> List.rev with
@@ -210,38 +149,17 @@ module internal InterfaceMockGenerator =
| last :: rest -> | last :: rest ->
(last, rest) (last, rest)
||> List.fold (fun trail next -> SynExpr.CreateApp (next, trail)) ||> List.fold SynExpr.applyTo
|> SynExpr.applyFunction ( |> SynExpr.applyFunction (
SynExpr.createLongIdent' [ Ident.Create "this" ; memberInfo.Identifier ] SynExpr.createLongIdent' [ Ident.create "this" ; memberInfo.Identifier ]
) )
SynMemberDefn.Member ( SynBinding.basic [ Ident.create "this" ; memberInfo.Identifier ] headArgs body
SynBinding.SynBinding ( |> SynMemberDefn.memberImplementation
None,
SynBindingKind.Normal,
false,
false,
[],
PreXmlDoc.Empty,
synValData,
headPat,
None,
body,
range0,
DebugPointAtBinding.Yes range0,
{
LeadingKeyword = SynLeadingKeyword.Member range0
InlineKeyword = None
EqualsRange = Some range0
}
),
range0
)
) )
let interfaceName = let interfaceName =
let baseName = let baseName = SynType.createLongIdent interfaceType.Name
SynType.CreateLongIdent (SynLongIdent.CreateFromLongIdent interfaceType.Name)
match interfaceType.Generics with match interfaceType.Generics with
| None -> baseName | None -> baseName
@@ -251,17 +169,9 @@ module internal InterfaceMockGenerator =
| SynTyparDecls.PostfixList (decls, _, _) -> decls | SynTyparDecls.PostfixList (decls, _, _) -> decls
| SynTyparDecls.PrefixList (decls, _) -> decls | SynTyparDecls.PrefixList (decls, _) -> decls
| SynTyparDecls.SinglePrefix (decl, _) -> [ decl ] | SynTyparDecls.SinglePrefix (decl, _) -> [ decl ]
|> List.map (fun (SynTyparDecl (_, typar)) -> SynType.Var (typar, range0)) |> List.map (fun (SynTyparDecl (_, typar)) -> SynType.var typar)
SynType.App ( SynType.app' baseName generics
baseName,
Some range0,
generics,
List.replicate (generics.Length - 1) range0,
Some range0,
false,
range0
)
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0) SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
@@ -279,18 +189,15 @@ module internal InterfaceMockGenerator =
|> Seq.map (fun inheritance -> |> Seq.map (fun inheritance ->
match inheritance with match inheritance with
| KnownInheritance.IDisposable -> | KnownInheritance.IDisposable ->
let binding = let mem =
SynBinding.basic SynExpr.createLongIdent [ "this" ; "Dispose" ]
(SynLongIdent.CreateFromLongIdent [ Ident.Create "this" ; Ident.Create "Dispose" ]) |> SynExpr.applyTo (SynExpr.CreateConst ())
[ SynPat.CreateConst SynConst.Unit ] |> SynBinding.basic [ Ident.create "this" ; Ident.create "Dispose" ] [ SynPat.unit ]
(SynExpr.CreateApp (SynExpr.createLongIdent [ "this" ; "Dispose" ], SynExpr.CreateUnit)) |> SynBinding.withReturnAnnotation SynType.unit
|> SynBinding.withReturnAnnotation (SynType.Unit ()) |> SynMemberDefn.memberImplementation
|> SynBinding.makeInstanceMember
let mem = SynMemberDefn.Member (binding, range0)
SynMemberDefn.Interface ( SynMemberDefn.Interface (
SynType.CreateLongIdent (SynLongIdent.Create [ "System" ; "IDisposable" ]), SynType.createLongIdent' [ "System" ; "IDisposable" ],
Some range0, Some range0,
Some [ mem ], Some [ mem ],
range0 range0
@@ -300,12 +207,14 @@ module internal InterfaceMockGenerator =
let record = let record =
{ {
Name = Ident.Create name Name = Ident.create name
Fields = fields Fields = fields
Members = Some ([ constructor ; interfaceMembers ] @ extraInterfaces) Members = Some ([ constructor ; interfaceMembers ] @ extraInterfaces)
XmlDoc = Some xmlDoc XmlDoc = Some xmlDoc
Generics = interfaceType.Generics Generics = interfaceType.Generics
Accessibility = Some access TypeAccessibility = Some access
ImplAccessibility = None
Attributes = []
} }
let typeDecl = AstHelper.defineRecordType record let typeDecl = AstHelper.defineRecordType record
@@ -314,36 +223,29 @@ module internal InterfaceMockGenerator =
let private buildType (x : ParameterInfo) : SynType = let private buildType (x : ParameterInfo) : SynType =
if x.IsOptional then if x.IsOptional then
SynType.App (SynType.CreateLongIdent "option", Some range0, [ x.Type ], [], Some range0, false, range0) SynType.app "option" [ x.Type ]
else else
x.Type x.Type
let private constructMemberSinglePlace (tuple : TupledArg) : SynType = let private constructMemberSinglePlace (tuple : TupledArg) : SynType =
match tuple.Args |> List.rev |> List.map buildType with tuple.Args
| [] -> failwith "no-arg functions not supported yet" |> List.map buildType
| [ x ] -> x |> SynType.tupleNoParen
| last :: rest -> |> Option.defaultWith (fun () -> failwith "no-arg functions not supported yet")
([ SynTupleTypeSegment.Type last ], rest) |> if tuple.HasParen then SynType.paren else id
||> List.fold (fun ty nextArg -> SynTupleTypeSegment.Type nextArg :: SynTupleTypeSegment.Star range0 :: ty)
|> fun segs -> SynType.Tuple (false, segs, range0)
|> fun ty -> if tuple.HasParen then SynType.Paren (ty, range0) else ty
let constructMember (mem : MemberInfo) : SynField = let constructMember (mem : MemberInfo) : SynField =
let inputType = mem.Args |> List.map constructMemberSinglePlace let inputType = mem.Args |> List.map constructMemberSinglePlace
let funcType = AstHelper.toFun inputType mem.ReturnType let funcType = SynType.toFun inputType mem.ReturnType
SynField.SynField ( {
[], Type = funcType
false, Attrs = []
Some mem.Identifier, Ident = Some mem.Identifier
funcType, }
false, |> SynField.make
mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty, |> SynField.withDocString (mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)
None,
range0,
SynFieldTrivia.Zero
)
let createRecord let createRecord
(namespaceId : LongIdent) (namespaceId : LongIdent)
@@ -353,7 +255,7 @@ module internal InterfaceMockGenerator =
= =
let interfaceType = AstHelper.parseInterface interfaceType let interfaceType = AstHelper.parseInterface interfaceType
let fields = interfaceType.Members |> List.map constructMember let fields = interfaceType.Members |> List.map constructMember
let docString = PreXmlDoc.Create " Mock record type for an interface" let docString = PreXmlDoc.create "Mock record type for an interface"
let name = let name =
List.last interfaceType.Name List.last interfaceType.Name
@@ -367,10 +269,10 @@ module internal InterfaceMockGenerator =
let typeDecl = createType spec name interfaceType docString fields let typeDecl = createType spec name interfaceType docString fields
SynModuleOrNamespace.CreateNamespace ( [ yield! opens |> List.map SynModuleDecl.openAny ; yield typeDecl ]
namespaceId, |> SynModuleOrNamespace.createNamespace namespaceId
decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ typeDecl ]
) open Myriad.Core
/// Myriad generator that creates a record which implements the given interface, /// Myriad generator that creates a record which implements the given interface,
/// but with every field mocked out. /// but with every field mocked out.

View File

@@ -4,8 +4,6 @@ open System
open System.Text open System.Text
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
open Myriad.Core
type internal JsonParseOutputSpec = type internal JsonParseOutputSpec =
{ {
@@ -15,7 +13,6 @@ type internal JsonParseOutputSpec =
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal JsonParseGenerator = module internal JsonParseGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Myriad.Core.Ast
type JsonParseOption = type JsonParseOption =
{ {
@@ -27,33 +24,26 @@ 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.CreateApp ( SynExpr.applyFunction
SynExpr.CreateApp ( (SynExpr.createIdent "sprintf")
SynExpr.CreateIdentString "sprintf", (SynExpr.CreateConst "Required key '%s' not found on JSON object")
SynExpr.CreateConstString "Required key '%s' not found on JSON object" |> SynExpr.applyTo (SynExpr.paren propertyName)
), |> SynExpr.paren
SynExpr.CreateParen propertyName
)
|> SynExpr.CreateParen
|> SynExpr.applyFunction ( |> SynExpr.applyFunction (
SynExpr.CreateLongIdent ( SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "KeyNotFoundException" ]
SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "KeyNotFoundException" ]
)
) )
|> SynExpr.CreateParen |> SynExpr.paren
|> SynExpr.applyFunction (SynExpr.CreateIdentString "raise") |> SynExpr.applyFunction (SynExpr.createIdent "raise")
SynExpr.CreateMatch ( [
indexed, SynMatchClause.create SynPat.createNull raiseExpr
[ SynMatchClause.create (SynPat.named "v") (SynExpr.createIdent "v")
SynMatchClause.Create (SynPat.CreateNull, None, raiseExpr) ]
SynMatchClause.Create (SynPat.CreateNamed (Ident.Create "v"), None, SynExpr.CreateIdentString "v") |> SynExpr.createMatch indexed
] |> SynExpr.paren
)
|> SynExpr.CreateParen
/// {node}.AsValue().GetValue<{typeName}> () /// {node}.AsValue().GetValue<{typeName}> ()
/// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`. /// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`.
@@ -69,7 +59,7 @@ module internal JsonParseGenerator =
| None -> node | None -> node
| Some propertyName -> assertNotNull propertyName node | Some propertyName -> assertNotNull propertyName node
|> SynExpr.callMethod "AsValue" |> SynExpr.callMethod "AsValue"
|> SynExpr.callGenericMethod "GetValue" typeName |> SynExpr.callGenericMethod (SynLongIdent.createS "GetValue") [ SynType.createLongIdent typeName ]
/// {node}.AsObject() /// {node}.AsObject()
/// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`. /// If `propertyName` is Some, uses `assertNotNull {node}` instead of `{node}`.
@@ -81,10 +71,8 @@ module internal JsonParseGenerator =
/// {type}.jsonParse {node} /// {type}.jsonParse {node}
let typeJsonParse (typeName : LongIdent) (node : SynExpr) : SynExpr = let typeJsonParse (typeName : LongIdent) (node : SynExpr) : SynExpr =
SynExpr.CreateApp ( node
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent (typeName @ [ Ident.Create "jsonParse" ])), |> SynExpr.applyFunction (SynExpr.createLongIdent' (typeName @ [ Ident.create "jsonParse" ]))
node
)
/// collectionType is e.g. "List"; we'll be calling `ofSeq` on it. /// collectionType is e.g. "List"; we'll be calling `ofSeq` on it.
/// body is the body of a lambda which takes a parameter `elt`. /// body is the body of a lambda which takes a parameter `elt`.
@@ -103,51 +91,24 @@ module internal JsonParseGenerator =
| Some propertyName -> assertNotNull propertyName node | Some propertyName -> assertNotNull propertyName node
|> SynExpr.callMethod "AsArray" |> SynExpr.callMethod "AsArray"
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction (SynExpr.createLongIdent [ "Seq" ; "map" ]) (SynExpr.createLambda "elt" body)
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]),
SynExpr.createLambda "elt" body
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ collectionType ; "ofSeq" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ collectionType ; "ofSeq" ])
/// match {node} with | null -> None | v -> {body} |> Some 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.CreateIdentString "Some") body
SynExpr.CreateMatch (
node,
[
SynMatchClause.Create (SynPat.CreateNull, None, SynExpr.CreateIdent (Ident.Create "None"))
SynMatchClause.Create (SynPat.CreateNamed (Ident.Create "v"), None, body)
]
)
/// Given e.g. "float", returns "System.Double.Parse"
let parseFunction (typeName : string) : LongIdent =
let qualified =
match AstHelper.qualifyPrimitiveType typeName with
| Some x -> x
| None -> failwith $"Could not recognise type %s{typeName} as a primitive."
List.append qualified [ Ident.Create "Parse" ]
/// fun kvp -> let key = {key(kvp)} in let value = {value(kvp)} in (key, value)) /// fun kvp -> let key = {key(kvp)} in let value = {value(kvp)} in (key, value))
/// The inputs will be fed with appropriate SynExprs to apply them to the `kvp.Key` and `kvp.Value` args. /// The inputs will be fed with appropriate SynExprs to apply them to the `kvp.Key` and `kvp.Value` args.
let dictionaryMapper (key : SynExpr -> SynExpr) (value : SynExpr -> SynExpr) : SynExpr = let dictionaryMapper (key : SynExpr -> SynExpr) (value : SynExpr -> SynExpr) : SynExpr =
let keyArg = SynExpr.createLongIdent [ "kvp" ; "Key" ] |> SynExpr.CreateParen let keyArg = SynExpr.createLongIdent [ "kvp" ; "Key" ] |> SynExpr.paren
let valueArg = SynExpr.createLongIdent [ "kvp" ; "Value" ] |> SynExpr.CreateParen let valueArg = SynExpr.createLongIdent [ "kvp" ; "Value" ] |> SynExpr.paren
SynExpr.CreateTuple [ SynExpr.CreateIdentString "key" ; SynExpr.CreateIdentString "value" ] // No need to paren here, we're on the LHS of a `let`
|> SynExpr.createLet SynExpr.tupleNoParen [ SynExpr.createIdent "key" ; SynExpr.createIdent "value" ]
[ |> SynExpr.createLet [ SynBinding.basic [ Ident.create "value" ] [] (value valueArg) ]
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "value"), expr = value valueArg) |> SynExpr.createLet [ SynBinding.basic [ Ident.create "key" ] [] (key keyArg) ]
]
|> SynExpr.createLet
[
SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create "key"), expr = key keyArg)
]
|> SynExpr.createLambda "kvp" |> SynExpr.createLambda "kvp"
/// A conforming JSON object has only strings as keys. But it would be reasonable to allow the user /// A conforming JSON object has only strings as keys. But it would be reasonable to allow the user
@@ -157,11 +118,52 @@ module internal JsonParseGenerator =
| String -> key | String -> key
| Uri -> | Uri ->
key key
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Uri" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Uri" ])
| _ -> | _ ->
failwithf failwithf
$"Unable to parse the key type %+A{desiredType} of a JSON object. Keys are strings, and this plugin does not know how to convert to that from a string." $"Unable to parse the key type %+A{desiredType} of a JSON object. Keys are strings, and this plugin does not know how to convert to that from a string."
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".
@@ -190,115 +192,95 @@ 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.CreateIdentString "exc",
range0,
SynLongIdent.CreateString "Message",
range0
)
|> SynExpr.callMethodArg
"Contains"
(SynExpr.CreateConst (SynConst.CreateString "cannot be converted to"))
let handler =
asValueGetValue propertyName "string" node
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent' (parseFunction typeName))
|> SynExpr.ifThenElse
(SynExpr.equals
option
(SynExpr.CreateLongIdent (
SynLongIdent.Create
[
"System"
"Text"
"Json"
"Serialization"
"JsonNumberHandling"
"AllowReadingFromString"
]
)))
SynExpr.reraise
|> SynExpr.ifThenElse cond SynExpr.reraise
basic
|> SynExpr.pipeThroughTryWith
(SynPat.IsInst (
SynType.LongIdent (SynLongIdent.Create [ "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.CreateIdentString "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.CreateLongIdent (SynLongIdent.CreateString "elt")) parseNode None options ty (SynExpr.createIdent "elt")
|> asArrayMapped propertyName "List" node |> asArrayMapped propertyName "List" node
| ArrayType ty -> | ArrayType ty ->
parseNode None options ty (SynExpr.CreateLongIdent (SynLongIdent.CreateString "elt")) parseNode None options ty (SynExpr.createIdent "elt")
|> asArrayMapped propertyName "Array" node |> asArrayMapped propertyName "Array" node
| IDictionaryType (keyType, valueType) -> | IDictionaryType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "dict" ])) |> SynExpr.pipeThroughFunction (SynExpr.createIdent "dict")
| DictionaryType (keyType, valueType) -> | DictionaryType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
SynExpr.CreateLongIdent ( (SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "KeyValuePair" ])
SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "KeyValuePair" ]
)
)
) )
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateLongIdent (SynLongIdent.Create [ "System" ; "Collections" ; "Generic" ; "Dictionary" ]) SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "Dictionary" ]
) )
| IReadOnlyDictionaryType (keyType, valueType) -> | IReadOnlyDictionaryType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "readOnlyDict" ])) |> SynExpr.pipeThroughFunction (SynExpr.createIdent "readOnlyDict")
| MapType (keyType, valueType) -> | MapType (keyType, valueType) ->
node node
|> asObject propertyName |> asObject propertyName
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "Seq" ; "map" ]), (SynExpr.createLongIdent [ "Seq" ; "map" ])
dictionaryMapper (parseKeyString keyType) (parseNode None options valueType) (dictionaryMapper (parseKeyString keyType) (parseNode None options valueType))
)
) )
|> SynExpr.pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Map" ; "ofSeq" ])) |> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "Map" ; "ofSeq" ])
| BigInt -> | BigInt ->
node node
|> SynExpr.callMethod "ToJsonString" |> SynExpr.callMethod "ToJsonString"
|> SynExpr.CreateParen |> 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)
| JsonNode -> node
| Unit -> SynExpr.CreateConst ()
| _ -> | _ ->
// 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 =
@@ -314,7 +296,7 @@ module internal JsonParseGenerator =
/// propertyName is probably a string literal, but it could be a [<Literal>] variable /// propertyName is probably a string literal, but it could be a [<Literal>] variable
/// The result of this function is the body of a let-binding (not including the LHS of that let-binding). /// The result of this function is the body of a let-binding (not including the LHS of that let-binding).
let createParseRhs (options : JsonParseOption) (propertyName : SynExpr) (fieldType : SynType) : SynExpr = let createParseRhs (options : JsonParseOption) (propertyName : SynExpr) (fieldType : SynType) : SynExpr =
let objectToParse = SynExpr.CreateIdentString "node" |> SynExpr.index propertyName let objectToParse = SynExpr.createIdent "node" |> SynExpr.index propertyName
parseNode (Some propertyName) options fieldType objectToParse parseNode (Some propertyName) options fieldType objectToParse
let isJsonNumberHandling (literal : LongIdent) : bool = let isJsonNumberHandling (literal : LongIdent) : bool =
@@ -331,54 +313,47 @@ module internal JsonParseGenerator =
/// That is, we give you access to a `JsonNode` called `node`, /// That is, we give you access to a `JsonNode` called `node`,
/// and you must return a `typeName`. /// and you must return a `typeName`.
let scaffolding (spec : JsonParseOutputSpec) (typeName : LongIdent) (functionBody : SynExpr) : SynModuleDecl = let scaffolding (spec : JsonParseOutputSpec) (typeName : LongIdent) (functionBody : SynExpr) : SynModuleDecl =
let xmlDoc = PreXmlDoc.Create " Parse from a JSON node." let xmlDoc = PreXmlDoc.create "Parse from a JSON node."
let returnInfo = SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName) let returnInfo = SynType.createLongIdent typeName
let inputArg = Ident.Create "node" let inputArg = "node"
let functionName = Ident.Create "jsonParse" let functionName = Ident.create "jsonParse"
let arg = let arg =
SynPat.CreateNamed inputArg SynPat.named inputArg
|> SynPat.annotateType ( |> SynPat.annotateType (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
SynType.LongIdent (SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
)
if spec.ExtensionMethods then if spec.ExtensionMethods then
let binding = let binding =
SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ arg ] functionBody SynBinding.basic [ functionName ] [ arg ] functionBody
|> SynBinding.makeStaticMember
|> SynBinding.withXmlDoc xmlDoc |> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo |> SynBinding.withReturnAnnotation returnInfo
|> SynMemberDefn.staticMember
let mem = SynMemberDefn.Member (binding, range0) let componentInfo =
SynComponentInfo.createLong typeName
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for JSON parsing")
let containingType = let containingType =
SynTypeDefn.SynTypeDefn ( SynTypeDefnRepr.augmentation ()
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create " Extension methods for JSON parsing"), |> SynTypeDefn.create componentInfo
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0), |> SynTypeDefn.withMemberDefns [ binding ]
[ mem ],
None,
range0,
{
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
EqualsRange = None
WithKeyword = None
}
)
SynModuleDecl.Types ([ containingType ], range0) SynModuleDecl.Types ([ containingType ], range0)
else else
SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ arg ] functionBody SynBinding.basic [ functionName ] [ arg ] functionBody
|> SynBinding.withXmlDoc xmlDoc |> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo |> SynBinding.withReturnAnnotation returnInfo
|> List.singleton |> SynModuleDecl.createLet
|> SynModuleDecl.CreateLet
let getParseOptions (fieldAttrs : SynAttribute list) = let getParseOptions (fieldAttrs : SynAttribute list) =
(JsonParseOption.None, fieldAttrs) (JsonParseOption.None, fieldAttrs)
||> List.fold (fun options attr -> ||> List.fold (fun options attr ->
if attr.TypeName.AsString.EndsWith ("JsonNumberHandling", StringComparison.Ordinal) then if
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonNumberHandling", StringComparison.Ordinal)
then
let qualifiedEnumValue = let qualifiedEnumValue =
match SynExpr.stripOptionalParen attr.ArgExpr with match SynExpr.stripOptionalParen attr.ArgExpr with
| SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when isJsonNumberHandling ident -> | SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when isJsonNumberHandling ident ->
@@ -401,18 +376,23 @@ module internal JsonParseGenerator =
options options
) )
let createRecordMaker (spec : JsonParseOutputSpec) (fields : SynFieldData<Ident> list) =
let createRecordMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : SynFieldData<Ident> list) = let propertyFields =
let assignments =
fields fields
|> List.mapi (fun i fieldData -> |> List.map (fun fieldData ->
let propertyNameAttr = let propertyNameAttr =
fieldData.Attrs fieldData.Attrs
|> List.tryFind (fun attr -> |> List.tryFind (fun attr ->
attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal) (SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
) )
let options = getParseOptions fieldData.Attrs let extensionDataAttr =
fieldData.Attrs
|> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonExtensionData", StringComparison.Ordinal)
)
let propertyName = let propertyName =
match propertyNameAttr with match propertyNameAttr with
@@ -425,19 +405,85 @@ module internal JsonParseGenerator =
if fieldData.Ident.idText.Length > 1 then if fieldData.Ident.idText.Length > 1 then
sb.Append (fieldData.Ident.idText.Substring 1) |> ignore<StringBuilder> sb.Append (fieldData.Ident.idText.Substring 1) |> ignore<StringBuilder>
sb.ToString () |> SynConst.CreateString |> SynExpr.CreateConst sb.ToString () |> SynExpr.CreateConst
| Some name -> name.ArgExpr | Some name -> name.ArgExpr
propertyName, extensionDataAttr
)
let namedPropertyFields =
propertyFields
|> List.choose (fun (name, extension) ->
match extension with
| Some _ -> None
| None -> Some name
)
let isNamedPropertyField =
match namedPropertyFields with
| [] -> SynExpr.CreateConst false
| _ ->
namedPropertyFields
|> List.map (fun fieldName -> SynExpr.equals (SynExpr.createIdent "key") fieldName)
|> List.reduce SynExpr.booleanOr
let assignments =
List.zip fields propertyFields
|> List.mapi (fun i (fieldData, (propertyName, extensionDataAttr)) ->
let options = getParseOptions fieldData.Attrs
let accIdent = Ident.create $"arg_%i{i}"
match extensionDataAttr with
| Some _ ->
// Can't go through the usual parse logic here, because that will try and identify the node that's
// been labelled. The whole point of JsonExtensionData is that there is no such node!
let valType =
match fieldData.Type with
| DictionaryType (String, v) -> v
| _ -> failwith "Expected JsonExtensionData to be Dictionary<string, _>"
SynExpr.ifThenElse
isNamedPropertyField
(SynExpr.callMethodArg
"Add"
(SynExpr.tuple
[
SynExpr.createIdent "key"
createParseRhs options (SynExpr.createIdent "key") valType
])
(SynExpr.createIdent "result"))
(SynExpr.CreateConst ())
|> SynExpr.createForEach
(SynPat.nameWithArgs "KeyValue" [ SynPat.named "key" ; SynPat.named "value" ])
(SynExpr.createIdent "node")
|> fun forEach -> [ forEach ; SynExpr.createIdent "result" ]
|> SynExpr.sequential
|> SynExpr.createLet
[
SynBinding.basic
[ Ident.create "result" ]
[]
(SynExpr.typeApp
[ SynType.string ; valType ]
(SynExpr.createLongIdent [ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|> SynExpr.applyTo (SynExpr.CreateConst ()))
SynBinding.basic
[ Ident.create "node" ]
[]
(SynExpr.createIdent "node" |> SynExpr.callMethod "AsObject")
]
|> SynBinding.basic [ accIdent ] []
| None ->
createParseRhs options propertyName fieldData.Type createParseRhs options propertyName fieldData.Type
|> SynBinding.basic (SynLongIdent.CreateString $"arg_%i{i}") [] |> SynBinding.basic [ accIdent ] []
) )
let finalConstruction = let finalConstruction =
fields fields
|> List.mapi (fun i fieldData -> |> List.mapi (fun i fieldData -> SynLongIdent.createI fieldData.Ident, SynExpr.createIdent $"arg_%i{i}")
(SynLongIdent.CreateFromLongIdent [ fieldData.Ident ], true),
Some (SynExpr.CreateLongIdent (SynLongIdent.CreateString $"arg_%i{i}"))
)
|> AstHelper.instantiateRecord |> AstHelper.instantiateRecord
(finalConstruction, assignments) (finalConstruction, assignments)
@@ -446,11 +492,11 @@ module internal JsonParseGenerator =
let createUnionMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : UnionCase<Ident> list) = let createUnionMaker (spec : JsonParseOutputSpec) (typeName : LongIdent) (fields : UnionCase<Ident> list) =
fields fields
|> List.map (fun case -> |> List.map (fun case ->
let propertyName = JsonSerializeGenerator.getPropertyName case.Ident case.Attrs let propertyName = JsonSerializeGenerator.getPropertyName case.Name case.Attributes
let body = let body =
if case.Fields.IsEmpty then if case.Fields.IsEmpty then
SynExpr.createLongIdent' (typeName @ [ case.Ident ]) SynExpr.createLongIdent' (typeName @ [ case.Name ])
else else
case.Fields case.Fields
|> List.map (fun field -> |> List.map (fun field ->
@@ -458,19 +504,19 @@ module internal JsonParseGenerator =
let options = getParseOptions field.Attrs let options = getParseOptions field.Attrs
createParseRhs options propertyName field.Type createParseRhs options propertyName field.Type
) )
|> SynExpr.CreateParenedTuple |> SynExpr.tuple
|> SynExpr.applyFunction (SynExpr.createLongIdent' (typeName @ [ case.Ident ])) |> SynExpr.applyFunction (SynExpr.createLongIdent' (typeName @ [ case.Name ]))
|> SynExpr.createLet |> SynExpr.createLet
[ [
SynExpr.index (SynExpr.CreateConstString "data") (SynExpr.CreateIdentString "node") SynExpr.index (SynExpr.CreateConst "data") (SynExpr.createIdent "node")
|> assertNotNull (SynExpr.CreateConstString "data") |> assertNotNull (SynExpr.CreateConst "data")
|> SynBinding.basic (SynLongIdent.CreateString "node") [] |> SynBinding.basic [ Ident.create "node" ] []
] ]
match propertyName with match propertyName with
| SynExpr.Const (synConst, _) -> | SynExpr.Const (synConst, _) ->
SynMatchClause.SynMatchClause ( SynMatchClause.SynMatchClause (
SynPat.CreateConst synConst, SynPat.createConst synConst,
None, None,
body, body,
range0, range0,
@@ -481,30 +527,19 @@ module internal JsonParseGenerator =
} }
) )
| _ -> | _ ->
SynMatchClause.SynMatchClause ( SynMatchClause.create (SynPat.named "x") body
SynPat.CreateNamed (Ident.Create "x"), |> SynMatchClause.withWhere (SynExpr.equals (SynExpr.createIdent "x") propertyName)
Some (SynExpr.equals (SynExpr.CreateIdentString "x") propertyName),
body,
range0,
DebugPointAtTarget.Yes,
{
ArrowRange = Some range0
BarRange = Some range0
}
)
) )
|> fun l -> |> fun l ->
l l
@ [ @ [
let fail = let fail =
SynExpr.plus SynExpr.plus (SynExpr.CreateConst "Unrecognised 'type' field value: ") (SynExpr.createIdent "v")
(SynExpr.CreateConstString "Unrecognised 'type' field value: ") |> SynExpr.paren
(SynExpr.CreateIdentString "v") |> SynExpr.applyFunction (SynExpr.createIdent "failwith")
|> SynExpr.CreateParen
|> SynExpr.applyFunction (SynExpr.CreateIdentString "failwith")
SynMatchClause.SynMatchClause ( SynMatchClause.SynMatchClause (
SynPat.CreateNamed (Ident.Create "v"), SynPat.named "v",
None, None,
fail, fail,
range0, range0,
@@ -515,50 +550,85 @@ module internal JsonParseGenerator =
} }
) )
] ]
|> SynExpr.createMatch (SynExpr.CreateIdentString "ty") |> SynExpr.createMatch (SynExpr.createIdent "ty")
|> SynExpr.createLet |> SynExpr.createLet
[ [
let property = SynExpr.CreateConstString "type" let property = SynExpr.CreateConst "type"
SynExpr.CreateIdentString "node" SynExpr.createIdent "node"
|> SynExpr.index property |> SynExpr.index property
|> assertNotNull property |> assertNotNull property
|> SynExpr.pipeThroughFunction ( |> SynExpr.pipeThroughFunction (
SynExpr.createLambda SynExpr.createLambda "v" (SynExpr.callGenericMethod' "GetValue" "string" (SynExpr.createIdent "v"))
"v"
(SynExpr.callGenericMethod "GetValue" [ Ident.Create "string" ] (SynExpr.CreateIdentString "v"))
) )
|> SynBinding.basic (SynLongIdent.CreateString "ty") [] |> SynBinding.basic [ Ident.create "ty" ] []
] ]
(*
let ty =
match node.["type"] with
| null -> raise (System.Collections.Generic.KeyNotFoundException ())
| v -> v.GetValue<string> ()
match ty with
| "emptyCase" -> FirstDu.EmptyCase
| "case1" ->
FirstDu.Case1
| "case2" -> FirstDu.Case2
| _ -> failwithf "Unrecognised case name: %s" ty
*)
let 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 =
if spec.ExtensionMethods then if spec.ExtensionMethods then
[ SynAttributeList.Create SynAttribute.autoOpen ] [ SynAttribute.autoOpen ]
else else
[ [ SynAttribute.requireQualifiedAccess ; SynAttribute.compilationRepresentation ]
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
SynAttributeList.Create SynAttribute.compilationRepresentation
]
let xmlDoc = let xmlDoc =
let fullyQualified = ident |> Seq.map (fun i -> i.idText) |> String.concat "." let fullyQualified = ident |> Seq.map (fun i -> i.idText) |> String.concat "."
@@ -569,8 +639,8 @@ module internal JsonParseGenerator =
else else
"methods" "methods"
$" Module containing JSON parsing %s{description} for the %s{fullyQualified} type" $"Module containing JSON parsing %s{description} for the %s{fullyQualified} type"
|> PreXmlDoc.Create |> PreXmlDoc.create
let moduleName = let moduleName =
if spec.ExtensionMethods then if spec.ExtensionMethods then
@@ -581,39 +651,47 @@ module internal JsonParseGenerator =
List.last ident List.last ident
|> fun i -> i.idText |> fun i -> i.idText
|> fun s -> s + "JsonParseExtension" |> fun s -> s + "JsonParseExtension"
|> Ident.Create |> Ident.create
List.take (List.length ident - 1) ident @ [ expanded ] List.take (List.length ident - 1) ident @ [ expanded ]
else else
ident ident
let info = let info =
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc) SynComponentInfo.createLong moduleName
|> SynComponentInfo.withDocString xmlDoc
|> SynComponentInfo.setAccessibility access
|> SynComponentInfo.addAttributes attributes
let decl = let decl =
match synTypeDefnRepr with match synTypeDefnRepr with
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, fields, _range), _) -> | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (_accessibility, fields, _range), _) ->
let fields = fields |> List.map SynField.extractWithIdent fields |> List.map SynField.extractWithIdent |> createRecordMaker spec
createRecordMaker spec ident fields
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, cases, _range), _) -> | SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (_accessibility, cases, _range), _) ->
let optionGet (i : Ident option) = let optionGet (i : Ident option) =
match i with match i with
| None -> failwith "WoofWare.Myriad requires union cases to have identifiers on each field." | None -> failwith "WoofWare.Myriad requires union cases to have identifiers on each field."
| Some i -> i | Some i -> i
let cases = cases
cases |> List.map UnionCase.ofSynUnionCase
|> List.map SynUnionCase.extract |> List.map (UnionCase.mapIdentFields optionGet)
|> List.map (UnionCase.mapIdentFields optionGet) |> createUnionMaker spec ident
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum (cases, _range), _) ->
createUnionMaker spec ident cases 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"
let mdl = [ scaffolding spec ident decl ]
[ scaffolding spec ident decl ] |> SynModuleDecl.nestedModule info
|> fun d -> SynModuleDecl.CreateNestedModule (info, d) |> List.singleton
|> SynModuleOrNamespace.createNamespace namespaceId
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ]) open Myriad.Core
/// Myriad generator that provides a method (possibly an extension method) for a record type, /// Myriad generator that provides a method (possibly an extension method) for a record type,
/// containing a JSON parse function. /// containing a JSON parse function.
@@ -627,20 +705,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

@@ -3,9 +3,6 @@ namespace WoofWare.Myriad.Plugins
open System open System
open System.Text open System.Text
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
open Myriad.Core
type internal JsonSerializeOutputSpec = type internal JsonSerializeOutputSpec =
{ {
@@ -15,63 +12,72 @@ type internal JsonSerializeOutputSpec =
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal JsonSerializeGenerator = module internal JsonSerializeGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Myriad.Core.Ast
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
// identically equal to null. We have to work around this later, but we might as well just
// be efficient here and whip up the null directly.
let private jsonNull () =
SynExpr.createNull ()
|> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
/// Given `input.Ident`, for example, choose how to add it to the ambient `node`. /// Given `input.Ident`, for example, choose how to add it to the ambient `node`.
/// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`. /// The result is a line like `(fun ident -> InnerType.toJsonNode ident)` or `(fun ident -> JsonValue.Create ident)`.
let rec serializeNode (fieldType : SynType) : SynExpr = /// Returns also a bool which is true if the resulting SynExpr represents something of type JsonNode.
let rec serializeNode (fieldType : SynType) : SynExpr * bool =
// TODO: serialization format for DateTime etc // TODO: serialization format for DateTime etc
match fieldType with match fieldType with
| DateOnly | DateOnly
| DateTime | DateTime
| NumberType _ | NumberType _
| Measure _
| PrimitiveType _ | PrimitiveType _
| Guid | 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 = jsonNull () |> SynMatchClause.create (SynPat.named "None")
SynMatchClause.Create (
SynPat.CreateLongIdent (SynLongIdent.CreateString "None", []),
None,
// The absolutely galaxy-brained implementation of JsonValue has `JsonValue.Parse "null"`
// identically equal to null. We have to work around this later, but we might as well just
// be efficient here and whip up the null directly.
SynExpr.CreateNull
|> SynExpr.upcast' (
SynType.CreateLongIdent (
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
)
)
)
SynMatchClause.Create ( let someClause =
SynPat.CreateLongIdent ( let inner, innerIsJsonNode = serializeNode ty
SynLongIdent.CreateString "Some", let target = SynExpr.applyFunction inner (SynExpr.createIdent "field")
[ SynPat.CreateNamed (Ident.Create "field") ]
), if innerIsJsonNode then
None, target
SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "field") else
|> SynExpr.CreateParen target
|> SynExpr.upcast' ( |> SynExpr.paren
SynType.CreateLongIdent ( |> SynExpr.upcast' (SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ])
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ] |> SynMatchClause.create (SynPat.nameWithArgs "Some" [ SynPat.named "field" ])
)
) [ noneClause ; someClause ]
) |> SynExpr.createMatch (SynExpr.createIdent "field")
]
|> SynExpr.createMatch (SynExpr.CreateIdentString "field")
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, true
| ArrayType ty | ArrayType ty
| ListType ty -> | ListType ty ->
// fun field -> // fun field ->
@@ -84,24 +90,24 @@ module internal JsonSerializeGenerator =
DebugPointAtInOrTo.Yes range0, DebugPointAtInOrTo.Yes range0,
SeqExprOnly.SeqExprOnly false, SeqExprOnly.SeqExprOnly false,
true, true,
SynPat.CreateNamed (Ident.Create "mem"), SynPat.named "mem",
SynExpr.CreateIdent (Ident.Create "field"), SynExpr.createIdent "field",
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.CreateLongIdent (SynLongIdent.Create [ "arr" ; "Add" ]), (SynExpr.createLongIdent [ "arr" ; "Add" ])
SynExpr.CreateParen (SynExpr.CreateApp (serializeNode ty, SynExpr.CreateIdentString "mem")) (SynExpr.paren (SynExpr.applyFunction (fst (serializeNode ty)) (SynExpr.createIdent "mem"))),
),
range0 range0
) )
SynExpr.CreateIdentString "arr" SynExpr.createIdent "arr"
] ]
|> SynExpr.CreateSequential |> SynExpr.sequential
|> SynExpr.createLet |> SynExpr.createLet
[ [
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonArray" ] SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonArray" ]
|> SynExpr.applyTo (SynExpr.CreateConst SynConst.Unit) |> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic (SynLongIdent.CreateString "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)
@@ -117,48 +123,36 @@ module internal JsonSerializeGenerator =
DebugPointAtInOrTo.Yes range0, DebugPointAtInOrTo.Yes range0,
SeqExprOnly.SeqExprOnly false, SeqExprOnly.SeqExprOnly false,
true, true,
SynPat.CreateParen ( SynPat.paren (SynPat.nameWithArgs "KeyValue" [ SynPat.named "key" ; SynPat.named "value" ]),
SynPat.CreateLongIdent ( SynExpr.createIdent "field",
SynLongIdent.CreateString "KeyValue", SynExpr.applyFunction
(SynExpr.createLongIdent [ "ret" ; "Add" ])
(SynExpr.tuple
[ [
SynPat.CreateParen ( SynExpr.createLongIdent [ "key" ; "ToString" ]
SynPat.Tuple ( |> SynExpr.applyTo (SynExpr.CreateConst ())
false, SynExpr.applyFunction (fst (serializeNode valueType)) (SynExpr.createIdent "value")
[ ]),
SynPat.CreateNamed (Ident.Create "key")
SynPat.CreateNamed (Ident.Create "value")
],
[ range0 ],
range0
)
)
]
)
),
SynExpr.CreateIdent (Ident.Create "field"),
SynExpr.CreateApp (
SynExpr.createLongIdent [ "ret" ; "Add" ],
SynExpr.CreateParenedTuple
[
SynExpr.CreateApp (
SynExpr.createLongIdent [ "key" ; "ToString" ],
SynExpr.CreateConst SynConst.Unit
)
SynExpr.CreateApp (serializeNode valueType, SynExpr.CreateIdentString "value")
]
),
range0 range0
) )
SynExpr.CreateIdentString "ret" SynExpr.createIdent "ret"
] ]
|> SynExpr.CreateSequential |> SynExpr.sequential
|> SynExpr.createLet |> SynExpr.createLet
[ [
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ] SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|> SynExpr.applyTo (SynExpr.CreateConst SynConst.Unit) |> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic (SynLongIdent.CreateString "ret") [] |> SynBinding.basic [ Ident.create "ret" ] []
] ]
|> SynExpr.createLambda "field" |> SynExpr.createLambda "field"
|> fun e -> e, false
| JsonNode -> SynExpr.createIdent "id", true
| Unit ->
SynExpr.createLambda
"value"
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|> SynExpr.applyTo (SynExpr.CreateConst ())),
false
| _ -> | _ ->
// {type}.toJsonNode // {type}.toJsonNode
let typeName = let typeName =
@@ -166,22 +160,28 @@ 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.CreateApp (serializeNode fieldType, SynExpr.createLongIdent' [ Ident.Create "input" ; fieldId ]) SynExpr.pipeThroughFunction
(fst (serializeNode fieldType))
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldId ])
|> SynExpr.paren
] ]
|> SynExpr.CreateParenedTuple |> SynExpr.tuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ]) |> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
let getPropertyName (fieldId : Ident) (attrs : SynAttribute list) : SynExpr = let getPropertyName (fieldId : Ident) (attrs : SynAttribute list) : SynExpr =
let propertyNameAttr = let propertyNameAttr =
attrs attrs
|> List.tryFind (fun attr -> attr.TypeName.AsString.EndsWith ("JsonPropertyName", StringComparison.Ordinal)) |> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
)
match propertyNameAttr with match propertyNameAttr with
| None -> | None ->
@@ -191,9 +191,17 @@ module internal JsonSerializeGenerator =
if fieldId.idText.Length > 1 then if fieldId.idText.Length > 1 then
sb.Append fieldId.idText.[1..] |> ignore sb.Append fieldId.idText.[1..] |> ignore
sb.ToString () |> SynConst.CreateString |> SynExpr.CreateConst sb.ToString () |> SynExpr.CreateConst
| Some name -> name.ArgExpr | Some name -> name.ArgExpr
let getIsJsonExtension (attrs : SynAttribute list) : bool =
attrs
|> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonExtensionData", StringComparison.Ordinal)
)
|> Option.isSome
/// `populateNode` will be inserted before we return the `node` variable. /// `populateNode` will be inserted before we return the `node` variable.
/// ///
/// That is, we give you access to a `JsonObject` called `node`, /// That is, we give you access to a `JsonObject` called `node`,
@@ -207,93 +215,106 @@ module internal JsonSerializeGenerator =
(populateNode : SynExpr) (populateNode : SynExpr)
: SynModuleDecl : SynModuleDecl
= =
let xmlDoc = PreXmlDoc.Create " Serialize to a JSON node" let xmlDoc = PreXmlDoc.create "Serialize to a JSON node"
let returnInfo = let returnInfo =
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ] SynLongIdent.createS' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|> SynType.LongIdent |> SynType.LongIdent
let functionName = Ident.Create "toJsonNode" let functionName = Ident.create "toJsonNode"
let assignments = let assignments =
[ [
populateNode populateNode
SynExpr.Upcast (SynExpr.CreateIdentString "node", SynType.Anon range0, range0) SynExpr.Upcast (SynExpr.createIdent "node", SynType.Anon range0, range0)
] ]
|> SynExpr.CreateSequential |> SynExpr.sequential
|> SynExpr.createLet |> SynExpr.createLet
[ [
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ] SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|> SynExpr.applyTo (SynExpr.CreateConst SynConst.Unit) |> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic (SynLongIdent.CreateString "node") [] |> SynBinding.basic [ Ident.create "node" ] []
] ]
let pattern = let pattern =
SynPat.CreateNamed inputArgName SynPat.namedI inputArgName
|> SynPat.annotateType (SynType.LongIdent (SynLongIdent.CreateFromLongIdent typeName)) |> SynPat.annotateType (SynType.LongIdent (SynLongIdent.create typeName))
if spec.ExtensionMethods then if spec.ExtensionMethods then
let binding = let componentInfo =
SynComponentInfo.createLong typeName
|> SynComponentInfo.withDocString (PreXmlDoc.create "Extension methods for JSON parsing")
let memberDef =
assignments assignments
|> SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ pattern ] |> SynBinding.basic [ functionName ] [ pattern ]
|> SynBinding.withXmlDoc xmlDoc |> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withReturnAnnotation returnInfo |> SynBinding.withReturnAnnotation returnInfo
|> SynBinding.makeStaticMember |> SynMemberDefn.staticMember
let mem = SynMemberDefn.Member (binding, range0)
let containingType = let containingType =
SynTypeDefn.SynTypeDefn ( SynTypeDefnRepr.augmentation ()
SynComponentInfo.Create (typeName, xmldoc = PreXmlDoc.Create " Extension methods for JSON parsing"), |> SynTypeDefn.create componentInfo
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0), |> SynTypeDefn.withMemberDefns [ memberDef ]
[ mem ],
None,
range0,
{
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
EqualsRange = None
WithKeyword = None
}
)
SynModuleDecl.Types ([ containingType ], range0) SynModuleDecl.Types ([ containingType ], range0)
else else
let binding = assignments
assignments |> SynBinding.basic [ functionName ] [ pattern ]
|> SynBinding.basic (SynLongIdent.CreateFromLongIdent [ functionName ]) [ pattern ] |> SynBinding.withReturnAnnotation returnInfo
|> SynBinding.withReturnAnnotation returnInfo |> SynBinding.withXmlDoc xmlDoc
|> SynBinding.withXmlDoc xmlDoc |> SynModuleDecl.createLet
SynModuleDecl.CreateLet [ binding ] 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
|> List.map (fun fieldData -> |> List.map (fun fieldData ->
let propertyName = getPropertyName fieldData.Ident fieldData.Attrs let propertyName = getPropertyName fieldData.Ident fieldData.Attrs
createSerializeRhsRecord propertyName fieldData.Ident fieldData.Type let isJsonExtension = getIsJsonExtension fieldData.Attrs
if isJsonExtension then
let valType =
match fieldData.Type with
| DictionaryType (String, v) -> v
| _ -> failwith "Expected JsonExtensionData to be a Dictionary<string, something>"
let serialise = fst (serializeNode valType)
SynExpr.createIdent "node"
|> SynExpr.callMethodArg
"Add"
(SynExpr.tuple
[
SynExpr.createIdent "key"
SynExpr.applyFunction serialise (SynExpr.createIdent "value")
])
|> SynExpr.createForEach
(SynPat.identWithArgs
[ Ident.create "KeyValue" ]
(SynArgPats.create [ SynPat.named "key" ; SynPat.named "value" ]))
(SynExpr.createLongIdent' [ Ident.create "input" ; fieldData.Ident ])
else
createSerializeRhsRecord propertyName fieldData.Ident fieldData.Type
) )
|> SynExpr.CreateSequential |> 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"
let fields = cases |> List.map SynUnionCase.extract let fields = cases |> List.map UnionCase.ofSynUnionCase
fields fields
|> List.map (fun unionCase -> |> List.map (fun unionCase ->
let propertyName = getPropertyName unionCase.Ident unionCase.Attrs let propertyName = getPropertyName unionCase.Name unionCase.Attributes
let caseNames = unionCase.Fields |> List.mapi (fun i _ -> Ident.Create $"arg%i{i}") let caseNames = unionCase.Fields |> List.mapi (fun i _ -> $"arg%i{i}")
let argPats = SynArgPats.create caseNames let argPats = SynArgPats.createNamed caseNames
let pattern = let pattern =
SynPat.LongIdent ( SynPat.LongIdent (
SynLongIdent.CreateFromLongIdent (typeName @ [ unionCase.Ident ]), SynLongIdent.create (typeName @ [ unionCase.Name ]),
None, None,
None, None,
argPats, argPats,
@@ -303,26 +324,18 @@ module internal JsonSerializeGenerator =
let typeLine = let typeLine =
[ [
SynExpr.CreateConstString "type" SynExpr.CreateConst "type"
SynExpr.CreateApp ( SynExpr.applyFunction
SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ], (SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonValue" ; "Create" ])
propertyName propertyName
)
] ]
|> SynExpr.CreateParenedTuple |> SynExpr.tuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ]) |> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
let dataNode = let dataNode =
SynBinding.Let ( SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
pattern = SynPat.CreateNamed (Ident.Create "dataNode"), |> SynExpr.applyTo (SynExpr.CreateConst ())
expr = |> SynBinding.basic [ Ident.create "dataNode" ] []
SynExpr.CreateApp (
SynExpr.CreateLongIdent (
SynLongIdent.Create [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
),
SynExpr.CreateConst SynConst.Unit
)
)
let dataBindings = let dataBindings =
(unionCase.Fields, caseNames) (unionCase.Fields, caseNames)
@@ -331,20 +344,20 @@ 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.CreateApp (serializeNode fieldData.Type, SynExpr.CreateIdent caseName) SynExpr.applyFunction (fst (serializeNode fieldData.Type)) (SynExpr.createIdent caseName)
[ propertyName ; node ] [ propertyName ; node ]
|> SynExpr.CreateParenedTuple |> SynExpr.tuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "dataNode" ; "Add" ]) |> SynExpr.applyFunction (SynExpr.createLongIdent [ "dataNode" ; "Add" ])
) )
let assignToNode = let assignToNode =
[ SynExpr.CreateConstString "data" ; SynExpr.CreateIdentString "dataNode" ] [ SynExpr.CreateConst "data" ; SynExpr.createIdent "dataNode" ]
|> SynExpr.CreateParenedTuple |> SynExpr.tuple
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ]) |> SynExpr.applyFunction (SynExpr.createLongIdent [ "node" ; "Add" ])
let dataNode = let dataNode =
SynExpr.CreateSequential (dataBindings @ [ assignToNode ]) SynExpr.sequential (dataBindings @ [ assignToNode ])
|> SynExpr.createLet [ dataNode ] |> SynExpr.createLet [ dataNode ]
let action = let action =
@@ -353,12 +366,73 @@ module internal JsonSerializeGenerator =
if not dataBindings.IsEmpty then if not dataBindings.IsEmpty then
yield dataNode yield dataNode
] ]
|> SynExpr.CreateSequential |> SynExpr.sequential
SynMatchClause.Create (pattern, None, action) SynMatchClause.create pattern action
) )
|> fun clauses -> SynExpr.CreateMatch (SynExpr.CreateIdent inputArg, clauses) |> 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)
@@ -369,17 +443,14 @@ 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 =
if spec.ExtensionMethods then if spec.ExtensionMethods then
[ SynAttributeList.Create SynAttribute.autoOpen ] [ SynAttribute.autoOpen ]
else else
[ [ SynAttribute.requireQualifiedAccess ; SynAttribute.compilationRepresentation ]
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
SynAttributeList.Create SynAttribute.compilationRepresentation
]
let xmlDoc = let xmlDoc =
let fullyQualified = ident |> Seq.map (fun i -> i.idText) |> String.concat "." let fullyQualified = ident |> Seq.map (fun i -> i.idText) |> String.concat "."
@@ -390,8 +461,8 @@ module internal JsonSerializeGenerator =
else else
"methods" "methods"
$" Module containing JSON serializing %s{description} for the %s{fullyQualified} type" $"Module containing JSON serializing %s{description} for the %s{fullyQualified} type"
|> PreXmlDoc.Create |> PreXmlDoc.create
let moduleName = let moduleName =
if spec.ExtensionMethods then if spec.ExtensionMethods then
@@ -402,29 +473,42 @@ module internal JsonSerializeGenerator =
List.last ident List.last ident
|> fun i -> i.idText |> fun i -> i.idText
|> fun s -> s + "JsonSerializeExtension" |> fun s -> s + "JsonSerializeExtension"
|> Ident.Create |> Ident.create
List.take (List.length ident - 1) ident @ [ expanded ] List.take (List.length ident - 1) ident @ [ expanded ]
else else
ident ident
let info = let info =
SynComponentInfo.Create (moduleName, attributes = attributes, xmldoc = xmlDoc) SynComponentInfo.createLong moduleName
|> SynComponentInfo.addAttributes attributes
|> SynComponentInfo.setAccessibility access
|> 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
let mdl = SynModuleDecl.CreateNestedModule (info, decls) [
yield! opens |> List.map SynModuleDecl.openAny
yield decls |> List.singleton |> SynModuleDecl.nestedModule info
]
|> SynModuleOrNamespace.createNamespace namespaceId
SynModuleOrNamespace.CreateNamespace ( open Myriad.Core
namespaceId,
decls = (opens |> List.map SynModuleDecl.CreateOpen) @ [ mdl ]
)
/// Myriad generator that provides a method (possibly an extension method) for a record type, /// Myriad generator that provides a method (possibly an extension method) for a record type,
/// containing a JSON serialization function. /// containing a JSON serialization function.
@@ -438,20 +522,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

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

View File

@@ -1,14 +1,11 @@
namespace WoofWare.Myriad.Plugins namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml open Fantomas.FCS.Xml
open Myriad.Core
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal RemoveOptionsGenerator = module internal RemoveOptionsGenerator =
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Myriad.Core.Ast
let private removeOption (s : SynField) : SynField = let private removeOption (s : SynField) : SynField =
let (SynField.SynField (synAttributeLists, let (SynField.SynField (synAttributeLists,
@@ -39,15 +36,15 @@ module internal RemoveOptionsGenerator =
trivia trivia
) )
// TODO: this option seems a bit odd
let createType let createType
(xmlDoc : PreXmlDoc option) (xmlDoc : PreXmlDoc option)
(accessibility : SynAccess option) (accessibility : SynAccess option)
(generics : SynTyparDecls option) (generics : SynTyparDecls option)
(fields : SynField list) (fields : SynField list)
: SynModuleDecl
= =
let fields : SynField list = fields |> List.map removeOption let fields : SynField list = fields |> List.map removeOption
let name = Ident.Create "Short" let name = Ident.create "Short"
let record = let record =
{ {
@@ -56,28 +53,20 @@ module internal RemoveOptionsGenerator =
Members = None Members = None
XmlDoc = xmlDoc XmlDoc = xmlDoc
Generics = generics Generics = generics
Accessibility = accessibility TypeAccessibility = accessibility
ImplAccessibility = None
Attributes = []
} }
let typeDecl = AstHelper.defineRecordType record let typeDecl = AstHelper.defineRecordType record
SynModuleDecl.Types ([ typeDecl ], range0) SynModuleDecl.Types ([ typeDecl ], range0)
let createMaker (withOptionsType : LongIdent) (withoutOptionsType : LongIdent) (fields : SynFieldData<Ident> list) = let createMaker (withOptionsType : LongIdent) (withoutOptionsType : Ident) (fields : SynFieldData<Ident> list) =
let xmlDoc = PreXmlDoc.Create " Remove the optional members of the input." let xmlDoc = PreXmlDoc.create "Remove the optional members of the input."
let returnInfo = let inputArg = Ident.create "input"
SynBindingReturnInfo.Create (SynType.LongIdent (SynLongIdent.CreateFromLongIdent withOptionsType)) let functionName = Ident.create "shorten"
let inputArg = Ident.Create "input"
let functionName = Ident.Create "shorten"
let inputVal =
SynValData.SynValData (
None,
SynValInfo.SynValInfo ([ [ SynArgInfo.CreateId functionName ] ], SynArgInfo.Empty),
Some inputArg
)
let body = let body =
fields fields
@@ -93,103 +82,56 @@ module internal RemoveOptionsGenerator =
let body = let body =
match fieldData.Type with match fieldData.Type with
| OptionType _ -> | OptionType _ ->
SynExpr.CreateApp ( accessor
SynExpr.CreateAppInfix ( |> SynExpr.pipeThroughFunction (
SynExpr.LongIdent ( SynExpr.applyFunction
false, (SynExpr.createLongIdent [ "Option" ; "defaultWith" ])
SynLongIdent.SynLongIdent ( (SynExpr.createLongIdent' (
[ Ident.Create "op_PipeRight" ], [ withoutOptionsType ]
[], @ [ Ident.create (sprintf "Default%s" fieldData.Ident.idText) ]
[ Some (IdentTrivia.OriginalNotation "|>") ] ))
),
None,
range0
),
accessor
),
SynExpr.CreateApp (
SynExpr.CreateLongIdent (SynLongIdent.CreateString "Option.defaultWith"),
SynExpr.CreateLongIdent (
SynLongIdent.CreateFromLongIdent (
withoutOptionsType
@ [ Ident.Create (sprintf "Default%s" fieldData.Ident.idText) ]
)
)
)
) )
| _ -> accessor | _ -> accessor
(SynLongIdent.CreateFromLongIdent [ fieldData.Ident ], true), Some body SynLongIdent.createI fieldData.Ident, body
) )
|> AstHelper.instantiateRecord |> AstHelper.instantiateRecord
let pattern = SynBinding.basic
SynPat.LongIdent ( [ functionName ]
SynLongIdent.CreateFromLongIdent [ functionName ], [
None, SynPat.named inputArg.idText
None, |> SynPat.annotateType (SynType.LongIdent (SynLongIdent.createI withoutOptionsType))
SynArgPats.Pats ]
[ body
SynPat.CreateTyped ( |> SynBinding.withXmlDoc xmlDoc
SynPat.CreateNamed inputArg, |> SynBinding.withReturnAnnotation (SynType.LongIdent (SynLongIdent.create withOptionsType))
SynType.LongIdent (SynLongIdent.CreateFromLongIdent withoutOptionsType) |> SynModuleDecl.createLet
)
|> SynPat.CreateParen
],
None,
range0
)
let binding = let createRecordModule (namespaceId : LongIdent) (typeDefn : RecordType) =
SynBinding.Let ( let fieldData = typeDefn.Fields |> List.map SynField.extractWithIdent
isInline = false,
isMutable = false,
xmldoc = xmlDoc,
returnInfo = returnInfo,
expr = body,
valData = inputVal,
pattern = pattern
)
SynModuleDecl.CreateLet [ binding ] let decls =
[
createType typeDefn.XmlDoc typeDefn.TypeAccessibility typeDefn.Generics typeDefn.Fields
createMaker [ Ident.create "Short" ] typeDefn.Name fieldData
]
let createRecordModule (namespaceId : LongIdent) (typeDefn : SynTypeDefn) = let xmlDoc =
let (SynTypeDefn (synComponentInfo, synTypeDefnRepr, _members, _implicitCtor, _, _)) = sprintf "Module containing an option-truncated version of the %s type" typeDefn.Name.idText
typeDefn |> PreXmlDoc.create
let (SynComponentInfo (_attributes, typeParams, _constraints, recordId, doc, _preferPostfix, _access, _)) = let info =
synComponentInfo SynComponentInfo.create typeDefn.Name
|> SynComponentInfo.withDocString xmlDoc
|> SynComponentInfo.addAttributes [ SynAttribute.compilationRepresentation ]
|> SynComponentInfo.addAttributes [ SynAttribute.requireQualifiedAccess ]
match synTypeDefnRepr with SynModuleDecl.nestedModule info decls
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (accessibility, fields, _range), _) -> |> List.singleton
let fieldData = fields |> List.map SynField.extractWithIdent |> SynModuleOrNamespace.createNamespace namespaceId
let decls = open Myriad.Core
[
createType (Some doc) accessibility typeParams fields
createMaker [ Ident.Create "Short" ] recordId fieldData
]
let attributes =
[
SynAttributeList.Create (SynAttribute.RequireQualifiedAccess ())
SynAttributeList.Create SynAttribute.compilationRepresentation
]
let xmlDoc =
recordId
|> Seq.map (fun i -> i.idText)
|> String.concat "."
|> sprintf " Module containing an option-truncated version of the %s type"
|> PreXmlDoc.Create
let info =
SynComponentInfo.Create (recordId, attributes = attributes, xmldoc = xmlDoc)
let mdl = SynModuleDecl.CreateNestedModule (info, decls)
SynModuleOrNamespace.CreateNamespace (namespaceId, decls = [ mdl ])
| _ -> failwithf "Not a record type"
/// Myriad generator that stamps out a record with option types stripped /// Myriad generator that stamps out a record with option types stripped
/// from the fields at the top level. /// from the fields at the top level.
@@ -210,7 +152,24 @@ type RemoveOptionsGenerator () =
|> List.choose (fun (ns, types) -> |> List.choose (fun (ns, types) ->
match types |> List.filter Ast.hasAttribute<RemoveOptionsAttribute> with match types |> List.filter Ast.hasAttribute<RemoveOptionsAttribute> with
| [] -> None | [] -> None
| types -> Some (ns, types) | types ->
let types =
types
|> List.map (fun ty ->
match ty with
| SynTypeDefn.SynTypeDefn (sci,
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access,
fields,
_),
_),
smd,
smdo,
_,
_) -> RecordType.OfRecord sci smd access fields
| _ -> failwith "unexpectedly not a record"
)
Some (ns, types)
) )
let modules = let modules =

View File

@@ -1,3 +1,5 @@
WoofWare.Myriad.Plugins.ArgParserGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
WoofWare.Myriad.Plugins.ArgParserGenerator..ctor [constructor]: unit
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
@@ -9,4 +11,27 @@ WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
WoofWare.Myriad.Plugins.SynFieldData`1 inherit obj
WoofWare.Myriad.Plugins.SynFieldData`1..ctor [constructor]: (Fantomas.FCS.Syntax.SynAttribute list, 'Ident, Fantomas.FCS.Syntax.SynType)
WoofWare.Myriad.Plugins.SynFieldData`1.Attrs [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list
WoofWare.Myriad.Plugins.SynFieldData`1.get_Attrs [method]: unit -> Fantomas.FCS.Syntax.SynAttribute list
WoofWare.Myriad.Plugins.SynFieldData`1.get_Ident [method]: unit -> 'Ident
WoofWare.Myriad.Plugins.SynFieldData`1.get_Type [method]: unit -> Fantomas.FCS.Syntax.SynType
WoofWare.Myriad.Plugins.SynFieldData`1.Ident [property]: [read-only] 'Ident
WoofWare.Myriad.Plugins.SynFieldData`1.Type [property]: [read-only] Fantomas.FCS.Syntax.SynType
WoofWare.Myriad.Plugins.UnionCase inherit obj
WoofWare.Myriad.Plugins.UnionCase.mapIdentFields [static method]: ('a -> 'b) -> 'a WoofWare.Myriad.Plugins.UnionCase -> 'b WoofWare.Myriad.Plugins.UnionCase
WoofWare.Myriad.Plugins.UnionCase.ofSynUnionCase [static method]: Fantomas.FCS.Syntax.SynUnionCase -> Fantomas.FCS.Syntax.Ident option WoofWare.Myriad.Plugins.UnionCase
WoofWare.Myriad.Plugins.UnionCase`1 inherit obj
WoofWare.Myriad.Plugins.UnionCase`1..ctor [constructor]: (Fantomas.FCS.Syntax.Ident, Fantomas.FCS.Xml.PreXmlDoc option, Fantomas.FCS.Syntax.SynAccess option, Fantomas.FCS.Syntax.SynAttribute list, 'ident WoofWare.Myriad.Plugins.SynFieldData list)
WoofWare.Myriad.Plugins.UnionCase`1.Access [property]: [read-only] Fantomas.FCS.Syntax.SynAccess option
WoofWare.Myriad.Plugins.UnionCase`1.Attributes [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list
WoofWare.Myriad.Plugins.UnionCase`1.Fields [property]: [read-only] 'ident WoofWare.Myriad.Plugins.SynFieldData list
WoofWare.Myriad.Plugins.UnionCase`1.get_Access [method]: unit -> Fantomas.FCS.Syntax.SynAccess option
WoofWare.Myriad.Plugins.UnionCase`1.get_Attributes [method]: unit -> Fantomas.FCS.Syntax.SynAttribute list
WoofWare.Myriad.Plugins.UnionCase`1.get_Fields [method]: unit -> 'ident WoofWare.Myriad.Plugins.SynFieldData list
WoofWare.Myriad.Plugins.UnionCase`1.get_Name [method]: unit -> Fantomas.FCS.Syntax.Ident
WoofWare.Myriad.Plugins.UnionCase`1.get_XmlDoc [method]: unit -> Fantomas.FCS.Xml.PreXmlDoc option
WoofWare.Myriad.Plugins.UnionCase`1.Name [property]: [read-only] Fantomas.FCS.Syntax.Ident
WoofWare.Myriad.Plugins.UnionCase`1.XmlDoc [property]: [read-only] Fantomas.FCS.Xml.PreXmlDoc option

View File

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

View File

@@ -0,0 +1,65 @@
namespace WoofWare.Myriad.Plugins
open System
open System.Text
open System.Text.RegularExpressions
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal Ident =
let inline create (s : string) = Ident (s, range0)
/// Fantomas bug, perhaps? "type" is not rendered as ``type``, although the ASTs are identical
/// apart from the ranges?
/// Awful hack: here is a function that does this sort of thing.
let createSanitisedParamName (s : string) =
match s with
| "type" -> create "type'"
| "private" -> create "private'"
| _ ->
let result = StringBuilder ()
for i = 0 to s.Length - 1 do
if Char.IsLetter s.[i] then
result.Append s.[i] |> ignore<StringBuilder>
elif Char.IsNumber s.[i] then
if result.Length > 0 then
result.Append s.[i] |> ignore<StringBuilder>
elif s.[i] = '_' || s.[i] = '-' then
result.Append '_' |> ignore<StringBuilder>
else
failwith $"could not convert to ident: %s{s}"
create (result.ToString ())
let private alnum = Regex @"^[a-zA-Z][a-zA-Z0-9]*$"
let createSanitisedTypeName (s : string) =
let result = StringBuilder ()
let mutable capitalize = true
for i = 0 to s.Length - 1 do
if Char.IsLetter s.[i] then
if capitalize then
result.Append (Char.ToUpperInvariant s.[i]) |> ignore<StringBuilder>
capitalize <- false
else
result.Append s.[i] |> ignore<StringBuilder>
elif Char.IsNumber s.[i] then
if result.Length > 0 then
result.Append s.[i] |> ignore<StringBuilder>
elif s.[i] = '_' then
capitalize <- true
if result.Length = 0 then
failwith $"String %s{s} was not suitable as a type identifier"
Ident (result.ToString (), range0)
let lowerFirstLetter (x : Ident) : Ident =
let result = StringBuilder x.idText.Length
result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore
result.Append x.idText.[1..] |> ignore
create ((result : StringBuilder).ToString ())

View File

@@ -0,0 +1,17 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Xml
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal PreXmlDoc =
let create (s : string) : PreXmlDoc =
let s = s.Split "\n"
for i = 0 to s.Length - 1 do
s.[i] <- " " + s.[i]
PreXmlDoc.Create (s, range0)
let create' (s : string seq) : PreXmlDoc =
PreXmlDoc.Create (Array.ofSeq s, range0)

View File

@@ -5,14 +5,26 @@ open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynArgPats = module internal SynArgPats =
let create (caseNames : Ident list) : SynArgPats = let createNamed (caseNames : string list) : SynArgPats =
if caseNames.IsEmpty then match caseNames.Length with
SynArgPats.Pats [] | 0 -> SynArgPats.Pats []
else | 1 ->
SynPat.Named (SynIdent.createS caseNames.[0], false, None, range0)
|> List.singleton
|> SynArgPats.Pats
| len ->
caseNames
|> List.map (fun name -> SynPat.Named (SynIdent.createS name, false, None, range0))
|> fun t -> SynPat.Tuple (false, t, List.replicate (len - 1) range0, range0)
|> fun t -> SynPat.Paren (t, range0)
|> List.singleton
|> SynArgPats.Pats
caseNames let create (pats : SynPat list) : SynArgPats =
|> List.map (fun ident -> SynPat.Named (SynIdent.SynIdent (ident, None), false, None, range0)) match pats.Length with
|> fun ps -> SynPat.Tuple (false, ps, List.replicate (ps.Length - 1) range0, range0) | 0 -> SynArgPats.Pats []
|> fun p -> SynPat.Paren (p, range0) | 1 -> [ pats.[0] ] |> SynArgPats.Pats
|> List.singleton | len ->
|> SynArgPats.Pats SynPat.Paren (SynPat.Tuple (false, pats, List.replicate (len - 1) range0, range0), range0)
|> List.singleton
|> SynArgPats.Pats

View File

@@ -2,30 +2,26 @@ namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
open Myriad.Core
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynAttribute = module internal SynAttribute =
let internal compilationRepresentation : SynAttribute = let inline create (typeName : SynLongIdent) (arg : SynExpr) : SynAttribute =
{ {
TypeName = SynLongIdent.CreateString "CompilationRepresentation" TypeName = typeName
ArgExpr = ArgExpr = arg
SynExpr.CreateLongIdent (
false,
SynLongIdent.Create [ "CompilationRepresentationFlags" ; "ModuleSuffix" ],
None
)
|> SynExpr.CreateParen
Target = None Target = None
AppliesToGetterAndSetter = false AppliesToGetterAndSetter = false
Range = range0 Range = range0
} }
let internal compilationRepresentation : SynAttribute =
[ "CompilationRepresentationFlags" ; "ModuleSuffix" ]
|> SynExpr.createLongIdent
|> SynExpr.paren
|> create (SynLongIdent.createS "CompilationRepresentation")
let internal requireQualifiedAccess : SynAttribute =
create (SynLongIdent.createS "RequireQualifiedAccess") (SynExpr.CreateConst ())
let internal autoOpen : SynAttribute = let internal autoOpen : SynAttribute =
{ create (SynLongIdent.createS "AutoOpen") (SynExpr.CreateConst ())
TypeName = SynLongIdent.CreateString "AutoOpen"
ArgExpr = SynExpr.CreateConst SynConst.Unit
Target = None
AppliesToGetterAndSetter = false
Range = range0
}

View File

@@ -0,0 +1,15 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynAttributes =
let ofAttrs (attrs : SynAttribute list) : SynAttributes =
attrs
|> List.map (fun a ->
{
Attributes = [ a ]
Range = range0
}
)

View File

@@ -16,14 +16,18 @@ module internal SynBinding =
let rec private getName (pat : SynPat) : Ident option = let rec private getName (pat : SynPat) : Ident option =
match stripParen pat with match stripParen pat with
| SynPat.Named (SynIdent.SynIdent (name, _), _, _, _) -> Some name | SynPat.Named (SynIdent.SynIdent (name, _), _, _, _) -> Some name
| SynPat.Wild _ -> None
| SynPat.Typed (pat, _, _) -> getName pat | SynPat.Typed (pat, _, _) -> getName pat
| SynPat.Const _ -> None
| SynPat.LongIdent (SynLongIdent.SynLongIdent (longIdent, _, _), _, _, _, _, _) -> | SynPat.LongIdent (SynLongIdent.SynLongIdent (longIdent, _, _), _, _, _, _, _) ->
match longIdent with match longIdent with
| [ x ] -> Some x | [ x ] -> Some x
| _ -> failwithf "got long ident %O ; can only get the name of a long ident with one component" longIdent | _ -> failwithf "got long ident %O ; can only get the name of a long ident with one component" longIdent
| _ -> failwithf "unrecognised pattern: %+A" pat | _ -> None
let private getArgInfo (pat : SynPat) : SynArgInfo list =
// TODO: this only copes with one layer of tupling
match stripParen pat with
| SynPat.Tuple (_, pats, _, _) -> pats |> List.map (fun pat -> SynArgInfo.SynArgInfo ([], false, getName pat))
| pat -> [ SynArgInfo.SynArgInfo (SynAttributes.Empty, false, getName pat) ]
let triviaZero (isMember : bool) = let triviaZero (isMember : bool) =
{ {
@@ -36,10 +40,10 @@ module internal SynBinding =
SynLeadingKeyword.Let range0 SynLeadingKeyword.Let range0
} }
let basic (name : SynLongIdent) (args : SynPat list) (body : SynExpr) : SynBinding = let basic (name : LongIdent) (args : SynPat list) (body : SynExpr) : SynBinding =
let valInfo : SynValInfo = let valInfo : SynValInfo =
args args
|> List.map (fun pat -> [ SynArgInfo.SynArgInfo (SynAttributes.Empty, false, getName pat) ]) |> List.map getArgInfo
|> fun x -> SynValInfo.SynValInfo (x, SynArgInfo.SynArgInfo ([], false, None)) |> fun x -> SynValInfo.SynValInfo (x, SynArgInfo.SynArgInfo ([], false, None))
SynBinding.SynBinding ( SynBinding.SynBinding (
@@ -50,7 +54,7 @@ module internal SynBinding =
[], [],
PreXmlDoc.Empty, PreXmlDoc.Empty,
SynValData.SynValData (None, valInfo, None), SynValData.SynValData (None, valInfo, None),
SynPat.LongIdent (name, None, None, SynArgPats.Pats args, None, range0), SynPat.identWithArgs name (SynArgPats.Pats args),
None, None,
body, body,
range0, range0,
@@ -58,6 +62,35 @@ module internal SynBinding =
triviaZero false triviaZero false
) )
let withMutability (mut : bool) (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (pat, kind, inl, _, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
let withRecursion (isRec : bool) (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
let trivia =
{ trivia with
LeadingKeyword =
match trivia.LeadingKeyword with
| SynLeadingKeyword.Let _ ->
if isRec then
SynLeadingKeyword.LetRec (range0, range0)
else
trivia.LeadingKeyword
| SynLeadingKeyword.LetRec _ ->
if isRec then
trivia.LeadingKeyword
else
trivia.LeadingKeyword
| existing ->
failwith
$"WoofWare.Myriad doesn't yet let you adjust the recursion modifier on a binding with modifier %O{existing}"
}
SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
let withAccessibility (acc : SynAccess option) (binding : SynBinding) : SynBinding = let withAccessibility (acc : SynAccess option) (binding : SynBinding) : SynBinding =
match binding with match binding with
| SynBinding (_, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) -> | SynBinding (_, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
@@ -103,7 +136,7 @@ module internal SynBinding =
trivia trivia
) )
let makeInline (binding : SynBinding) : SynBinding = let inline makeInline (binding : SynBinding) : SynBinding =
match binding with match binding with
| SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) -> | SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
SynBinding ( SynBinding (
@@ -124,6 +157,33 @@ module internal SynBinding =
} }
) )
let inline makeNotInline (binding : SynBinding) : SynBinding =
match binding with
| SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
SynBinding (
acc,
kind,
false,
mut,
attrs,
doc,
valData,
headPat,
ret,
expr,
range,
debugPoint,
{ trivia with
InlineKeyword = None
}
)
let inline setInline (isInline : bool) (binding : SynBinding) : SynBinding =
if isInline then
makeInline binding
else
makeNotInline binding
let makeStaticMember (binding : SynBinding) : SynBinding = let makeStaticMember (binding : SynBinding) : SynBinding =
let memberFlags = let memberFlags =
{ {

View File

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

View File

@@ -3,14 +3,25 @@ namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia open Fantomas.FCS.SyntaxTrivia
open Myriad.Core open Myriad.Core
open Myriad.Core.Ast
open Fantomas.FCS.Text.Range open Fantomas.FCS.Text.Range
type internal CompExprBinding = [<AutoOpen>]
| LetBang of varName : string * rhs : SynExpr module internal SynExprExtensions =
| Let of varName : string * rhs : SynExpr type SynExpr with
| Use of varName : string * rhs : SynExpr static member CreateConst (s : string) : SynExpr =
| Do of body : SynExpr SynExpr.Const (SynConst.String (s, SynStringKind.Regular, range0), range0)
static member CreateConst () : SynExpr = SynExpr.Const (SynConst.Unit, range0)
static member CreateConst (b : bool) : SynExpr = SynExpr.Const (SynConst.Bool b, range0)
static member CreateConst (c : char) : SynExpr =
// apparent Myriad bug: `IndexOf '?'` gets formatted as `IndexOf ?` which is clearly wrong
SynExpr.CreateApp (SynExpr.Ident (Ident.Create "char"), SynExpr.CreateConst (int c))
|> fun e -> SynExpr.Paren (e, range0, Some range0, range0)
static member CreateConst (i : int32) : SynExpr =
SynExpr.Const (SynConst.Int32 i, range0)
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynExpr = module internal SynExpr =
@@ -19,20 +30,11 @@ module internal SynExpr =
let applyFunction (f : SynExpr) (x : SynExpr) : SynExpr = SynExpr.CreateApp (f, x) let applyFunction (f : SynExpr) (x : SynExpr) : SynExpr = SynExpr.CreateApp (f, x)
/// {f} {x} /// {f} {x}
let applyTo (x : SynExpr) (f : SynExpr) : SynExpr = SynExpr.CreateApp (f, x) let inline applyTo (x : SynExpr) (f : SynExpr) : SynExpr = applyFunction f x
/// {expr} |> {func} /// {expr} |> {func}
let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr = let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr =
SynExpr.CreateAppInfix ( SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.pipe, expr)
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
[ Ident.Create "op_PipeRight" ],
[],
[ Some (IdentTrivia.OriginalNotation "|>") ]
)
),
expr
)
|> applyTo func |> applyTo func
/// if {cond} then {trueBranch} else {falseBranch} /// if {cond} then {trueBranch} else {falseBranch}
@@ -58,7 +60,7 @@ module internal SynExpr =
/// try {body} with | {exc} as exc -> {handler} /// try {body} with | {exc} as exc -> {handler}
let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr = let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr =
let clause = let clause =
SynMatchClause.Create (SynPat.As (exc, SynPat.CreateNamed (Ident.Create "exc"), range0), None, handler) SynMatchClause.create (SynPat.As (exc, SynPat.named "exc", range0)) handler
SynExpr.TryWith ( SynExpr.TryWith (
body, body,
@@ -76,16 +78,16 @@ module internal SynExpr =
/// {a} = {b} /// {a} = {b}
let equals (a : SynExpr) (b : SynExpr) = let equals (a : SynExpr) (b : SynExpr) =
SynExpr.CreateAppInfix ( SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.eq, a) |> applyTo b
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent ( /// {a} && {b}
Ident.CreateLong "op_Equality", let booleanAnd (a : SynExpr) (b : SynExpr) =
[], SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.booleanAnd, a)
[ Some (IdentTrivia.OriginalNotation "=") ] |> applyTo b
)
), /// {a} || {b}
a let booleanOr (a : SynExpr) (b : SynExpr) =
) SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.booleanOr, a)
|> applyTo b |> applyTo b
/// {a} + {b} /// {a} + {b}
@@ -102,56 +104,70 @@ 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 SynConst.Unit) obj callMethodArg meth (SynExpr.CreateConst ()) obj
let callGenericMethod (meth : string) (ty : LongIdent) (obj : SynExpr) : SynExpr = let typeApp (types : SynType list) (operand : SynExpr) =
SynExpr.TypeApp ( SynExpr.TypeApp (operand, range0, types, List.replicate (types.Length - 1) range0, Some range0, range0, range0)
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0),
range0, /// {obj}.{meth}<types,...>()
[ SynType.LongIdent (SynLongIdent.CreateFromLongIdent ty) ], let callGenericMethod (meth : SynLongIdent) (types : SynType list) (obj : SynExpr) : SynExpr =
[], SynExpr.DotGet (obj, range0, meth, range0)
Some range0, |> typeApp types
range0, |> applyTo (SynExpr.CreateConst ())
range0
)
|> applyTo (SynExpr.CreateConst SynConst.Unit)
/// {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 ( callGenericMethod (SynLongIdent.createS meth) [ SynType.createLongIdent' [ ty ] ] obj
SynExpr.DotGet (obj, range0, SynLongIdent.Create [ meth ], range0),
range0, let inline index (property : SynExpr) (obj : SynExpr) : SynExpr =
[ SynType.CreateLongIdent ty ], SynExpr.DotIndexedGet (obj, property, range0, range0)
[],
Some range0, let inline arrayIndexRange (start : SynExpr option) (endRange : SynExpr option) (arr : SynExpr) : SynExpr =
SynExpr.DotIndexedGet (
arr,
(SynExpr.IndexRange (start, range0, endRange, range0, range0, range0)),
range0, range0,
range0 range0
) )
|> applyTo (SynExpr.CreateConst SynConst.Unit)
let index (property : SynExpr) (obj : SynExpr) : SynExpr = let inline paren (e : SynExpr) : SynExpr =
SynExpr.DotIndexedGet (obj, property, range0, range0) SynExpr.Paren (e, range0, Some range0, range0)
/// (fun {varName} -> {body}) /// (fun {varName} -> {body})
let createLambda (varName : string) (body : SynExpr) : SynExpr = let createLambda (varName : string) (body : SynExpr) : SynExpr =
let parsedDataPat = [ SynPat.CreateNamed (Ident.Create varName) ] let parsedDataPat = [ SynPat.named varName ]
SynExpr.Lambda ( SynExpr.Lambda (
false, false,
@@ -164,39 +180,102 @@ module internal SynExpr =
ArrowRange = Some range0 ArrowRange = Some range0
} }
) )
|> SynExpr.CreateParen |> paren
let reraise : SynExpr = let createThunk (body : SynExpr) : SynExpr =
SynExpr.CreateIdent (Ident.Create "reraise") SynExpr.Lambda (
|> applyTo (SynExpr.CreateConst SynConst.Unit) false,
false,
SynSimplePats.Create [],
body,
Some ([ SynPat.unit ], body),
range0,
{
ArrowRange = Some range0
}
)
|> paren
let inline createIdent (s : string) : SynExpr = SynExpr.Ident (Ident (s, range0))
let inline createIdent' (i : Ident) : SynExpr = SynExpr.Ident i
let inline createLongIdent' (ident : Ident list) : SynExpr =
SynExpr.LongIdent (false, SynLongIdent.create ident, None, range0)
let inline createLongIdent (ident : string list) : SynExpr =
createLongIdent' (ident |> List.map Ident.create)
let tupleNoParen (args : SynExpr list) : SynExpr =
SynExpr.Tuple (false, args, List.replicate (args.Length - 1) range0, range0)
let inline tuple (args : SynExpr list) = args |> tupleNoParen |> paren
/// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct) /// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct)
let startAsTask (ct : SynLongIdent) (body : SynExpr) = let startAsTask (ct : Ident) (body : SynExpr) =
let lambda = let lambda =
[ [
SynExpr.CreateLongIdent (SynLongIdent.CreateString "a") createIdent "a"
equals equals
(SynExpr.LongIdent (true, SynLongIdent.CreateString "cancellationToken", None, range0)) (SynExpr.LongIdent (true, SynLongIdent.createS "cancellationToken", None, range0))
(SynExpr.CreateLongIdent ct) (createIdent' ct)
] ]
|> SynExpr.CreateParenedTuple |> tuple
|> applyFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "StartAsTask" ])) |> applyFunction (createLongIdent [ "Async" ; "StartAsTask" ])
|> createLambda "a" |> createLambda "a"
pipeThroughFunction lambda body pipeThroughFunction lambda body
let createLongIdent (ident : string list) : SynExpr = let inline createForEach (pat : SynPat) (enumExpr : SynExpr) (body : SynExpr) : SynExpr =
SynExpr.CreateLongIdent (SynLongIdent.Create ident) SynExpr.ForEach (
DebugPointAtFor.No,
DebugPointAtInOrTo.No,
SeqExprOnly.SeqExprOnly false,
true,
pat,
enumExpr,
body,
range0
)
let createLongIdent' (ident : Ident list) : SynExpr = let inline createLet (bindings : SynBinding list) (body : SynExpr) : SynExpr =
SynExpr.CreateLongIdent (SynLongIdent.CreateFromLongIdent ident)
let createLet (bindings : SynBinding list) (body : SynExpr) : SynExpr =
SynExpr.LetOrUse (false, false, bindings, body, range0, SynExprLetOrUseTrivia.empty) SynExpr.LetOrUse (false, false, bindings, body, range0, SynExprLetOrUseTrivia.empty)
let createMatch (matchOn : SynExpr) (cases : SynMatchClause list) : SynExpr = SynExpr.CreateMatch (matchOn, cases) let inline createDo (body : SynExpr) : SynExpr = SynExpr.Do (body, range0)
let typeAnnotate (ty : SynType) (expr : SynExpr) : SynExpr = SynExpr.CreateTyped (expr, ty) let inline createMatch (matchOn : SynExpr) (cases : SynMatchClause list) : SynExpr =
SynExpr.Match (
DebugPointAtBinding.Yes range0,
matchOn,
cases,
range0,
{
MatchKeyword = range0
WithKeyword = range0
}
)
let typeAnnotate (ty : SynType) (expr : SynExpr) : SynExpr = SynExpr.Typed (expr, ty, range0)
let inline createNew (ty : SynType) (args : SynExpr) : SynExpr =
SynExpr.New (false, ty, paren args, range0)
let inline createWhile (cond : SynExpr) (body : SynExpr) : SynExpr =
SynExpr.While (DebugPointAtWhile.Yes range0, cond, body, range0)
let inline createNull () : SynExpr = SynExpr.Null range0
let reraise : SynExpr = createIdent "reraise" |> applyTo (SynExpr.CreateConst ())
let sequential (exprs : SynExpr list) : SynExpr =
exprs
|> List.reduce (fun a b -> SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, false, a, b, range0))
let listLiteral (elts : SynExpr list) : SynExpr =
SynExpr.ArrayOrListComputed (false, sequential elts, range0)
let arrayLiteral (elts : SynExpr list) : SynExpr =
SynExpr.ArrayOrListComputed (true, sequential elts, range0)
/// {compExpr} { {lets} ; return {ret} } /// {compExpr} { {lets} ; return {ret} }
let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr = let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr =
@@ -211,7 +290,7 @@ module internal SynExpr =
DebugPointAtBinding.Yes range0, DebugPointAtBinding.Yes range0,
false, false,
true, true,
SynPat.CreateNamed (Ident.Create lhs), SynPat.named lhs,
rhs, rhs,
[], [],
state, state,
@@ -220,90 +299,78 @@ module internal SynExpr =
EqualsRange = Some range0 EqualsRange = Some range0
} }
) )
| Let (lhs, rhs) -> | Let (lhs, rhs) -> createLet [ SynBinding.basic [ Ident.create lhs ] [] rhs ] state
createLet [ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ] state
| Use (lhs, rhs) -> | Use (lhs, rhs) ->
SynExpr.LetOrUse ( SynExpr.LetOrUse (
false, false,
true, true,
[ SynBinding.Let (pattern = SynPat.CreateNamed (Ident.Create lhs), expr = rhs) ], [ SynBinding.basic [ Ident.create lhs ] [] rhs ],
state, state,
range0, range0,
{ {
SynExprLetOrUseTrivia.InKeyword = None SynExprLetOrUseTrivia.InKeyword = None
} }
) )
| Do body -> SynExpr.CreateSequential [ SynExpr.Do (body, range0) ; state ] | Do body -> sequential [ SynExpr.Do (body, range0) ; state ]
) )
SynExpr.CreateApp ( applyFunction (createIdent compExpr) (SynExpr.ComputationExpr (false, contents, range0))
SynExpr.CreateIdent (Ident.Create compExpr),
SynExpr.ComputationExpr (false, contents, range0)
)
/// {expr} |> Async.AwaitTask /// {expr} |> Async.AwaitTask
let awaitTask (expr : SynExpr) : SynExpr = let awaitTask (expr : SynExpr) : SynExpr =
expr expr |> pipeThroughFunction (createLongIdent [ "Async" ; "AwaitTask" ])
|> pipeThroughFunction (SynExpr.CreateLongIdent (SynLongIdent.Create [ "Async" ; "AwaitTask" ]))
/// {ident}.ToString () /// {ident}.ToString ()
/// with special casing for some types like DateTime /// with special casing for some types like DateTime
let toString (ty : SynType) (ident : SynExpr) = let toString (ty : SynType) (ident : SynExpr) =
match ty with match ty with
| DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-dd") | DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-dd")
| DateTime -> | DateTime -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-ddTHH:mm:ss")
ident
|> callMethodArg "ToString" (SynExpr.CreateConstString "yyyy-MM-ddTHH:mm:ss")
| _ -> callMethod "ToString" ident | _ -> callMethod "ToString" ident
let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0) let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0)
/// {ident} - {rhs} /// {ident} - {rhs}
let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr = let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr =
SynExpr.CreateApp ( SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.sub, SynExpr.CreateLongIdent ident)
SynExpr.CreateAppInfix ( |> applyTo rhs
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
[ Ident.Create "op_Subtraction" ],
[],
[ Some (IdentTrivia.OriginalNotation "-") ]
)
),
SynExpr.CreateLongIdent ident
),
rhs
)
/// {ident} - {n} /// {ident} - {n}
let minusN (ident : SynLongIdent) (n : int) : SynExpr = let minusN (ident : SynLongIdent) (n : int) : SynExpr = minus ident (SynExpr.CreateConst n)
minus ident (SynExpr.CreateConst (SynConst.Int32 n))
/// {y} > {x} /// {y} > {x}
let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr = let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr =
SynExpr.CreateApp ( SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.gt, y) |> applyTo x
SynExpr.CreateAppInfix (
SynExpr.CreateLongIdent ( /// {y} < {x}
SynLongIdent.SynLongIdent ( let lessThan (x : SynExpr) (y : SynExpr) : SynExpr =
[ Ident.Create "op_GreaterThan" ], SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.lt, y) |> applyTo x
[],
[ Some (IdentTrivia.OriginalNotation ">") ]
)
),
y
),
x
)
/// {y} >= {x} /// {y} >= {x}
let greaterThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr = let greaterThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr =
SynExpr.CreateAppInfix ( SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.geq, y)
SynExpr.CreateLongIdent (
SynLongIdent.SynLongIdent (
[ Ident.Create "op_GreaterThanOrEqual" ],
[],
[ Some (IdentTrivia.OriginalNotation ">=") ]
)
),
y
)
|> applyTo x |> applyTo x
/// {y} <= {x}
let lessThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr =
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.leq, y)
|> applyTo x
/// {x} :: {y}
let listCons (x : SynExpr) (y : SynExpr) : SynExpr =
SynExpr.CreateAppInfix (
SynExpr.LongIdent (
false,
SynLongIdent.SynLongIdent (
[ Ident.create "op_ColonColon" ],
[],
[ Some (IdentTrivia.OriginalNotation "::") ]
),
None,
range0
),
tupleNoParen [ x ; y ]
)
|> paren
let assign (lhs : SynLongIdent) (rhs : SynExpr) : SynExpr = SynExpr.LongIdentSet (lhs, rhs, range0)

View File

@@ -1,11 +1,21 @@
namespace WoofWare.Myriad.Plugins namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Text.Range
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
type internal SynFieldData<'Ident> = /// The data needed to reconstitute a single piece of data within a union field, or a single record field.
/// This is generic on whether the field is identified. For example, in `type Foo = Blah of int`, the `int`
/// field is not identified; whereas in `type Foo = Blah of baz : int`, it is identified.
type SynFieldData<'Ident> =
{ {
/// Attributes on this field. I think you can only get these if this is a *record* field.
Attrs : SynAttribute list Attrs : SynAttribute list
/// The identifier of this field (see docstring for SynFieldData).
Ident : 'Ident Ident : 'Ident
/// The type of the data contained in this field. For example, `type Foo = { Blah : int }`
/// has this being `int`.
Type : SynType Type : SynType
} }
@@ -37,3 +47,30 @@ module internal SynField =
| None -> failwith "expected field identifier to have a value, but it did not" | None -> failwith "expected field identifier to have a value, but it did not"
| Some i -> i | Some i -> i
) )
let make (data : SynFieldData<Ident option>) : SynField =
let attrs : SynAttributeList list =
data.Attrs
|> List.map (fun l ->
{
Attributes = [ l ]
Range = range0
}
)
SynField.SynField (
attrs,
false,
data.Ident,
data.Type,
false,
PreXmlDoc.Empty,
None,
range0,
SynFieldTrivia.Zero
)
let withDocString (doc : PreXmlDoc) (f : SynField) : SynField =
match f with
| SynField (attributes, isStatic, idOpt, fieldType, isMutable, _, accessibility, range, trivia) ->
SynField (attributes, isStatic, idOpt, fieldType, isMutable, doc, accessibility, range, trivia)

View File

@@ -0,0 +1,10 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
[<RequireQualifiedAccess>]
module internal SynIdent =
let inline createI (i : Ident) : SynIdent = SynIdent.SynIdent (i, None)
let inline createS (i : string) : SynIdent =
SynIdent.SynIdent (Ident.create i, None)

View File

@@ -0,0 +1,134 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Text.Range
open Fantomas.FCS.Syntax
[<RequireQualifiedAccess>]
module internal SynLongIdent =
let geq =
SynLongIdent.SynLongIdent (
[ Ident.create "op_GreaterThanOrEqual" ],
[],
[ Some (IdentTrivia.OriginalNotation ">=") ]
)
let leq =
SynLongIdent.SynLongIdent (
[ Ident.create "op_LessThanOrEqual" ],
[],
[ Some (IdentTrivia.OriginalNotation "<=") ]
)
let gt =
SynLongIdent.SynLongIdent ([ Ident.create "op_GreaterThan" ], [], [ Some (IdentTrivia.OriginalNotation ">") ])
let lt =
SynLongIdent.SynLongIdent ([ Ident.create "op_LessThan" ], [], [ Some (IdentTrivia.OriginalNotation "<") ])
let sub =
SynLongIdent.SynLongIdent ([ Ident.create "op_Subtraction" ], [], [ Some (IdentTrivia.OriginalNotation "-") ])
let eq =
SynLongIdent.SynLongIdent ([ Ident.create "op_Equality" ], [], [ Some (IdentTrivia.OriginalNotation "=") ])
let booleanAnd =
SynLongIdent.SynLongIdent ([ Ident.create "op_BooleanAnd" ], [], [ Some (IdentTrivia.OriginalNotation "&&") ])
let booleanOr =
SynLongIdent.SynLongIdent ([ Ident.create "op_BooleanOr" ], [], [ Some (IdentTrivia.OriginalNotation "||") ])
let pipe =
SynLongIdent.SynLongIdent ([ Ident.create "op_PipeRight" ], [], [ Some (IdentTrivia.OriginalNotation "|>") ])
let toString (sli : SynLongIdent) : string =
sli.LongIdent |> List.map _.idText |> String.concat "."
let create (ident : LongIdent) : SynLongIdent =
let commas =
match ident with
| [] -> []
| _ :: commas -> commas |> List.map (fun _ -> range0)
SynLongIdent.SynLongIdent (ident, commas, List.replicate ident.Length None)
let inline createI (i : Ident) : SynLongIdent = create [ i ]
let inline createS (s : string) : SynLongIdent = createI (Ident (s, range0))
let inline createS' (s : string list) : SynLongIdent =
create (s |> List.map (fun i -> Ident (i, range0)))
let isUnit (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "unit", System.StringComparison.OrdinalIgnoreCase) -> true
| _ -> false
let isList (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true
// TODO: consider FSharpList or whatever it is
| _ -> false
let isArray (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when
System.String.Equals (i.idText, "array", System.StringComparison.OrdinalIgnoreCase)
|| System.String.Equals (i.idText, "[]", System.StringComparison.Ordinal)
->
true
| _ -> false
let isOption (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "option", System.StringComparison.OrdinalIgnoreCase) -> true
// TODO: consider Microsoft.FSharp.Option or whatever it is
| _ -> false
let isChoice (ident : SynLongIdent) : bool =
match ident.LongIdent with
| [ i ] when System.String.Equals (i.idText, "Choice", System.StringComparison.Ordinal) -> true
// TODO: consider Microsoft.FSharp.Choice or whatever it is
| _ -> false
let isNullable (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "System" ; "Nullable" ]
| [ "Nullable" ] -> true
| _ -> false
let isResponse (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Response" ]
| [ "RestEase" ; "Response" ] -> true
| _ -> false
let isMap (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Map" ] -> true
| _ -> false
let isReadOnlyDictionary (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "IReadOnlyDictionary" ]
| [ "Generic" ; "IReadOnlyDictionary" ]
| [ "Collections" ; "Generic" ; "IReadOnlyDictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "IReadOnlyDictionary" ] -> true
| _ -> false
let isDictionary (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "Dictionary" ]
| [ "Generic" ; "Dictionary" ]
| [ "Collections" ; "Generic" ; "Dictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "Dictionary" ] -> true
| _ -> false
let isIDictionary (ident : SynLongIdent) : bool =
match ident.LongIdent |> List.map _.idText with
| [ "IDictionary" ]
| [ "Generic" ; "IDictionary" ]
| [ "Collections" ; "Generic" ; "IDictionary" ]
| [ "System" ; "Collections" ; "Generic" ; "IDictionary" ] -> true
| _ -> false

View File

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

View File

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

View File

@@ -0,0 +1,30 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynModuleDecl =
let inline openAny (ident : SynOpenDeclTarget) : SynModuleDecl = SynModuleDecl.Open (ident, range0)
let inline createLets (bindings : SynBinding list) : SynModuleDecl =
SynModuleDecl.Let (false, bindings, range0)
let inline createLet (binding : SynBinding) : SynModuleDecl = createLets [ binding ]
let inline createTypes (tys : SynTypeDefn list) : SynModuleDecl = SynModuleDecl.Types (tys, range0)
let nestedModule (info : SynComponentInfo) (decls : SynModuleDecl list) : SynModuleDecl =
SynModuleDecl.NestedModule (
info,
false,
decls,
false,
range0,
{
ModuleKeyword = Some range0
EqualsRange = Some range0
}
)

View File

@@ -0,0 +1,24 @@
namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax
open Fantomas.FCS.SyntaxTrivia
open Fantomas.FCS.Xml
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
module internal SynModuleOrNamespace =
let createNamespace (name : LongIdent) (decls : SynModuleDecl list) =
SynModuleOrNamespace.SynModuleOrNamespace (
name,
false,
SynModuleOrNamespaceKind.DeclaredNamespace,
decls,
PreXmlDoc.Empty,
[],
None,
range0,
{
LeadingKeyword = SynModuleOrNamespaceLeadingKeyword.Namespace range0
}
)

View File

@@ -5,6 +5,50 @@ open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynPat = module internal SynPat =
let inline paren (pat : SynPat) : SynPat = SynPat.Paren (pat, range0)
let annotateType (ty : SynType) (pat : SynPat) = let anon : SynPat = SynPat.Wild range0
SynPat.Paren (SynPat.Typed (pat, ty, range0), range0)
let inline annotateTypeNoParen (ty : SynType) (pat : SynPat) = SynPat.Typed (pat, ty, range0)
let inline annotateType (ty : SynType) (pat : SynPat) = paren (annotateTypeNoParen ty pat)
let inline named (s : string) : SynPat =
SynPat.Named (SynIdent.SynIdent (Ident (s, range0), None), false, None, range0)
let inline namedI (i : Ident) : SynPat =
SynPat.Named (SynIdent.SynIdent (i, None), false, None, range0)
let inline identWithArgs (i : LongIdent) (args : SynArgPats) : SynPat =
SynPat.LongIdent (SynLongIdent.create i, None, None, args, None, range0)
let inline nameWithArgs (i : string) (args : SynPat list) : SynPat =
identWithArgs [ Ident.create i ] (SynArgPats.create args)
let inline tupleNoParen (elements : SynPat list) : SynPat =
match elements with
| [] -> failwith "Can't tuple no elements in a pattern"
| [ p ] -> p
| elements -> SynPat.Tuple (false, elements, List.replicate (elements.Length - 1) range0, range0)
let inline tuple (elements : SynPat list) : SynPat = tupleNoParen elements |> paren
let inline createConst (c : SynConst) = SynPat.Const (c, range0)
let unit = createConst SynConst.Unit
let createNull = SynPat.Null range0
let emptyList = SynPat.ArrayOrList (false, [], range0)
let listCons (lhs : SynPat) (rhs : SynPat) =
SynPat.ListCons (
lhs,
rhs,
range0,
{
ColonColonRange = range0
}
)
let emptyArray = SynPat.ArrayOrList (true, [], range0)

View File

@@ -1,6 +1,287 @@
namespace WoofWare.Myriad.Plugins namespace WoofWare.Myriad.Plugins
open System
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
[<AutoOpen>]
module internal SynTypePatterns =
let (|OptionType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isOption ident ->
Some innerType
| _ -> None
let (|ChoiceType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, inner, _, _, _, _) when SynLongIdent.isChoice ident -> Some inner
| _ -> 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 =
match fieldType with
| SynType.LongIdent ident when SynLongIdent.isUnit ident -> Some ()
| _ -> None
let (|ListType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isList ident ->
Some innerType
| _ -> None
let (|ArrayType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isArray ident ->
Some innerType
| SynType.Array (1, innerType, _) -> Some innerType
| _ -> None
let (|RestEaseResponseType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isResponse ident ->
Some innerType
| _ -> None
let (|DictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isDictionary ident ->
Some (key, value)
| _ -> None
let (|IDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isIDictionary ident ->
Some (key, value)
| _ -> None
let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when
SynLongIdent.isReadOnlyDictionary ident
->
Some (key, value)
| _ -> None
let (|MapType|_|) (fieldType : SynType) =
match fieldType with
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isMap ident ->
Some (key, value)
| _ -> None
let (|BigInt|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map _.idText with
| [ "bigint" ]
| [ "BigInteger" ]
| [ "Numerics" ; "BigInteger" ]
| [ "System" ; "Numerics" ; "BigInteger" ] -> Some ()
| _ -> None
| _ -> None
/// Returns the type, qualified as in e.g. `System.Boolean`.
let (|PrimitiveType|_|) (fieldType : SynType) : LongIdent option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] -> Primitives.qualifyType i.idText
| _ -> None
| _ -> None
let (|String|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] ->
[ "string" ]
|> List.tryFind (fun s -> s = i.idText)
|> Option.map ignore<string>
| _ -> None
| _ -> None
let (|Byte|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] -> [ "byte" ] |> List.tryFind (fun s -> s = i.idText) |> Option.map ignore<string>
| _ -> None
| _ -> None
let (|Guid|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Guid" ]
| [ "Guid" ] -> Some ()
| _ -> None
| _ -> None
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Net" ; "Http" ; "HttpResponseMessage" ]
| [ "Net" ; "Http" ; "HttpResponseMessage" ]
| [ "Http" ; "HttpResponseMessage" ]
| [ "HttpResponseMessage" ] -> Some ()
| _ -> None
| _ -> None
let (|HttpContent|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "Net" ; "Http" ; "HttpContent" ]
| [ "Net" ; "Http" ; "HttpContent" ]
| [ "Http" ; "HttpContent" ]
| [ "HttpContent" ] -> Some ()
| _ -> None
| _ -> None
let (|Stream|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent |> List.map (fun i -> i.idText) with
| [ "System" ; "IO" ; "Stream" ]
| [ "IO" ; "Stream" ]
| [ "Stream" ] -> Some ()
| _ -> None
| _ -> None
let (|NumberType|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent ident ->
match ident.LongIdent with
| [ i ] ->
// 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
/// 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 (|JsonNode|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
| [ "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
| [ "Json" ; "Nodes" ; "JsonNode" ]
| [ "Nodes" ; "JsonNode" ]
| [ "JsonNode" ] -> Some ()
| _ -> None
| _ -> None
let (|Unit|_|) (fieldType : SynType) : unit option =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText.ToLowerInvariant ()) with
| [ "microsoft" ; "fsharp" ; "core" ; "unit" ]
| [ "fsharp" ; "core" ; "unit" ]
| [ "core" ; "unit" ]
| [ "unit" ] -> Some ()
| _ -> None
| _ -> None
let (|DateOnly|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "DateOnly" ]
| [ "DateOnly" ] -> Some ()
| _ -> None
| _ -> None
let (|DateTime|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "DateTime" ]
| [ "DateTime" ] -> Some ()
| _ -> None
| _ -> None
let (|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) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "Uri" ]
| [ "Uri" ] -> Some ()
| _ -> None
| _ -> None
let (|Task|_|) (fieldType : SynType) : SynType option =
match fieldType with
| SynType.App (SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)), _, args, _, _, _, _) ->
match ident |> List.map (fun i -> i.idText) with
| [ "Task" ]
| [ "Tasks" ; "Task" ]
| [ "Threading" ; "Tasks" ; "Task" ]
| [ "System" ; "Threading" ; "Tasks" ; "Task" ] ->
match args with
| [ arg ] -> Some arg
| _ -> failwithf "Expected Task to be applied to exactly one arg, but got: %+A" args
| _ -> None
| _ -> None
let (|DirectoryInfo|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "IO" ; "DirectoryInfo" ]
| [ "IO" ; "DirectoryInfo" ]
| [ "DirectoryInfo" ] -> Some ()
| _ -> None
| _ -> None
let (|FileInfo|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "IO" ; "FileInfo" ]
| [ "IO" ; "FileInfo" ]
| [ "FileInfo" ] -> Some ()
| _ -> None
| _ -> None
let (|TimeSpan|_|) (fieldType : SynType) =
match fieldType with
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
match ident |> List.map (fun i -> i.idText) with
| [ "System" ; "TimeSpan" ]
| [ "TimeSpan" ] -> Some ()
| _ -> None
| _ -> None
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynType = module internal SynType =
@@ -8,3 +289,251 @@ module internal SynType =
match ty with match ty with
| SynType.Paren (ty, _) -> stripOptionalParen ty | SynType.Paren (ty, _) -> stripOptionalParen ty
| ty -> ty | ty -> ty
let inline paren (ty : SynType) : SynType = SynType.Paren (ty, range0)
let inline createLongIdent (ident : LongIdent) : SynType =
SynType.LongIdent (SynLongIdent.create ident)
let inline createLongIdent' (ident : string list) : SynType =
SynType.LongIdent (SynLongIdent.createS' ident)
let inline named (name : string) = createLongIdent' [ name ]
let inline app' (name : SynType) (args : SynType list) : SynType =
if args.IsEmpty then
failwith "Type cannot be applied to no arguments"
SynType.App (name, Some range0, args, List.replicate (args.Length - 1) range0, Some range0, false, range0)
let inline app (name : string) (args : SynType list) : SynType = app' (named name) args
/// Returns None if the input list was empty.
let inline tupleNoParen (ty : SynType list) : SynType option =
match List.rev ty with
| [] -> None
| [ t ] -> Some t
| t :: rest ->
([ SynTupleTypeSegment.Type t ], rest)
||> List.fold (fun ty nextArg -> SynTupleTypeSegment.Type nextArg :: SynTupleTypeSegment.Star range0 :: ty)
|> fun segs -> SynType.Tuple (false, segs, range0)
|> Some
let inline appPostfix (name : string) (arg : SynType) : SynType =
SynType.App (named name, None, [ arg ], [], None, true, range0)
let inline appPostfix' (name : string list) (arg : SynType) : SynType =
SynType.App (createLongIdent' name, None, [ arg ], [], None, true, range0)
let inline funFromDomain (domain : SynType) (range : SynType) : SynType =
SynType.Fun (
domain,
range,
range0,
{
ArrowRange = range0
}
)
let inline signatureParamOfType
(attrs : SynAttribute list)
(ty : SynType)
(optional : bool)
(name : Ident option)
: SynType
=
SynType.SignatureParameter (
attrs
|> List.map (fun attr ->
{
Attributes = [ attr ]
Range = range0
}
),
optional,
name,
ty,
range0
)
let inline var (ty : SynTypar) : SynType = SynType.Var (ty, range0)
let unit : SynType = named "unit"
let obj : SynType = named "obj"
let bool : SynType = named "bool"
let int : SynType = named "int"
let array (elt : SynType) : SynType = SynType.Array (1, elt, range0)
let list (elt : SynType) : SynType =
SynType.App (named "list", None, [ elt ], [], None, true, range0)
let option (elt : SynType) : SynType =
SynType.App (named "option", None, [ elt ], [], None, true, range0)
let anon : SynType = SynType.Anon range0
let task (elt : SynType) : SynType =
SynType.App (
createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "Task" ],
None,
[ elt ],
[],
None,
true,
range0
)
let string : SynType = named "string"
/// Given ['a1, 'a2] and 'ret, returns 'a1 -> 'a2 -> 'ret.
let toFun (inputs : SynType list) (ret : SynType) : SynType =
(ret, List.rev inputs) ||> List.fold (fun ty input -> funFromDomain input ty)
let primitiveToHumanReadableString (name : LongIdent) : string =
match name |> List.map _.idText with
| [ "System" ; "Single" ] -> "single"
| [ "System" ; "Double" ] -> "double"
| [ "System" ; "Byte" ] -> "byte"
| [ "System" ; "SByte" ] -> "signed byte"
| [ "System" ; "Int16" ] -> "int16"
| [ "System" ; "Int32" ] -> "int32"
| [ "System" ; "Int64" ] -> "int64"
| [ "System" ; "UInt16" ] -> "uint16"
| [ "System" ; "UInt32" ] -> "uint32"
| [ "System" ; "UInt64" ] -> "uint64"
| [ "System" ; "Char" ] -> "char"
| [ "System" ; "Decimal" ] -> "decimal"
| [ "System" ; "String" ] -> "string"
| [ "System" ; "Boolean" ] -> "bool"
| ty ->
ty
|> String.concat "."
|> failwithf "could not create human-readable string for primitive type %s"
let rec toHumanReadableString (ty : SynType) : string =
match ty with
| PrimitiveType t1 -> primitiveToHumanReadableString t1
| OptionType t1 -> toHumanReadableString t1 + " option"
| NullableType t1 -> toHumanReadableString t1 + " Nullable"
| ChoiceType ts ->
ts
|> List.map toHumanReadableString
|> String.concat ", "
|> sprintf "Choice<%s>"
| MapType (k, v)
| DictionaryType (k, v)
| IDictionaryType (k, v)
| IReadOnlyDictionaryType (k, v) -> sprintf "map<%s, %s>" (toHumanReadableString k) (toHumanReadableString v)
| ListType t1 -> toHumanReadableString t1 + " list"
| ArrayType t1 -> toHumanReadableString t1 + " array"
| Task t1 -> toHumanReadableString t1 + " Task"
| UnitType -> "unit"
| FileInfo -> "FileInfo"
| DirectoryInfo -> "DirectoryInfo"
| Uri -> "URI"
| Stream -> "Stream"
| Guid -> "GUID"
| BigInt -> "bigint"
| DateTimeOffset -> "DateTimeOffset"
| DateOnly -> "DateOnly"
| TimeSpan -> "TimeSpan"
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> ident |> List.map _.idText |> String.concat "."
| ty -> failwithf "could not compute human-readable string for type: %O" ty
/// Guess whether the types are equal. We err on the side of saying "no, they're different".
let rec provablyEqual (ty1 : SynType) (ty2 : SynType) : bool =
if Object.ReferenceEquals (ty1, ty2) then
true
else
match ty1 with
| PrimitiveType t1 ->
match ty2 with
| PrimitiveType t2 -> (t1 |> List.map _.idText) = (t2 |> List.map _.idText)
| _ -> false
| OptionType t1 ->
match ty2 with
| OptionType t2 -> provablyEqual t1 t2
| _ -> false
| NullableType t1 ->
match ty2 with
| NullableType t2 -> provablyEqual t1 t2
| _ -> false
| ChoiceType t1 ->
match ty2 with
| ChoiceType t2 ->
t1.Length = t2.Length
&& List.forall (fun (a, b) -> provablyEqual a b) (List.zip t1 t2)
| _ -> false
| DictionaryType (k1, v1) ->
match ty2 with
| DictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
| _ -> false
| IDictionaryType (k1, v1) ->
match ty2 with
| IDictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
| _ -> false
| IReadOnlyDictionaryType (k1, v1) ->
match ty2 with
| IReadOnlyDictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
| _ -> false
| MapType (k1, v1) ->
match ty2 with
| MapType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
| _ -> false
| ListType t1 ->
match ty2 with
| ListType t2 -> provablyEqual t1 t2
| _ -> false
| ArrayType t1 ->
match ty2 with
| ArrayType t2 -> provablyEqual t1 t2
| _ -> false
| Task t1 ->
match ty2 with
| Task t2 -> provablyEqual t1 t2
| _ -> false
| UnitType ->
match ty2 with
| UnitType -> true
| _ -> false
| FileInfo ->
match ty2 with
| FileInfo -> true
| _ -> false
| DirectoryInfo ->
match ty2 with
| DirectoryInfo -> true
| _ -> false
| Uri ->
match ty2 with
| Uri -> true
| _ -> false
| Stream ->
match ty2 with
| Stream -> true
| _ -> false
| Guid ->
match ty2 with
| Guid -> true
| _ -> false
| BigInt ->
match ty2 with
| BigInt -> true
| _ -> false
| DateTimeOffset ->
match ty2 with
| DateTimeOffset -> true
| _ -> false
| DateOnly ->
match ty2 with
| DateOnly -> true
| _ -> false
| _ ->
match ty1, ty2 with
| SynType.LongIdent (SynLongIdent (ident1, _, _)), SynType.LongIdent (SynLongIdent (ident2, _, _)) ->
let ident1 = ident1 |> List.map _.idText
let ident2 = ident2 |> List.map _.idText
ident1 = ident2
| _, _ -> false

View File

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

View File

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

View File

@@ -1,41 +1,55 @@
namespace WoofWare.Myriad.Plugins namespace WoofWare.Myriad.Plugins
open Fantomas.FCS.Syntax open Fantomas.FCS.Syntax
open Fantomas.FCS.Text.Range
open Fantomas.FCS.Xml
open Fantomas.FCS.SyntaxTrivia
type internal UnionCase<'Ident> = /// Represents everything you need to know about a union case.
/// This is generic on whether each field of this case must be named.
type UnionCase<'ident> =
{ {
Fields : SynFieldData<'Ident> list /// The name of the case: e.g. `| Foo of blah` has this being `Foo`.
Attrs : SynAttribute list Name : Ident
Ident : Ident /// Any docstring associated with this case.
XmlDoc : PreXmlDoc option
/// Any accessibility modifier: e.g. `type Foo = private | Blah`.
Access : SynAccess option
/// Attributes on the case: for example, `| [<Attr>] Foo of blah`.
Attributes : SynAttribute list
/// The data contained within the case: for example, `[blah]` in `| Foo of blah`.
Fields : SynFieldData<'ident> list
} }
[<RequireQualifiedAccess>]
module internal UnionCase =
let mapIdentFields<'a, 'b> (f : 'a -> 'b) (unionCase : UnionCase<'a>) : UnionCase<'b> =
{
Fields = unionCase.Fields |> List.map (SynField.mapIdent f)
Attrs = unionCase.Attrs
Ident = unionCase.Ident
}
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module internal SynUnionCase = module internal SynUnionCase =
let extract (SynUnionCase (attrs, id, caseType, _, _, _, _)) : UnionCase<Ident option> = let create (case : UnionCase<Ident>) : SynUnionCase =
match caseType with let fields =
| SynUnionCaseKind.FullType _ -> failwith "WoofWare.Myriad does not support FullType union cases." case.Fields
| SynUnionCaseKind.Fields fields -> |> List.map (fun field ->
SynField.SynField (
SynAttributes.ofAttrs field.Attrs,
false,
Some field.Ident,
field.Type,
false,
PreXmlDoc.Empty,
None,
range0,
{
LeadingKeyword = None
}
)
)
let fields = fields |> List.map SynField.extract SynUnionCase.SynUnionCase (
SynAttributes.ofAttrs case.Attributes,
let id = SynIdent.createI case.Name,
match id with SynUnionCaseKind.Fields fields,
| SynIdent.SynIdent (ident, _) -> ident case.XmlDoc |> Option.defaultValue PreXmlDoc.Empty,
case.Access,
// As far as I can tell, there's no way to get any attributes here? :shrug: range0,
let attrs = attrs |> List.collect (fun l -> l.Attributes) {
BarRange = Some range0
{ }
Fields = fields )
Attrs = attrs
Ident = id
}

View File

@@ -0,0 +1,18 @@
namespace WoofWare.Myriad.Plugins
// Extracted from https://github.com/G-Research/TypeEquality
// which is Apache-2.0 licenced. See `TeqLicence.txt`.
// We inline this code because Myriad doesn't seem to reliably load package references in the generator.
// I have reformatted a little, and stripped out all the code I don't use.
type internal Teq<'a, 'b> = private | Teq of ('a -> 'b) * ('b -> 'a)
[<RequireQualifiedAccess>]
module internal Teq =
let refl<'a> : Teq<'a, 'a> = Teq (id, id)
let cast (Teq (f, _)) a = f a
[<RequireQualifiedAccess>]
module Cong =
let believeMe<'a, 'b, 'a2, 'b2> (_ : Teq<'a, 'b>) : Teq<'a2, 'b2> = unbox <| (refl : Teq<'a2, 'a2>)

View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
@@ -18,30 +18,47 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Myriad.Core" Version="0.8.3" PrivateAssets="all"/> <PackageReference Include="Myriad.Core" Version="0.8.3" />
<!-- the lowest version allowed by Myriad.Core --> <!-- the lowest version allowed by Myriad.Core -->
<PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/> <PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="List.fs"/> <Compile Include="List.fs"/>
<Compile Include="Ident.fs" /> <Compile Include="Teq.fs" />
<Compile Include="AstHelper.fs"/> <Compile Include="Primitives.fs" />
<Compile Include="SynExpr\SynAttributes.fs" />
<Compile Include="SynExpr\PreXmlDoc.fs" />
<Compile Include="SynExpr\Ident.fs" />
<Compile Include="SynExpr\SynIdent.fs" />
<Compile Include="SynExpr\SynLongIdent.fs" />
<Compile Include="SynExpr\SynExprLetOrUseTrivia.fs" /> <Compile Include="SynExpr\SynExprLetOrUseTrivia.fs" />
<Compile Include="SynExpr\SynBinding.fs" />
<Compile Include="SynExpr\SynExpr.fs" />
<Compile Include="SynExpr\SynType.fs" />
<Compile Include="SynExpr\SynAttribute.fs" />
<Compile Include="SynExpr\SynArgPats.fs" /> <Compile Include="SynExpr\SynArgPats.fs" />
<Compile Include="SynExpr\SynPat.fs" />
<Compile Include="SynExpr\SynBinding.fs" />
<Compile Include="SynExpr\SynType.fs" />
<Compile Include="SynExpr\SynMatchClause.fs" />
<Compile Include="SynExpr\CompExpr.fs" />
<Compile Include="SynExpr\SynExpr.fs" />
<Compile Include="SynExpr\SynField.fs" /> <Compile Include="SynExpr\SynField.fs" />
<Compile Include="SynExpr\SynUnionCase.fs" /> <Compile Include="SynExpr\SynUnionCase.fs" />
<Compile Include="SynExpr\SynPat.fs" /> <Compile Include="SynExpr\SynTypeDefnRepr.fs" />
<Compile Include="SynExpr\SynTypeDefn.fs" />
<Compile Include="SynExpr\SynComponentInfo.fs" />
<Compile Include="SynExpr\SynMemberDefn.fs" />
<Compile Include="SynExpr\SynAttribute.fs" />
<Compile Include="SynExpr\SynModuleDecl.fs" />
<Compile Include="SynExpr\SynModuleOrNamespace.fs" />
<Compile Include="Measure.fs" />
<Compile Include="AstHelper.fs" />
<Compile Include="RemoveOptionsGenerator.fs"/> <Compile Include="RemoveOptionsGenerator.fs"/>
<Compile Include="InterfaceMockGenerator.fs"/> <Compile Include="InterfaceMockGenerator.fs"/>
<Compile Include="JsonSerializeGenerator.fs"/> <Compile Include="JsonSerializeGenerator.fs"/>
<Compile Include="JsonParseGenerator.fs"/> <Compile Include="JsonParseGenerator.fs"/>
<Compile Include="HttpClientGenerator.fs"/> <Compile Include="HttpClientGenerator.fs"/>
<Compile Include="CataGenerator.fs" /> <Compile Include="CataGenerator.fs" />
<Compile Include="ArgParserGenerator.fs" />
<None Include="TeqLicence.txt" />
<EmbeddedResource Include="version.json"/> <EmbeddedResource Include="version.json"/>
<EmbeddedResource Include="SurfaceBaseline.txt"/> <EmbeddedResource Include="SurfaceBaseline.txt"/>
<None Include="..\README.md"> <None Include="..\README.md">

View File

@@ -1,12 +1,14 @@
{ {
"version": "2.1", "version": "3.0",
"publicReleaseRefSpec": [ "publicReleaseRefSpec": [
"^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",
":/README.md",
":/Directory.Build.props"
] ]
} }

View File

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

12
flake.lock generated
View File

@@ -5,11 +5,11 @@
"systems": "systems" "systems": "systems"
}, },
"locked": { "locked": {
"lastModified": 1701680307, "lastModified": 1710146030,
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=", "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
"owner": "numtide", "owner": "numtide",
"repo": "flake-utils", "repo": "flake-utils",
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725", "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -20,11 +20,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1706367331, "lastModified": 1725534445,
"narHash": "sha256-AqgkGHRrI6h/8FWuVbnkfFmXr4Bqsr4fV23aISqj/xg=", "narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "160b762eda6d139ac10ae081f8f78d640dd523eb", "rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39",
"type": "github" "type": "github"
}, },
"original": { "original": {

View File

@@ -17,7 +17,7 @@
dotnet-sdk = pkgs.dotnet-sdk_8; dotnet-sdk = pkgs.dotnet-sdk_8;
dotnet-runtime = pkgs.dotnetCorePackages.runtime_8_0; dotnet-runtime = pkgs.dotnetCorePackages.runtime_8_0;
version = "0.1"; version = "0.1";
dotnetTool = dllOverride: toolName: toolVersion: sha256: dotnetTool = dllOverride: toolName: toolVersion: hash:
pkgs.stdenvNoCC.mkDerivation rec { pkgs.stdenvNoCC.mkDerivation rec {
name = toolName; name = toolName;
version = toolVersion; version = toolVersion;
@@ -25,7 +25,7 @@
src = pkgs.fetchNuGet { src = pkgs.fetchNuGet {
pname = name; pname = name;
version = version; version = version;
sha256 = sha256; hash = hash;
installPhase = ''mkdir -p $out/bin && cp -r tools/net6.0/any/* $out/bin''; installPhase = ''mkdir -p $out/bin && cp -r tools/net6.0/any/* $out/bin'';
}; };
installPhase = let installPhase = let
@@ -43,46 +43,21 @@
}; };
in { in {
packages = { packages = {
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).sha256; fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).hash;
fsharp-analyzers = dotnetTool "FSharp.Analyzers.Cli" "fsharp-analyzers" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fsharp-analyzers.version (builtins.head (builtins.filter (elem: elem.pname == "fsharp-analyzers") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).sha256; fsharp-analyzers = dotnetTool "FSharp.Analyzers.Cli" "fsharp-analyzers" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fsharp-analyzers.version (builtins.head (builtins.filter (elem: elem.pname == "fsharp-analyzers") ((import ./nix/deps.nix) {fetchNuGet = x: x;}))).hash;
fetchDeps = let
flags = [];
runtimeIds = ["win-x64"] ++ map (system: pkgs.dotnetCorePackages.systemToDotnetRid system) dotnet-sdk.meta.platforms;
in
pkgs.writeShellScriptBin "fetch-${pname}-deps" (builtins.readFile (pkgs.substituteAll {
src = ./nix/fetchDeps.sh;
pname = pname;
binPath = pkgs.lib.makeBinPath [pkgs.coreutils dotnet-sdk (pkgs.nuget-to-nix.override {inherit dotnet-sdk;})];
projectFiles = toString ["./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj" "./ConsumePlugin/ConsumePlugin.fsproj" "./WoofWare.Myriad.Plugins.Attributes/WoofWare.Myriad.Plugins.Attributes.fsproj"];
testProjectFiles = ["./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj" "./WoofWare.Myriad.Plugins.Attributes/Test/Woofware.Myriad.Plugins.Attributes.Test.fsproj"];
rids = pkgs.lib.concatStringsSep "\" \"" runtimeIds;
packages = dotnet-sdk.packages;
storeSrc = pkgs.srcOnly {
src = ./.;
pname = pname;
version = version;
};
}));
default = pkgs.buildDotnetModule { default = pkgs.buildDotnetModule {
pname = pname; inherit pname version dotnet-sdk dotnet-runtime;
name = "WoofWare.Myriad.Plugins"; name = "WoofWare.Myriad.Plugins";
version = version;
src = ./.; src = ./.;
projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj"; projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj";
nugetDeps = ./nix/deps.nix; testProjectFile = "./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj";
disabledTests = ["WoofWare.Myriad.Plugins.Test.TestSurface.CheckVersionAgainstRemote"];
nugetDeps = ./nix/deps.nix; # `nix build .#default.passthru.fetch-deps && ./result` and put the result here
doCheck = true; doCheck = true;
dotnet-sdk = dotnet-sdk;
dotnet-runtime = dotnet-runtime;
}; };
}; };
devShell = pkgs.mkShell { devShell = pkgs.mkShell {
buildInputs = with pkgs; [ buildInputs = [dotnet-sdk];
(with dotnetCorePackages;
combinePackages [
dotnet-sdk_8
dotnetPackages.Nuget
])
];
packages = [ packages = [
pkgs.alejandra pkgs.alejandra
pkgs.nodePackages.markdown-link-check pkgs.nodePackages.markdown-link-check

View File

@@ -1,6 +1,6 @@
{ {
"sdk": { "sdk": {
"version": "8.0.100", "version": "8.0.100",
"rollForward": "latestFeature" "rollForward": "latestMajor"
} }
} }

View File

@@ -1,444 +1,329 @@
# This file was automatically generated by passthru.fetch-deps. # This file was automatically generated by passthru.fetch-deps.
# Please don't edit it manually, your changes might get overwritten! # Please dont edit it manually, your changes might get overwritten!
{fetchNuGet}: [ {fetchNuGet}: [
(fetchNuGet { (fetchNuGet {
pname = "fsharp-analyzers"; pname = "ApiSurface";
version = "0.26.0"; version = "4.1.5";
sha256 = "sha256-60Bl36LOb/zVNdH2SBSuQ5O41lP9dKTNZbs5vvYs+3U="; hash = "sha256-Kbt18XLk1gvZfzGca885HaXZB119APay85KzI546PYM=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "fantomas"; pname = "fantomas";
version = "6.3.4"; version = "6.3.12";
sha256 = "sha256-1aWqZynBkQoznenGoP0sbf1PcUXAbcHiWyECuv89xa0="; hash = "sha256-LFZn2cO72FlsmLI0vTLz52Bn4XBeGILTOr8rz/EuXeg=";
})
(fetchNuGet {
pname = "ApiSurface";
version = "4.0.40";
sha256 = "1c9z0b6minlripwrjmv4yd5w8zj4lcpak4x41izh7ygx8kgmbvx0";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Fantomas.Core"; pname = "Fantomas.Core";
version = "6.1.1"; version = "6.1.1";
sha256 = "1h2wsiy4fzwsg9vrlpk6w7zsvx6bc4wg4x25zqc48akg04fwpi0m"; hash = "sha256-FcTLHQFvKkQY/kV08jhhy/St/+FmXpp3epp/R3zUXMA=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Fantomas.FCS"; pname = "Fantomas.FCS";
version = "6.1.1"; version = "6.1.1";
sha256 = "0733dm5zjdp8w5wwalqlv1q52pghfr04863i9wy807f4qfd7rrin"; hash = "sha256-NuZ8msPEHYA8T3EYREB28F1RcNgUU8V54eg2+UttYxw=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "FsCheck"; pname = "FsCheck";
version = "2.16.6"; version = "2.16.6";
sha256 = "176rwky6b5rk8dzldiz4068p7m9c5y9ygzbhadrs14jkl94pc56n"; hash = "sha256-1hR2SaJTkqBzU3D955MvLNVzkQHkx0Z/QzOXZfzk2Zw=";
})
(fetchNuGet {
pname = "fsharp-analyzers";
version = "0.27.0";
hash = "sha256-QhLi2veTY1wZlQKJLTyVPgx/ImkaZugQNjSN5VJCNEA=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "FSharp.Core"; pname = "FSharp.Core";
version = "4.3.4"; version = "4.3.4";
sha256 = "1sg6i4q5nwyzh769g76f6c16876nvdpn83adqjr2y9x6xsiv5p5j"; hash = "sha256-styyo+6mJy+yxE0NZG/b1hxkAjPOnJfMgd9zWzCJ5uk=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "FSharp.Core"; pname = "FSharp.Core";
version = "6.0.1"; version = "6.0.1";
sha256 = "0qks2aadkhsffg9a6xq954ll9xacnph852avd7ljh9n2g6vj06qj"; hash = "sha256-Ehsgt3nCJijpaVuJguC1TPVEKSkJd6PSc07D2ZQSemI=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "FSharp.Core"; pname = "FSharp.Core";
version = "8.0.101"; version = "8.0.400";
sha256 = "0prgcnki6s0rlrfbarrcv50w1bbhaalsyhhw5gsnjs2is7qrjbii"; hash = "sha256-wlrcAjjvI5YtnHR7kFH8uRUA4GomJYmqr41K5LYjCGs=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "FsUnit"; pname = "FsUnit";
version = "6.0.0"; version = "6.0.0";
sha256 = "18q3p0z155znwj1l0qq3vq9nh9wl2i4mlfx4pmrnia4czr0xdkmb"; hash = "sha256-q87WQf6MqGhzvaQ7WkkUlCdoE94DY0CD5PaXEj64A6M=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.AspNetCore.App.Ref"; pname = "Microsoft.AspNetCore.App.Ref";
version = "6.0.26"; version = "6.0.33";
sha256 = "1d8nkz24vsm0iy2xm8y5ak2q1w1p99dxyz0y26acs6sfk2na0vm6"; hash = "sha256-GcPiO+iI0JsHYlqURAmzWjOnDX2jDCUY4jYaIwr8ojs=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Ref";
version = "8.0.1";
sha256 = "0yaaiqq7mi6sclyrb1v0fyncanbx0ifmnnhv9whynqj8439jsdwh";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64"; pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
version = "6.0.26"; version = "6.0.33";
sha256 = "1za8lc52m4z54d68wd64c2nhzy05g3gx171k5cdlx73fbymiys9z"; hash = "sha256-g5zbB1DnCSKuCOWtF09GEqGn1uJLdlTN6kqdnSCzRjQ=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
version = "8.0.1";
sha256 = "0dsdgqg7566qximmjfza4x9if3icy4kskq698ddj5apdia88h2mw";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64"; pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
version = "6.0.26"; version = "6.0.33";
sha256 = "1zpbmz6z8758gwywzg0bac8kx9x39sxxc9j4a4r2jl74l9ssw4vm"; hash = "sha256-ToaiqVy5qonomAVBg5PO1GgrPKL4Cc1BZTJ0z/2LquA=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
version = "8.0.1";
sha256 = "1gjz379y61ag9whi78qxx09bwkwcznkx2mzypgycibxk61g11da1";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64"; pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
version = "6.0.26"; version = "6.0.33";
sha256 = "1i8ydlwjzk7j0mzvn0rpljxfp1h50zwaqalnyvfxai1fwgigzgw5"; hash = "sha256-OY/vdqAzZ99I4lEZbOOQw12TE0AIb5pXxKTvDxO2M2Q=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
version = "8.0.1";
sha256 = "0w3mrs4zdl9mfanl1j81759xwwrzmicsjxn6yfxv5yrxbxzq695n";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64"; pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
version = "6.0.26"; version = "6.0.33";
sha256 = "02src68hd3213sd1a2ms1my7i92knfmdxclvv90il9cky2zsq8kw"; hash = "sha256-53MAV3RO1kXzy5IpdZDZIOhoUzFqWHn7+A3aWwdTONQ=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
version = "8.0.1";
sha256 = "0a9aljr4fy4haq6ndz2y723liv5hbfpss1rn45s88nmgcp27m15m";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
version = "6.0.26";
sha256 = "1gxlmfdkfzmhw9pac5jiv674nn6i1zymcp2hj81irjwhhjk01mf5";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.win-x64";
version = "8.0.1";
sha256 = "01kzndyqmsvcq49i2jrv7ymfp0l71yxfylv1cy3nhkdbprqz8ipx";
})
(fetchNuGet {
pname = "Microsoft.Build.Tasks.Git";
version = "8.0.0";
sha256 = "0055f69q3hbagqp8gl3nk0vfn4qyqyxsxyy7pd0g7wm3z28byzmx";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.CodeCoverage"; pname = "Microsoft.CodeCoverage";
version = "17.10.0"; version = "17.11.1";
sha256 = "0s0v7jmrq85n356xv7zixvwa4z94fszjcr5vll8x4im1a2lp00f9"; hash = "sha256-1dLlK3NGh88PuFYZiYpT+izA96etxhU3BSgixDgdtGA=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NET.Test.Sdk"; pname = "Microsoft.NET.Test.Sdk";
version = "17.10.0"; version = "17.11.1";
sha256 = "13g8fwl09li8fc71nk13dgkb7gahd4qhamyg2xby7am63nlchhdf"; hash = "sha256-0JUEucQ2lzaPgkrjm/NFLBTbqU1dfhvhN3Tl3moE6mI=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-arm64"; pname = "Microsoft.NETCore.App.Host.linux-arm64";
version = "6.0.26"; version = "6.0.33";
sha256 = "19y6c6v20bgf7x7rrh4rx9y7s5fy8vp5m4j9b6gi1wp4rpb5mza4"; hash = "sha256-rwWOpf2Pdg84c8bKIUcMYuDTI0kXUELL/nl9psSmX+E=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-arm64";
version = "8.0.1";
sha256 = "0dhpdlcdz7adcfh9w01fc867051m35fqaxnvj3fqvqhgcm2n3143";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-x64"; pname = "Microsoft.NETCore.App.Host.linux-x64";
version = "6.0.26"; version = "6.0.33";
sha256 = "0p7hhidaa3mnyiwnsijwy8578v843x8hh99255s69qwwyld6falv"; hash = "sha256-5iYNZATXOePDsLA9lI80o1Gjxw4E+B4bJbwdYJJHcZY=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-x64";
version = "8.0.1";
sha256 = "1aw6mc7zcmzs1grxz2wa9cw9kfj8pz7zpj417xnp1a9n4ix1bxgr";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-arm64"; pname = "Microsoft.NETCore.App.Host.osx-arm64";
version = "6.0.26"; version = "6.0.33";
sha256 = "1mq11xsv9g1vsasp6k80y7xlvwi9hrpk5dgm773fvy8538s01gfv"; hash = "sha256-k3LenomOlacyzq4FlBY/TwV7+ClbK4U0A/O9r0pZHT4=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-arm64";
version = "8.0.1";
sha256 = "1dzg3prng9zfdzz7gcgywjdbwzhwm85j89z0jahynxx4q2dra4b9";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-x64"; pname = "Microsoft.NETCore.App.Host.osx-x64";
version = "6.0.26"; version = "6.0.33";
sha256 = "1chac9b4424ihrrnlzvc7qz6j4ymfjyv4kzyazzzw19yhymdkh2s"; hash = "sha256-tu72AwDH1+oAIXjOJcNbeyKm1s4pncYp0avbMSBrcJQ=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-x64";
version = "8.0.1";
sha256 = "010f8wn15s2kv7yyzgys3pv9i1mxw20hpv1ig2zhybjxs8lpj8jj";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.win-x64";
version = "6.0.26";
sha256 = "0i7g9fsqjnbh9rc6807m57r2idg5pkcw6xjfwhnxkcpgqm96258v";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.win-x64";
version = "8.0.1";
sha256 = "1ssj1cyam3nfidm8q82kvh4i3fzm2lzb3bxw6ck09hwhvwh909z4";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Ref"; pname = "Microsoft.NETCore.App.Ref";
version = "6.0.26"; version = "6.0.33";
sha256 = "12gb52dhg5h9hgnyqh1zgj2w46paxv2pfh33pphl9ajhrdr7hlsb"; hash = "sha256-BiGUcXo1FQTlZdR6ndhUQ8lrYG3KaGXNXRVF+Fc3L28=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Ref";
version = "8.0.1";
sha256 = "02r4jg4ha0qksix9v6s3cpmvavmz54gkawkxy9bvknw5ynxhhl1l";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-arm64"; pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
version = "6.0.26"; version = "6.0.33";
sha256 = "164hfrwqz5dxcbb441lridk4mzcqmarb0b7ckgvqhsvpawyjw88v"; hash = "sha256-obRKiJEVpZ5E3TE7q2oHaYwFYhI23rMiHwp+8ORkwXY=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
version = "8.0.1";
sha256 = "0353whnjgz3sqhzsfrviad3a3db4pk7hl7m4wwppv5mqdg9i9ri5";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-x64"; pname = "Microsoft.NETCore.App.Runtime.linux-x64";
version = "6.0.26"; version = "6.0.33";
sha256 = "0islayddpnflviqpbq4djc4f3v9nhsa2y76k5x6il3csq5vdw2hq"; hash = "sha256-2xdhvnKsFc8utDWN09zeXzZ5op+WUqkoWLuzdtQAkrA=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
version = "8.0.1";
sha256 = "1g5b30f4l8a1zjjr3b8pk9mcqxkxqwa86362f84646xaj4iw3a4d";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-arm64"; pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
version = "6.0.26"; version = "6.0.33";
sha256 = "1acn5zw1pxzmcg3c0pbf9hal36fbdh9mvbsiwra7simrk7hzqpdc"; hash = "sha256-9KHubWicibZOcixiByzuBKPnJM2u5DSQC9jR3MAR1bI=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
version = "8.0.1";
sha256 = "0cdrpdaq5sl3602anfx1p0z0ncx2sjjvl6mgsd6y38g47n7f95jc";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-x64"; pname = "Microsoft.NETCore.App.Runtime.osx-x64";
version = "6.0.26"; version = "6.0.33";
sha256 = "00f9l9dkdz0zv5csaw8fkm6s8ckrj5n9k3ygz12daa22l3bcn6ii"; hash = "sha256-smh6SiTtCAuFglqWrXiGGsoIDP9dhGuIKdYjmw+xCyY=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
version = "8.0.1";
sha256 = "1fk1flqp6ji0l4c2gvh83ykndpx7a2nkkgrgkgql3c75j1k2v1s9";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.win-x64";
version = "6.0.26";
sha256 = "0i2p356phfc5y6qnr3vyrzjfi1mrbwfb6g85k4q37bbyxjfp7zl9";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.win-x64";
version = "8.0.1";
sha256 = "198576cdkl72xs29zznff9ls763p8pfr0zji7b74dqxd5ga0s3bd";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.Platforms"; pname = "Microsoft.NETCore.Platforms";
version = "1.1.0"; version = "1.1.0";
sha256 = "08vh1r12g6ykjygq5d3vq09zylgb84l63k49jc4v8faw9g93iqqm"; hash = "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.Platforms"; pname = "Microsoft.NETCore.Platforms";
version = "1.1.1"; version = "1.1.1";
sha256 = "164wycgng4mi9zqi2pnsf1pq6gccbqvw6ib916mqizgjmd8f44pj"; hash = "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.Platforms"; pname = "Microsoft.NETCore.Platforms";
version = "2.0.0"; version = "2.0.0";
sha256 = "1fk2fk2639i7nzy58m9dvpdnzql4vb8yl8vr19r2fp8lmj9w2jr0"; hash = "sha256-IEvBk6wUXSdyCnkj6tHahOJv290tVVT8tyemYcR0Yro=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.NETCore.Targets"; pname = "Microsoft.NETCore.Targets";
version = "1.1.3"; version = "1.1.3";
sha256 = "05smkcyxir59rgrmp7d6327vvrlacdgldfxhmyr1azclvga1zfsq"; hash = "sha256-WLsf1NuUfRWyr7C7Rl9jiua9jximnVvzy6nk2D2bVRc=";
})
(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.11.1";
sha256 = "07j69cw8r39533w4p39mnj00kahazz38760in3jfc45kmlcdb26x"; hash = "sha256-5vX+vCzFY3S7xfMVIv8OlMMFtdedW9UIJzc0WEc+vm4=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Microsoft.TestPlatform.TestHost"; pname = "Microsoft.TestPlatform.TestHost";
version = "17.10.0"; version = "17.11.1";
sha256 = "1bl471s7fx9jycr0cc8rylwf34mrvlg9qn1an6l86nisavfcyb7v"; hash = "sha256-wSkY0H1fQAq0H3LcKT4u7Y5RzhAAPa6yueVN84g8HxU=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Myriad.Core"; pname = "Myriad.Core";
version = "0.8.3"; version = "0.8.3";
sha256 = "0s5pdckjw4x0qrbd4i3cz9iili5cppg5qnjbr7zjbbhhmxzb24xw"; hash = "sha256-vBOxfq8QriX/yUtaXN69rEQaY/psRNJWxqATLidrt2g=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Myriad.Sdk"; pname = "Myriad.Sdk";
version = "0.8.3"; version = "0.8.3";
sha256 = "0qv78c5s5m04xb8h17nnn2ig26zcyya91k2dpj745cm1cbnzvvgc"; hash = "sha256-7O397WKhskKOvE3MkJT37BvxorDWngDR6gTUogtDZ2M=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Nerdbank.GitVersioning"; pname = "Nerdbank.GitVersioning";
version = "3.6.133"; version = "3.6.143";
sha256 = "1cdw8krvsnx0n34f7fm5hiiy7bs6h3asvncqcikc0g46l50w2j80"; hash = "sha256-OhOtMzP+2obDIR+npR7SsoXo0KrmcsL+VCE8Z3t5gzQ=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NETStandard.Library"; pname = "NETStandard.Library";
version = "2.0.3"; version = "2.0.3";
sha256 = "1fn9fxppfcg4jgypp2pmrpr6awl3qz1xmnri0cygpkwvyx27df1y"; hash = "sha256-Prh2RPebz/s8AzHb2sPHg3Jl8s31inv9k+Qxd293ybo=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Newtonsoft.Json"; pname = "Newtonsoft.Json";
version = "13.0.1"; version = "13.0.1";
sha256 = "0fijg0w6iwap8gvzyjnndds0q4b8anwxxvik7y8vgq97dram4srb"; hash = "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "Newtonsoft.Json"; pname = "Newtonsoft.Json";
version = "13.0.3"; version = "13.0.3";
sha256 = "0xrwysmrn4midrjal8g2hr1bbg38iyisl0svamb11arqws4w2bw7"; hash = "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Common"; pname = "NuGet.Common";
version = "6.10.0"; version = "6.11.0";
sha256 = "0nizrnilmlcqbm945293h8q3wfqfchb4xi8g50x4kjn0rbpd1kbh"; hash = "sha256-eb7G07RyZv4AQT6ItRqdBuUf9e9BXcQygsy5RNEXfNE=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Configuration"; pname = "NuGet.Configuration";
version = "6.10.0"; version = "6.11.0";
sha256 = "1aqaknaawnqx4mnvx9qw73wvj48jjzv0d78dzwl7m9zjlrl9myhz"; hash = "sha256-2SNZkX64SB15glzQx3k+vI7btr8Yqg4CayaaaK1B0AQ=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Frameworks"; pname = "NuGet.Frameworks";
version = "6.10.0"; version = "6.11.0";
sha256 = "0hrd8y31zx9a0wps49czw0qgbrakb49zn3abfgylc9xrq990zkqk"; hash = "sha256-8DC7V2IlCjiMDQ9yWbl7QQHia6OpBrbWh5rL0qa0Opw=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Packaging"; pname = "NuGet.Packaging";
version = "6.10.0"; version = "6.11.0";
sha256 = "18s53cvrf51lihmaqqdf48p2qi6ky1l48jv0hvbp76cxwdg7rba4"; hash = "sha256-LVLvxcB6SMdayxAsrc5bCuLLt25fqPr6KfYcYoWWIQk=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Protocol"; pname = "NuGet.Protocol";
version = "6.10.0"; version = "6.11.0";
sha256 = "0hmv4q0ks9i34mfgpb13l01la9v3jjllfh1qd3aqv105xrqrdxac"; hash = "sha256-3vdB/8IiJ2LMHhFXLWOzf0H59Ow/zcoq6W4uCHbihCQ=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NuGet.Versioning"; pname = "NuGet.Versioning";
version = "6.10.0"; version = "6.11.0";
sha256 = "1x19njx4x0sw9fz8y5fibi15xfsrw5avir0cx0599yd7p3ykik5g"; hash = "sha256-03edgWvbqUtbzpBBTIxTwsSRoj1T2muGVL+vTuIHXag=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NUnit"; pname = "NUnit";
version = "4.1.0"; version = "4.2.2";
sha256 = "0fj6xwgqaxq3mrai86bklclfmjkzf038mrslwfqf4ignaz9f7g5j"; hash = "sha256-+0OS67ITalmG9arYCgQF/+YbmPRnB3pIIykew0kvoCc=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "NUnit3TestAdapter"; pname = "NUnit3TestAdapter";
version = "4.5.0"; version = "4.6.0";
sha256 = "1srx1629s0k1kmf02nmz251q07vj6pv58mdafcr5dr0bbn1fh78i"; hash = "sha256-9Yav2fYhC4w0OgsyUwU4/5rDy4FVDTpKnWHuwl/uKJQ=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "RestEase"; pname = "RestEase";
version = "1.6.4"; version = "1.6.4";
sha256 = "1mvi3nbrr450g3fgd1y4wg3bwl9k1agyjfd9wdkqk12714bsln8l"; hash = "sha256-FFmqFwlHhIln46k56Z8KM1G+xuPEh/bceKCQnJcdcdc=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "runtime.any.System.Runtime"; pname = "runtime.any.System.Runtime";
version = "4.3.0"; version = "4.3.0";
sha256 = "1cqh1sv3h5j7ixyb7axxbdkqx6cxy00p4np4j91kpm492rf4s25b"; hash = "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "runtime.native.System"; pname = "runtime.native.System";
version = "4.3.0"; version = "4.3.0";
sha256 = "15hgf6zaq9b8br2wi1i3x0zvmk410nlmsmva9p0bbg73v6hml5k4"; hash = "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "runtime.unix.System.Private.Uri"; pname = "runtime.unix.System.Private.Uri";
version = "4.3.0"; version = "4.3.0";
sha256 = "1jx02q6kiwlvfksq1q9qr17fj78y5v6mwsszav4qcz9z25d5g6vk"; hash = "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Diagnostics.DiagnosticSource"; pname = "System.Diagnostics.DiagnosticSource";
version = "7.0.0"; version = "7.0.0";
sha256 = "1jxhvsh5mzdf0sgb4dfmbys1b12ylyr5pcfyj1map354fiq3qsgm"; hash = "sha256-9Wk8cHSkjKtqkN6xW7KnXoQVtF/VNbKeBq79WqDesMs=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Formats.Asn1"; pname = "System.Formats.Asn1";
version = "6.0.0"; version = "6.0.0";
sha256 = "1vvr7hs4qzjqb37r0w1mxq7xql2b17la63jwvmgv65s1hj00g8r9"; hash = "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.IO.Abstractions"; pname = "System.IO.Abstractions";
version = "4.2.13"; version = "4.2.13";
sha256 = "0s784iphsmj4vhkrzq9q3w39vsn76w44zclx3hsygsw458zbyh4y"; hash = "sha256-nkC/PiqE6+c1HJ2yTwg3x+qdBh844Z8n3ERWDW8k6Gg=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.IO.FileSystem.AccessControl"; pname = "System.IO.FileSystem.AccessControl";
version = "4.5.0"; version = "4.5.0";
sha256 = "1gq4s8w7ds1sp8f9wqzf8nrzal40q5cd2w4pkf4fscrl2ih3hkkj"; hash = "sha256-ck44YBQ0M+2Im5dw0VjBgFD1s0XuY54cujrodjjSBL8=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Memory"; pname = "System.Memory";
version = "4.5.5"; version = "4.5.5";
sha256 = "08jsfwimcarfzrhlyvjjid61j02irx6xsklf32rv57x2aaikvx0h"; hash = "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Private.Uri"; pname = "System.Private.Uri";
version = "4.3.0"; version = "4.3.0";
sha256 = "04r1lkdnsznin0fj4ya1zikxiqr0h6r6a1ww2dsm60gqhdrf0mvx"; hash = "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Reflection.Metadata"; pname = "System.Reflection.Metadata";
version = "1.6.0"; version = "1.6.0";
sha256 = "1wdbavrrkajy7qbdblpbpbalbdl48q3h34cchz24gvdgyrlf15r4"; hash = "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Runtime"; pname = "System.Runtime";
version = "4.3.1"; version = "4.3.1";
sha256 = "03ch4d2acf6q037a4njxpll2kkx3dwzlg07yxr4z5m6j1kqgmm27"; hash = "sha256-R9T68AzS1PJJ7v6ARz9vo88pKL1dWqLOANg4pkQjkA0=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Runtime.CompilerServices.Unsafe"; pname = "System.Runtime.CompilerServices.Unsafe";
version = "6.0.0"; version = "6.0.0";
sha256 = "0qm741kh4rh57wky16sq4m0v05fxmkjjr87krycf5vp9f0zbahbc"; hash = "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Security.AccessControl"; pname = "System.Security.AccessControl";
version = "4.5.0"; version = "4.5.0";
sha256 = "1wvwanz33fzzbnd2jalar0p0z3x0ba53vzx1kazlskp7pwyhlnq0"; hash = "sha256-AFsKPb/nTk2/mqH/PYpaoI8PLsiKKimaXf+7Mb5VfPM=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Security.Cryptography.Pkcs"; pname = "System.Security.Cryptography.Pkcs";
version = "6.0.4"; version = "6.0.4";
sha256 = "0hh5h38pnxmlrnvs72f2hzzpz4b2caiiv6xf8y7fzdg84r3imvfr"; hash = "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Security.Cryptography.ProtectedData"; pname = "System.Security.Cryptography.ProtectedData";
version = "4.4.0"; version = "4.4.0";
sha256 = "1q8ljvqhasyynp94a1d7jknk946m20lkwy2c3wa8zw2pc517fbj6"; hash = "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Security.Principal.Windows"; pname = "System.Security.Principal.Windows";
version = "4.5.0"; version = "4.5.0";
sha256 = "0rmj89wsl5yzwh0kqjgx45vzf694v9p92r4x4q6yxldk1cv1hi86"; hash = "sha256-BkUYNguz0e4NJp1kkW7aJBn3dyH9STwB5N8XqnlCsmY=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Text.Encodings.Web"; pname = "System.Text.Encodings.Web";
version = "7.0.0"; version = "7.0.0";
sha256 = "1151hbyrcf8kyg1jz8k9awpbic98lwz9x129rg7zk1wrs6vjlpxl"; hash = "sha256-tF8qt9GZh/nPy0mEnj6nKLG4Lldpoi/D8xM5lv2CoYQ=";
}) })
(fetchNuGet { (fetchNuGet {
pname = "System.Text.Json"; pname = "System.Text.Json";
version = "7.0.3"; version = "7.0.3";
sha256 = "0zjrnc9lshagm6kdb9bdh45dmlnkpwcpyssa896sda93ngbmj8k9"; hash = "sha256-aSJZ17MjqaZNQkprfxm/09LaCoFtpdWmqU9BTROzWX4=";
}) })
] ]

View File

@@ -1,73 +0,0 @@
#!/bin/bash
# This file was adapted from
# https://github.com/NixOS/nixpkgs/blob/b981d811453ab84fb3ea593a9b33b960f1ab9147/pkgs/build-support/dotnet/build-dotnet-module/default.nix#L173
set -euo pipefail
export PATH="@binPath@"
for arg in "$@"; do
case "$arg" in
--keep-sources|-k)
keepSources=1
shift
;;
--help|-h)
echo "usage: $0 [--keep-sources] [--help] <output path>"
echo " <output path> The path to write the lockfile to. A temporary file is used if this is not set"
echo " --keep-sources Don't remove temporary directories upon exit, useful for debugging"
echo " --help Show this help message"
exit
;;
esac
done
tmp=$(mktemp -td "@pname@-tmp-XXXXXX")
export tmp
HOME=$tmp/home
exitTrap() {
test -n "${ranTrap-}" && return
ranTrap=1
if test -n "${keepSources-}"; then
echo -e "Path to the source: $tmp/src\nPath to the fake home: $tmp/home"
else
rm -rf "$tmp"
fi
# Since mktemp is used this will be empty if the script didnt succesfully complete
if ! test -s "$depsFile"; then
rm -rf "$depsFile"
fi
}
trap exitTrap EXIT INT TERM
dotnetRestore() {
local -r project="${1-}"
local -r rid="$2"
dotnet restore "${project-}" \
-p:ContinuousIntegrationBuild=true \
-p:Deterministic=true \
--packages "$tmp/nuget_pkgs" \
--runtime "$rid" \
--no-cache \
--force
}
declare -a projectFiles=( @projectFiles@ )
declare -a testProjectFiles=( @testProjectFiles@ )
export DOTNET_NOLOGO=1
export DOTNET_CLI_TELEMETRY_OPTOUT=1
depsFile=$(realpath "${1:-$(mktemp -t "@pname@-deps-XXXXXX.nix")}")
mkdir -p "$tmp/nuget_pkgs"
storeSrc="@storeSrc@"
src="$tmp/src"
cp -rT "$storeSrc" "$src"
chmod -R +w "$src"
cd "$src"
echo "Restoring project..."
rids=("@rids@")
for rid in "${rids[@]}"; do
(( ${#projectFiles[@]} == 0 )) && dotnetRestore "" "$rid"
for project in "${projectFiles[@]-}" "${testProjectFiles[@]-}"; do
dotnetRestore "$project" "$rid"
done
done
echo "Successfully restored project"
echo "Writing lockfile..."
echo -e "# This file was automatically generated by passthru.fetch-deps.\n# Please don't edit it manually, your changes might get overwritten!\n" > "$depsFile"
nuget-to-nix "$tmp/nuget_pkgs" "@packages@" >> "$depsFile"
echo "Successfully wrote lockfile to $depsFile"