Compare commits

...

65 Commits

Author SHA1 Message Date
Smaug123
f686109331 More 2025-04-18 15:08:20 +01:00
Smaug123
7b2c3d2168 Start on the union generator 2025-04-17 21:56:25 +01:00
Smaug123
3ed8d4db00 Add help text placeholder 2025-04-17 21:38:44 +01:00
Smaug123
75ce8c1f64 Fix test 2025-04-17 18:45:57 +01:00
Smaug123
01714aeba0 Fix all but the help text 2025-04-17 18:31:25 +01:00
Smaug123
2f266b052d Fix another test 2025-04-17 16:02:20 +01:00
Smaug123
d3d50cae7c Fix another test 2025-04-17 15:49:12 +01:00
Smaug123
573d410416 Fix another test 2025-04-17 12:47:48 +01:00
Smaug123
a82ece0f6c Plumb through indices 2025-04-17 12:41:55 +01:00
Smaug123
51991cab74 Fix another test 2025-04-17 11:45:34 +01:00
Smaug123
55a3876610 More 2025-04-17 11:13:37 +01:00
Smaug123
c14f89f807 Fix another test 2025-04-17 11:05:08 +01:00
Smaug123
54e3f17d9c It's sort of working 2025-04-17 00:18:53 +01:00
Smaug123
4013271254 More 2025-04-16 23:04:23 +01:00
Smaug123
aa2ef830c3 Add flags 2025-04-16 21:26:30 +01:00
Smaug123
4e62a154c0 Undo accidental revert 2025-04-15 22:51:43 +01:00
Smaug123
751e43eec4 More 2025-04-15 22:50:14 +01:00
Smaug123
fccc981045 WIP 2025-04-15 09:23:24 +01:00
Smaug123
f8a1505b99 Undo build-offline mode 2025-04-14 23:08:40 +01:00
Smaug123
eb25b9ccb8 Merge branch 'main' into du-parser 2025-04-14 23:07:34 +01:00
dependabot[bot]
325f8634a4 Bump FSharp.Core and WoofWare.Whippet.Fantomas (#361)
* Bump FsCheck and FSharp.Core

Bumps [FsCheck](https://github.com/Fscheck/fscheck) and [FSharp.Core](https://github.com/dotnet/fsharp). These dependencies needed to be updated together.

Updates `FsCheck` from 3.1.0 to 3.2.0
- [Release notes](https://github.com/Fscheck/fscheck/releases)
- [Changelog](https://github.com/fscheck/FsCheck/blob/master/FsCheck%20Release%20Notes.md)
- [Commits](https://github.com/Fscheck/fscheck/compare/3.1.0...3.2.0)

Updates `FSharp.Core` from 4.3.4 to 5.0.2
- [Release notes](https://github.com/dotnet/fsharp/releases)
- [Changelog](https://github.com/dotnet/fsharp/blob/main/release-notes.md)
- [Commits](https://github.com/dotnet/fsharp/commits)

---
updated-dependencies:
- dependency-name: FsCheck
  dependency-version: 3.2.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
- dependency-name: FSharp.Core
  dependency-version: 5.0.2
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* Bump FSharp.Core and WoofWare.Whippet.Fantomas

Bumps [FSharp.Core](https://github.com/dotnet/fsharp) and [WoofWare.Whippet.Fantomas](https://github.com/Smaug123/WoofWare.Whippet). These dependencies needed to be updated together.

Updates `FSharp.Core` from 4.3.4 to 6.0.1
- [Release notes](https://github.com/dotnet/fsharp/releases)
- [Changelog](https://github.com/dotnet/fsharp/blob/main/release-notes.md)
- [Commits](https://github.com/dotnet/fsharp/commits)

Updates `WoofWare.Whippet.Fantomas` from 0.3.2 to 0.5.1
- [Commits](https://github.com/Smaug123/WoofWare.Whippet/commits)

---
updated-dependencies:
- dependency-name: FSharp.Core
  dependency-version: 6.0.1
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: WoofWare.Whippet.Fantomas
  dependency-version: 0.5.1
  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>
2025-04-14 22:05:23 +00:00
Smaug123
34587b8dea Merge branch 'main' into du-parser 2025-04-14 23:02:19 +01:00
Patrick Stevens
3e5d663544 Better docs on PositionalArgsAttribute (#362) 2025-04-14 23:01:26 +01:00
Smaug123
963a097360 First line of implementation 2025-04-14 22:52:18 +01:00
Smaug123
67eb89cfc0 Getting there 2025-04-14 22:27:57 +01:00
Smaug123
0c5ddf9df7 WIP 2025-04-14 00:01:55 +01:00
Smaug123
8535481e0d Bump Whippet 2025-04-13 21:09:12 +01:00
Smaug123
df6079e763 WIP 2025-04-13 20:34:52 +01:00
Smaug123
4befdb93e5 WIP: define the helper types 2025-04-13 18:43:23 +01:00
Smaug123
17da7317e8 Merge branch 'main' into du-parser 2025-04-13 16:07:59 +01:00
Smaug123
fa022b75ea WIP: complete arg parser 2025-04-13 11:52:17 +01:00
patrick-conscriptus[bot]
bb88f80c85 Automated commit (#358)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-13 02:53:09 +00:00
dependabot[bot]
71f26930c6 Bump actions/create-github-app-token from 1 to 2 (#357) 2025-04-07 17:19:12 +01:00
patrick-conscriptus[bot]
680728a06e Automated commit (#356)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-04-06 01:29:18 +00:00
dependabot[bot]
cdc6f2d511 Bump fsharp-analyzers from 0.29.1 to 0.30.0 (#355)
* Bump fsharp-analyzers from 0.29.1 to 0.30.0

Bumps [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK) from 0.29.1 to 0.30.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.29.1...v0.30.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>

* 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>
2025-04-01 08:23:43 +00:00
patrick-conscriptus[bot]
3be487c328 Automated commit (#354)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-03-30 01:30:44 +00:00
dependabot[bot]
a5f4d169ca Bump FSharp.Core and WoofWare.Whippet.Fantomas (#352)
* Bump FSharp.Core and WoofWare.Whippet.Fantomas

Bumps [FSharp.Core](https://github.com/dotnet/fsharp) and [WoofWare.Whippet.Fantomas](https://github.com/Smaug123/WoofWare.Whippet). These dependencies needed to be updated together.

Updates `FSharp.Core` from 4.3.4 to 6.0.1
- [Release notes](https://github.com/dotnet/fsharp/releases)
- [Changelog](https://github.com/dotnet/fsharp/blob/main/release-notes.md)
- [Commits](https://github.com/dotnet/fsharp/commits)

Updates `WoofWare.Whippet.Fantomas` from 0.3.1 to 0.3.2
- [Commits](https://github.com/Smaug123/WoofWare.Whippet/commits)

---
updated-dependencies:
- dependency-name: FSharp.Core
  dependency-type: direct:production
  update-type: version-update:semver-major
- dependency-name: WoofWare.Whippet.Fantomas
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* Bump ApiSurface and FSharp.Core

Bumps [ApiSurface](https://github.com/G-Research/ApiSurface) and [FSharp.Core](https://github.com/Microsoft/visualfsharp). These dependencies needed to be updated together.

Updates `ApiSurface` from 4.1.17 to 4.1.20
- [Release notes](https://github.com/G-Research/ApiSurface/releases)
- [Commits](https://github.com/G-Research/ApiSurface/compare/ApiSurface.4.1.17...ApiSurface.4.1.20)

Updates `FSharp.Core` from 4.3.4 to 4.3.4
- [Release notes](https://github.com/Microsoft/visualfsharp/releases)
- [Changelog](https://github.com/dotnet/fsharp/blob/main/release-notes.md)
- [Commits](https://github.com/Microsoft/visualfsharp/commits)

---
updated-dependencies:
- dependency-name: ApiSurface
  dependency-type: direct:production
  update-type: version-update:semver-patch
- dependency-name: FSharp.Core
  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>
2025-03-24 19:38:01 +00:00
patrick-conscriptus[bot]
ce634efff2 Automated commit (#351)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-03-23 01:28:44 +00:00
Patrick Stevens
1529dd1fb2 Bump NBGV (#350) 2025-03-21 14:33:32 +00:00
dependabot[bot]
59558b0766 Bump cachix/install-nix-action from 30 to 31 (#349)
Bumps [cachix/install-nix-action](https://github.com/cachix/install-nix-action) from 30 to 31.
- [Release notes](https://github.com/cachix/install-nix-action/releases)
- [Commits](https://github.com/cachix/install-nix-action/compare/v30...v31)

---
updated-dependencies:
- dependency-name: cachix/install-nix-action
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-03-17 19:56:20 +00:00
dependabot[bot]
8602894efc Bump fsharp-analyzers from 0.29.0 to 0.29.1 (#348)
* Bump fsharp-analyzers from 0.29.0 to 0.29.1

Bumps [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK) from 0.29.0 to 0.29.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.29.0...v0.29.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>

* 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>
2025-03-10 18:41:51 +00:00
dependabot[bot]
51d349b365 Bump actions/attest-build-provenance from 2.2.2 to 2.2.3 (#347) 2025-03-10 12:13:39 +00:00
dependabot[bot]
120df84bbf Bump actions/attest-build-provenance from 2.2.0 to 2.2.2 (#345) 2025-03-03 11:24:42 +00:00
dependabot[bot]
603f875a12 Bump ApiSurface and FSharp.Core (#341)
* Automated commit

* Bump Microsoft.NET.Test.Sdk from 17.12.0 to 17.13.0

Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.12.0 to 17.13.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.12.0...v17.13.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 and FSharp.Core

Bumps [ApiSurface](https://github.com/G-Research/ApiSurface) and [FSharp.Core](https://github.com/Microsoft/visualfsharp). These dependencies needed to be updated together.

Updates `ApiSurface` from 4.1.16 to 4.1.17
- [Release notes](https://github.com/G-Research/ApiSurface/releases)
- [Commits](https://github.com/G-Research/ApiSurface/compare/ApiSurface.4.1.16...ApiSurface.4.1.17)

Updates `FSharp.Core` from 4.3.4 to 4.3.4
- [Release notes](https://github.com/Microsoft/visualfsharp/releases)
- [Changelog](https://github.com/dotnet/fsharp/blob/main/release-notes.md)
- [Commits](https://github.com/Microsoft/visualfsharp/commits)

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

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

* Bump fantomas from 7.0.0 to 7.0.1

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 7.0.0 to 7.0.1.
- [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/v7.0.0...v7.0.1)

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

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

* Bump NUnit3TestAdapter from 4.6.0 to 5.0.0

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

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

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

* Deps

* Suppress build warning

* Revert "Merge remote-tracking branch 'origin/auto_pr2025_02_23-01_20_56_533960' into dependabot/nuget/multi-be38daf731"

This reverts commit d2bb029b2b, reversing
changes made to 7a2fe4a014.

* Deps

* Revert

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-02-25 00:28:26 +00:00
dependabot[bot]
2df41555de Bump fsharp-analyzers from 0.28.0 to 0.29.0, and FsUnit (#338)
* Bump fsharp-analyzers from 0.28.0 to 0.29.0

Bumps [fsharp-analyzers](https://github.com/ionide/FSharp.Analyzers.SDK) from 0.28.0 to 0.29.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.28.0...v0.29.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 analysers

* Deps

* Upgrade

---------

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>
2025-02-20 10:44:45 +00:00
patrick-conscriptus[bot]
49e31e52b5 Automated commit (#337)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-16 01:25:03 +00:00
Patrick Stevens
277a186fda Allow properties in mocked interfaces (#336) 2025-02-12 23:53:15 +00:00
patrick-conscriptus[bot]
129687ec1c Automated commit (#334)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-09 01:22:26 +00:00
dependabot[bot]
c7fea55e28 Bump FsCheck from 3.0.1 to 3.1.0 (#333)
* Bump FsCheck from 3.0.1 to 3.1.0

Bumps [FsCheck](https://github.com/Fscheck/fscheck) from 3.0.1 to 3.1.0.
- [Release notes](https://github.com/Fscheck/fscheck/releases)
- [Changelog](https://github.com/fscheck/FsCheck/blob/master/FsCheck%20Release%20Notes.md)
- [Commits](https://github.com/Fscheck/fscheck/compare/3.0.1...3.1.0)

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

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>
2025-02-03 17:28:07 +00:00
patrick-conscriptus[bot]
ded7b32771 Automated commit (#332)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-02-02 01:21:17 +00:00
dependabot[bot]
b272f8b645 Bump FsCheck from 3.0.0 to 3.0.1 (#330)
* Bump FsCheck from 3.0.0 to 3.0.1

Bumps [FsCheck](https://github.com/Fscheck/fscheck) from 3.0.0 to 3.0.1.
- [Release notes](https://github.com/Fscheck/fscheck/releases)
- [Changelog](https://github.com/fscheck/FsCheck/blob/master/FsCheck%20Release%20Notes.md)
- [Commits](https://github.com/Fscheck/fscheck/compare/3.0.0...3.0.1)

---
updated-dependencies:
- dependency-name: FsCheck
  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>
2025-01-29 00:49:28 +00:00
dependabot[bot]
b8d60aec90 Bump actions/attest-build-provenance from 2.1.0 to 2.2.0 (#331) 2025-01-27 11:36:25 +00:00
patrick-conscriptus[bot]
0e3510e1e5 Automated commit (#329)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-26 01:19:46 +00:00
patrick-conscriptus[bot]
8a1edd90d5 Automated commit (#328)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-19 01:22:30 +00:00
dependabot[bot]
74fdd7c0a9 Bump fantomas from 6.3.16 to 7.0.0 (#326)
* Bump fantomas from 6.3.16 to 7.0.0

Bumps [fantomas](https://github.com/fsprojects/fantomas) from 6.3.16 to 7.0.0.
- [Release notes](https://github.com/fsprojects/fantomas/releases)
- [Changelog](https://github.com/fsprojects/fantomas/blob/main/CHANGELOG.md)
- [Commits](https://github.com/fsprojects/fantomas/compare/v6.3.16...v7.0.0)

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

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

* Deps

* Format

---------

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>
2025-01-14 10:35:58 +00:00
dependabot[bot]
23f55814f9 Bump ApiSurface from 4.1.15 to 4.1.16 (#325)
* Bump FsCheck from 2.16.6 to 3.0.0

Bumps [FsCheck](https://github.com/Fscheck/fscheck) from 2.16.6 to 3.0.0.
- [Release notes](https://github.com/Fscheck/fscheck/releases)
- [Changelog](https://github.com/fscheck/FsCheck/blob/master/FsCheck%20Release%20Notes.md)
- [Commits](https://github.com/Fscheck/fscheck/compare/2.16.6...3.0.0)

---
updated-dependencies:
- dependency-name: FsCheck
  dependency-type: direct:production
  update-type: version-update:semver-major
...

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

* Bump ApiSurface from 4.1.15 to 4.1.16

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

---
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.7.112 to 3.7.115

Bumps [Nerdbank.GitVersioning](https://github.com/dotnet/Nerdbank.GitVersioning) from 3.7.112 to 3.7.115.
- [Release notes](https://github.com/dotnet/Nerdbank.GitVersioning/releases)
- [Commits](https://github.com/dotnet/Nerdbank.GitVersioning/commits)

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

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

* Lockfile

* Fix tests

* Fix

---------

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>
2025-01-14 01:16:20 +00:00
Patrick Stevens
15c04bb373 Use GR's GitHub release action (#323) 2025-01-13 09:34:27 +00:00
patrick-conscriptus[bot]
a860a93f9c Automated commit (#322)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-12 01:25:56 +00:00
patrick-conscriptus[bot]
b056af348e Automated commit (#321)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-01-05 01:24:38 +00:00
dependabot[bot]
b44c8db6e9 Bump NUnit from 4.3.1 to 4.3.2 (#320)
* Bump NUnit from 4.3.1 to 4.3.2

Bumps [NUnit](https://github.com/nunit/nunit) from 4.3.1 to 4.3.2.
- [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.3.1...4.3.2)

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

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

* Fix

---------

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-12-30 22:43:17 +00:00
patrick-conscriptus[bot]
7d6a2cea01 Automated commit (#319)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-29 01:25:23 +00:00
Patrick Stevens
d779a602f4 Fix flake update after recent nixpkgs bump (#318) 2024-12-24 19:49:30 +00:00
Patrick Stevens
23cd5272fb Use non-deprecated nixpkgs methods (#317) 2024-12-24 19:32:39 +00:00
dependabot[bot]
93538ee6b4 Bump NUnit from 4.3.0 to 4.3.1 (#315)
* Bump NUnit from 4.3.0 to 4.3.1

Bumps [NUnit](https://github.com/nunit/nunit) from 4.3.0 to 4.3.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.3.0...4.3.1)

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

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

* Bump ApiSurface from 4.1.14 to 4.1.15

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

---
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-12-23 11:45:33 +00:00
patrick-conscriptus[bot]
8a29c2f444 Automated commit (#314)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2024-12-22 01:24:49 +00:00
40 changed files with 9873 additions and 3985 deletions

View File

@@ -3,13 +3,13 @@
"isRoot": true,
"tools": {
"fantomas": {
"version": "6.3.16",
"version": "7.0.1",
"commands": [
"fantomas"
]
},
"fsharp-analyzers": {
"version": "0.28.0",
"version": "0.30.0",
"commands": [
"fsharp-analyzers"
]

View File

@@ -29,7 +29,7 @@ jobs:
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -50,7 +50,7 @@ jobs:
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -67,7 +67,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -82,7 +82,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -97,7 +97,7 @@ jobs:
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -116,7 +116,7 @@ jobs:
- name: Checkout
uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -129,7 +129,7 @@ jobs:
steps:
- uses: actions/checkout@master
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -142,7 +142,7 @@ jobs:
steps:
- uses: actions/checkout@master
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -156,7 +156,7 @@ jobs:
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -198,27 +198,40 @@ jobs:
# Verify that there is exactly one nupkg in the artifact that would be NuGet published
run: if [[ $(find packed-attribute -maxdepth 1 -name 'WoofWare.Myriad.Plugins.Attributes.*.nupkg' -printf c | wc -c) -ne "1" ]]; then exit 1; fi
github-release-plugin-dry-run:
needs: [nuget-pack]
github-release-dry-run:
strategy:
matrix:
artifact:
- nuget-package-plugin
- nuget-package-attribute
runs-on: ubuntu-latest
needs: [nuget-pack]
steps:
- uses: actions/checkout@v4
- name: Download NuGet artifact (plugin)
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
- name: Download NuGet artifact (attribute)
uses: actions/download-artifact@v4
with:
name: nuget-package-attribute
- name: Tag and release plugin
name: ${{ matrix.artifact }}
- name: Compute package path
id: compute-path
run: |
find . -maxdepth 1 -type f -name 'WoofWare.Myriad.*.nupkg' -exec sh -c 'echo "output=$(basename "$1")" >> $GITHUB_OUTPUT' shell {} \;
- name: Compute tag name
id: compute-tag
env:
DRY_RUN: 1
GITHUB_TOKEN: mock-token
run: sh .github/workflows/tag.sh
NUPKG_PATH: ${{ steps.compute-path.outputs.output }}
run: echo "output=$(basename "$NUPKG_PATH" .nupkg)" >> $GITHUB_OUTPUT
- name: Tag and release
uses: G-Research/common-actions/github-release@19d7281a0f9f83e13c78f99a610dbc80fc59ba3b
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
target-commitish: ${{ github.sha }}
tag: ${{ steps.compute-tag.outputs.output }}
binary-contents: ${{ steps.compute-path.outputs.output }}
dry-run: true
all-required-checks-complete:
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-dry-run]
if: ${{ always() }}
runs-on: ubuntu-latest
steps:
@@ -241,7 +254,7 @@ jobs:
name: nuget-package-attribute
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
with:
subject-path: "packed/*.nupkg"
@@ -260,7 +273,7 @@ jobs:
name: nuget-package-plugin
path: packed
- name: Attest Build Provenance
uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb # v2.1.0
uses: actions/attest-build-provenance@c074443f1aee8d4aeeae555aebba3282517141b2 # v2.2.3
with:
subject-path: "packed/*.nupkg"
@@ -276,7 +289,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -309,7 +322,7 @@ jobs:
steps:
- uses: actions/checkout@v4
- name: Install Nix
uses: cachix/install-nix-action@v30
uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
@@ -330,7 +343,12 @@ jobs:
nupkg-dir: packed/
dotnet: ${{ steps.dotnet-identify.outputs.dotnet }}
github-release-plugin:
github-release:
strategy:
matrix:
artifact:
- nuget-package-attribute
- nuget-package-plugin
runs-on: ubuntu-latest
if: ${{ !github.event.repository.fork && github.ref == 'refs/heads/main' }}
needs: [all-required-checks-complete]
@@ -339,15 +357,23 @@ jobs:
contents: write
steps:
- uses: actions/checkout@v4
- name: Download NuGet artifact (plugin)
- name: Download NuGet artifact
uses: actions/download-artifact@v4
with:
name: nuget-package-plugin
- name: Download NuGet artifact (attribute)
uses: actions/download-artifact@v4
with:
name: nuget-package-attribute
- name: Tag and release plugin
name: ${{ matrix.artifact }}
- name: Compute package path
id: compute-path
run: |
find . -maxdepth 1 -type f -name 'WoofWare.Myriad.*.nupkg' -exec sh -c 'echo "output=$(basename "$1")" >> $GITHUB_OUTPUT' shell {} \;
- name: Compute tag name
id: compute-tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: sh .github/workflows/tag.sh
NUPKG_PATH: ${{ steps.compute-path.outputs.output }}
run: echo "output=$(basename "$NUPKG_PATH" .nupkg)" >> $GITHUB_OUTPUT
- name: Tag and release
uses: G-Research/common-actions/github-release@19d7281a0f9f83e13c78f99a610dbc80fc59ba3b
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
target-commitish: ${{ github.sha }}
tag: ${{ steps.compute-tag.outputs.output }}
binary-contents: ${{ steps.compute-path.outputs.output }}

View File

@@ -21,20 +21,20 @@ jobs:
- name: Update Nix flake
run: 'nix flake update'
- name: Build passthru
run: 'nix build ".#default.passthru.fetch-deps"'
- name: Build fetch-deps
run: 'nix build ".#default.fetch-deps"'
- name: Run passthru
- name: Run fetch-deps
run: |
set -o pipefail
./result nix/deps.nix
./result nix/deps.json
- name: Format
run: 'nix develop --command alejandra .'
- name: Create token
id: generate-token
uses: actions/create-github-app-token@v1
uses: actions/create-github-app-token@v2
with:
# https://github.com/actions/create-github-app-token/issues/136
app-id: ${{ secrets.APP_ID }}

View File

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

View File

@@ -235,3 +235,9 @@ type FlagsIntoPositionalArgs' =
[<PositionalArgs false>]
DontGrabEverything : string list
}
[<ArgParser true>]
type PassThru =
{
A : ParentRecordChildPos
}

View File

@@ -0,0 +1,35 @@
namespace ConsumePlugin.ArgsWithUnions
open System
open System.IO
open WoofWare.Myriad.Plugins
type BasicNoPositionals =
{
Foo : int
Bar : string
Baz : bool
Rest : int list
}
type UsernamePasswordAuth =
{
Username : string
Password : string
}
type TokenAuth =
{
Token : string
}
type AuthOptions =
| UsernamePassword of UsernamePasswordAuth
| Token of TokenAuth
[<ArgParser>]
type DoTheThing =
{
Basics : BasicNoPositionals
Auth : AuthOptions
}

View File

@@ -12,6 +12,7 @@
<ItemGroup>
<None Include="myriad.toml"/>
<Compile Include="AssemblyInfo.fs" />
<!--
<Compile Include="RecordFile.fs"/>
<Compile Include="GeneratedRecord.fs">
<MyriadFile>RecordFile.fs</MyriadFile>
@@ -66,10 +67,16 @@
<Compile Include="ListCata.fs">
<MyriadFile>List.fs</MyriadFile>
</Compile>
-->
<Compile Include="Args.fs" />
<Compile Include="GeneratedArgs.fs">
<MyriadFile>Args.fs</MyriadFile>
</Compile>
<Compile Include="ArgsWithUnions.fs" />
<Compile Include="GeneratedArgsWithUnions.fs">
<MyriadFile>ArgsWithUnions.fs</MyriadFile>
</Compile>
<!--
<None Include="swagger-gitea.json" />
<Compile Include="GeneratedSwaggerGitea.fs">
<MyriadFile>swagger-gitea.json</MyriadFile>
@@ -81,6 +88,7 @@
<Compile Include="Generated2SwaggerGitea.fs">
<MyriadFile>GeneratedSwaggerGitea.fs</MyriadFile>
</Compile>
-->
</ItemGroup>
<ItemGroup>

View File

@@ -26769,8 +26769,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"admin/cron/{task}"
.Replace ("{task}", task.ToString () |> System.Uri.EscapeDataString),
"admin/cron/{task}".Replace ("{task}", task.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -26902,8 +26901,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"admin/hooks/{id}"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
"admin/hooks/{id}".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -26945,8 +26943,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"admin/hooks/{id}"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
"admin/hooks/{id}".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -27584,8 +27581,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"amdin/hooks/{id}"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
"amdin/hooks/{id}".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -28008,8 +28004,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"org/{org}/repos"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
"org/{org}/repos".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -28292,8 +28287,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("orgs/{org}/hooks"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
("orgs/{org}/hooks".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
+ (if "orgs/{org}/hooks".IndexOf (char 63) >= 0 then
"&"
else
@@ -28345,8 +28339,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"orgs/{org}/hooks"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
"orgs/{org}/hooks".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -28538,8 +28531,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("orgs/{org}/labels"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
("orgs/{org}/labels".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
+ (if "orgs/{org}/labels".IndexOf (char 63) >= 0 then
"&"
else
@@ -28591,8 +28583,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"orgs/{org}/labels"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
"orgs/{org}/labels".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -28784,8 +28775,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("orgs/{org}/members"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
("orgs/{org}/members".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
+ (if "orgs/{org}/members".IndexOf (char 63) >= 0 then
"&"
else
@@ -29086,8 +29076,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("orgs/{org}/repos"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
("orgs/{org}/repos".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
+ (if "orgs/{org}/repos".IndexOf (char 63) >= 0 then
"&"
else
@@ -29142,8 +29131,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"orgs/{org}/repos"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
"orgs/{org}/repos".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -29197,8 +29185,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("orgs/{org}/teams"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
("orgs/{org}/teams".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString)
+ (if "orgs/{org}/teams".IndexOf (char 63) >= 0 then
"&"
else
@@ -29250,8 +29237,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"orgs/{org}/teams"
.Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
"orgs/{org}/teams".Replace ("{org}", org.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -39381,8 +39367,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"repositories/{id}"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
"repositories/{id}".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)
@@ -39745,8 +39730,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("teams/{id}/members"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString)
("teams/{id}/members".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString)
+ (if "teams/{id}/members".IndexOf (char 63) >= 0 then
"&"
else
@@ -39918,8 +39902,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
("teams/{id}/repos"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString)
("teams/{id}/repos".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString)
+ (if "teams/{id}/repos".IndexOf (char 63) >= 0 then
"&"
else
@@ -40807,8 +40790,7 @@ module Gitea =
System.Uri ("/api/v1/", System.UriKind.Relative)
),
System.Uri (
"user/gpg_keys/{id}"
.Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
"user/gpg_keys/{id}".Replace ("{id}", id.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,686 @@
//------------------------------------------------------------------------------
// This code was generated by myriad.
// Changes to this file will be lost when the code is regenerated.
//------------------------------------------------------------------------------
namespace ArgParserHelpers
/// Helper types for arg parsing
module internal ArgParseHelpers_ConsumePlugin_ArgsWithUnions =
open System
open System.IO
open WoofWare.Myriad.Plugins
open ConsumePlugin.ArgsWithUnions
/// A partially-parsed BasicNoPositionals.
type internal BasicNoPositionals_InProgress =
{
mutable Bar : string option
mutable Baz : bool option
mutable Foo : int option
mutable Rest : ResizeArray<int>
}
/// Freeze this in-progress type. On success, returns the frozen type and the arg (if any) which consumed the input positional args.
member this.Assemble_
(getEnvironmentVariable : string -> string)
(positionals : Choice<string * int, string * int> list)
: Result<BasicNoPositionals * string option, string list>
=
let errors = ResizeArray<string> ()
let positionalConsumers = ResizeArray<string> ()
let outOfPlacePositionals : ResizeArray<string> = ResizeArray ()
let arg0 : int =
match this.Foo with
| Some result -> result
| None ->
errors.Add (sprintf "Required argument '--%s' received no value" "foo")
Unchecked.defaultof<_>
let arg1 : string =
match this.Bar with
| Some result -> result
| None ->
errors.Add (sprintf "Required argument '--%s' received no value" "bar")
Unchecked.defaultof<_>
let arg2 : bool =
match this.Baz with
| Some result -> result
| None ->
errors.Add (sprintf "Required argument '--%s' received no value" "baz")
Unchecked.defaultof<_>
let arg3 : int list = this.Rest |> Seq.toList
if positionalConsumers.Count <= 1 then
if outOfPlacePositionals.Count > 0 then
outOfPlacePositionals
|> String.concat " "
|> (fun x ->
if 0 = outOfPlacePositionals.Count then
"Unmatched args which look like they are meant to be flags. " + x
else
sprintf
"Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `%s=` syntax, or place them after a trailing `--`. %s"
positionalConsumers.[0]
x
)
|> errors.Add
else
()
if errors.Count = 0 then
Ok (
{
Foo = arg0
Bar = arg1
Baz = arg2
Rest = arg3
},
Seq.tryExactlyOne positionalConsumers
)
else
errors |> Seq.toList |> Error
else
("Multiple parsers consumed positional args; this is an error in the application, not an error by the user: "
+ String.concat ", " positionalConsumers)
|> List.singleton
|> Error
static member _Empty () : BasicNoPositionals_InProgress =
{
Bar = None
Baz = None
Foo = None
Rest = ResizeArray ()
}
/// Processes the key-value pair, returning Error if no key was matched.
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
member this.ProcessKeyValueSelf_
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
if System.String.Equals (key, sprintf "--%s" "rest", System.StringComparison.OrdinalIgnoreCase) then
value |> (fun x -> System.Int32.Parse x) |> (fun x -> x) |> this.Rest.Add
() |> Ok
else if System.String.Equals (key, sprintf "--%s" "foo", System.StringComparison.OrdinalIgnoreCase) then
match this.Foo with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "foo")
(x.ToString ())
(value.ToString ())
|> errors_.Add
Ok ()
| None ->
try
this.Foo <- value |> (fun x -> System.Int32.Parse x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
match this.Baz with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "baz")
(x.ToString ())
(value.ToString ())
|> errors_.Add
Ok ()
| None ->
try
this.Baz <- value |> (fun x -> System.Boolean.Parse x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if System.String.Equals (key, sprintf "--%s" "bar", System.StringComparison.OrdinalIgnoreCase) then
match this.Bar with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "bar")
(x.ToString ())
(value.ToString ())
|> errors_.Add
Ok ()
| None ->
try
this.Bar <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else
Error None
member this.ProcessKeyValue
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
match this.ProcessKeyValueSelf_ argNum_ errors_ key value with
| Ok () -> Ok ()
| Error None -> Error None
| Error (Some errorFromLeaf) -> Error (Some errorFromLeaf)
/// Returns false if we didn't set a value.
member this.SetFlagValue_ (errors_ : ResizeArray<string>) (key : string) : bool =
if System.String.Equals (key, sprintf "--%s" "baz", System.StringComparison.OrdinalIgnoreCase) then
match this.Baz with
| Some _ ->
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "baz")
|> errors_.Add
true
| None ->
this.Baz <- true |> Some
true
else
false
/// Compute help text for this parser, optionally noting the given prefix on each argument and indenting each line by this many spaces.
static member HelpText_ (prefix : string option) (indent : int) : string = failwith "TODO"
/// A partially-parsed UsernamePasswordAuth.
type internal UsernamePasswordAuth_InProgress =
{
mutable Password : string option
mutable Username : string option
}
/// Freeze this in-progress type. On success, returns the frozen type and the arg (if any) which consumed the input positional args.
member this.Assemble_
(getEnvironmentVariable : string -> string)
(positionals : Choice<string * int, string * int> list)
: Result<UsernamePasswordAuth * string option, string list>
=
let errors = ResizeArray<string> ()
let positionalConsumers = ResizeArray<string> ()
let outOfPlacePositionals : ResizeArray<string> = ResizeArray ()
let arg0 : string =
match this.Username with
| Some result -> result
| None ->
errors.Add (sprintf "Required argument '--%s' received no value" "username")
Unchecked.defaultof<_>
let arg1 : string =
match this.Password with
| Some result -> result
| None ->
errors.Add (sprintf "Required argument '--%s' received no value" "password")
Unchecked.defaultof<_>
if positionalConsumers.Count <= 1 then
if outOfPlacePositionals.Count > 0 then
outOfPlacePositionals
|> String.concat " "
|> (fun x ->
if 0 = outOfPlacePositionals.Count then
"Unmatched args which look like they are meant to be flags. " + x
else
sprintf
"Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `%s=` syntax, or place them after a trailing `--`. %s"
positionalConsumers.[0]
x
)
|> errors.Add
else
()
if errors.Count = 0 then
Ok (
{
Username = arg0
Password = arg1
},
Seq.tryExactlyOne positionalConsumers
)
else
errors |> Seq.toList |> Error
else
("Multiple parsers consumed positional args; this is an error in the application, not an error by the user: "
+ String.concat ", " positionalConsumers)
|> List.singleton
|> Error
static member _Empty () : UsernamePasswordAuth_InProgress =
{
Password = None
Username = None
}
/// Processes the key-value pair, returning Error if no key was matched.
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
member this.ProcessKeyValueSelf_
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
if System.String.Equals (key, sprintf "--%s" "username", System.StringComparison.OrdinalIgnoreCase) then
match this.Username with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "username")
(x.ToString ())
(value.ToString ())
|> errors_.Add
Ok ()
| None ->
try
this.Username <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if
System.String.Equals (key, sprintf "--%s" "password", System.StringComparison.OrdinalIgnoreCase)
then
match this.Password with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "password")
(x.ToString ())
(value.ToString ())
|> errors_.Add
Ok ()
| None ->
try
this.Password <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else
Error None
member this.ProcessKeyValue
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
match this.ProcessKeyValueSelf_ argNum_ errors_ key value with
| Ok () -> Ok ()
| Error None -> Error None
| Error (Some errorFromLeaf) -> Error (Some errorFromLeaf)
/// Returns false if we didn't set a value.
member this.SetFlagValue_ (errors_ : ResizeArray<string>) (key : string) : bool = false
/// Compute help text for this parser, optionally noting the given prefix on each argument and indenting each line by this many spaces.
static member HelpText_ (prefix : string option) (indent : int) : string = failwith "TODO"
/// A partially-parsed TokenAuth.
type internal TokenAuth_InProgress =
{
mutable Token : string option
}
/// Freeze this in-progress type. On success, returns the frozen type and the arg (if any) which consumed the input positional args.
member this.Assemble_
(getEnvironmentVariable : string -> string)
(positionals : Choice<string * int, string * int> list)
: Result<TokenAuth * string option, string list>
=
let errors = ResizeArray<string> ()
let positionalConsumers = ResizeArray<string> ()
let outOfPlacePositionals : ResizeArray<string> = ResizeArray ()
let arg0 : string =
match this.Token with
| Some result -> result
| None ->
errors.Add (sprintf "Required argument '--%s' received no value" "token")
Unchecked.defaultof<_>
if positionalConsumers.Count <= 1 then
if outOfPlacePositionals.Count > 0 then
outOfPlacePositionals
|> String.concat " "
|> (fun x ->
if 0 = outOfPlacePositionals.Count then
"Unmatched args which look like they are meant to be flags. " + x
else
sprintf
"Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `%s=` syntax, or place them after a trailing `--`. %s"
positionalConsumers.[0]
x
)
|> errors.Add
else
()
if errors.Count = 0 then
Ok (
{
Token = arg0
},
Seq.tryExactlyOne positionalConsumers
)
else
errors |> Seq.toList |> Error
else
("Multiple parsers consumed positional args; this is an error in the application, not an error by the user: "
+ String.concat ", " positionalConsumers)
|> List.singleton
|> Error
static member _Empty () : TokenAuth_InProgress =
{
Token = None
}
/// Processes the key-value pair, returning Error if no key was matched.
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
member this.ProcessKeyValueSelf_
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
if System.String.Equals (key, sprintf "--%s" "token", System.StringComparison.OrdinalIgnoreCase) then
match this.Token with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "token")
(x.ToString ())
(value.ToString ())
|> errors_.Add
Ok ()
| None ->
try
this.Token <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else
Error None
member this.ProcessKeyValue
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
match this.ProcessKeyValueSelf_ argNum_ errors_ key value with
| Ok () -> Ok ()
| Error None -> Error None
| Error (Some errorFromLeaf) -> Error (Some errorFromLeaf)
/// Returns false if we didn't set a value.
member this.SetFlagValue_ (errors_ : ResizeArray<string>) (key : string) : bool = false
/// Compute help text for this parser, optionally noting the given prefix on each argument and indenting each line by this many spaces.
static member HelpText_ (prefix : string option) (indent : int) : string = failwith "TODO"
/// A partially-parsed AuthOptions.
type internal AuthOptions_InProgress =
{
Token : TokenAuth_InProgress
UsernamePassword : UsernamePasswordAuth_InProgress
}
/// Freeze this in-progress type. On success, returns the frozen type and the arg (if any) which consumed the input positional args.
member this.Assemble_
(getEnvironmentVariable : string -> string)
(positionals : Choice<string * int, string * int> list)
: Result<AuthOptions * string option, string list>
=
failwith "TODO"
static member _Empty () : AuthOptions_InProgress =
{
Token = TokenAuth_InProgress._Empty ()
UsernamePassword = UsernamePasswordAuth_InProgress._Empty ()
}
/// Compute help text for this parser, optionally noting the given prefix on each argument and indenting each line by this many spaces.
static member HelpText_ (prefix : string option) (indent : int) : string = failwith "TODO"
/// A partially-parsed DoTheThing.
type internal DoTheThing_InProgress =
{
mutable Auth : AuthOptions_InProgress
mutable Basics : BasicNoPositionals_InProgress
}
/// Freeze this in-progress type. On success, returns the frozen type and the arg (if any) which consumed the input positional args.
member this.Assemble_
(getEnvironmentVariable : string -> string)
(positionals : Choice<string * int, string * int> list)
: Result<DoTheThing * string option, string list>
=
let errors = ResizeArray<string> ()
let positionalConsumers = ResizeArray<string> ()
let outOfPlacePositionals : ResizeArray<string> = ResizeArray ()
let arg0 : BasicNoPositionals =
match this.Basics.Assemble_ getEnvironmentVariable positionals with
| Ok (result, consumedPositional) ->
match consumedPositional with
| None -> ()
| Some positionalConsumer -> positionalConsumers.Add positionalConsumer
result
| Error err ->
errors.AddRange err
Unchecked.defaultof<_>
let arg1 : AuthOptions =
match this.Auth.Assemble_ getEnvironmentVariable positionals with
| Ok (result, consumedPositional) ->
match consumedPositional with
| None -> ()
| Some positionalConsumer -> positionalConsumers.Add positionalConsumer
result
| Error err ->
errors.AddRange err
Unchecked.defaultof<_>
if positionalConsumers.Count <= 1 then
if outOfPlacePositionals.Count > 0 then
outOfPlacePositionals
|> String.concat " "
|> (fun x ->
if 0 = outOfPlacePositionals.Count then
"Unmatched args which look like they are meant to be flags. " + x
else
sprintf
"Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `%s=` syntax, or place them after a trailing `--`. %s"
positionalConsumers.[0]
x
)
|> errors.Add
else
()
if errors.Count = 0 then
Ok (
{
Basics = arg0
Auth = arg1
},
Seq.tryExactlyOne positionalConsumers
)
else
errors |> Seq.toList |> Error
else
("Multiple parsers consumed positional args; this is an error in the application, not an error by the user: "
+ String.concat ", " positionalConsumers)
|> List.singleton
|> Error
static member _Empty () : DoTheThing_InProgress =
{
Basics = BasicNoPositionals_InProgress._Empty ()
Auth = AuthOptions_InProgress._Empty ()
}
/// Passes the key-value pair to any child records, returning Error if no key was matched.
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
member this.ProcessKeyValueRecord_
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
let errors : ResizeArray<string> = ResizeArray ()
match this.Basics.ProcessKeyValue argNum_ errors_ key value with
| Ok () -> Ok ()
| Error e -> Error None
member this.ProcessKeyValue
(argNum_ : int)
(errors_ : ResizeArray<string>)
(key : string)
(value : string)
: Result<unit, string option>
=
match this.ProcessKeyValueRecord_ argNum_ errors_ key value with
| Ok () -> Ok ()
| Error errorFromRecord -> Error errorFromRecord
/// Returns false if we didn't set a value.
member this.SetFlagValue_ (errors_ : ResizeArray<string>) (key : string) : bool = false
/// Compute help text for this parser, optionally noting the given prefix on each argument and indenting each line by this many spaces.
static member HelpText_ (prefix : string option) (indent : int) : string = failwith "TODO"
namespace ConsumePlugin.ArgsWithUnions
open ArgParserHelpers
open System
open System.IO
open WoofWare.Myriad.Plugins
/// Methods to parse arguments for the type DoTheThing
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module DoTheThing =
type internal ParseState_DoTheThing =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
let parse' (getEnvironmentVariable : string -> string) (args : string list) : DoTheThing =
let inProgress =
ArgParseHelpers_ConsumePlugin_ArgsWithUnions.DoTheThing_InProgress._Empty ()
let positionals : ResizeArray<Choice<string * int, string * int>> = ResizeArray ()
let errors_ = ResizeArray ()
let rec go (argNum_ : int) (state : ParseState_DoTheThing) (args : string list) =
match args with
| [] ->
match state with
| ParseState_DoTheThing.AwaitingKey -> ()
| ParseState_DoTheThing.AwaitingValue key ->
if inProgress.SetFlagValue_ errors_ key then
()
else
sprintf
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
key
|> errors_.Add
| "--" :: rest -> positionals.AddRange (rest |> Seq.map (fun x -> (x, argNum_ + 1)) |> Seq.map Choice2Of2)
| arg :: args ->
match state with
| ParseState_DoTheThing.AwaitingKey ->
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
if arg = "--help" then
"TODO" |> failwithf "Help text requested.\n%s"
else
let equals = arg.IndexOf (char 61)
if equals < 0 then
go (argNum_ + 1) (ParseState_DoTheThing.AwaitingValue arg) args
else
let key = arg.[0 .. equals - 1]
let value = arg.[equals + 1 ..]
match inProgress.ProcessKeyValue argNum_ errors_ key value with
| Ok () -> go (argNum_ + 1) ParseState_DoTheThing.AwaitingKey args
| Error x ->
match x with
| None ->
positionals.Add (Choice1Of2 (arg, argNum_))
go (argNum_ + 1) ParseState_DoTheThing.AwaitingKey args
| Some msg ->
sprintf "%s (at arg %s)" msg arg |> errors_.Add
go (argNum_ + 1) ParseState_DoTheThing.AwaitingKey args
else
(arg, argNum_) |> Choice1Of2 |> positionals.Add
go (argNum_ + 1) ParseState_DoTheThing.AwaitingKey args
| ParseState_DoTheThing.AwaitingValue key ->
match inProgress.ProcessKeyValue argNum_ errors_ key arg with
| Ok () -> go argNum_ ParseState_DoTheThing.AwaitingKey args
| Error exc ->
if inProgress.SetFlagValue_ errors_ key then
go argNum_ ParseState_DoTheThing.AwaitingKey (arg :: args)
else
(key, argNum_) |> Choice1Of2 |> positionals.Add
go (argNum_ + 1) ParseState_DoTheThing.AwaitingKey (arg :: args)
go 0 ParseState_DoTheThing.AwaitingKey args
if 0 = errors_.Count then
()
else
errors_
|> String.concat System.Environment.NewLine
|> (fun x -> "Errors during parse!\n" + x)
|> failwith
match inProgress.Assemble_ getEnvironmentVariable (positionals |> Seq.toList) with
| Ok (result, posConsumer) ->
if positionals.Count > 0 && posConsumer.IsNone then
positionals
|> Seq.map (fun choiceValue ->
match choiceValue with
| Choice1Of2 (arg, _) -> arg
| Choice2Of2 (arg, _) -> arg
)
|> String.concat " "
|> sprintf "Parse error: The following arguments were not consumed: %s"
|> failwith
else
result
| Error e ->
e
|> String.concat System.Environment.NewLine
|> (fun x -> "Errors during parse!\n" + x)
|> failwith
let parse (args : string list) : DoTheThing =
parse' System.Environment.GetEnvironmentVariable args

View File

@@ -206,3 +206,34 @@ type internal TypeWithInterfaceMock =
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins
/// Mock record type for an interface
type internal TypeWithPropertiesMock =
{
/// Implementation of IDisposable.Dispose
Dispose : unit -> unit
Prop1 : unit -> int
Prop2 : unit -> unit Async
Mem1 : string option -> string[] Async
}
/// An implementation where every method throws.
static member Empty : TypeWithPropertiesMock =
{
Dispose = (fun () -> ())
Prop1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Prop1"))
Prop2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Prop2"))
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
}
interface TypeWithProperties with
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
member this.Prop1 = this.Prop1 ()
member this.Prop2 = this.Prop2 ()
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()

View File

@@ -160,8 +160,7 @@ module PureGymApi =
| null -> System.Uri "https://whatnot.com/"
| v -> v),
System.Uri (
"v1/gyms/{gym}"
.Replace ("{gym}", gym.ToString () |> System.Uri.EscapeDataString),
"v1/gyms/{gym}".Replace ("{gym}", gym.ToString () |> System.Uri.EscapeDataString),
System.UriKind.Relative
)
)

View File

@@ -48,3 +48,10 @@ type TypeWithInterface =
inherit IDisposable
abstract Mem1 : string option -> string[] Async
abstract Mem2 : unit -> string[] Async
[<GenerateMock>]
type TypeWithProperties =
inherit IDisposable
abstract Mem1 : string option -> string[] Async
abstract Prop1 : int
abstract Prop2 : unit Async

View File

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

47
Playground/Domain.fs Normal file
View File

@@ -0,0 +1,47 @@
namespace Playground
open System
open WoofWare.Myriad.Plugins
[<ArgParser>]
type SubMode1 =
{
Info1 : int
Info2 : string
Rest : string list
}
[<ArgParser>]
type SubMode2 =
{
Info1 : int
Info2 : string
Rest : int list
}
[<ArgParser>]
type Mode1 =
{
Things : SubMode1
Whatnot : int
}
[<ArgParser>]
type Mode2 =
{
Things : SubMode2
Whatnot : DateTime
}
[<ArgParser>]
type Modes =
| Mode1 of Mode1
| Mode2 of Mode2
[<ArgParser>]
type Args =
{
WhatToDo : Modes
[<PositionalArgs>]
OtherArgs : string list
}

563
Playground/Library.fs Normal file
View File

@@ -0,0 +1,563 @@
//------------------------------------------------------------------------------
// This code was generated by myriad.
// Changes to this file will be lost when the code is regenerated.
//------------------------------------------------------------------------------
namespace Playground // Assuming a namespace
open System
open System.IO
open WoofWare.Myriad.Plugins // Assuming attributes are here
// Assume original type definitions are accessible here
// [<ArgParser>] type SubMode1 = { Info1 : int; Info2 : string; Rest : string list }
// [<ArgParser>] type SubMode2 = { Info1 : int; Info2 : string; Rest : int list }
// [<ArgParser>] type Mode1 = { Things : SubMode1; Whatnot : int }
// [<ArgParser>] type Mode2 = { Things : SubMode2; Whatnot : DateTime }
// [<ArgParser>] type Modes = | Mode1 of Mode1 | Mode2 of Mode2
// [<ArgParser>] type Args = { WhatToDo : Modes; [<PositionalArgs>] OtherArgs : string list }
/// Methods to parse arguments for the type Args
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module Args =
//--------------------------------------------------------------------------
// Internal state definitions (Non-Flattened with combined Assemble/Validate)
//--------------------------------------------------------------------------
/// State representing the parse progress for SubMode1 record
type private State_SubMode1 =
{
mutable Info1 : int option
mutable Info2 : string option
Rest : ResizeArray<string> // Corresponds to --rest
}
static member Create () =
{
Info1 = None
Info2 = None
Rest = ResizeArray ()
}
/// Check completeness and assemble the SubMode1 record from state.
member this.Assemble () : Result<SubMode1, string list> =
let errors = ResizeArray<string> ()
let mutable complete = true
if this.Info1.IsNone then
complete <- false
errors.Add ("Argument '--info1' is required.")
if this.Info2.IsNone then
complete <- false
errors.Add ("Argument '--info2' is required.")
// Rest is list, always 'complete'
if complete then
Ok
{
Info1 = this.Info1.Value
Info2 = this.Info2.Value
Rest = this.Rest |> Seq.toList
}
else
Error (errors |> Seq.toList)
/// State representing the parse progress for SubMode2 record
type private State_SubMode2 =
{
mutable Info1 : int option
mutable Info2 : string option
Rest : ResizeArray<int> // Corresponds to --rest
}
static member Create () =
{
Info1 = None
Info2 = None
Rest = ResizeArray ()
}
/// Check completeness and assemble the SubMode2 record from state.
member this.Assemble () : Result<SubMode2, string list> =
let errors = ResizeArray<string> ()
if this.Info1.IsNone then
errors.Add ("Argument '--info1' is required.")
if this.Info2.IsNone then
errors.Add ("Argument '--info2' is required.")
// Rest is list, always 'complete'
if errors.Count = 0 then
Ok
{
Info1 = this.Info1.Value
Info2 = this.Info2.Value
Rest = this.Rest |> Seq.toList
}
else
Error (errors |> Seq.toList)
/// State representing the parse progress for Mode1 record (references SubMode1 state)
type private State_Mode1 =
{
ThingsState : State_SubMode1 // Holds state for the nested record
mutable Whatnot : int option
}
static member Create () =
{
ThingsState = State_SubMode1.Create ()
Whatnot = None
}
/// Check completeness and assemble the Mode1 record from state (including nested).
member this.Assemble () : Result<Mode1, string list> =
let errors = ResizeArray<string> ()
// Check direct fields
if this.Whatnot.IsNone then
errors.Add ("Argument '--whatnot' is required for Mode1.")
// Assemble nested state (which includes its own validation)
let thingsResult = this.ThingsState.Assemble ()
let mutable thingsValue = None
match thingsResult with
| Ok v -> thingsValue <- Some v
| Error nestedErrors -> errors.AddRange (nestedErrors |> List.map (sprintf "Things: %s")) // Add context
if errors.Count = 0 then
Ok
{
Things = thingsValue.Value
Whatnot = this.Whatnot.Value
}
else
Error (errors |> Seq.toList)
/// State representing the parse progress for Mode2 record (references SubMode2 state)
type private State_Mode2 =
{
ThingsState : State_SubMode2 // Holds state for the nested record
mutable Whatnot : DateTime option
}
static member Create () =
{
ThingsState = State_SubMode2.Create ()
Whatnot = None
}
/// Check completeness and assemble the Mode2 record from state (including nested).
member this.Assemble () : Result<Mode2, string list> =
let errors = ResizeArray<string> ()
// Check direct fields
if this.Whatnot.IsNone then
errors.Add ("Argument '--whatnot' is required for Mode2.")
// Assemble nested state (which includes its own validation)
let thingsResult = this.ThingsState.Assemble ()
let mutable thingsValue = Unchecked.defaultof<_>
match thingsResult with
| Ok v -> thingsValue <- v
| Error nestedErrors -> errors.AddRange (nestedErrors |> List.map (sprintf "Things: %s")) // Add context
if errors.Count = 0 then
{
Things = thingsValue
Whatnot = this.Whatnot.Value
}
|> Ok
else
Error (errors |> Seq.toList)
/// State for a single candidate parse path for the Modes DU (Structure unchanged)
type private CandidateParseState_Modes =
{
CaseName : string // "Mode1" or "Mode2"
mutable IsViable : bool
Errors : ResizeArray<string> // Errors specific to this candidate's path
ConsumedArgIndices : System.Collections.Generic.HashSet<int> // Indices consumed *by this candidate*
CaseState : obj // Holds either State_Mode1 or State_Mode2
}
static member CreateMode1 () =
{
CaseName = "Mode1"
IsViable = true
Errors = ResizeArray ()
ConsumedArgIndices = System.Collections.Generic.HashSet ()
CaseState = State_Mode1.Create () :> obj
}
static member CreateMode2 () =
{
CaseName = "Mode2"
IsViable = true
Errors = ResizeArray ()
ConsumedArgIndices = System.Collections.Generic.HashSet ()
CaseState = State_Mode2.Create () :> obj
}
//--------------------------------------------------------------------------
// Main Parser Logic
//--------------------------------------------------------------------------
type private ParseState_Args =
| AwaitingArg
| AwaitingValue of keyIndex : int * key : string
let parse' (getEnvironmentVariable : string -> string) (args : string list) : Args =
let ArgParser_errors = ResizeArray () // Global errors accumulator
let helpText () =
// Help text generation unchanged
[
(sprintf "%s int32%s%s" (sprintf "--%s" "info1") "" " (for Mode1/Mode2 Things)")
(sprintf "%s string%s%s" (sprintf "--%s" "info2") "" " (for Mode1/Mode2 Things)")
(sprintf "%s string%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode1 Things)")
(sprintf "%s int32%s%s" (sprintf "--%s" "rest") " (can be repeated)" " (for Mode2 Things)")
(sprintf "%s int32%s%s" (sprintf "--%s" "whatnot") "" " (for Mode1)")
(sprintf "%s DateTime%s%s" (sprintf "--%s" "whatnot") "" " (for Mode2)")
(sprintf "%s string%s%s" (sprintf "--%s" "other-args") " (positional args) (can be repeated)" "")
]
|> String.concat "\n"
let arg_OtherArgs : string ResizeArray = ResizeArray ()
let mutable candidates_WhatToDo : CandidateParseState_Modes list =
[
CandidateParseState_Modes.CreateMode1 ()
CandidateParseState_Modes.CreateMode2 ()
]
let consumedArgIndices_WhatToDo = System.Collections.Generic.HashSet<int> ()
//----------------------------------------------------------------------
// Helper functions for applying args (applyKeyValueToSubModeXState unchanged)
//----------------------------------------------------------------------
let applyKeyValueToSubMode1State
(argIndex : int)
(keyIndex : int)
(key : string)
(value : string)
(subState : State_SubMode1)
(candidate : CandidateParseState_Modes)
: unit
=
// ... (Implementation identical to previous version) ...
if String.Equals (key, "--info1", StringComparison.OrdinalIgnoreCase) then
match subState.Info1 with
| Some _ ->
candidate.Errors.Add (sprintf "Argument '--info1' supplied multiple times (SubMode1)")
candidate.IsViable <- false
| None ->
try
subState.Info1 <- Some (Int32.Parse value)
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
with ex ->
candidate.Errors.Add (
sprintf "Failed to parse '%s' for --info1 (SubMode1): %s" value ex.Message
)
candidate.IsViable <- false
elif String.Equals (key, "--info2", StringComparison.OrdinalIgnoreCase) then
match subState.Info2 with
| Some _ ->
candidate.Errors.Add (sprintf "Argument '--info2' supplied multiple times (SubMode1)")
candidate.IsViable <- false
| None ->
subState.Info2 <- Some value
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
elif String.Equals (key, "--rest", StringComparison.OrdinalIgnoreCase) then
subState.Rest.Add value
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
else
()
let applyKeyValueToSubMode2State
(argIndex : int)
(keyIndex : int)
(key : string)
(value : string)
(subState : State_SubMode2)
(candidate : CandidateParseState_Modes)
: unit
=
// ... (Implementation identical to previous version) ...
if String.Equals (key, "--info1", StringComparison.OrdinalIgnoreCase) then
match subState.Info1 with
| Some _ ->
candidate.Errors.Add (sprintf "Argument '--info1' supplied multiple times (SubMode2)")
candidate.IsViable <- false
| None ->
try
subState.Info1 <- Some (Int32.Parse value)
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
with ex ->
candidate.Errors.Add (
sprintf "Failed to parse '%s' for --info1 (SubMode2): %s" value ex.Message
)
candidate.IsViable <- false
elif String.Equals (key, "--info2", StringComparison.OrdinalIgnoreCase) then
match subState.Info2 with
| Some _ ->
candidate.Errors.Add (sprintf "Argument '--info2' supplied multiple times (SubMode2)")
candidate.IsViable <- false
| None ->
subState.Info2 <- Some value
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
elif String.Equals (key, "--rest", StringComparison.OrdinalIgnoreCase) then
try
subState.Rest.Add (Int32.Parse value)
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
with ex ->
candidate.Errors.Add (
sprintf "Failed to parse '%s' as int32 for --rest (SubMode2): %s" value ex.Message
)
candidate.IsViable <- false
else
()
//----------------------------------------------------------------------
// Routing and Main Application Logic (applyKeyValueToCandidate unchanged)
//----------------------------------------------------------------------
let applyKeyValueToCandidate
(argIndex : int, keyIndex : int, key : string, value : string)
(candidate : CandidateParseState_Modes)
: unit
=
// ... (Implementation identical to previous version, calling sub-state helpers) ...
if not candidate.IsViable then
()
else
match candidate.CaseName with
| "Mode1" ->
let state = candidate.CaseState :?> State_Mode1
if String.Equals (key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
match state.Whatnot with
| Some _ ->
candidate.Errors.Add (
sprintf "Argument '--whatnot' supplied multiple times for Mode1 candidate"
)
candidate.IsViable <- false
| None ->
try
state.Whatnot <- Some (Int32.Parse value)
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
with ex ->
candidate.Errors.Add (
sprintf "Failed to parse '%s' as int32 for --whatnot (Mode1): %s" value ex.Message
)
candidate.IsViable <- false
elif key = "--info1" || key = "--info2" || key = "--rest" then
applyKeyValueToSubMode1State argIndex keyIndex key value state.ThingsState candidate
else
()
| "Mode2" ->
let state = candidate.CaseState :?> State_Mode2
if String.Equals (key, "--whatnot", StringComparison.OrdinalIgnoreCase) then
match state.Whatnot with
| Some _ ->
candidate.Errors.Add (
sprintf "Argument '--whatnot' supplied multiple times for Mode2 candidate"
)
candidate.IsViable <- false
| None ->
try
state.Whatnot <- Some (DateTime.Parse value)
candidate.ConsumedArgIndices.Add argIndex |> ignore
candidate.ConsumedArgIndices.Add keyIndex |> ignore
with ex ->
candidate.Errors.Add (
sprintf "Failed to parse '%s' as DateTime for --whatnot (Mode2): %s" value ex.Message
)
candidate.IsViable <- false
elif key = "--info1" || key = "--info2" || key = "--rest" then
applyKeyValueToSubMode2State argIndex keyIndex key value state.ThingsState candidate
else
()
| _ -> failwith "Internal error: Unknown case name"
// processKeyValue, setFlagValue, and main loop `go` are identical to previous version
let processKeyValue (keyIndex : int, key : string, valueIndex : int, value : string) : bool =
let mutable handled = false
for candidate in candidates_WhatToDo do
let initialConsumedCount = candidate.ConsumedArgIndices.Count
if candidate.IsViable then
applyKeyValueToCandidate (valueIndex, keyIndex, key, value) candidate
if candidate.IsViable && candidate.ConsumedArgIndices.Count > initialConsumedCount then
handled <- true
consumedArgIndices_WhatToDo.Add keyIndex |> ignore
consumedArgIndices_WhatToDo.Add valueIndex |> ignore
handled
let setFlagValue (keyIndex : int) (key : string) : bool = false // No flags
let rec go (state : ParseState_Args) (args : (int * string) list) =
// ... (Implementation identical to previous version) ...
match args with
| [] ->
match state with
| ParseState_Args.AwaitingArg -> ()
| ParseState_Args.AwaitingValue (i, k) ->
if not (setFlagValue i k) then
ArgParser_errors.Add (sprintf "Trailing argument '%s' (at index %d) requires a value." k i)
| (idx, arg) :: rest ->
match state with
| ParseState_Args.AwaitingArg ->
if arg = "--" then
rest
|> List.iter (fun (i, v) ->
if not (consumedArgIndices_WhatToDo.Contains i) then
arg_OtherArgs.Add v
)
go ParseState_Args.AwaitingArg []
elif arg.StartsWith ("--") then
if arg = "--help" then
helpText () |> failwithf "Help text requested:\n%s"
else
let eq = arg.IndexOf ('=')
if eq > 0 then
let k = arg.[.. eq - 1]
let v = arg.[eq + 1 ..]
if not (processKeyValue (idx, k, idx, v)) then
if not (consumedArgIndices_WhatToDo.Contains idx) then
arg_OtherArgs.Add arg
go ParseState_Args.AwaitingArg rest
elif setFlagValue idx arg then
consumedArgIndices_WhatToDo.Add idx |> ignore
go ParseState_Args.AwaitingArg rest
else
go (ParseState_Args.AwaitingValue (idx, arg)) rest
else
if not (consumedArgIndices_WhatToDo.Contains idx) then
arg_OtherArgs.Add arg
go ParseState_Args.AwaitingArg rest
| ParseState_Args.AwaitingValue (keyIdx, key) ->
if processKeyValue (keyIdx, key, idx, arg) then
go ParseState_Args.AwaitingArg rest
elif setFlagValue keyIdx key then
consumedArgIndices_WhatToDo.Add keyIdx |> ignore<bool>
go ParseState_Args.AwaitingArg ((idx, arg) :: rest) // Reprocess arg
elif not (consumedArgIndices_WhatToDo.Contains keyIdx) then
arg_OtherArgs.Add key
if not (consumedArgIndices_WhatToDo.Contains idx) then
arg_OtherArgs.Add arg
go ParseState_Args.AwaitingArg rest
args |> List.mapi (fun i s -> (i, s)) |> go ParseState_Args.AwaitingArg
//----------------------------------------------------------------------
// Final Validation and Assembly (Uses new Assemble methods)
//----------------------------------------------------------------------
let viableWinners = candidates_WhatToDo |> List.filter (fun c -> c.IsViable)
// No longer filter based on IsComplete here; Assemble handles it.
// Still need to check for relative leftovers if that logic were implemented.
let whatToDoResult =
match viableWinners with
| [] ->
// Add specific errors from candidates that were viable *before* Assemble check
ArgParser_errors.Add ("No valid parse found for 'WhatToDo'.")
candidates_WhatToDo
|> List.iter (fun c ->
if c.Errors.Count <> 0 then
ArgParser_errors.Add (
sprintf " Candidate %s parse errors: %s" c.CaseName (String.concat "; " c.Errors)
)
// Potentially try to Assemble even non-viable ones to get completion errors? Maybe too complex.
)
Unchecked.defaultof<_> // Error path
| [ winner ] ->
// Assemble the winning case, checking the Result for completion errors
match winner.CaseName with
| "Mode1" ->
match (winner.CaseState :?> State_Mode1).Assemble () with
| Ok mode1Value -> Modes.Mode1 mode1Value
| Error completionErrors ->
ArgParser_errors.Add (sprintf "Validation failed for selected candidate Mode1:")
ArgParser_errors.AddRange completionErrors
Unchecked.defaultof<_> // Error path
| "Mode2" ->
match (winner.CaseState :?> State_Mode2).Assemble () with
| Ok mode2Value -> Modes.Mode2 mode2Value
| Error completionErrors ->
ArgParser_errors.Add (sprintf "Validation failed for selected candidate Mode2:")
ArgParser_errors.AddRange completionErrors
Unchecked.defaultof<_> // Error path
| _ -> failwith "Internal error: Unknown winning case name"
| winners -> // Ambiguous parse
ArgParser_errors.Add ("Ambiguous parse for 'WhatToDo'. Multiple modes potentially viable:")
winners
|> List.iter (fun c ->
ArgParser_errors.Add (
sprintf
" - %s (Initial Errors: %s)"
c.CaseName
(if c.Errors.Count = 0 then
"None"
else
String.concat "; " c.Errors)
)
)
Unchecked.defaultof<_> // Error path
// Finalize OtherArgs (unchanged)
let otherArgsResult = arg_OtherArgs |> Seq.toList
// Assemble Final Result or Fail (unchanged)
if ArgParser_errors.Count > 0 then
ArgParser_errors
|> String.concat "\n"
|> failwithf "Errors during parse!\n%s\n\nHelp Text:\n%s" (helpText ())
else
{
WhatToDo = whatToDoResult
OtherArgs = otherArgsResult
}
let parse (args : string list) : Args =
parse' System.Environment.GetEnvironmentVariable args

View File

@@ -0,0 +1,19 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="Domain.fs" />
<Compile Include="Library.fs"/>
<Compile Include="Program.fs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj" />
</ItemGroup>
</Project>

10
Playground/Program.fs Normal file
View File

@@ -0,0 +1,10 @@
namespace Playground
module Program =
[<EntryPoint>]
let main argv =
[ "--whatnot=2024-01-12" ; "--info1=4" ; "--info2=hi" ]
|> Args.parse
|> printfn "%O"
0

View File

@@ -26,6 +26,11 @@ type ArgParserAttribute (isExtensionMethod : bool) =
/// an argument which looks like a flag but which we don't recognise.)
/// We will still interpret `--help` as requesting help, unless it comes after
/// a standalone `--` separator.
///
/// If the type of the PositionalArgs field is `Choice<'a, 'a>`, then we will
/// tell you whether each arg came before or after a standalone `--` separator.
/// For example, `MyApp foo bar -- baz` with PositionalArgs of `Choice<string, string>`
/// would yield `Choice1Of2 foo, Choice1Of2 bar, Choice2Of2 baz`.
type PositionalArgsAttribute (includeFlagLike : bool) =
inherit Attribute ()

View File

@@ -17,10 +17,10 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="4.1.14" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="NUnit" Version="4.3.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
<PackageReference Include="ApiSurface" Version="4.1.20" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0"/>
<PackageReference Include="NUnit" Version="4.3.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
</ItemGroup>
<ItemGroup>

View File

@@ -79,11 +79,8 @@ module TestArgParser =
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"""
"""Errors during parse!
Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `--rest=` syntax, or place them after a trailing `--`. --non-existent"""
[<Test>]
let ``Can supply positional args with key`` () =
@@ -318,8 +315,7 @@ Required argument '--baz' received no value"""
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"""
Input string was not in a correct format. (at arg --invariant-exact=23:59)"""
let exc =
Assert.Throws<exn> (fun () ->
@@ -337,8 +333,7 @@ Required argument '--invariant-exact' received no value"""
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"""
Input string was not in a correct format. (at arg --exact=11:34)"""
count.Value |> shouldEqual 0
@@ -444,7 +439,7 @@ Required argument '--exact' received no value"""
]
|> List.map TestCaseData
[<TestCaseSource(nameof (boolCases))>]
[<TestCaseSource(nameof boolCases)>]
let ``Bool env vars can be populated`` (envValue : string, boolValue : bool) =
let getEnvVar (s : string) =
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
@@ -604,7 +599,10 @@ Required argument '--exact' received no value"""
)
exc.Message
|> shouldEqual """Unable to process argument --do-the-thing=foo as key --do-the-thing and value foo"""
|> shouldEqual
"""Errors during parse!
Required argument '--do-something-else' received no value
Required argument '--turn-it-on' received no value"""
[<Test>]
let ``Long-form args help text`` () =
@@ -692,7 +690,9 @@ Required argument '--exact' received no value"""
)
exc.Message
|> shouldEqual """Unable to process argument --b=false as key --b and value false"""
|> shouldEqual
"""Errors during parse!
Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `--dont-grab-everything=` syntax, or place them after a trailing `--`. --b=false --c"""
let exc =
Assert.Throws<exn> (fun () ->
@@ -703,4 +703,6 @@ Required argument '--exact' received no value"""
// 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"""
|> shouldEqual
"""Errors during parse!
Unmatched args which look like they are meant to be flags. If you intended them as positional args, explicitly pass them with the `--my-arg-name=` syntax, or place them after a trailing `--`. --c=hi"""

View File

@@ -0,0 +1,21 @@
namespace WoofWare.Myriad.Plugins.Test
open FsUnitTyped
open NUnit.Framework
open ConsumePlugin.ArgsWithUnions
[<TestFixture>]
module TestArgsWithUnions =
let argsWithUnionsCases =
[
["--token" ; "hello" ; "--foo" ; "3" ; "--bar=hi" ; "--baz"], { Auth = AuthOptions.Token { Token = "hello" } ; Basics = { Foo = 3 ; Bar = "hi" ; Baz = true ; Rest = [] } }
]
|> List.map TestCaseData
[<TestCaseSource (nameof argsWithUnionsCases)>]
let ``foo`` (args : string list, expected : DoTheThing) : unit =
args
|> DoTheThing.parse' (fun _ -> failwith "didn't expect env var")
|> shouldEqual expected

View File

@@ -3,7 +3,7 @@ namespace WoofWare.Myriad.Plugins.Test
open System
open System.Collections.Generic
open System.Text.Json.Nodes
open FsCheck.Random
open FsCheck.FSharp
open Microsoft.FSharp.Reflection
open NUnit.Framework
open FsCheck
@@ -15,21 +15,21 @@ module TestJsonSerde =
let uriGen : Gen<Uri> =
gen {
let! suffix = Arb.generate<int>
let! suffix = ArbMap.generate<int> ArbMap.defaults
return Uri $"https://example.com/%i{suffix}"
}
let rec innerGen (count : int) : Gen<InnerTypeWithBoth> =
gen {
let! guid = Arb.generate<Guid>
let! mapKeys = Gen.listOf Arb.generate<NonNull<string>>
let! guid = ArbMap.generate<Guid> ArbMap.defaults
let! mapKeys = Gen.listOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
let mapKeys = mapKeys |> List.map _.Get |> List.distinct
let! mapValues = Gen.listOfLength mapKeys.Length uriGen
let map = List.zip mapKeys mapValues |> Map.ofList
let! concreteDictKeys =
if count > 0 then
Gen.listOf Arb.generate<NonNull<string>>
Gen.listOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
else
Gen.constant []
@@ -50,13 +50,16 @@ module TestJsonSerde =
|> List.map KeyValuePair
|> Dictionary
let! readOnlyDictKeys = Gen.listOf Arb.generate<NonNull<string>>
let! readOnlyDictKeys = Gen.listOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
let readOnlyDictKeys = readOnlyDictKeys |> List.map _.Get |> List.distinct
let! readOnlyDictValues = Gen.listOfLength readOnlyDictKeys.Length (Gen.listOf Arb.generate<char>)
let! readOnlyDictValues =
Gen.listOfLength readOnlyDictKeys.Length (Gen.listOf (ArbMap.generate<char> ArbMap.defaults))
let readOnlyDict = List.zip readOnlyDictKeys readOnlyDictValues |> readOnlyDict
let! dictKeys = Gen.listOf uriGen
let! dictValues = Gen.listOfLength dictKeys.Length Arb.generate<bool>
let! dictValues = Gen.listOfLength dictKeys.Length (ArbMap.generate<bool> ArbMap.defaults)
let dict = List.zip dictKeys dictValues |> dict
return
@@ -71,28 +74,38 @@ module TestJsonSerde =
let outerGen : Gen<JsonRecordTypeWithBoth> =
gen {
let! a = Arb.generate<int>
let! b = Arb.generate<NonNull<string>>
let! c = Gen.listOf Arb.generate<int>
let! a = ArbMap.generate<int> ArbMap.defaults
let! b = ArbMap.generate<NonNull<string>> ArbMap.defaults
let! c = Gen.listOf (ArbMap.generate<int> ArbMap.defaults)
let! depth = Gen.choose (0, 2)
let! d = innerGen depth
let! e = Gen.arrayOf Arb.generate<NonNull<string>>
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! e = Gen.arrayOf (ArbMap.generate<NonNull<string>> ArbMap.defaults)
let! arr = Gen.arrayOf (ArbMap.generate<int> ArbMap.defaults)
let! byte = ArbMap.generate ArbMap.defaults
let! sbyte = ArbMap.generate ArbMap.defaults
let! i = ArbMap.generate ArbMap.defaults
let! i32 = ArbMap.generate ArbMap.defaults
let! i64 = ArbMap.generate ArbMap.defaults
let! u = ArbMap.generate ArbMap.defaults
let! u32 = ArbMap.generate ArbMap.defaults
let! u64 = ArbMap.generate ArbMap.defaults
let! f =
ArbMap.generate ArbMap.defaults
|> Gen.filter (fun s -> Double.IsFinite (s / 1.0<measure>))
let! f32 =
ArbMap.generate ArbMap.defaults
|> Gen.filter (fun s -> Single.IsFinite (s / 1.0f<measure>))
let! single =
ArbMap.generate ArbMap.defaults
|> Gen.filter (fun s -> Single.IsFinite (s / 1.0f<measure>))
let! intMeasureOption = ArbMap.generate ArbMap.defaults
let! intMeasureNullable = ArbMap.generate ArbMap.defaults
let! someEnum = Gen.choose (0, 1)
let! timestamp = Arb.generate
let! timestamp = ArbMap.generate ArbMap.defaults
return
{
@@ -270,10 +283,10 @@ module TestJsonSerde =
match case with
| 0 -> return FirstDu.EmptyCase
| 1 ->
let! s = Arb.generate<NonNull<string>>
let! s = ArbMap.generate<NonNull<string>> ArbMap.defaults
return FirstDu.Case1 s.Get
| 2 ->
let! i = Arb.generate<int>
let! i = ArbMap.generate<int> ArbMap.defaults
let! record = outerGen
return FirstDu.Case2 (record, i)
| _ -> return failwith $"unexpected: %i{case}"
@@ -293,7 +306,6 @@ module TestJsonSerde =
[<Test>]
let ``DU generator covers all cases`` () =
let rand = Random ()
let cases = FSharpType.GetUnionCases typeof<FirstDu>
let counts = Array.zeroCreate<int> cases.Length
@@ -301,11 +313,13 @@ module TestJsonSerde =
let mutable i = 0
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 property (du : FirstDu) =
let tag = decompose du
counts.[tag] <- counts.[tag] + 1
i <- i + 1
true
Check.One (Config.Quick, Prop.forAll (Arb.fromGen duGen) property)
for i in counts do
i |> shouldBeGreaterThan 0

View File

@@ -34,3 +34,16 @@ module TestMockGenerator =
mock.Mem1 3 'a' |> shouldEqual "aaa"
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
[<Test>]
let ``Example of use: properties`` () =
let mock : TypeWithProperties =
{ TypeWithPropertiesMock.Empty with
Mem1 = fun i -> async { return Option.toArray i }
Prop1 = fun () -> 44
}
:> _
mock.Mem1 (Some "hi") |> Async.RunSynchronously |> shouldEqual [| "hi" |]
mock.Prop1 |> shouldEqual 44

View File

@@ -9,9 +9,11 @@
I have not yet seen a single instance where I care about this warning
-->
<NoWarn>$(NoWarn),NU1903</NoWarn>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
</PropertyGroup>
<ItemGroup>
<!--
<Compile Include="HttpClient.fs"/>
<Compile Include="PureGymDtos.fs"/>
<Compile Include="TestJsonParse\TestJsonParse.fs" />
@@ -33,20 +35,24 @@
<Compile Include="TestCataGenerator\TestGift.fs" />
<Compile Include="TestCataGenerator\TestMyList.fs" />
<Compile Include="TestCataGenerator\TestMyList2.fs" />
-->
<Compile Include="TestArgParser\TestArgParser.fs" />
<Compile Include="TestArgParser\TestArgsWithUnions.fs" />
<!--
<Compile Include="TestSwagger\TestSwaggerParse.fs" />
<Compile Include="TestRemoveOptions.fs"/>
<Compile Include="TestSurface.fs"/>
-->
<None Include="../.github/workflows/dotnet.yaml" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="ApiSurface" Version="4.1.14"/>
<PackageReference Include="FsCheck" Version="2.16.6"/>
<PackageReference Include="FsUnit" Version="6.0.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0"/>
<PackageReference Include="NUnit" Version="4.3.0"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
<PackageReference Include="ApiSurface" Version="4.1.20"/>
<PackageReference Include="FsCheck" Version="3.2.0"/>
<PackageReference Include="FsUnit" Version="7.0.1"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.13.0"/>
<PackageReference Include="NUnit" Version="4.3.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="5.0.0"/>
</ItemGroup>
<ItemGroup>

View File

@@ -7,82 +7,6 @@ open Fantomas.FCS.Text.Range
open TypeEquality
open WoofWare.Whippet.Fantomas
type internal ArgParserOutputSpec =
{
ExtensionMethods : bool
}
type internal FlagDu =
{
Name : Ident
Case1Name : Ident
Case2Name : Ident
/// Hopefully this is simply the const bool True or False, but it might e.g. be a literal
Case1Arg : SynExpr
/// Hopefully this is simply the const bool True or False, but it might e.g. be a literal
Case2Arg : SynExpr
}
static member FromBoolean (flagDu : FlagDu) (value : SynExpr) =
SynExpr.ifThenElse
(SynExpr.equals value flagDu.Case1Arg)
(SynExpr.createLongIdent' [ flagDu.Name ; flagDu.Case2Name ])
(SynExpr.createLongIdent' [ flagDu.Name ; flagDu.Case1Name ])
/// The default value of an argument which admits default values can be pulled from different sources.
/// This defines which source a particular default value comes from.
type private ArgumentDefaultSpec =
/// From parsing the environment variable with the given name (e.g. "WOOFWARE_DISABLE_FOO" or whatever).
| EnvironmentVariable of name : SynExpr
/// From calling the static member `{typeWeParseInto}.Default{name}()`
/// For example, if `type MyArgs = { Thing : Choice<int, int> }`, then
/// we would use `MyArgs.DefaultThing () : int`.
///
| FunctionCall of name : Ident
type private Accumulation<'choice> =
| Required
| Optional
| Choice of 'choice
| List of Accumulation<'choice>
type private ParseFunction<'acc> =
{
FieldName : Ident
TargetVariable : Ident
/// Any of the forms in this set are acceptable, but make sure they all start with a dash, or we might
/// get confused with positional args or something! I haven't thought that hard about this.
/// In the default case, this is `Const("arg-name")` for the `ArgName : blah` field; note that we have
/// omitted the initial `--` that will be required at runtime.
ArgForm : SynExpr list
/// If this is a boolean-like field (e.g. a bool or a flag DU), the help text should look a bit different:
/// we should lie to the user about the value of the cases there.
/// Similarly, if we're reading from an environment variable with the laxer parsing rules of accepting e.g.
/// "0" instead of "false", we need to know if we're reading a bool.
/// In that case, `boolCases` is Some, and contains the construction of the flag (or boolean, in which case
/// you get no data).
BoolCases : Choice<FlagDu, unit> option
Help : SynExpr option
/// A function string -> %TargetType%, where TargetVariable is probably a `%TargetType% option`.
/// (Depending on `Accumulation`, we'll remove the `option` at the end of the parse, asserting that the
/// argument was supplied.)
/// This is allowed to throw if it fails to parse.
Parser : SynExpr
/// If `Accumulation` is `List`, then this is the type of the list *element*; analogously for optionals
/// and choices and so on.
TargetType : SynType
Accumulation : 'acc
}
/// A SynExpr of type `string` which we can display to the user at generated-program runtime to display all
/// the ways they can refer to this arg.
member arg.HumanReadableArgForm : SynExpr =
let formatString = List.replicate arg.ArgForm.Length "--%s" |> String.concat " / "
(SynExpr.applyFunction (SynExpr.createIdent "sprintf") (SynExpr.CreateConst formatString), arg.ArgForm)
||> List.fold SynExpr.applyFunction
|> SynExpr.paren
[<RequireQualifiedAccess>]
type private ChoicePositional =
| Normal of includeFlagLike : SynExpr option
@@ -114,14 +38,14 @@ type private ParseTree<'hasPositional> =
/// `assemble` takes the SynExpr's (e.g. each record field contents) corresponding to each `Ident` in
/// the branch (e.g. each record field name),
/// and composes them into a `SynExpr` (e.g. the record-typed object).
| Branch of
| DescendRecord of
fields : (Ident * ParseTree<HasNoPositional>) list *
assemble : (Map<string, SynExpr> -> SynExpr) *
Teq<'hasPositional, HasNoPositional>
/// `assemble` takes the SynExpr's (e.g. each record field contents) corresponding to each `Ident` in
/// the branch (e.g. each record field name),
/// and composes them into a `SynExpr` (e.g. the record-typed object).
| BranchPos of
| DescendRecordPos of
posField : Ident *
fields : ParseTree<HasPositional> *
(Ident * ParseTree<HasNoPositional>) list *
@@ -184,63 +108,6 @@ module private ParseTree =
go None ([], None) subs
let rec accumulatorsNonPos (tree : ParseTree<HasNoPositional>) : ParseFunctionNonPositional list =
match tree with
| ParseTree.PositionalLeaf (_, teq) -> exFalso teq
| ParseTree.BranchPos (_, _, _, _, teq) -> exFalso teq
| ParseTree.NonPositionalLeaf (pf, _) -> [ pf ]
| ParseTree.Branch (trees, _, _) -> trees |> List.collect (snd >> accumulatorsNonPos)
/// Returns the positional arg separately.
let rec accumulatorsPos
(tree : ParseTree<HasPositional>)
: ParseFunctionNonPositional list * ParseFunctionPositional
=
match tree with
| ParseTree.PositionalLeaf (pf, _) -> [], pf
| ParseTree.NonPositionalLeaf (_, teq) -> exFalso' teq
| ParseTree.Branch (_, _, teq) -> exFalso' teq
| ParseTree.BranchPos (_, tree, trees, _, _) ->
let nonPos = trees |> List.collect (snd >> accumulatorsNonPos)
let nonPos2, pos = accumulatorsPos tree
nonPos @ nonPos2, pos
/// Collect all the ParseFunctions which are necessary to define variables, throwing away
/// all information relevant to composing the resulting variables into records.
/// Returns the list of non-positional parsers, and any positional parser that exists.
let accumulators<'a> (tree : ParseTree<'a>) : ParseFunctionNonPositional list * ParseFunctionPositional option =
// Sad duplication of some code here, but it was the easiest way to make it type-safe :(
match tree with
| ParseTree.PositionalLeaf (pf, _) -> [], Some pf
| ParseTree.NonPositionalLeaf (pf, _) -> [ pf ], None
| ParseTree.Branch (trees, _, _) -> trees |> List.collect (snd >> accumulatorsNonPos) |> (fun i -> i, None)
| ParseTree.BranchPos (_, tree, trees, _, _) ->
let nonPos = trees |> List.collect (snd >> accumulatorsNonPos)
let nonPos2, pos = accumulatorsPos tree
nonPos @ nonPos2, Some pos
|> fun (nonPos, pos) ->
let duplicateArgs =
// This is best-effort. We can't necessarily detect all SynExprs here, but usually it'll be strings.
Option.toList (pos |> Option.map _.ArgForm) @ (nonPos |> List.map _.ArgForm)
|> Seq.concat
|> Seq.choose (fun expr ->
match expr |> SynExpr.stripOptionalParen with
| SynExpr.Const (SynConst.String (s, _, _), _) -> Some s
| _ -> None
)
|> List.ofSeq
|> List.groupBy id
|> List.choose (fun (key, v) -> if v.Length > 1 then Some key else None)
match duplicateArgs with
| [] -> nonPos, pos
| dups ->
let dups = dups |> String.concat " "
failwith $"Duplicate args detected! %s{dups}"
/// Build the return value.
let rec instantiate<'a> (tree : ParseTree<'a>) : SynExpr =
match tree with
@@ -673,7 +540,7 @@ module internal ArgParserGenerator =
args
|> Map.toList
|> List.map (fun (ident, expr) -> SynLongIdent.create [ Ident.create ident ], expr)
|> AstHelper.instantiateRecord
|> SynExpr.createRecord None
)
tree, counter

View File

@@ -36,13 +36,6 @@ module internal AstHelper =
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Enum _, _) -> true
| _ -> false
let instantiateRecord (fields : (SynLongIdent * SynExpr) list) : SynExpr =
let fields =
fields
|> List.map (fun (rfn, synExpr) -> SynExprRecordField ((rfn, true), Some range0, Some synExpr, None))
SynExpr.Record (None, None, fields, range0)
let defineRecordType (record : RecordType) : SynTypeDefn =
let name =
SynComponentInfo.create record.Name

View File

@@ -89,7 +89,7 @@ module internal InterfaceMockGenerator =
[]
else
[ SynPat.unit ])
(AstHelper.instantiateRecord constructorFields)
(SynExpr.createRecord None constructorFields)
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every method throws.")
|> SynBinding.withReturnAnnotation constructorReturnType
|> SynMemberDefn.staticMember
@@ -159,6 +159,15 @@ module internal InterfaceMockGenerator =
|> SynMemberDefn.memberImplementation
)
let properties =
interfaceType.Properties
|> List.map (fun pi ->
SynExpr.createLongIdent' [ Ident.create "this" ; pi.Identifier ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic [ Ident.create "this" ; pi.Identifier ] []
|> SynMemberDefn.memberImplementation
)
let interfaceName =
let baseName = SynType.createLongIdent interfaceType.Name
@@ -174,7 +183,7 @@ module internal InterfaceMockGenerator =
SynType.app' baseName generics
SynMemberDefn.Interface (interfaceName, Some range0, Some members, range0)
SynMemberDefn.Interface (interfaceName, Some range0, Some (members @ properties), range0)
let access =
match interfaceType.Accessibility, spec.IsInternal with
@@ -248,6 +257,15 @@ module internal InterfaceMockGenerator =
|> SynField.make
|> SynField.withDocString (mem.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)
let constructProperty (prop : PropertyInfo) : SynField =
{
Attrs = []
Ident = Some prop.Identifier
Type = SynType.toFun [ SynType.unit ] prop.Type
}
|> SynField.make
|> SynField.withDocString (prop.XmlDoc |> Option.defaultValue PreXmlDoc.Empty)
let createRecord
(namespaceId : LongIdent)
(opens : SynOpenDeclTarget list)
@@ -255,7 +273,12 @@ module internal InterfaceMockGenerator =
: SynModuleOrNamespace
=
let interfaceType = AstHelper.parseInterface interfaceType
let fields = interfaceType.Members |> List.map constructMember
let fields =
interfaceType.Members
|> List.map constructMember
|> List.append (interfaceType.Properties |> List.map constructProperty)
let docString = PreXmlDoc.create "Mock record type for an interface"
let name =

View File

@@ -351,10 +351,7 @@ module internal JsonParseGenerator =
let getParseOptions (fieldAttrs : SynAttribute list) =
(JsonParseOption.None, fieldAttrs)
||> List.fold (fun options attr ->
if
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonNumberHandling", StringComparison.Ordinal)
then
if (SynLongIdent.toString attr.TypeName).EndsWith ("JsonNumberHandling", StringComparison.Ordinal) then
let qualifiedEnumValue =
match SynExpr.stripOptionalParen attr.ArgExpr with
| SynExpr.LongIdent (_, SynLongIdent (ident, _, _), _, _) when isJsonNumberHandling ident ->
@@ -384,15 +381,13 @@ module internal JsonParseGenerator =
let propertyNameAttr =
fieldData.Attrs
|> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
(SynLongIdent.toString attr.TypeName).EndsWith ("JsonPropertyName", StringComparison.Ordinal)
)
let extensionDataAttr =
fieldData.Attrs
|> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonExtensionData", StringComparison.Ordinal)
(SynLongIdent.toString attr.TypeName).EndsWith ("JsonExtensionData", StringComparison.Ordinal)
)
let propertyName =
@@ -485,7 +480,7 @@ module internal JsonParseGenerator =
let finalConstruction =
fields
|> List.mapi (fun i fieldData -> SynLongIdent.createI fieldData.Ident, SynExpr.createIdent $"arg_%i{i}")
|> AstHelper.instantiateRecord
|> SynExpr.createRecord None
(finalConstruction, assignments)
||> List.fold (fun final assignment -> SynExpr.createLet [ assignment ] final)

View File

@@ -180,8 +180,7 @@ module internal JsonSerializeGenerator =
let propertyNameAttr =
attrs
|> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonPropertyName", StringComparison.Ordinal)
(SynLongIdent.toString attr.TypeName).EndsWith ("JsonPropertyName", StringComparison.Ordinal)
)
match propertyNameAttr with
@@ -198,8 +197,7 @@ module internal JsonSerializeGenerator =
let getIsJsonExtension (attrs : SynAttribute list) : bool =
attrs
|> List.tryFind (fun attr ->
(SynLongIdent.toString attr.TypeName)
.EndsWith ("JsonExtensionData", StringComparison.Ordinal)
(SynLongIdent.toString attr.TypeName).EndsWith ("JsonExtensionData", StringComparison.Ordinal)
)
|> Option.isSome

View File

@@ -21,3 +21,13 @@ module private List =
| Some head :: tail -> go (head :: acc) tail
go [] l
/// Return the first error encountered, or the entire list.
let allOkOrError<'ok, 'err> (l : Result<'ok, 'err> list) : Result<'ok list, 'err> =
let rec go acc l =
match l with
| [] -> Ok (List.rev acc)
| Error e :: _ -> Error e
| Ok o :: rest -> go (o :: acc) rest
go [] l

View File

@@ -97,7 +97,7 @@ module internal RemoveOptionsGenerator =
SynLongIdent.createI fieldData.Ident, body
)
|> AstHelper.instantiateRecord
|> SynExpr.createRecord None
SynBinding.basic
[ functionName ]

File diff suppressed because it is too large Load Diff

View File

@@ -22,7 +22,7 @@
<ItemGroup>
<PackageReference Include="Myriad.Core" Version="0.8.3" />
<PackageReference Include="TypeEquality" Version="0.3.0" />
<PackageReference Include="WoofWare.Whippet.Fantomas" Version="0.3.1" />
<PackageReference Include="WoofWare.Whippet.Fantomas" Version="0.5.1" />
<!-- the lowest version allowed by Myriad.Core -->
<PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/>
</ItemGroup>
@@ -40,7 +40,8 @@
<Compile Include="JsonParseGenerator.fs"/>
<Compile Include="HttpClientGenerator.fs"/>
<Compile Include="CataGenerator.fs" />
<Compile Include="ArgParserGenerator.fs" />
<Compile Include="ShibaGenerator.fs" />
<None Include="ArgParserGenerator.fs" />
<Compile Include="Swagger.fs" />
<Compile Include="SwaggerClientGenerator.fs" />
<None Include="ApacheLicence.txt" />

View File

@@ -10,6 +10,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Att
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Attributes.Test", "WoofWare.Myriad.Plugins.Attributes\Test\WoofWare.Myriad.Plugins.Attributes.Test.fsproj", "{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "Playground", "Playground\Playground.fsproj", "{6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -36,5 +38,9 @@ Global
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.Build.0 = Release|Any CPU
{6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DF8C756-DE59-4AFF-A4BB-2D05C74192A4}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View File

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

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1734126203,
"narHash": "sha256-0XovF7BYP50rTD2v4r55tR5MuBLet7q4xIz6Rgh3BBU=",
"lastModified": 1744502386,
"narHash": "sha256-QAd1L37eU7ktL2WeLLLTmI6P9moz9+a/ONO8qNBYJgM=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "71a6392e367b08525ee710a93af2e80083b5b3e2",
"rev": "f6db44a8daa59c40ae41ba6e5823ec77fe0d2124",
"type": "github"
},
"original": {

View File

@@ -44,9 +44,11 @@
'';
};
in {
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;}))).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;}))).hash;
packages = let
deps = builtins.fromJSON (builtins.readFile ./nix/deps.json);
in {
fantomas = dotnetTool null "fantomas" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fantomas.version (builtins.head (builtins.filter (elem: elem.pname == "fantomas") deps)).hash;
fsharp-analyzers = dotnetTool "FSharp.Analyzers.Cli" "fsharp-analyzers" (builtins.fromJSON (builtins.readFile ./.config/dotnet-tools.json)).tools.fsharp-analyzers.version (builtins.head (builtins.filter (elem: elem.pname == "fsharp-analyzers") deps)).hash;
default = pkgs.buildDotnetModule {
inherit pname version dotnet-sdk dotnet-runtime;
name = "WoofWare.Myriad.Plugins";
@@ -54,7 +56,7 @@
projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj";
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 nix/deps.nix`
nugetDeps = ./nix/deps.json; # `nix build .#default.fetch-deps && ./result nix/deps.json`
doCheck = true;
};
};

387
nix/deps.json Normal file
View File

@@ -0,0 +1,387 @@
[
{
"pname": "ApiSurface",
"version": "4.1.20",
"hash": "sha256-koWgO9FC9ax+Ij56ug8kxeyknl0yhLqnNLOUdxtqqo4="
},
{
"pname": "fantomas",
"version": "7.0.1",
"hash": "sha256-2aGD6Kjh83gmssRqqZ/Uihi7VbNqNUelX4otIfCuhTI="
},
{
"pname": "Fantomas.Core",
"version": "6.1.1",
"hash": "sha256-FcTLHQFvKkQY/kV08jhhy/St/+FmXpp3epp/R3zUXMA="
},
{
"pname": "Fantomas.FCS",
"version": "6.1.1",
"hash": "sha256-NuZ8msPEHYA8T3EYREB28F1RcNgUU8V54eg2+UttYxw="
},
{
"pname": "FsCheck",
"version": "3.2.0",
"hash": "sha256-ksZ4vLgWwyQOzFuK2BczdtDtWWYmedG7UBAg4pYuI8g="
},
{
"pname": "fsharp-analyzers",
"version": "0.30.0",
"hash": "sha256-7oaSwpHAU1opzpz4szLU/gDaJC/ww9eiFkPu0nr4Mj4="
},
{
"pname": "FSharp.Core",
"version": "4.3.4",
"hash": "sha256-styyo+6mJy+yxE0NZG/b1hxkAjPOnJfMgd9zWzCJ5uk="
},
{
"pname": "FSharp.Core",
"version": "6.0.1",
"hash": "sha256-Ehsgt3nCJijpaVuJguC1TPVEKSkJd6PSc07D2ZQSemI="
},
{
"pname": "FSharp.Core",
"version": "9.0.202",
"hash": "sha256-64Gub0qemmCoMa1tDus6TeTuB1+5sHfE6KD2j4o84mA="
},
{
"pname": "FsUnit",
"version": "7.0.1",
"hash": "sha256-K85CIdxMeFSHEKZk6heIXp/oFjWAn7dBILKrw49pJUY="
},
{
"pname": "Microsoft.ApplicationInsights",
"version": "2.22.0",
"hash": "sha256-mUQ63atpT00r49ca50uZu2YCiLg3yd6r3HzTryqcuEA="
},
{
"pname": "Microsoft.AspNetCore.App.Ref",
"version": "6.0.36",
"hash": "sha256-9jDkWbjw/nd8yqdzVTagCuqr6owJ/DUMi4BlUZT4hWU="
},
{
"pname": "Microsoft.AspNetCore.App.Runtime.linux-arm64",
"version": "6.0.36",
"hash": "sha256-JQULJyF0ivLoUU1JaFfK/HHg+/qzpN7V2RR2Cc+WlQ4="
},
{
"pname": "Microsoft.AspNetCore.App.Runtime.linux-x64",
"version": "6.0.36",
"hash": "sha256-zUsVIpV481vMLAXaLEEUpEMA9/f1HGOnvaQnaWdzlyY="
},
{
"pname": "Microsoft.AspNetCore.App.Runtime.osx-arm64",
"version": "6.0.36",
"hash": "sha256-2seqZcz0JeUjkzh3QcGa9TcJ4LUafpFjTRk+Nm8T6T0="
},
{
"pname": "Microsoft.AspNetCore.App.Runtime.osx-x64",
"version": "6.0.36",
"hash": "sha256-yxLafxiBKkvfkDggPk0P9YZIHBkDJOsFTO7/V9mEHuU="
},
{
"pname": "Microsoft.CodeCoverage",
"version": "17.13.0",
"hash": "sha256-GKrIxeyQo5Az1mztfQgea1kGtJwonnNOrXK/0ULfu8o="
},
{
"pname": "Microsoft.NET.Test.Sdk",
"version": "17.13.0",
"hash": "sha256-sc2wvyV8cGm1FrNP2GGHEI584RCvRPu15erYCsgw5QY="
},
{
"pname": "Microsoft.NETCore.App.Host.linux-arm64",
"version": "6.0.36",
"hash": "sha256-9lC/LYnthYhjkWWz2kkFCvlA5LJOv11jdt59SDnpdy0="
},
{
"pname": "Microsoft.NETCore.App.Host.linux-x64",
"version": "6.0.36",
"hash": "sha256-VFRDzx7LJuvI5yzKdGmw/31NYVbwHWPKQvueQt5xc10="
},
{
"pname": "Microsoft.NETCore.App.Host.osx-arm64",
"version": "6.0.36",
"hash": "sha256-DaSWwYACJGolEBuMhzDVCj/rQTdDt061xCVi+gyQnuo="
},
{
"pname": "Microsoft.NETCore.App.Host.osx-x64",
"version": "6.0.36",
"hash": "sha256-FrRny9EI6HKCKQbu6mcLj5w4ooSRrODD4Vj2ZMGnMd4="
},
{
"pname": "Microsoft.NETCore.App.Ref",
"version": "6.0.36",
"hash": "sha256-9LZgVoIFF8qNyUu8kdJrYGLutMF/cL2K82HN2ywwlx8="
},
{
"pname": "Microsoft.NETCore.App.Runtime.linux-arm64",
"version": "6.0.36",
"hash": "sha256-k3rxvUhCEU0pVH8KgEMtkPiSOibn+nBh+0zT2xIfId8="
},
{
"pname": "Microsoft.NETCore.App.Runtime.linux-x64",
"version": "6.0.36",
"hash": "sha256-U8wJ2snSDFqeAgDVLXjnniidC7Cr5aJ1/h/BMSlyu0c="
},
{
"pname": "Microsoft.NETCore.App.Runtime.osx-arm64",
"version": "6.0.36",
"hash": "sha256-UfLcrL2Gj/OLz0s92Oo+OCJeDpZFAcQLPLiSNND8D5Y="
},
{
"pname": "Microsoft.NETCore.App.Runtime.osx-x64",
"version": "6.0.36",
"hash": "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs="
},
{
"pname": "Microsoft.NETCore.Platforms",
"version": "1.1.0",
"hash": "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM="
},
{
"pname": "Microsoft.NETCore.Platforms",
"version": "1.1.1",
"hash": "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg="
},
{
"pname": "Microsoft.NETCore.Platforms",
"version": "2.0.0",
"hash": "sha256-IEvBk6wUXSdyCnkj6tHahOJv290tVVT8tyemYcR0Yro="
},
{
"pname": "Microsoft.NETCore.Targets",
"version": "1.1.0",
"hash": "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ="
},
{
"pname": "Microsoft.NETCore.Targets",
"version": "1.1.3",
"hash": "sha256-WLsf1NuUfRWyr7C7Rl9jiua9jximnVvzy6nk2D2bVRc="
},
{
"pname": "Microsoft.Testing.Extensions.Telemetry",
"version": "1.5.3",
"hash": "sha256-bIXwPSa3jkr2b6xINOqMUs6/uj/r4oVFM7xq3uVIZDU="
},
{
"pname": "Microsoft.Testing.Extensions.TrxReport.Abstractions",
"version": "1.5.3",
"hash": "sha256-IfMRfcyaIKEMRtx326ICKtinDBEfGw/Sv8ZHawJ96Yc="
},
{
"pname": "Microsoft.Testing.Extensions.VSTestBridge",
"version": "1.5.3",
"hash": "sha256-XpM/yFjhLSsuzyDV+xKubs4V1zVVYiV05E0+N4S1h0g="
},
{
"pname": "Microsoft.Testing.Platform",
"version": "1.5.3",
"hash": "sha256-y61Iih6w5D79dmrj2V675mcaeIiHoj1HSa1FRit2BLM="
},
{
"pname": "Microsoft.Testing.Platform.MSBuild",
"version": "1.5.3",
"hash": "sha256-YspvjE5Jfi587TAfsvfDVJXNrFOkx1B3y1CKV6m7YLY="
},
{
"pname": "Microsoft.TestPlatform.ObjectModel",
"version": "17.12.0",
"hash": "sha256-3XBHBSuCxggAIlHXmKNQNlPqMqwFlM952Av6RrLw1/w="
},
{
"pname": "Microsoft.TestPlatform.ObjectModel",
"version": "17.13.0",
"hash": "sha256-6S0fjfj8vA+h6dJVNwLi6oZhYDO/I/6hBZaq2VTW+Uk="
},
{
"pname": "Microsoft.TestPlatform.TestHost",
"version": "17.13.0",
"hash": "sha256-L/CJzou7dhmShUgXq3aXL3CaLTJll17Q+JY2DBdUUpo="
},
{
"pname": "Myriad.Core",
"version": "0.8.3",
"hash": "sha256-vBOxfq8QriX/yUtaXN69rEQaY/psRNJWxqATLidrt2g="
},
{
"pname": "Myriad.Sdk",
"version": "0.8.3",
"hash": "sha256-7O397WKhskKOvE3MkJT37BvxorDWngDR6gTUogtDZ2M="
},
{
"pname": "Nerdbank.GitVersioning",
"version": "3.8.38-alpha",
"hash": "sha256-gPMrVbjOZxXoofczF/pn6eVkLhjVSJIyQrLO2oljrDc="
},
{
"pname": "NETStandard.Library",
"version": "2.0.3",
"hash": "sha256-Prh2RPebz/s8AzHb2sPHg3Jl8s31inv9k+Qxd293ybo="
},
{
"pname": "Newtonsoft.Json",
"version": "13.0.1",
"hash": "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo="
},
{
"pname": "Newtonsoft.Json",
"version": "13.0.3",
"hash": "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc="
},
{
"pname": "NuGet.Common",
"version": "6.13.2",
"hash": "sha256-ASLa/Jigg5Eop0ZrXPl98RW2rxnJRC7pbbxhuV74hFw="
},
{
"pname": "NuGet.Configuration",
"version": "6.13.2",
"hash": "sha256-z8VW1YdRDanyyRTDYRvRkSv/XPR3c/hMM1y8cNNjx0Y="
},
{
"pname": "NuGet.Frameworks",
"version": "6.13.2",
"hash": "sha256-caDyc+WgYOo43AUTjtbP0MyvYDb6JweEKDdIul61Cac="
},
{
"pname": "NuGet.Packaging",
"version": "6.13.2",
"hash": "sha256-lhO+SFwIYZ4aPHxIGm5ubkkE2a5Ve2xgtroRbNh7hpw="
},
{
"pname": "NuGet.Protocol",
"version": "6.13.2",
"hash": "sha256-5lnAHHZjy7A4vgv65AeBAs64mSNpuoUjxW3HnrMpuzY="
},
{
"pname": "NuGet.Versioning",
"version": "6.13.2",
"hash": "sha256-gmpyBpKnt+GHqgx/2uFKp+J2csbxEAy1E7WdVT117sw="
},
{
"pname": "NUnit",
"version": "4.3.2",
"hash": "sha256-0RWe8uFoxYp6qhPlDDEghOMcKJgyw2ybvEoAqBLebeE="
},
{
"pname": "NUnit3TestAdapter",
"version": "5.0.0",
"hash": "sha256-7jZM4qAbIzne3AcdFfMbvbgogqpxvVe6q2S7Ls8xQy0="
},
{
"pname": "RestEase",
"version": "1.6.4",
"hash": "sha256-FFmqFwlHhIln46k56Z8KM1G+xuPEh/bceKCQnJcdcdc="
},
{
"pname": "runtime.any.System.Runtime",
"version": "4.3.0",
"hash": "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM="
},
{
"pname": "runtime.native.System",
"version": "4.3.0",
"hash": "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y="
},
{
"pname": "runtime.unix.System.Private.Uri",
"version": "4.3.0",
"hash": "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs="
},
{
"pname": "System.Collections.Immutable",
"version": "8.0.0",
"hash": "sha256-F7OVjKNwpqbUh8lTidbqJWYi476nsq9n+6k0+QVRo3w="
},
{
"pname": "System.Diagnostics.DiagnosticSource",
"version": "5.0.0",
"hash": "sha256-6mW3N6FvcdNH/pB58pl+pFSCGWgyaP4hfVtC/SMWDV4="
},
{
"pname": "System.Diagnostics.DiagnosticSource",
"version": "7.0.0",
"hash": "sha256-9Wk8cHSkjKtqkN6xW7KnXoQVtF/VNbKeBq79WqDesMs="
},
{
"pname": "System.Formats.Asn1",
"version": "6.0.0",
"hash": "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8="
},
{
"pname": "System.Formats.Asn1",
"version": "8.0.1",
"hash": "sha256-may/Wg+esmm1N14kQTG4ESMBi+GQKPp0ZrrBo/o6OXM="
},
{
"pname": "System.IO.Abstractions",
"version": "4.2.13",
"hash": "sha256-nkC/PiqE6+c1HJ2yTwg3x+qdBh844Z8n3ERWDW8k6Gg="
},
{
"pname": "System.IO.FileSystem.AccessControl",
"version": "4.5.0",
"hash": "sha256-ck44YBQ0M+2Im5dw0VjBgFD1s0XuY54cujrodjjSBL8="
},
{
"pname": "System.Memory",
"version": "4.5.5",
"hash": "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI="
},
{
"pname": "System.Private.Uri",
"version": "4.3.0",
"hash": "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM="
},
{
"pname": "System.Reflection.Metadata",
"version": "1.6.0",
"hash": "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E="
},
{
"pname": "System.Runtime",
"version": "4.3.1",
"hash": "sha256-R9T68AzS1PJJ7v6ARz9vo88pKL1dWqLOANg4pkQjkA0="
},
{
"pname": "System.Runtime.CompilerServices.Unsafe",
"version": "6.0.0",
"hash": "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I="
},
{
"pname": "System.Security.AccessControl",
"version": "4.5.0",
"hash": "sha256-AFsKPb/nTk2/mqH/PYpaoI8PLsiKKimaXf+7Mb5VfPM="
},
{
"pname": "System.Security.Cryptography.Pkcs",
"version": "6.0.4",
"hash": "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI="
},
{
"pname": "System.Security.Cryptography.ProtectedData",
"version": "4.4.0",
"hash": "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE="
},
{
"pname": "System.Security.Principal.Windows",
"version": "4.5.0",
"hash": "sha256-BkUYNguz0e4NJp1kkW7aJBn3dyH9STwB5N8XqnlCsmY="
},
{
"pname": "System.Text.Json",
"version": "8.0.5",
"hash": "sha256-yKxo54w5odWT6nPruUVsaX53oPRe+gKzGvLnnxtwP68="
},
{
"pname": "TypeEquality",
"version": "0.3.0",
"hash": "sha256-V50xAOzzyUJrY+MYPRxtnqW5MVeATXCes89wPprv1r4="
},
{
"pname": "WoofWare.Whippet.Fantomas",
"version": "0.5.1",
"hash": "sha256-59CwnOZQAq5ZJoUkd87OiP8KUwx8xYDLMimMMTlKeZA="
}
]

View File

@@ -1,369 +0,0 @@
# This file was automatically generated by passthru.fetch-deps.
# Please dont edit it manually, your changes might get overwritten!
{fetchNuGet}: [
(fetchNuGet {
pname = "ApiSurface";
version = "4.1.14";
hash = "sha256-Ly7XqGeqseQqu+EcBISzVySZvBQ28rj7JUke1qN7Yw8=";
})
(fetchNuGet {
pname = "fantomas";
version = "6.3.16";
hash = "sha256-4tRdYf+/Q1iedx+DDuIKVGlIWQdr6erM51VdKzZkhCs=";
})
(fetchNuGet {
pname = "Fantomas.Core";
version = "6.1.1";
hash = "sha256-FcTLHQFvKkQY/kV08jhhy/St/+FmXpp3epp/R3zUXMA=";
})
(fetchNuGet {
pname = "Fantomas.FCS";
version = "6.1.1";
hash = "sha256-NuZ8msPEHYA8T3EYREB28F1RcNgUU8V54eg2+UttYxw=";
})
(fetchNuGet {
pname = "FsCheck";
version = "2.16.6";
hash = "sha256-1hR2SaJTkqBzU3D955MvLNVzkQHkx0Z/QzOXZfzk2Zw=";
})
(fetchNuGet {
pname = "fsharp-analyzers";
version = "0.28.0";
hash = "sha256-BqGk9MzHHA3oRPNfWuANcM1YELsdhzWI+kLF4mUDUx8=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "4.2.3";
hash = "sha256-yMGMrMUY+4Lwnh9JIbD2/3FBGIFcSK7leGURWaSQsLQ=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "4.3.4";
hash = "sha256-styyo+6mJy+yxE0NZG/b1hxkAjPOnJfMgd9zWzCJ5uk=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "4.5.0";
hash = "sha256-7+B+8rsm/dMKvNBty8z5qB+kOxMwUzExy0F04xM88sk=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "5.0.2";
hash = "sha256-YOoosLEiszPsOOaNAkWhFGU04JJKDOFVoA/ggrZMN10=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "6.0.1";
hash = "sha256-Ehsgt3nCJijpaVuJguC1TPVEKSkJd6PSc07D2ZQSemI=";
})
(fetchNuGet {
pname = "FSharp.Core";
version = "9.0.100";
hash = "sha256-V1q3CjbRvWZqxpi6cXD/R0F7pyXGGtH83M5Z/ITDrp8=";
})
(fetchNuGet {
pname = "FsUnit";
version = "6.0.1";
hash = "sha256-vka/aAgWhDCl5tu+kgO7GtSaHOOvlSaWxG+tExwGXpI=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Ref";
version = "6.0.36";
hash = "sha256-9jDkWbjw/nd8yqdzVTagCuqr6owJ/DUMi4BlUZT4hWU=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-arm64";
version = "6.0.36";
hash = "sha256-JQULJyF0ivLoUU1JaFfK/HHg+/qzpN7V2RR2Cc+WlQ4=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.linux-x64";
version = "6.0.36";
hash = "sha256-zUsVIpV481vMLAXaLEEUpEMA9/f1HGOnvaQnaWdzlyY=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-arm64";
version = "6.0.36";
hash = "sha256-2seqZcz0JeUjkzh3QcGa9TcJ4LUafpFjTRk+Nm8T6T0=";
})
(fetchNuGet {
pname = "Microsoft.AspNetCore.App.Runtime.osx-x64";
version = "6.0.36";
hash = "sha256-yxLafxiBKkvfkDggPk0P9YZIHBkDJOsFTO7/V9mEHuU=";
})
(fetchNuGet {
pname = "Microsoft.CodeCoverage";
version = "17.12.0";
hash = "sha256-lGjifppD0OBMBp28pjUfPipaeXg739n8cPhtHWoo5RE=";
})
(fetchNuGet {
pname = "Microsoft.NET.Test.Sdk";
version = "17.12.0";
hash = "sha256-DKFEbhh2wPzahNeHdEoFig8tZh/LEVrFc5+zpT43Btg=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-arm64";
version = "6.0.36";
hash = "sha256-9lC/LYnthYhjkWWz2kkFCvlA5LJOv11jdt59SDnpdy0=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.linux-x64";
version = "6.0.36";
hash = "sha256-VFRDzx7LJuvI5yzKdGmw/31NYVbwHWPKQvueQt5xc10=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-arm64";
version = "6.0.36";
hash = "sha256-DaSWwYACJGolEBuMhzDVCj/rQTdDt061xCVi+gyQnuo=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Host.osx-x64";
version = "6.0.36";
hash = "sha256-FrRny9EI6HKCKQbu6mcLj5w4ooSRrODD4Vj2ZMGnMd4=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Ref";
version = "6.0.36";
hash = "sha256-9LZgVoIFF8qNyUu8kdJrYGLutMF/cL2K82HN2ywwlx8=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-arm64";
version = "6.0.36";
hash = "sha256-k3rxvUhCEU0pVH8KgEMtkPiSOibn+nBh+0zT2xIfId8=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.linux-x64";
version = "6.0.36";
hash = "sha256-U8wJ2snSDFqeAgDVLXjnniidC7Cr5aJ1/h/BMSlyu0c=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-arm64";
version = "6.0.36";
hash = "sha256-UfLcrL2Gj/OLz0s92Oo+OCJeDpZFAcQLPLiSNND8D5Y=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.App.Runtime.osx-x64";
version = "6.0.36";
hash = "sha256-0xIJYFzxdMcnCj3wzkFRQZSnQcPHzPHMzePRIOA3oJs=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Platforms";
version = "1.1.0";
hash = "sha256-FeM40ktcObQJk4nMYShB61H/E8B7tIKfl9ObJ0IOcCM=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Platforms";
version = "1.1.1";
hash = "sha256-8hLiUKvy/YirCWlFwzdejD2Db3DaXhHxT7GSZx/znJg=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Platforms";
version = "2.0.0";
hash = "sha256-IEvBk6wUXSdyCnkj6tHahOJv290tVVT8tyemYcR0Yro=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Targets";
version = "1.1.0";
hash = "sha256-0AqQ2gMS8iNlYkrD+BxtIg7cXMnr9xZHtKAuN4bjfaQ=";
})
(fetchNuGet {
pname = "Microsoft.NETCore.Targets";
version = "1.1.3";
hash = "sha256-WLsf1NuUfRWyr7C7Rl9jiua9jximnVvzy6nk2D2bVRc=";
})
(fetchNuGet {
pname = "Microsoft.TestPlatform.ObjectModel";
version = "17.12.0";
hash = "sha256-3XBHBSuCxggAIlHXmKNQNlPqMqwFlM952Av6RrLw1/w=";
})
(fetchNuGet {
pname = "Microsoft.TestPlatform.TestHost";
version = "17.12.0";
hash = "sha256-rf8Sh0fQq44Sneuvs64unkkIHg8kOjDGWE35j9iLx5I=";
})
(fetchNuGet {
pname = "Myriad.Core";
version = "0.8.3";
hash = "sha256-vBOxfq8QriX/yUtaXN69rEQaY/psRNJWxqATLidrt2g=";
})
(fetchNuGet {
pname = "Myriad.Sdk";
version = "0.8.3";
hash = "sha256-7O397WKhskKOvE3MkJT37BvxorDWngDR6gTUogtDZ2M=";
})
(fetchNuGet {
pname = "Nerdbank.GitVersioning";
version = "3.7.112";
hash = "sha256-vrItlaH5MpBHa4MI1cQgI11NAe4W3XsxR9DizFE7fus=";
})
(fetchNuGet {
pname = "NETStandard.Library";
version = "2.0.3";
hash = "sha256-Prh2RPebz/s8AzHb2sPHg3Jl8s31inv9k+Qxd293ybo=";
})
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.1";
hash = "sha256-K2tSVW4n4beRPzPu3rlVaBEMdGvWSv/3Q1fxaDh4Mjo=";
})
(fetchNuGet {
pname = "Newtonsoft.Json";
version = "13.0.3";
hash = "sha256-hy/BieY4qxBWVVsDqqOPaLy1QobiIapkbrESm6v2PHc=";
})
(fetchNuGet {
pname = "NuGet.Common";
version = "6.12.1";
hash = "sha256-k6JAFqHFinTakwNuW666aXYPhR7TpI/rb+KbHm1S2TM=";
})
(fetchNuGet {
pname = "NuGet.Configuration";
version = "6.12.1";
hash = "sha256-e/4lvyl7o7g4aWTAtr9S2yiGgk7V0E9p6DXpsy7GgVw=";
})
(fetchNuGet {
pname = "NuGet.Frameworks";
version = "6.12.1";
hash = "sha256-GGpkbas+PNLx35vvr3nyAVz5lY/aeoMx6qjmT368Lpg=";
})
(fetchNuGet {
pname = "NuGet.Packaging";
version = "6.12.1";
hash = "sha256-3h8Nmjpt383+dCg9GJ1BJ26UirwEQsWCPcTiT0+wGeI=";
})
(fetchNuGet {
pname = "NuGet.Protocol";
version = "6.12.1";
hash = "sha256-l+CHnAcit6Y9OjBxereRP5JzOuWbuZZQYkFOKsUkdQ8=";
})
(fetchNuGet {
pname = "NuGet.Versioning";
version = "6.12.1";
hash = "sha256-f/ejCuzCAwKs4N4Ec6yf2RovrhBT0nj0hRDP+03/Iy4=";
})
(fetchNuGet {
pname = "NUnit";
version = "4.0.1";
hash = "sha256-jd1CD5nHVXkpvBNpVDJcJyfTggCHLyDBySVSvtrA8Uk=";
})
(fetchNuGet {
pname = "NUnit";
version = "4.3.0";
hash = "sha256-dL8+6yL7nwgQKZR3wo2hAOUt3d2EFN10GJH1LzY89I8=";
})
(fetchNuGet {
pname = "NUnit3TestAdapter";
version = "4.6.0";
hash = "sha256-9Yav2fYhC4w0OgsyUwU4/5rDy4FVDTpKnWHuwl/uKJQ=";
})
(fetchNuGet {
pname = "RestEase";
version = "1.6.4";
hash = "sha256-FFmqFwlHhIln46k56Z8KM1G+xuPEh/bceKCQnJcdcdc=";
})
(fetchNuGet {
pname = "runtime.any.System.Runtime";
version = "4.3.0";
hash = "sha256-qwhNXBaJ1DtDkuRacgHwnZmOZ1u9q7N8j0cWOLYOELM=";
})
(fetchNuGet {
pname = "runtime.native.System";
version = "4.3.0";
hash = "sha256-ZBZaodnjvLXATWpXXakFgcy6P+gjhshFXmglrL5xD5Y=";
})
(fetchNuGet {
pname = "runtime.unix.System.Private.Uri";
version = "4.3.0";
hash = "sha256-c5tXWhE/fYbJVl9rXs0uHh3pTsg44YD1dJvyOA0WoMs=";
})
(fetchNuGet {
pname = "System.Diagnostics.DiagnosticSource";
version = "7.0.0";
hash = "sha256-9Wk8cHSkjKtqkN6xW7KnXoQVtF/VNbKeBq79WqDesMs=";
})
(fetchNuGet {
pname = "System.Formats.Asn1";
version = "6.0.0";
hash = "sha256-KaMHgIRBF7Nf3VwOo+gJS1DcD+41cJDPWFh+TDQ8ee8=";
})
(fetchNuGet {
pname = "System.Formats.Asn1";
version = "8.0.1";
hash = "sha256-may/Wg+esmm1N14kQTG4ESMBi+GQKPp0ZrrBo/o6OXM=";
})
(fetchNuGet {
pname = "System.IO.Abstractions";
version = "4.2.13";
hash = "sha256-nkC/PiqE6+c1HJ2yTwg3x+qdBh844Z8n3ERWDW8k6Gg=";
})
(fetchNuGet {
pname = "System.IO.FileSystem.AccessControl";
version = "4.5.0";
hash = "sha256-ck44YBQ0M+2Im5dw0VjBgFD1s0XuY54cujrodjjSBL8=";
})
(fetchNuGet {
pname = "System.Memory";
version = "4.5.5";
hash = "sha256-EPQ9o1Kin7KzGI5O3U3PUQAZTItSbk9h/i4rViN3WiI=";
})
(fetchNuGet {
pname = "System.Private.Uri";
version = "4.3.0";
hash = "sha256-fVfgcoP4AVN1E5wHZbKBIOPYZ/xBeSIdsNF+bdukIRM=";
})
(fetchNuGet {
pname = "System.Reflection.Metadata";
version = "1.6.0";
hash = "sha256-JJfgaPav7UfEh4yRAQdGhLZF1brr0tUWPl6qmfNWq/E=";
})
(fetchNuGet {
pname = "System.Runtime";
version = "4.3.1";
hash = "sha256-R9T68AzS1PJJ7v6ARz9vo88pKL1dWqLOANg4pkQjkA0=";
})
(fetchNuGet {
pname = "System.Runtime.CompilerServices.Unsafe";
version = "6.0.0";
hash = "sha256-bEG1PnDp7uKYz/OgLOWs3RWwQSVYm+AnPwVmAmcgp2I=";
})
(fetchNuGet {
pname = "System.Security.AccessControl";
version = "4.5.0";
hash = "sha256-AFsKPb/nTk2/mqH/PYpaoI8PLsiKKimaXf+7Mb5VfPM=";
})
(fetchNuGet {
pname = "System.Security.Cryptography.Pkcs";
version = "6.0.4";
hash = "sha256-2e0aRybote+OR66bHaNiYpF//4fCiaO3zbR2e9GABUI=";
})
(fetchNuGet {
pname = "System.Security.Cryptography.ProtectedData";
version = "4.4.0";
hash = "sha256-Ri53QmFX8I8UH0x4PikQ1ZA07ZSnBUXStd5rBfGWFOE=";
})
(fetchNuGet {
pname = "System.Security.Principal.Windows";
version = "4.5.0";
hash = "sha256-BkUYNguz0e4NJp1kkW7aJBn3dyH9STwB5N8XqnlCsmY=";
})
(fetchNuGet {
pname = "System.Text.Encodings.Web";
version = "8.0.0";
hash = "sha256-IUQkQkV9po1LC0QsqrilqwNzPvnc+4eVvq+hCvq8fvE=";
})
(fetchNuGet {
pname = "System.Text.Json";
version = "8.0.4";
hash = "sha256-g5oT7fbXxQ9Iah1nMCr4UUX/a2l+EVjJyTrw3FTbIaI=";
})
(fetchNuGet {
pname = "TypeEquality";
version = "0.3.0";
hash = "sha256-V50xAOzzyUJrY+MYPRxtnqW5MVeATXCes89wPprv1r4=";
})
(fetchNuGet {
pname = "WoofWare.Whippet.Fantomas";
version = "0.3.1";
hash = "sha256-i5oiqcrxzM90Ocuq5MIu2Ha5lV0aYu5nCvuwmFqp6NA=";
})
]