mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-10-07 13:08:40 +00:00
Compare commits
14 Commits
WoofWare.M
...
WoofWare.M
Author | SHA1 | Date | |
---|---|---|---|
|
40106d8aa3 | ||
|
4d641b0662 | ||
|
16e6b91548 | ||
|
8488883835 | ||
|
0652744c57 | ||
|
9252979673 | ||
|
1120a3752d | ||
|
7ca6b0c0eb | ||
|
50efb8d9bc | ||
|
93a1b630c8 | ||
|
4482038e8a | ||
|
a41fa9dd37 | ||
|
fc5acc2f58 | ||
|
0a1783d6ed |
@@ -3,7 +3,7 @@
|
|||||||
"isRoot": true,
|
"isRoot": true,
|
||||||
"tools": {
|
"tools": {
|
||||||
"fantomas": {
|
"fantomas": {
|
||||||
"version": "6.3.12",
|
"version": "6.3.15",
|
||||||
"commands": [
|
"commands": [
|
||||||
"fantomas"
|
"fantomas"
|
||||||
]
|
]
|
||||||
|
22
.github/workflows/dotnet.yaml
vendored
22
.github/workflows/dotnet.yaml
vendored
@@ -29,7 +29,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -50,7 +50,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -67,7 +67,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -82,7 +82,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -97,7 +97,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -116,7 +116,7 @@ jobs:
|
|||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -129,7 +129,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -142,7 +142,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@master
|
- uses: actions/checkout@master
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -156,7 +156,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -276,7 +276,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
@@ -309,7 +309,7 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- name: Install Nix
|
- name: Install Nix
|
||||||
uses: cachix/install-nix-action@V27
|
uses: cachix/install-nix-action@v29
|
||||||
with:
|
with:
|
||||||
extra_nix_config: |
|
extra_nix_config: |
|
||||||
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
access-tokens = github.com=${{ secrets.GITHUB_TOKEN }}
|
||||||
|
3
.github/workflows/flake_update.yaml
vendored
3
.github/workflows/flake_update.yaml
vendored
@@ -27,8 +27,7 @@ jobs:
|
|||||||
- name: Run passthru
|
- name: Run passthru
|
||||||
run: |
|
run: |
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
./result | tee /tmp/passthru.txt
|
./result nix/deps.nix
|
||||||
cp /"$(cat /tmp/passthru.txt | grep " wrote lockfile to " | cut -d / -f 2-)" nix/deps.nix
|
|
||||||
|
|
||||||
- name: Format
|
- name: Format
|
||||||
run: 'nix develop --command alejandra .'
|
run: 'nix develop --command alejandra .'
|
||||||
|
@@ -1,5 +1,14 @@
|
|||||||
Notable changes are recorded here.
|
Notable changes are recorded here.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 3.0.1
|
||||||
|
|
||||||
|
Semantics of `HttpClient`'s URI component composition changed:
|
||||||
|
we now implicitly insert `/` characters after `[<BaseAddress>]` and `[<BasePath>]`, so that URI composition doesn't silently drop the last component if you didn't put a slash there.
|
||||||
|
|
||||||
|
# WoofWare.Myriad.Plugins 2.3.9
|
||||||
|
|
||||||
|
`JsonParse` and `JsonSerialize` now interpret `[<JsonExtensionData>]`, which must be on a `Dictionary<string, _>`; this collects any extra components that were present on the JSON object.
|
||||||
|
|
||||||
# WoofWare.Myriad.Plugins 2.2.1, WoofWare.Myriad.Plugins.Attributes 3.2.1
|
# WoofWare.Myriad.Plugins 2.2.1, WoofWare.Myriad.Plugins.Attributes 3.2.1
|
||||||
|
|
||||||
New generator: `ArgParser`, a basic reflection-free argument parser.
|
New generator: `ArgParser`, a basic reflection-free argument parser.
|
||||||
|
@@ -32,6 +32,20 @@
|
|||||||
<Compile Include="GeneratedMock.fs">
|
<Compile Include="GeneratedMock.fs">
|
||||||
<MyriadFile>MockExample.fs</MyriadFile>
|
<MyriadFile>MockExample.fs</MyriadFile>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<Compile Include="MockExampleNoAttributes.fs" />
|
||||||
|
<Compile Include="GeneratedMockNoAttributes.fs">
|
||||||
|
<MyriadFile>MockExampleNoAttributes.fs</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<IPublicTypeNoAttr>GenerateMock</IPublicTypeNoAttr>
|
||||||
|
<IPublicTypeInternalFalseNoAttr>GenerateMock(false)</IPublicTypeInternalFalseNoAttr>
|
||||||
|
<InternalTypeNoAttr>GenerateMock</InternalTypeNoAttr>
|
||||||
|
<PrivateTypeNoAttr>GenerateMock</PrivateTypeNoAttr>
|
||||||
|
<PrivateTypeInternalFalseNoAttr>GenerateMock(false)</PrivateTypeInternalFalseNoAttr>
|
||||||
|
<VeryPublicTypeNoAttr>GenerateMock</VeryPublicTypeNoAttr>
|
||||||
|
<CurriedNoAttr>GenerateMock</CurriedNoAttr>
|
||||||
|
<TypeWithInterfaceNoAttr>GenerateMock</TypeWithInterfaceNoAttr>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
<Compile Include="Vault.fs" />
|
<Compile Include="Vault.fs" />
|
||||||
<Compile Include="GeneratedVault.fs">
|
<Compile Include="GeneratedVault.fs">
|
||||||
<MyriadFile>Vault.fs</MyriadFile>
|
<MyriadFile>Vault.fs</MyriadFile>
|
||||||
@@ -56,6 +70,17 @@
|
|||||||
<Compile Include="GeneratedArgs.fs">
|
<Compile Include="GeneratedArgs.fs">
|
||||||
<MyriadFile>Args.fs</MyriadFile>
|
<MyriadFile>Args.fs</MyriadFile>
|
||||||
</Compile>
|
</Compile>
|
||||||
|
<None Include="swagger-gitea.json" />
|
||||||
|
<Compile Include="GeneratedSwaggerGitea.fs">
|
||||||
|
<MyriadFile>swagger-gitea.json</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<GenerateMockInternal>true</GenerateMockInternal>
|
||||||
|
<ClassName>Gitea</ClassName>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Generated2SwaggerGitea.fs">
|
||||||
|
<MyriadFile>GeneratedSwaggerGitea.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
42444
ConsumePlugin/Generated2SwaggerGitea.fs
Normal file
42444
ConsumePlugin/Generated2SwaggerGitea.fs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -31,7 +31,7 @@ module FileSystemItemCata =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
type private Instruction =
|
type private Instruction =
|
||||||
| Process__FileSystemItem of FileSystemItem
|
| Process__FileSystemItem of FileSystemItem
|
||||||
| FileSystemItem_Directory of string * int * int
|
| FileSystemItem_Directory of name : string * dirSize : int * contents : int
|
||||||
|
|
||||||
let private loop (cata : FileSystemCata<'FileSystemItem>) (instructions : ResizeArray<Instruction>) =
|
let private loop (cata : FileSystemCata<'FileSystemItem>) (instructions : ResizeArray<Instruction>) =
|
||||||
let fileSystemItemStack = ResizeArray<'FileSystemItem> ()
|
let fileSystemItemStack = ResizeArray<'FileSystemItem> ()
|
||||||
@@ -106,7 +106,7 @@ module GiftCata =
|
|||||||
| Process__Gift of Gift
|
| Process__Gift of Gift
|
||||||
| Gift_Wrapped of WrappingPaperStyle
|
| Gift_Wrapped of WrappingPaperStyle
|
||||||
| Gift_Boxed
|
| Gift_Boxed
|
||||||
| Gift_WithACard of string
|
| Gift_WithACard of message : string
|
||||||
|
|
||||||
let private loop (cata : GiftCata<'Gift>) (instructions : ResizeArray<Instruction>) =
|
let private loop (cata : GiftCata<'Gift>) (instructions : ResizeArray<Instruction>) =
|
||||||
let giftStack = ResizeArray<'Gift> ()
|
let giftStack = ResizeArray<'Gift> ()
|
||||||
|
200
ConsumePlugin/GeneratedMockNoAttributes.fs
Normal file
200
ConsumePlugin/GeneratedMockNoAttributes.fs
Normal file
@@ -0,0 +1,200 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
// This code was generated by myriad.
|
||||||
|
// Changes to this file will be lost when the code is regenerated.
|
||||||
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal PublicTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type public PublicTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> string list
|
||||||
|
Mem2 : string -> int
|
||||||
|
Mem3 : int * option<System.Threading.CancellationToken> -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PublicTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface IPublicTypeInternalFalseNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
member this.Mem3 (arg_0_0, arg_0_1) = this.Mem3 (arg_0_0, arg_0_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal InternalTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : InternalTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface InternalTypeNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type private PrivateTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 : string * int -> unit
|
||||||
|
Mem2 : string -> int
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : PrivateTypeInternalFalseNoAttrMock =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface PrivateTypeInternalFalseNoAttr with
|
||||||
|
member this.Mem1 (arg_0_0, arg_0_1) = this.Mem1 (arg_0_0, arg_0_1)
|
||||||
|
member this.Mem2 arg_0_0 = this.Mem2 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal VeryPublicTypeNoAttrMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 : 'a -> 'b
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : VeryPublicTypeNoAttrMock<'a, 'b> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface VeryPublicTypeNoAttr<'a, 'b> with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal CurriedNoAttrMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 : int -> 'a -> string
|
||||||
|
Mem2 : int * string -> 'a -> string
|
||||||
|
Mem3 : (int * string) -> 'a -> string
|
||||||
|
Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
Mem5 : int * string -> ('a * int) -> string
|
||||||
|
Mem6 : int * string -> 'a * int -> string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty () : CurriedNoAttrMock<'a> =
|
||||||
|
{
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
Mem3 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem3"))
|
||||||
|
Mem4 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem4"))
|
||||||
|
Mem5 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem5"))
|
||||||
|
Mem6 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem6"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface CurriedNoAttr<'a> with
|
||||||
|
member this.Mem1 arg_0_0 arg_1_0 = this.Mem1 (arg_0_0) (arg_1_0)
|
||||||
|
member this.Mem2 (arg_0_0, arg_0_1) arg_1_0 = this.Mem2 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
member this.Mem3 ((arg_0_0, arg_0_1)) arg_1_0 = this.Mem3 (arg_0_0, arg_0_1) (arg_1_0)
|
||||||
|
|
||||||
|
member this.Mem4 ((arg_0_0, arg_0_1)) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem4 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem5 (arg_0_0, arg_0_1) ((arg_1_0, arg_1_1)) =
|
||||||
|
this.Mem5 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
|
||||||
|
member this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1) =
|
||||||
|
this.Mem6 (arg_0_0, arg_0_1) (arg_1_0, arg_1_1)
|
||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
/// Mock record type for an interface
|
||||||
|
type internal TypeWithInterfaceNoAttrMock =
|
||||||
|
{
|
||||||
|
/// Implementation of IDisposable.Dispose
|
||||||
|
Dispose : unit -> unit
|
||||||
|
Mem1 : string option -> string[] Async
|
||||||
|
Mem2 : unit -> string[] Async
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An implementation where every method throws.
|
||||||
|
static member Empty : TypeWithInterfaceNoAttrMock =
|
||||||
|
{
|
||||||
|
Dispose = (fun () -> ())
|
||||||
|
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
|
||||||
|
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
|
||||||
|
}
|
||||||
|
|
||||||
|
interface TypeWithInterfaceNoAttr with
|
||||||
|
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
|
||||||
|
member this.Mem2 () = this.Mem2 (())
|
||||||
|
|
||||||
|
interface System.IDisposable with
|
||||||
|
member this.Dispose () : unit = this.Dispose ()
|
@@ -29,7 +29,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (("v1/gyms/"), System.UriKind.Relative)
|
System.Uri (("v1/gyms/"), System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -59,11 +59,11 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/gyms/{gym_id}/attendance"
|
"v1/gyms/{gym_id}/attendance"
|
||||||
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{gym_id}", gymId.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -93,11 +93,11 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/gyms/{gym_id}/attendance"
|
"v1/gyms/{gym_id}/attendance"
|
||||||
.Replace ("{gym_id}", gymId.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{gym_id}", gymId.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -127,7 +127,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("v1/member", System.UriKind.Relative)
|
System.Uri ("v1/member", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -157,11 +157,11 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/gyms/{gym}"
|
"v1/gyms/{gym}"
|
||||||
.Replace ("{gym}", gym.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{gym}", gym.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -191,7 +191,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("v1/member/activity", System.UriKind.Relative)
|
System.Uri ("v1/member/activity", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -221,7 +221,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("some/url", System.UriKind.Relative)
|
System.Uri ("some/url", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -251,7 +251,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("some/url", System.UriKind.Relative)
|
System.Uri ("some/url", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -317,7 +317,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
("/v2/gymSessions/member"
|
("/v2/gymSessions/member"
|
||||||
@@ -326,9 +326,9 @@ module PureGymApi =
|
|||||||
else
|
else
|
||||||
"?")
|
"?")
|
||||||
+ "fromDate="
|
+ "fromDate="
|
||||||
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
|
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Uri.EscapeDataString)
|
||||||
+ "&toDate="
|
+ "&toDate="
|
||||||
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
|
+ ((toDate.ToString "yyyy-MM-dd") |> System.Uri.EscapeDataString)),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -358,7 +358,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
("/v2/gymSessions/member?foo=1"
|
("/v2/gymSessions/member?foo=1"
|
||||||
@@ -367,9 +367,9 @@ module PureGymApi =
|
|||||||
else
|
else
|
||||||
"?")
|
"?")
|
||||||
+ "fromDate="
|
+ "fromDate="
|
||||||
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)
|
+ ((fromDate.ToString "yyyy-MM-dd") |> System.Uri.EscapeDataString)
|
||||||
+ "&toDate="
|
+ "&toDate="
|
||||||
+ ((toDate.ToString "yyyy-MM-dd") |> System.Web.HttpUtility.UrlEncode)),
|
+ ((toDate.ToString "yyyy-MM-dd") |> System.Uri.EscapeDataString)),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -399,7 +399,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -426,7 +426,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -453,7 +453,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -480,7 +480,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -507,7 +507,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -534,7 +534,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -567,7 +567,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -600,7 +600,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -633,7 +633,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("users/new", System.UriKind.Relative)
|
System.Uri ("users/new", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -659,11 +659,11 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"endpoint/{param}"
|
"endpoint/{param}"
|
||||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -688,7 +688,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -713,7 +713,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -738,7 +738,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -763,7 +763,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -787,7 +787,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -811,7 +811,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -835,7 +835,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -859,7 +859,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -895,7 +895,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -931,7 +931,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -967,7 +967,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -1003,7 +1003,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -1026,7 +1026,7 @@ module PureGymApi =
|
|||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
(match client.BaseAddress with
|
||||||
| null -> System.Uri "https://whatnot.com"
|
| null -> System.Uri "https://whatnot.com/"
|
||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri ("endpoint", System.UriKind.Relative)
|
System.Uri ("endpoint", System.UriKind.Relative)
|
||||||
)
|
)
|
||||||
@@ -1076,7 +1076,7 @@ module internal ApiWithoutBaseAddress =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"endpoint/{param}"
|
"endpoint/{param}"
|
||||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1116,18 +1116,21 @@ module ApiWithBasePath =
|
|||||||
|
|
||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
System.Uri (
|
||||||
| null ->
|
(match client.BaseAddress with
|
||||||
raise (
|
| null ->
|
||||||
System.ArgumentNullException (
|
raise (
|
||||||
nameof (client.BaseAddress),
|
System.ArgumentNullException (
|
||||||
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
| v -> v),
|
||||||
| v -> v),
|
System.Uri ("foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"endpoint/{param}"
|
"endpoint/{param}"
|
||||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1167,12 +1170,321 @@ module ApiWithBasePathAndAddress =
|
|||||||
|
|
||||||
let uri =
|
let uri =
|
||||||
System.Uri (
|
System.Uri (
|
||||||
(match client.BaseAddress with
|
System.Uri (
|
||||||
| null -> System.Uri "https://whatnot.com"
|
(match client.BaseAddress with
|
||||||
| v -> v),
|
| null -> System.Uri "https://whatnot.com/thing/"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"endpoint/{param}"
|
"endpoint/{param}"
|
||||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
|
module ApiWithAbsoluteBasePath =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePath =
|
||||||
|
{ new IApiWithAbsoluteBasePath with
|
||||||
|
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("/foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
|
System.Uri (
|
||||||
|
"endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
|
module ApiWithAbsoluteBasePathAndAddress =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePathAndAddress =
|
||||||
|
{ new IApiWithAbsoluteBasePathAndAddress with
|
||||||
|
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com/thing/"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("/foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
|
System.Uri (
|
||||||
|
"endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
|
module ApiWithBasePathAndAbsoluteEndpoint =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAbsoluteEndpoint =
|
||||||
|
{ new IApiWithBasePathAndAbsoluteEndpoint with
|
||||||
|
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
|
System.Uri (
|
||||||
|
"/endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
|
module ApiWithBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IApiWithBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
{ new IApiWithBasePathAndAddressAndAbsoluteEndpoint with
|
||||||
|
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com/thing/"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
|
System.Uri (
|
||||||
|
"/endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = ct))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
|
module ApiWithAbsoluteBasePathAndAbsoluteEndpoint =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePathAndAbsoluteEndpoint =
|
||||||
|
{ new IApiWithAbsoluteBasePathAndAbsoluteEndpoint with
|
||||||
|
member _.GetPathParam (parameter : string, cancellationToken : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null ->
|
||||||
|
raise (
|
||||||
|
System.ArgumentNullException (
|
||||||
|
nameof (client.BaseAddress),
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient."
|
||||||
|
)
|
||||||
|
)
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("/foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
|
System.Uri (
|
||||||
|
"/endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
|
System.UriKind.Relative
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let httpMessage =
|
||||||
|
new System.Net.Http.HttpRequestMessage (
|
||||||
|
Method = System.Net.Http.HttpMethod.Get,
|
||||||
|
RequestUri = uri
|
||||||
|
)
|
||||||
|
|
||||||
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
|
return responseString
|
||||||
|
}
|
||||||
|
|> (fun a -> Async.StartAsTask (a, ?cancellationToken = cancellationToken))
|
||||||
|
}
|
||||||
|
namespace PureGym
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Threading
|
||||||
|
open System.Threading.Tasks
|
||||||
|
open System.IO
|
||||||
|
open System.Net
|
||||||
|
open System.Net.Http
|
||||||
|
open RestEase
|
||||||
|
|
||||||
|
/// Module for constructing a REST client.
|
||||||
|
[<CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix) ; RequireQualifiedAccess>]
|
||||||
|
module ApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
/// Create a REST client.
|
||||||
|
let make (client : System.Net.Http.HttpClient) : IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
{ new IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint with
|
||||||
|
member _.GetPathParam (parameter : string, ct : CancellationToken option) =
|
||||||
|
async {
|
||||||
|
let! ct = Async.CancellationToken
|
||||||
|
|
||||||
|
let uri =
|
||||||
|
System.Uri (
|
||||||
|
System.Uri (
|
||||||
|
(match client.BaseAddress with
|
||||||
|
| null -> System.Uri "https://whatnot.com/thing/"
|
||||||
|
| v -> v),
|
||||||
|
System.Uri ("/foo/", System.UriKind.Relative)
|
||||||
|
),
|
||||||
|
System.Uri (
|
||||||
|
"/endpoint/{param}"
|
||||||
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1231,7 +1543,7 @@ module ApiWithHeaders =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"endpoint/{param}"
|
"endpoint/{param}"
|
||||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -1245,6 +1557,7 @@ module ApiWithHeaders =
|
|||||||
do httpMessage.Headers.Add ("X-Foo", this.SomeHeader.ToString ())
|
do httpMessage.Headers.Add ("X-Foo", this.SomeHeader.ToString ())
|
||||||
do httpMessage.Headers.Add ("Authorization", this.SomeOtherHeader.ToString ())
|
do httpMessage.Headers.Add ("Authorization", this.SomeOtherHeader.ToString ())
|
||||||
do httpMessage.Headers.Add ("Header-Name", "Header-Value")
|
do httpMessage.Headers.Add ("Header-Name", "Header-Value")
|
||||||
|
do httpMessage.Headers.Add ("Something-Else", "val")
|
||||||
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
let! response = client.SendAsync (httpMessage, ct) |> Async.AwaitTask
|
||||||
let response = response.EnsureSuccessStatusCode ()
|
let response = response.EnsureSuccessStatusCode ()
|
||||||
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
let! responseString = response.Content.ReadAsStringAsync ct |> Async.AwaitTask
|
||||||
@@ -1293,7 +1606,7 @@ module ApiWithHeaders2 =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"endpoint/{param}"
|
"endpoint/{param}"
|
||||||
.Replace ("{param}", parameter.ToString () |> System.Web.HttpUtility.UrlEncode),
|
.Replace ("{param}", parameter.ToString () |> System.Uri.EscapeDataString),
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
6432
ConsumePlugin/GeneratedSwaggerGitea.fs
Normal file
6432
ConsumePlugin/GeneratedSwaggerGitea.fs
Normal file
File diff suppressed because it is too large
Load Diff
@@ -476,11 +476,8 @@ module VaultClient =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/{mountPoint}/{path}"
|
"v1/{mountPoint}/{path}"
|
||||||
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
|
.Replace("{path}", path.ToString () |> System.Uri.EscapeDataString)
|
||||||
.Replace (
|
.Replace ("{mountPoint}", mountPoint.ToString () |> System.Uri.EscapeDataString),
|
||||||
"{mountPoint}",
|
|
||||||
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
|
|
||||||
),
|
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -573,11 +570,8 @@ module VaultClientNonExtensionMethod =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/{mountPoint}/{path}"
|
"v1/{mountPoint}/{path}"
|
||||||
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
|
.Replace("{path}", path.ToString () |> System.Uri.EscapeDataString)
|
||||||
.Replace (
|
.Replace ("{mountPoint}", mountPoint.ToString () |> System.Uri.EscapeDataString),
|
||||||
"{mountPoint}",
|
|
||||||
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
|
|
||||||
),
|
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -673,11 +667,8 @@ module VaultClientExtensionMethodHttpClientExtension =
|
|||||||
| v -> v),
|
| v -> v),
|
||||||
System.Uri (
|
System.Uri (
|
||||||
"v1/{mountPoint}/{path}"
|
"v1/{mountPoint}/{path}"
|
||||||
.Replace("{path}", path.ToString () |> System.Web.HttpUtility.UrlEncode)
|
.Replace("{path}", path.ToString () |> System.Uri.EscapeDataString)
|
||||||
.Replace (
|
.Replace ("{mountPoint}", mountPoint.ToString () |> System.Uri.EscapeDataString),
|
||||||
"{mountPoint}",
|
|
||||||
mountPoint.ToString () |> System.Web.HttpUtility.UrlEncode
|
|
||||||
),
|
|
||||||
System.UriKind.Relative
|
System.UriKind.Relative
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@@ -31,7 +31,7 @@ module MyListCata =
|
|||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
type private Instruction<'a> =
|
type private Instruction<'a> =
|
||||||
| Process__MyList of MyList<'a>
|
| Process__MyList of MyList<'a>
|
||||||
| MyList_Cons of 'a
|
| MyList_Cons of head : 'a
|
||||||
|
|
||||||
let private loop (cata : MyListCata<'a, 'MyList>) (instructions : ResizeArray<Instruction<'a>>) =
|
let private loop (cata : MyListCata<'a, 'MyList>) (instructions : ResizeArray<Instruction<'a>>) =
|
||||||
let myListStack = ResizeArray<'MyList> ()
|
let myListStack = ResizeArray<'MyList> ()
|
||||||
|
41
ConsumePlugin/MockExampleNoAttributes.fs
Normal file
41
ConsumePlugin/MockExampleNoAttributes.fs
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
namespace SomeNamespace
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
type IPublicTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type IPublicTypeInternalFalseNoAttr =
|
||||||
|
abstract Mem1 : string * int -> string list
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
abstract Mem3 : x : int * ?ct : System.Threading.CancellationToken -> string
|
||||||
|
|
||||||
|
type internal InternalTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type private PrivateTypeInternalFalseNoAttr =
|
||||||
|
abstract Mem1 : string * int -> unit
|
||||||
|
abstract Mem2 : string -> int
|
||||||
|
|
||||||
|
type VeryPublicTypeNoAttr<'a, 'b> =
|
||||||
|
abstract Mem1 : 'a -> 'b
|
||||||
|
|
||||||
|
type CurriedNoAttr<'a> =
|
||||||
|
abstract Mem1 : int -> 'a -> string
|
||||||
|
abstract Mem2 : int * string -> 'a -> string
|
||||||
|
abstract Mem3 : (int * string) -> 'a -> string
|
||||||
|
abstract Mem4 : (int * string) -> ('a * int) -> string
|
||||||
|
abstract Mem5 : x : int * string -> ('a * int) -> string
|
||||||
|
abstract Mem6 : int * string -> y : 'a * int -> string
|
||||||
|
|
||||||
|
type TypeWithInterfaceNoAttr =
|
||||||
|
inherit IDisposable
|
||||||
|
abstract Mem1 : string option -> string[] Async
|
||||||
|
abstract Mem2 : unit -> string[] Async
|
@@ -122,8 +122,6 @@ type internal IApiWithoutBaseAddress =
|
|||||||
[<Get "endpoint/{param}">]
|
[<Get "endpoint/{param}">]
|
||||||
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
// TODO: implement BasePath support
|
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
[<BasePath "foo">]
|
[<BasePath "foo">]
|
||||||
type IApiWithBasePath =
|
type IApiWithBasePath =
|
||||||
@@ -132,12 +130,54 @@ type IApiWithBasePath =
|
|||||||
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
[<BaseAddress "https://whatnot.com">]
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
[<BasePath "foo">]
|
[<BasePath "foo">]
|
||||||
type IApiWithBasePathAndAddress =
|
type IApiWithBasePathAndAddress =
|
||||||
[<Get "endpoint/{param}">]
|
[<Get "endpoint/{param}">]
|
||||||
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePath =
|
||||||
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePathAndAddress =
|
||||||
|
[<Get "endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BasePath "foo">]
|
||||||
|
type IApiWithBasePathAndAbsoluteEndpoint =
|
||||||
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
|
[<BasePath "foo">]
|
||||||
|
type IApiWithBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
[<Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePathAndAbsoluteEndpoint =
|
||||||
|
// Example where we use the bundled attributes rather than RestEase's
|
||||||
|
[<WoofWare.Myriad.Plugins.RestEase.Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?cancellationToken : CancellationToken -> Task<string>
|
||||||
|
|
||||||
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
[<BaseAddress "https://whatnot.com/thing">]
|
||||||
|
[<BasePath "/foo">]
|
||||||
|
type IApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint =
|
||||||
|
[<Get "/endpoint/{param}">]
|
||||||
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
[<Header("Header-Name", "Header-Value")>]
|
[<Header("Header-Name", "Header-Value")>]
|
||||||
type IApiWithHeaders =
|
type IApiWithHeaders =
|
||||||
@@ -148,6 +188,7 @@ type IApiWithHeaders =
|
|||||||
abstract SomeOtherHeader : int
|
abstract SomeOtherHeader : int
|
||||||
|
|
||||||
[<Get "endpoint/{param}">]
|
[<Get "endpoint/{param}">]
|
||||||
|
[<Header("Something-Else", "val")>]
|
||||||
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
abstract GetPathParam : [<Path "param">] parameter : string * ?ct : CancellationToken -> Task<string>
|
||||||
|
|
||||||
[<WoofWare.Myriad.Plugins.HttpClient>]
|
[<WoofWare.Myriad.Plugins.HttpClient>]
|
||||||
|
21054
ConsumePlugin/swagger-gitea.json
Normal file
21054
ConsumePlugin/swagger-gitea.json
Normal file
File diff suppressed because it is too large
Load Diff
120
README.md
120
README.md
@@ -14,7 +14,8 @@ Currently implemented:
|
|||||||
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods).
|
* `JsonSerialize` (to stamp out `toJsonNode : 'T -> JsonNode` methods).
|
||||||
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
|
* `HttpClient` (to stamp out a [RestEase](https://github.com/canton7/RestEase)-style HTTP client).
|
||||||
* `GenerateMock` (to stamp out a record type corresponding to an interface, like a compile-time [Foq](https://github.com/fsprojects/Foq)).
|
* `GenerateMock` (to stamp out a record type corresponding to an interface, like a compile-time [Foq](https://github.com/fsprojects/Foq)).
|
||||||
* `ArgParser` (to stamp out a basic argument parser)
|
* `ArgParser` (to stamp out a basic argument parser).
|
||||||
|
* `SwaggerClient` (to stamp out an HTTP client for a Swagger API).
|
||||||
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
|
* `CreateCatamorphism` (to stamp out a non-stack-overflowing [catamorphism](https://fsharpforfunandprofit.com/posts/recursive-types-and-folds/) for a discriminated union).
|
||||||
* `RemoveOptions` (to strip `option` modifiers from a type) - this one is particularly half-baked!
|
* `RemoveOptions` (to strip `option` modifiers from a type) - this one is particularly half-baked!
|
||||||
|
|
||||||
@@ -156,6 +157,10 @@ For an example of using both `JsonParse` and `JsonSerialize` together with compl
|
|||||||
Takes a record like this:
|
Takes a record like this:
|
||||||
|
|
||||||
```fsharp
|
```fsharp
|
||||||
|
type DryRunMode =
|
||||||
|
| [<ArgumentFlag true> Dry
|
||||||
|
| [<ArgumentFlag false> Wet
|
||||||
|
|
||||||
[<ArgParser>]
|
[<ArgParser>]
|
||||||
type Foo =
|
type Foo =
|
||||||
{
|
{
|
||||||
@@ -166,12 +171,16 @@ type Foo =
|
|||||||
B : Choice<int, int>
|
B : Choice<int, int>
|
||||||
[<ArgumentDefaultEnvironmentVariable "MY_ENV_VAR">]
|
[<ArgumentDefaultEnvironmentVariable "MY_ENV_VAR">]
|
||||||
BWithEnv : Choice<int, int>
|
BWithEnv : Choice<int, int>
|
||||||
|
[<ArgumentDefaultFunction>]
|
||||||
|
DryRun : DryRunMode
|
||||||
|
[<ArgumentLongForm "longer-form-replaces-c">]
|
||||||
C : float list
|
C : float list
|
||||||
// optionally:
|
// optionally:
|
||||||
[<PositionalArgs>]
|
[<PositionalArgs>]
|
||||||
Rest : string list // or e.g. `int list` if you want them parsed into a type too
|
Rest : string list // or e.g. `int list` if you want them parsed into a type too
|
||||||
}
|
}
|
||||||
static member DefaultB () = 4
|
static member DefaultB () = 4
|
||||||
|
static member DefaultDryRun () = DryRunMode.Wet
|
||||||
```
|
```
|
||||||
|
|
||||||
and stamps out a basic `parse` method of this signature:
|
and stamps out a basic `parse` method of this signature:
|
||||||
@@ -218,6 +227,85 @@ This is very bare-bones, but do raise GitHub issues if you like (or if you find
|
|||||||
|
|
||||||
It should work fine if you just want to compose a few primitive types, though.
|
It should work fine if you just want to compose a few primitive types, though.
|
||||||
|
|
||||||
|
## `SwaggerClient`
|
||||||
|
|
||||||
|
Takes a JSON-schema definition of a [Swagger API](https://swagger.io/), and stamps out a client like this:
|
||||||
|
|
||||||
|
```fsharp
|
||||||
|
/// A type which was defined in the Swagger spec
|
||||||
|
[<JsonParse true ; JsonSerialize true>]
|
||||||
|
type SwaggerType1 =
|
||||||
|
{
|
||||||
|
[<System.Text.Json.Serialization.JsonExtensionData>]
|
||||||
|
AdditionalProperties : System.Collections.Generic.Dictionary<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
Message : string
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Documentation from the Swagger spec
|
||||||
|
[<HttpClient false ; RestEase.BasePath "/api/v1">]
|
||||||
|
type IGitea =
|
||||||
|
/// Returns the Person actor for a user
|
||||||
|
[<RestEase.Get "/activitypub/user/{username}">]
|
||||||
|
abstract ActivitypubPerson :
|
||||||
|
[<RestEase.Path "username">] username : string * ?ct : System.Threading.CancellationToken ->
|
||||||
|
ActivityPub System.Threading.Tasks.Task
|
||||||
|
```
|
||||||
|
|
||||||
|
Notice that we automatically decorate the type with our `[<HttpClient>]` attribute, so if you choose to do so, you can chain another Myriad generated file off this one and you'll get a RestEase-style client stamped out.
|
||||||
|
(See below, searching on the string `"Generated2SwaggerGitea.fs"`, for an example.)
|
||||||
|
|
||||||
|
You don't need to `Content Include` or `EmbeddedResource Include` the JSON schema.
|
||||||
|
`None Include` will do; we only need the source to be available at build time.
|
||||||
|
|
||||||
|
You *do* need to include the following configuration:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Compile Include="GeneratedClient.fs">
|
||||||
|
<!-- This bit is normal: -->
|
||||||
|
<MyriadFile>swagger.json</MyriadFile>
|
||||||
|
<!-- This bit is new and required! -->
|
||||||
|
<MyriadParams>
|
||||||
|
<ClassName>GiteaClient</ClassName>
|
||||||
|
<!-- Optionally: -->
|
||||||
|
<GenerateMock>true</GenerateMock>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
```
|
||||||
|
|
||||||
|
The `<ClassName />` key tells us what to name the resulting interface (it gets an `I` prepended for you).
|
||||||
|
You can optionally also set `<GenerateMockVisibility>v</GenerateMockVisibility>` to add the `[<GenerateMock>]` attribute to the type
|
||||||
|
(where `v` should be `internal` or `public`, indicating "resulting mock type is internal" vs "is public"),
|
||||||
|
so that the following manoeuvre will result in a generated mock:
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<None Include="swagger-gitea.json" />
|
||||||
|
<Compile Include="GeneratedSwaggerGitea.fs">
|
||||||
|
<MyriadFile>swagger-gitea.json</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<GenerateMockVisibility>public</GenerateMockVisibility>
|
||||||
|
<ClassName>Gitea</ClassName>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
<Compile Include="Generated2SwaggerGitea.fs">
|
||||||
|
<MyriadFile>GeneratedSwaggerGitea.fs</MyriadFile>
|
||||||
|
</Compile>
|
||||||
|
```
|
||||||
|
|
||||||
|
(Note that you do have to create the `GeneratedSwaggerGitea.fs` file manually before code generation happens. Myriad will throw if that file isn't there, because `Generated2SwaggerGitea.fs` depends on it so Myriad wants to compute its hash. Just make an empty file.)
|
||||||
|
|
||||||
|
### What's the point?
|
||||||
|
|
||||||
|
[`SwaggerProvider`](https://github.com/fsprojects/SwaggerProvider) is *absolutely magical*, but it's kind of witchcraft.
|
||||||
|
I fear no man, but that thing… it scares me.
|
||||||
|
|
||||||
|
Also, builds using `SwaggerProvider` appear to be inherently nondeterministic, even if the data source doesn't change.
|
||||||
|
|
||||||
|
## Limitations
|
||||||
|
|
||||||
|
Swagger API specs appear to be pretty cowboy in the wild.
|
||||||
|
I try to cope with invalid schemas I have seen, but I can't guarantee I do so correctly.
|
||||||
|
Definitely do perform integration tests and let me know of weird specs you encounter, and bits of the (very extensive) Swagger spec I have omitted!
|
||||||
|
|
||||||
## `RemoveOptions`
|
## `RemoveOptions`
|
||||||
|
|
||||||
Takes a record like this:
|
Takes a record like this:
|
||||||
@@ -516,6 +604,36 @@ For example, this specifies that Myriad is to use the contents of `Client.fs` to
|
|||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Alternative use without the attributes
|
||||||
|
|
||||||
|
You can avoid taking a reference on the `WoofWare.Myriad.Plugins.Attributes` assembly, instead putting all the configuration into the project file.
|
||||||
|
This is implemented for everything except the SwaggerClientGenerator.
|
||||||
|
|
||||||
|
```xml
|
||||||
|
<Project>
|
||||||
|
<ItemGroup>
|
||||||
|
<Compile Include="Client.fs" />
|
||||||
|
<Compile Include="GeneratedClient.fs">
|
||||||
|
<MyriadFile>Client.fs</MyriadFile>
|
||||||
|
<MyriadParams>
|
||||||
|
<MyTypeName1>GenerateMock(false)!JsonParse</MyTypeName1>
|
||||||
|
<SomeOtherTypeName>GenerateMock</SomeOtherTypeName>
|
||||||
|
</MyriadParams>
|
||||||
|
</Compile>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="WoofWare.Myriad.Plugins" Version="$(WoofWareMyriadPluginVersion)" PrivateAssets="all" />
|
||||||
|
<PackageReference Include="Myriad.Sdk" Version="0.8.3" PrivateAssets="all" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
||||||
|
```
|
||||||
|
|
||||||
|
That is, you specify a `!`-delimited list of the attributes you *would* apply to the type.
|
||||||
|
Supply "arguments" to the attribute name in the project file as you would to the attribute itself.
|
||||||
|
|
||||||
|
(Yes, this is indeed incredibly cumbersome, and you're not interested in the reasons it's all so mad!
|
||||||
|
I'm hopefully going to get round to writing a more powerful source generation system which won't have these limitations.)
|
||||||
|
|
||||||
### Myriad Gotchas
|
### Myriad Gotchas
|
||||||
|
|
||||||
* MsBuild doesn't always realise that it needs to invoke Myriad during rebuild.
|
* MsBuild doesn't always realise that it needs to invoke Myriad during rebuild.
|
||||||
|
@@ -45,6 +45,9 @@ module RestEase =
|
|||||||
|
|
||||||
/// Indicates that this interface represents a REST client which accesses an API whose paths are
|
/// Indicates that this interface represents a REST client which accesses an API whose paths are
|
||||||
/// all relative to the given address.
|
/// all relative to the given address.
|
||||||
|
///
|
||||||
|
/// We will essentially unconditionally append a slash to this for you, on the grounds that you probably don't
|
||||||
|
/// intend the base path *itself* to be an endpoint.
|
||||||
type BaseAddressAttribute (addr : string) =
|
type BaseAddressAttribute (addr : string) =
|
||||||
inherit Attribute ()
|
inherit Attribute ()
|
||||||
|
|
||||||
@@ -61,3 +64,21 @@ module RestEase =
|
|||||||
inherit Attribute ()
|
inherit Attribute ()
|
||||||
new (path : string) = PathAttribute (Some path)
|
new (path : string) = PathAttribute (Some path)
|
||||||
new () = PathAttribute None
|
new () = PathAttribute None
|
||||||
|
|
||||||
|
/// Indicates that this argument to a method is passed to the remote API by being serialised into the request
|
||||||
|
/// body.
|
||||||
|
type BodyAttribute () =
|
||||||
|
inherit Attribute ()
|
||||||
|
|
||||||
|
/// This is interpolated into every URL, between the BaseAddress and the path specified by e.g. [<Get>].
|
||||||
|
/// Note that if the [<Get>]-specified path starts with a slash, the BasePath is ignored, because then [<Get>]
|
||||||
|
/// is considered to be relative to the URL root (i.e. the host part of the BaseAddress).
|
||||||
|
/// Similarly, if the [<BasePath>] starts with a slash, then any path component of the BaseAddress is ignored.
|
||||||
|
///
|
||||||
|
/// We will essentially unconditionally append a slash to this for you, on the grounds that you probably don't
|
||||||
|
/// intend the base path *itself* to be an endpoint.
|
||||||
|
///
|
||||||
|
/// Can contain {placeholders}; hopefully your methods define values for those placeholders with [<Path>]
|
||||||
|
/// attributes!
|
||||||
|
type BasePathAttribute (path : string) =
|
||||||
|
inherit Attribute ()
|
||||||
|
@@ -49,6 +49,10 @@ WoofWare.Myriad.Plugins.RemoveOptionsAttribute..ctor [constructor]: unit
|
|||||||
WoofWare.Myriad.Plugins.RestEase inherit obj
|
WoofWare.Myriad.Plugins.RestEase inherit obj
|
||||||
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute inherit System.Attribute
|
||||||
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute..ctor [constructor]: string
|
WoofWare.Myriad.Plugins.RestEase+BaseAddressAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BasePathAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BasePathAttribute..ctor [constructor]: string
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BodyAttribute inherit System.Attribute
|
||||||
|
WoofWare.Myriad.Plugins.RestEase+BodyAttribute..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute inherit System.Attribute
|
||||||
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute..ctor [constructor]: string
|
WoofWare.Myriad.Plugins.RestEase+DeleteAttribute..ctor [constructor]: string
|
||||||
WoofWare.Myriad.Plugins.RestEase+GetAttribute inherit System.Attribute
|
WoofWare.Myriad.Plugins.RestEase+GetAttribute inherit System.Attribute
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "3.5",
|
"version": "3.6",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
|
@@ -9,18 +9,18 @@ open FsUnitTyped
|
|||||||
|
|
||||||
[<TestFixture>]
|
[<TestFixture>]
|
||||||
module TestBasePath =
|
module TestBasePath =
|
||||||
|
let replyWithUrl (message : HttpRequestMessage) : HttpResponseMessage Async =
|
||||||
|
async {
|
||||||
|
message.Method |> shouldEqual HttpMethod.Get
|
||||||
|
let content = new StringContent (message.RequestUri.ToString ())
|
||||||
|
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
||||||
|
resp.Content <- content
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Base address is respected`` () =
|
let ``Base address is respected`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
async {
|
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
|
||||||
let content = new StringContent (message.RequestUri.ToString ())
|
|
||||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
|
||||||
resp.Content <- content
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
use client = HttpClientMock.makeNoUri proc
|
|
||||||
let api = PureGymApi.make client
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
let observedUri = api.GetPathParam("param").Result
|
let observedUri = api.GetPathParam("param").Result
|
||||||
@@ -28,38 +28,28 @@ module TestBasePath =
|
|||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Without a base address attr but with BaseAddress on client, request goes through`` () =
|
let ``Without a base address attr but with BaseAddress on client, request goes through`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
use client = HttpClientMock.make (Uri "https://baseaddress.com") replyWithUrl
|
||||||
async {
|
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
|
||||||
let content = new StringContent (message.RequestUri.ToString ())
|
|
||||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
|
||||||
resp.Content <- content
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
use client = HttpClientMock.make (System.Uri "https://baseaddress.com") proc
|
|
||||||
let api = ApiWithoutBaseAddress.make client
|
let api = ApiWithoutBaseAddress.make client
|
||||||
|
|
||||||
let observedUri = api.GetPathParam("param").Result
|
let observedUri = api.GetPathParam("param").Result
|
||||||
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
||||||
|
|
||||||
[<Test>]
|
[<Test>]
|
||||||
let ``Without a base address attr or BaseAddress on client, request throws`` () =
|
let ``Base address on client takes precedence`` () =
|
||||||
let proc (message : HttpRequestMessage) : HttpResponseMessage Async =
|
use client = HttpClientMock.make (Uri "https://baseaddress.com") replyWithUrl
|
||||||
async {
|
let api = PureGymApi.make client
|
||||||
message.Method |> shouldEqual HttpMethod.Get
|
|
||||||
let content = new StringContent (message.RequestUri.ToString ())
|
|
||||||
let resp = new HttpResponseMessage (HttpStatusCode.OK)
|
|
||||||
resp.Content <- content
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
|
|
||||||
use client = HttpClientMock.makeNoUri proc
|
let observedUri = api.GetPathParam("param").Result
|
||||||
|
observedUri |> shouldEqual "https://baseaddress.com/endpoint/param"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Without a base address attr or BaseAddress on client, request throws`` () =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
let api = ApiWithoutBaseAddress.make client
|
let api = ApiWithoutBaseAddress.make client
|
||||||
|
|
||||||
let observedExc =
|
let observedExc =
|
||||||
async {
|
async {
|
||||||
let! result = api.GetPathParam ("param") |> Async.AwaitTask |> Async.Catch
|
let! result = api.GetPathParam "param" |> Async.AwaitTask |> Async.Catch
|
||||||
|
|
||||||
match result with
|
match result with
|
||||||
| Choice1Of2 _ -> return failwith "test failure"
|
| Choice1Of2 _ -> return failwith "test failure"
|
||||||
@@ -78,3 +68,103 @@ module TestBasePath =
|
|||||||
observedExc.Message
|
observedExc.Message
|
||||||
|> shouldEqual
|
|> shouldEqual
|
||||||
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, no base address, relative attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePath.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithBasePath.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/thing/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, base address, relative attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAddress.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/thing/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, no base address, relative attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePath.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePath.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, base address, relative attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAddress.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/foo/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, no base address, absolute attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAbsoluteEndpoint.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Relative base path, base address, absolute attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithBasePathAndAddressAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, no base address, absolute attribute`` () : unit =
|
||||||
|
do
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAbsoluteEndpoint.make client
|
||||||
|
|
||||||
|
let exc =
|
||||||
|
Assert.Throws<AggregateException> (fun () -> api.GetPathParam("hi").Result |> ignore<string>)
|
||||||
|
|
||||||
|
exc.InnerException.Message
|
||||||
|
|> shouldEqual
|
||||||
|
"No base address was supplied on the type, and no BaseAddress was on the HttpClient. (Parameter 'BaseAddress')"
|
||||||
|
|
||||||
|
use client = HttpClientMock.make (Uri "https://whatnot.com/thing/") replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Absolute base path, base address, absolute attribute`` () : unit =
|
||||||
|
use client = HttpClientMock.makeNoUri replyWithUrl
|
||||||
|
let api = ApiWithAbsoluteBasePathAndAddressAndAbsoluteEndpoint.make client
|
||||||
|
let result = api.GetPathParam("hi").Result
|
||||||
|
result |> shouldEqual "https://whatnot.com/endpoint/hi"
|
||||||
|
@@ -33,4 +33,4 @@ module TestPathParam =
|
|||||||
let api = PureGymApi.make client
|
let api = PureGymApi.make client
|
||||||
|
|
||||||
api.GetPathParam("hello/world?(hi)").Result
|
api.GetPathParam("hello/world?(hi)").Result
|
||||||
|> shouldEqual "hello%2fworld%3f(hi)"
|
|> shouldEqual "hello%2Fworld%3F%28hi%29"
|
||||||
|
@@ -52,7 +52,13 @@ module TestVariableHeader =
|
|||||||
|
|
||||||
api.GetPathParam("param").Result.Split "\n"
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|> Array.sort
|
|> Array.sort
|
||||||
|> shouldEqual [| "Authorization: -99" ; "Header-Name: Header-Value" ; "X-Foo: 11" |]
|
|> shouldEqual
|
||||||
|
[|
|
||||||
|
"Authorization: -99"
|
||||||
|
"Header-Name: Header-Value"
|
||||||
|
"Something-Else: val"
|
||||||
|
"X-Foo: 11"
|
||||||
|
|]
|
||||||
|
|
||||||
someHeaderCount.Value |> shouldEqual 11
|
someHeaderCount.Value |> shouldEqual 11
|
||||||
someOtherHeaderCount.Value |> shouldEqual -99
|
someOtherHeaderCount.Value |> shouldEqual -99
|
||||||
@@ -98,11 +104,23 @@ module TestVariableHeader =
|
|||||||
|
|
||||||
api.GetPathParam("param").Result.Split "\n"
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|> Array.sort
|
|> Array.sort
|
||||||
|> shouldEqual [| "Authorization: -99" ; "Header-Name: Header-Value" ; "X-Foo: 11" |]
|
|> shouldEqual
|
||||||
|
[|
|
||||||
|
"Authorization: -99"
|
||||||
|
"Header-Name: Header-Value"
|
||||||
|
"Something-Else: val"
|
||||||
|
"X-Foo: 11"
|
||||||
|
|]
|
||||||
|
|
||||||
api.GetPathParam("param").Result.Split "\n"
|
api.GetPathParam("param").Result.Split "\n"
|
||||||
|> Array.sort
|
|> Array.sort
|
||||||
|> shouldEqual [| "Authorization: -98" ; "Header-Name: Header-Value" ; "X-Foo: 12" |]
|
|> shouldEqual
|
||||||
|
[|
|
||||||
|
"Authorization: -98"
|
||||||
|
"Header-Name: Header-Value"
|
||||||
|
"Something-Else: val"
|
||||||
|
"X-Foo: 12"
|
||||||
|
|]
|
||||||
|
|
||||||
someHeaderCount.Value |> shouldEqual 12
|
someHeaderCount.Value |> shouldEqual 12
|
||||||
someOtherHeaderCount.Value |> shouldEqual -98
|
someOtherHeaderCount.Value |> shouldEqual -98
|
||||||
|
@@ -0,0 +1,36 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System
|
||||||
|
open SomeNamespace
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestMockGeneratorNoAttr =
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: IPublicType`` () =
|
||||||
|
let mock : IPublicTypeNoAttr =
|
||||||
|
{ PublicTypeNoAttrMock.Empty with
|
||||||
|
Mem1 = fun (s, count) -> List.replicate count s
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
let _ =
|
||||||
|
Assert.Throws<NotImplementedException> (fun () -> mock.Mem2 "hi" |> ignore<int>)
|
||||||
|
|
||||||
|
mock.Mem1 ("hi", 3) |> shouldEqual [ "hi" ; "hi" ; "hi" ]
|
||||||
|
|
||||||
|
[<Test>]
|
||||||
|
let ``Example of use: curried args`` () =
|
||||||
|
let mock : CurriedNoAttr<_> =
|
||||||
|
{ CurriedNoAttrMock.Empty () with
|
||||||
|
Mem1 = fun i c -> Array.replicate i c |> String
|
||||||
|
Mem2 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
Mem3 = fun (i, s) c -> String.concat $"%c{c}" (List.replicate i s)
|
||||||
|
}
|
||||||
|
:> _
|
||||||
|
|
||||||
|
mock.Mem1 3 'a' |> shouldEqual "aaa"
|
||||||
|
mock.Mem2 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
||||||
|
mock.Mem3 (3, "hi") 'a' |> shouldEqual "hiahiahi"
|
84
WoofWare.Myriad.Plugins.Test/TestSwagger/TestSwaggerParse.fs
Normal file
84
WoofWare.Myriad.Plugins.Test/TestSwagger/TestSwaggerParse.fs
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins.Test
|
||||||
|
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
open NUnit.Framework
|
||||||
|
open FsUnitTyped
|
||||||
|
open WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
[<TestFixture>]
|
||||||
|
module TestSwaggerParse =
|
||||||
|
[<Test>]
|
||||||
|
let ``Can parse parameters`` () : unit =
|
||||||
|
let s =
|
||||||
|
"""{
|
||||||
|
"tags": [
|
||||||
|
"organization"
|
||||||
|
],
|
||||||
|
"summary": "Check if a user is a member of an organization",
|
||||||
|
"operationId": "orgIsMember",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "name of the organization",
|
||||||
|
"name": "org",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "string",
|
||||||
|
"description": "username of the user",
|
||||||
|
"name": "username",
|
||||||
|
"in": "path",
|
||||||
|
"required": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "user is a member"
|
||||||
|
},
|
||||||
|
"303": {
|
||||||
|
"description": "redirection to /orgs/{org}/public_members/{username}"
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "user is not a member"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|> JsonNode.Parse
|
||||||
|
|
||||||
|
s.AsObject ()
|
||||||
|
|> SwaggerEndpoint.Parse
|
||||||
|
|> shouldEqual
|
||||||
|
{
|
||||||
|
Consumes = None
|
||||||
|
Produces = None
|
||||||
|
Tags = [ "organization" ]
|
||||||
|
Summary = "Check if a user is a member of an organization"
|
||||||
|
OperationId = OperationId "orgIsMember"
|
||||||
|
Parameters =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
Type = Definition.String
|
||||||
|
Description = Some "name of the organization"
|
||||||
|
Name = "org"
|
||||||
|
In = ParameterIn.Path "org"
|
||||||
|
Required = Some true
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Type = Definition.String
|
||||||
|
Description = Some "username of the user"
|
||||||
|
Name = "username"
|
||||||
|
In = ParameterIn.Path "username"
|
||||||
|
Required = Some true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|> Some
|
||||||
|
Responses =
|
||||||
|
[
|
||||||
|
204, Definition.Unspecified
|
||||||
|
303, Definition.Unspecified
|
||||||
|
404, Definition.Unspecified
|
||||||
|
]
|
||||||
|
|> Map.ofList
|
||||||
|
}
|
@@ -26,6 +26,7 @@
|
|||||||
<Compile Include="TestHttpClient\TestVaultClient.fs" />
|
<Compile Include="TestHttpClient\TestVaultClient.fs" />
|
||||||
<Compile Include="TestHttpClient\TestVariableHeader.fs" />
|
<Compile Include="TestHttpClient\TestVariableHeader.fs" />
|
||||||
<Compile Include="TestMockGenerator\TestMockGenerator.fs" />
|
<Compile Include="TestMockGenerator\TestMockGenerator.fs" />
|
||||||
|
<Compile Include="TestMockGenerator\TestMockGeneratorNoAttr.fs" />
|
||||||
<Compile Include="TestJsonSerialize\TestJsonSerde.fs" />
|
<Compile Include="TestJsonSerialize\TestJsonSerde.fs" />
|
||||||
<Compile Include="TestCataGenerator\TestCataGenerator.fs" />
|
<Compile Include="TestCataGenerator\TestCataGenerator.fs" />
|
||||||
<Compile Include="TestCataGenerator\TestDirectory.fs" />
|
<Compile Include="TestCataGenerator\TestDirectory.fs" />
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
<Compile Include="TestCataGenerator\TestMyList.fs" />
|
<Compile Include="TestCataGenerator\TestMyList.fs" />
|
||||||
<Compile Include="TestCataGenerator\TestMyList2.fs" />
|
<Compile Include="TestCataGenerator\TestMyList2.fs" />
|
||||||
<Compile Include="TestArgParser\TestArgParser.fs" />
|
<Compile Include="TestArgParser\TestArgParser.fs" />
|
||||||
|
<Compile Include="TestSwagger\TestSwaggerParse.fs" />
|
||||||
<Compile Include="TestRemoveOptions.fs"/>
|
<Compile Include="TestRemoveOptions.fs"/>
|
||||||
<Compile Include="TestSurface.fs"/>
|
<Compile Include="TestSurface.fs"/>
|
||||||
<None Include="../.github/workflows/dotnet.yaml" />
|
<None Include="../.github/workflows/dotnet.yaml" />
|
||||||
@@ -41,7 +43,7 @@
|
|||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ApiSurface" Version="4.1.5"/>
|
<PackageReference Include="ApiSurface" Version="4.1.5"/>
|
||||||
<PackageReference Include="FsCheck" Version="2.16.6"/>
|
<PackageReference Include="FsCheck" Version="2.16.6"/>
|
||||||
<PackageReference Include="FsUnit" Version="6.0.0"/>
|
<PackageReference Include="FsUnit" Version="6.0.1"/>
|
||||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1"/>
|
||||||
<PackageReference Include="NUnit" Version="4.2.2"/>
|
<PackageReference Include="NUnit" Version="4.2.2"/>
|
||||||
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
|
<PackageReference Include="NUnit3TestAdapter" Version="4.6.0"/>
|
||||||
|
@@ -4,8 +4,7 @@ open System
|
|||||||
open System.Text
|
open System.Text
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
open Fantomas.FCS.Xml
|
open WoofWare.Whippet.Fantomas
|
||||||
open Myriad.Core
|
|
||||||
|
|
||||||
type internal ArgParserOutputSpec =
|
type internal ArgParserOutputSpec =
|
||||||
{
|
{
|
||||||
@@ -1224,7 +1223,7 @@ module internal ArgParserGenerator =
|
|||||||
(SynExpr.CreateConst ()))
|
(SynExpr.CreateConst ()))
|
||||||
])
|
])
|
||||||
SynMatchClause.create
|
SynMatchClause.create
|
||||||
(SynPat.listCons (SynPat.createConst (SynConst.CreateString "--")) (SynPat.named "rest"))
|
(SynPat.listCons (SynPat.createConst (SynConst.Create "--")) (SynPat.named "rest"))
|
||||||
(SynExpr.callMethodArg
|
(SynExpr.callMethodArg
|
||||||
"AddRange"
|
"AddRange"
|
||||||
(SynExpr.paren (
|
(SynExpr.paren (
|
||||||
@@ -1643,7 +1642,7 @@ module internal ArgParserGenerator =
|
|||||||
let modInfo =
|
let modInfo =
|
||||||
SynComponentInfo.create modName
|
SynComponentInfo.create modName
|
||||||
|> SynComponentInfo.withDocString (
|
|> SynComponentInfo.withDocString (
|
||||||
PreXmlDoc.Create $" Methods to parse arguments for the type %s{taggedType.Name.idText}"
|
PreXmlDoc.create $"Methods to parse arguments for the type %s{taggedType.Name.idText}"
|
||||||
)
|
)
|
||||||
|> SynComponentInfo.addAttributes modAttrs
|
|> SynComponentInfo.addAttributes modAttrs
|
||||||
|
|
||||||
@@ -1666,7 +1665,7 @@ module internal ArgParserGenerator =
|
|||||||
[
|
[
|
||||||
{
|
{
|
||||||
Attrs = []
|
Attrs = []
|
||||||
Ident = Ident.create "key"
|
Ident = Some (Ident.create "key")
|
||||||
Type = SynType.string
|
Type = SynType.string
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -1740,75 +1739,12 @@ module internal ArgParserGenerator =
|
|||||||
|
|
||||||
[
|
[
|
||||||
for openStatement in opens do
|
for openStatement in opens do
|
||||||
yield SynModuleDecl.CreateOpen openStatement
|
yield SynModuleDecl.openAny openStatement
|
||||||
yield taggedMod
|
yield taggedMod
|
||||||
]
|
]
|
||||||
|> SynModuleOrNamespace.createNamespace ns
|
|> SynModuleOrNamespace.createNamespace ns
|
||||||
|
|
||||||
let generate (context : GeneratorContext) : Output =
|
open Myriad.Core
|
||||||
let ast, _ =
|
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
|
||||||
|
|
||||||
let types =
|
|
||||||
Ast.extractTypeDefn ast
|
|
||||||
|> List.groupBy (fst >> List.map _.idText >> String.concat ".")
|
|
||||||
|> List.map (fun (_, v) -> fst (List.head v), List.collect snd v)
|
|
||||||
|
|
||||||
let opens = AstHelper.extractOpens ast
|
|
||||||
|
|
||||||
let namespaceAndTypes =
|
|
||||||
types
|
|
||||||
|> List.collect (fun (ns, types) ->
|
|
||||||
let typeWithAttr =
|
|
||||||
types
|
|
||||||
|> List.choose (fun ty ->
|
|
||||||
match Ast.getAttribute<ArgParserAttribute> ty with
|
|
||||||
| None -> None
|
|
||||||
| Some attr ->
|
|
||||||
let arg =
|
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
|
||||||
| SynExpr.Const (SynConst.Bool value, _) -> value
|
|
||||||
| SynExpr.Const (SynConst.Unit, _) -> ArgParserAttribute.DefaultIsExtensionMethod
|
|
||||||
| arg ->
|
|
||||||
failwith
|
|
||||||
$"Unrecognised argument %+A{arg} to [<%s{nameof ArgParserAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
|
||||||
|
|
||||||
let spec =
|
|
||||||
{
|
|
||||||
ExtensionMethods = arg
|
|
||||||
}
|
|
||||||
|
|
||||||
Some (ty, spec)
|
|
||||||
)
|
|
||||||
|
|
||||||
typeWithAttr
|
|
||||||
|> List.map (fun taggedType ->
|
|
||||||
let unions, records, others =
|
|
||||||
(([], [], []), types)
|
|
||||||
||> List.fold (fun
|
|
||||||
(unions, records, others)
|
|
||||||
(SynTypeDefn.SynTypeDefn (sci, repr, smd, _, _, _) as ty) ->
|
|
||||||
match repr with
|
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (access, cases, _), _) ->
|
|
||||||
UnionType.OfUnion sci smd access cases :: unions, records, others
|
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _) ->
|
|
||||||
unions, RecordType.OfRecord sci smd access fields :: records, others
|
|
||||||
| _ -> unions, records, ty :: others
|
|
||||||
)
|
|
||||||
|
|
||||||
if not others.IsEmpty then
|
|
||||||
failwith
|
|
||||||
$"Error: all types recursively defined together with an ArgParserGenerator type must be discriminated unions or records. %+A{others}"
|
|
||||||
|
|
||||||
(ns, taggedType, unions, records)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
let modules =
|
|
||||||
namespaceAndTypes
|
|
||||||
|> List.map (fun (ns, taggedType, unions, records) -> createModule opens ns taggedType unions records)
|
|
||||||
|
|
||||||
Output.Ast modules
|
|
||||||
|
|
||||||
/// Myriad generator that provides a catamorphism for an algebraic data type.
|
/// Myriad generator that provides a catamorphism for an algebraic data type.
|
||||||
[<MyriadGenerator("arg-parser")>]
|
[<MyriadGenerator("arg-parser")>]
|
||||||
@@ -1817,4 +1753,69 @@ type ArgParserGenerator () =
|
|||||||
interface IMyriadGenerator with
|
interface IMyriadGenerator with
|
||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) = ArgParserGenerator.generate context
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let ast, _ =
|
||||||
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
|
let types =
|
||||||
|
Ast.extractTypeDefn ast
|
||||||
|
|> List.groupBy (fst >> List.map _.idText >> String.concat ".")
|
||||||
|
|> List.map (fun (_, v) -> fst (List.head v), List.collect snd v)
|
||||||
|
|
||||||
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
|
let namespaceAndTypes =
|
||||||
|
types
|
||||||
|
|> List.collect (fun (ns, types) ->
|
||||||
|
let typeWithAttr =
|
||||||
|
types
|
||||||
|
|> List.choose (fun ty ->
|
||||||
|
match SynTypeDefn.getAttribute typeof<ArgParserAttribute>.Name ty with
|
||||||
|
| None -> None
|
||||||
|
| Some attr ->
|
||||||
|
let arg =
|
||||||
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
| SynExpr.Const (SynConst.Bool value, _) -> value
|
||||||
|
| SynExpr.Const (SynConst.Unit, _) -> ArgParserAttribute.DefaultIsExtensionMethod
|
||||||
|
| arg ->
|
||||||
|
failwith
|
||||||
|
$"Unrecognised argument %+A{arg} to [<%s{nameof ArgParserAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
||||||
|
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
ExtensionMethods = arg
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (ty, spec)
|
||||||
|
)
|
||||||
|
|
||||||
|
typeWithAttr
|
||||||
|
|> List.map (fun taggedType ->
|
||||||
|
let unions, records, others =
|
||||||
|
(([], [], []), types)
|
||||||
|
||> List.fold (fun
|
||||||
|
(unions, records, others)
|
||||||
|
(SynTypeDefn.SynTypeDefn (sci, repr, smd, _, _, _) as ty) ->
|
||||||
|
match repr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (access, cases, _), _) ->
|
||||||
|
UnionType.OfUnion sci smd access cases :: unions, records, others
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _) ->
|
||||||
|
unions, RecordType.OfRecord sci smd access fields :: records, others
|
||||||
|
| _ -> unions, records, ty :: others
|
||||||
|
)
|
||||||
|
|
||||||
|
if not others.IsEmpty then
|
||||||
|
failwith
|
||||||
|
$"Error: all types recursively defined together with an ArgParserGenerator type must be discriminated unions or records. %+A{others}"
|
||||||
|
|
||||||
|
(ns, taggedType, unions, records)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let modules =
|
||||||
|
namespaceAndTypes
|
||||||
|
|> List.map (fun (ns, taggedType, unions, records) ->
|
||||||
|
ArgParserGenerator.createModule opens ns taggedType unions records
|
||||||
|
)
|
||||||
|
|
||||||
|
Output.Ast modules
|
||||||
|
@@ -2,173 +2,7 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
open Fantomas.FCS.Xml
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
type internal ParameterInfo =
|
|
||||||
{
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
IsOptional : bool
|
|
||||||
Id : Ident option
|
|
||||||
Type : SynType
|
|
||||||
}
|
|
||||||
|
|
||||||
type internal TupledArg =
|
|
||||||
{
|
|
||||||
HasParen : bool
|
|
||||||
Args : ParameterInfo list
|
|
||||||
}
|
|
||||||
|
|
||||||
type internal MemberInfo =
|
|
||||||
{
|
|
||||||
ReturnType : SynType
|
|
||||||
Accessibility : SynAccess option
|
|
||||||
/// Each element of this list is a list of args in a tuple, or just one arg if not a tuple.
|
|
||||||
Args : TupledArg list
|
|
||||||
Identifier : Ident
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
IsInline : bool
|
|
||||||
IsMutable : bool
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
type internal PropertyAccessors =
|
|
||||||
| Get
|
|
||||||
| Set
|
|
||||||
| GetSet
|
|
||||||
|
|
||||||
type internal PropertyInfo =
|
|
||||||
{
|
|
||||||
Type : SynType
|
|
||||||
Accessibility : SynAccess option
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
Accessors : PropertyAccessors
|
|
||||||
IsInline : bool
|
|
||||||
Identifier : Ident
|
|
||||||
}
|
|
||||||
|
|
||||||
type internal InterfaceType =
|
|
||||||
{
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
Name : LongIdent
|
|
||||||
Inherits : SynType list
|
|
||||||
Members : MemberInfo list
|
|
||||||
Properties : PropertyInfo list
|
|
||||||
Generics : SynTyparDecls option
|
|
||||||
Accessibility : SynAccess option
|
|
||||||
}
|
|
||||||
|
|
||||||
type internal RecordType =
|
|
||||||
{
|
|
||||||
Name : Ident
|
|
||||||
Fields : SynField list
|
|
||||||
/// Any additional members which are not record fields.
|
|
||||||
Members : SynMemberDefns option
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
Generics : SynTyparDecls option
|
|
||||||
TypeAccessibility : SynAccess option
|
|
||||||
ImplAccessibility : SynAccess option
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Parse from the AST.
|
|
||||||
static member OfRecord
|
|
||||||
(sci : SynComponentInfo)
|
|
||||||
(smd : SynMemberDefns)
|
|
||||||
(access : SynAccess option)
|
|
||||||
(recordFields : SynField list)
|
|
||||||
: RecordType
|
|
||||||
=
|
|
||||||
match sci with
|
|
||||||
| SynComponentInfo.SynComponentInfo (attrs, typars, _, longId, doc, _, implAccess, _) ->
|
|
||||||
{
|
|
||||||
Name = List.last longId
|
|
||||||
Fields = recordFields
|
|
||||||
Members = if smd.IsEmpty then None else Some smd
|
|
||||||
XmlDoc = if doc.IsEmpty then None else Some doc
|
|
||||||
Generics = typars
|
|
||||||
ImplAccessibility = implAccess
|
|
||||||
TypeAccessibility = access
|
|
||||||
Attributes = attrs |> List.collect (fun l -> l.Attributes)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Methods for manipulating UnionCase.
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module UnionCase =
|
|
||||||
/// Construct our structured `UnionCase` from an FCS `SynUnionCase`: extract everything
|
|
||||||
/// we care about from the AST representation.
|
|
||||||
let ofSynUnionCase (case : SynUnionCase) : UnionCase<Ident option> =
|
|
||||||
match case with
|
|
||||||
| SynUnionCase.SynUnionCase (attributes, ident, caseType, xmlDoc, access, _, _) ->
|
|
||||||
|
|
||||||
let ident =
|
|
||||||
match ident with
|
|
||||||
| SynIdent.SynIdent (ident, _) -> ident
|
|
||||||
|
|
||||||
let fields =
|
|
||||||
match caseType with
|
|
||||||
| SynUnionCaseKind.Fields cases -> cases
|
|
||||||
| SynUnionCaseKind.FullType _ -> failwith "unexpected FullType union"
|
|
||||||
|
|
||||||
{
|
|
||||||
Name = ident
|
|
||||||
XmlDoc = if xmlDoc.IsEmpty then None else Some xmlDoc
|
|
||||||
Access = access
|
|
||||||
Attributes = attributes |> List.collect (fun t -> t.Attributes)
|
|
||||||
Fields = fields |> List.map SynField.extract
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Functorial `map`.
|
|
||||||
let mapIdentFields<'a, 'b> (f : 'a -> 'b) (unionCase : UnionCase<'a>) : UnionCase<'b> =
|
|
||||||
{
|
|
||||||
Attributes = unionCase.Attributes
|
|
||||||
Name = unionCase.Name
|
|
||||||
Access = unionCase.Access
|
|
||||||
XmlDoc = unionCase.XmlDoc
|
|
||||||
Fields = unionCase.Fields |> List.map (SynField.mapIdent f)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Everything you need to know about a discriminated union definition.
|
|
||||||
type internal UnionType =
|
|
||||||
{
|
|
||||||
/// The name of the DU: for example, `type Foo = | Blah` has this being `Foo`.
|
|
||||||
Name : Ident
|
|
||||||
/// Any additional members which are not union cases.
|
|
||||||
Members : SynMemberDefns option
|
|
||||||
/// Any docstring associated with the DU itself (not its cases).
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
/// Generic type parameters this DU takes: `type Foo<'a> = | ...`.
|
|
||||||
Generics : SynTyparDecls option
|
|
||||||
/// Attributes of the DU (not its cases): `[<Attr>] type Foo = | ...`
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
/// Accessibility modifier of the DU: `type private Foo = ...`
|
|
||||||
TypeAccessibility : SynAccess option
|
|
||||||
/// Accessibility modifier of the DU's implementation: `type Foo = private | ...`
|
|
||||||
ImplAccessibility : SynAccess option
|
|
||||||
/// The actual DU cases themselves.
|
|
||||||
Cases : UnionCase<Ident option> list
|
|
||||||
}
|
|
||||||
|
|
||||||
static member OfUnion
|
|
||||||
(sci : SynComponentInfo)
|
|
||||||
(smd : SynMemberDefns)
|
|
||||||
(access : SynAccess option)
|
|
||||||
(cases : SynUnionCase list)
|
|
||||||
: UnionType
|
|
||||||
=
|
|
||||||
match sci with
|
|
||||||
| SynComponentInfo.SynComponentInfo (attrs, typars, _, longId, doc, _, implAccess, _) ->
|
|
||||||
{
|
|
||||||
Name = List.last longId
|
|
||||||
Members = if smd.IsEmpty then None else Some smd
|
|
||||||
XmlDoc = if doc.IsEmpty then None else Some doc
|
|
||||||
Generics = typars
|
|
||||||
Attributes = attrs |> List.collect (fun l -> l.Attributes)
|
|
||||||
TypeAccessibility = access
|
|
||||||
ImplAccessibility = implAccess
|
|
||||||
Cases = cases |> List.map UnionCase.ofSynUnionCase
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Anything that is part of an ADT.
|
/// Anything that is part of an ADT.
|
||||||
/// A record is a product of stuff; this type represents one of those stuffs.
|
/// A record is a product of stuff; this type represents one of those stuffs.
|
||||||
|
@@ -3,12 +3,11 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
open Myriad.Core
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal CataGenerator =
|
module internal CataGenerator =
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
open Myriad.Core.Ast
|
|
||||||
|
|
||||||
/// The user-provided DU contains cases, each of which contains fields.
|
/// The user-provided DU contains cases, each of which contains fields.
|
||||||
/// We have a hard-coded set of things we know how to deal with as field contents.
|
/// We have a hard-coded set of things we know how to deal with as field contents.
|
||||||
@@ -175,20 +174,14 @@ module internal CataGenerator =
|
|||||||
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "Seq" ; "exactlyOne" ])
|
|> SynExpr.applyFunction (SynExpr.createLongIdent [ "Seq" ; "exactlyOne" ])
|
||||||
|> SynExpr.createLet
|
|> SynExpr.createLet
|
||||||
[
|
[
|
||||||
SynBinding.Let (
|
SynBinding.basicTuple
|
||||||
valData = SynValData.SynValData (None, SynValInfo.Empty, None),
|
(allArtificialTyparNames
|
||||||
pattern =
|
|> List.map (fun (t : Ident) ->
|
||||||
SynPat.tupleNoParen (
|
SynPat.namedI (Ident.create (t.idText + "Stack") |> Ident.lowerFirstLetter)
|
||||||
allArtificialTyparNames
|
))
|
||||||
|> List.map (fun (t : Ident) ->
|
(SynExpr.applyFunction
|
||||||
SynPat.namedI (Ident.create (t.idText + "Stack") |> Ident.lowerFirstLetter)
|
(SynExpr.applyFunction (SynExpr.createIdent "loop") (SynExpr.createIdent "cata"))
|
||||||
)
|
(SynExpr.createIdent "instructions"))
|
||||||
),
|
|
||||||
expr =
|
|
||||||
SynExpr.applyFunction
|
|
||||||
(SynExpr.applyFunction (SynExpr.createIdent "loop") (SynExpr.createIdent "cata"))
|
|
||||||
(SynExpr.createIdent "instructions")
|
|
||||||
)
|
|
||||||
]
|
]
|
||||||
]
|
]
|
||||||
|> SynExpr.sequential
|
|> SynExpr.sequential
|
||||||
@@ -463,18 +456,39 @@ module internal CataGenerator =
|
|||||||
{
|
{
|
||||||
SynFieldData.Type = field.Type
|
SynFieldData.Type = field.Type
|
||||||
Attrs = []
|
Attrs = []
|
||||||
Ident = None
|
Ident = field.Name
|
||||||
}
|
}
|
||||||
|> SynField.make
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SynUnionCase.Create (unionCase.Name, fields)
|
{
|
||||||
|
Name = unionCase.Name
|
||||||
|
XmlDoc = None
|
||||||
|
Access = None
|
||||||
|
Attributes = []
|
||||||
|
Fields = fields
|
||||||
|
}
|
||||||
|
|> SynUnionCase.create
|
||||||
)
|
)
|
||||||
|
|
||||||
let casesFromCases =
|
let casesFromCases =
|
||||||
recursiveCases analysis
|
recursiveCases analysis
|
||||||
|> List.map (fun case ->
|
|> List.map (fun case ->
|
||||||
SynUnionCase.Create (case.Name, case.Fields |> List.map (fun field -> SynField.Create field.Type))
|
{
|
||||||
|
UnionCase.Name = case.Name
|
||||||
|
XmlDoc = None
|
||||||
|
Access = None
|
||||||
|
Attributes = []
|
||||||
|
Fields =
|
||||||
|
case.Fields
|
||||||
|
|> List.map (fun field ->
|
||||||
|
{
|
||||||
|
SynFieldData.Type = field.Type
|
||||||
|
Attrs = []
|
||||||
|
Ident = field.Name
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|> SynUnionCase.create
|
||||||
)
|
)
|
||||||
|
|
||||||
let cases = casesFromProcess @ casesFromCases
|
let cases = casesFromProcess @ casesFromCases
|
||||||
@@ -539,8 +553,8 @@ module internal CataGenerator =
|
|||||||
|> List.map (fun case ->
|
|> List.map (fun case ->
|
||||||
let arity =
|
let arity =
|
||||||
SynValInfo.SynValInfo (
|
SynValInfo.SynValInfo (
|
||||||
case.Fields |> List.map (fun field -> [ SynArgInfo.Empty ]),
|
case.Fields |> List.map (fun field -> [ SynArgInfo.empty ]),
|
||||||
SynArgInfo.Empty
|
SynArgInfo.empty
|
||||||
)
|
)
|
||||||
|
|
||||||
(SynType.var generics.[analysis.GenericName.idText], List.rev case.FlattenedFields)
|
(SynType.var generics.[analysis.GenericName.idText], List.rev case.FlattenedFields)
|
||||||
@@ -852,9 +866,7 @@ module internal CataGenerator =
|
|||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
|
|
||||||
SynMatchClause.create
|
SynMatchClause.create (SynPat.identWithArgs unionCase.Match (SynArgPats.create matchLhs)) matchBody
|
||||||
(SynPat.CreateLongIdent (SynLongIdent.create unionCase.Match, matchLhs))
|
|
||||||
matchBody
|
|
||||||
)
|
)
|
||||||
|
|
||||||
SynExpr.createMatch (SynExpr.createIdent "x") matchCases
|
SynExpr.createMatch (SynExpr.createIdent "x") matchCases
|
||||||
@@ -1059,7 +1071,7 @@ module internal CataGenerator =
|
|||||||
(SynExpr.CreateConst 0)
|
(SynExpr.CreateConst 0)
|
||||||
(SynExpr.createLongIdent [ "instructions" ; "Count" ]))
|
(SynExpr.createLongIdent [ "instructions" ; "Count" ]))
|
||||||
body
|
body
|
||||||
SynExpr.CreateTuple (
|
SynExpr.tupleNoParen (
|
||||||
analysis
|
analysis
|
||||||
|> List.map (fun unionAnalysis -> [ unionAnalysis.StackName ] |> SynExpr.createLongIdent')
|
|> List.map (fun unionAnalysis -> [ unionAnalysis.StackName ] |> SynExpr.createLongIdent')
|
||||||
)
|
)
|
||||||
@@ -1103,7 +1115,7 @@ module internal CataGenerator =
|
|||||||
let modInfo =
|
let modInfo =
|
||||||
SynComponentInfo.create moduleName
|
SynComponentInfo.create moduleName
|
||||||
|> SynComponentInfo.withDocString (
|
|> SynComponentInfo.withDocString (
|
||||||
PreXmlDoc.Create $" Methods to perform a catamorphism over the type %s{parentName}"
|
PreXmlDoc.create $"Methods to perform a catamorphism over the type %s{parentName}"
|
||||||
)
|
)
|
||||||
|> SynComponentInfo.addAttributes [ SynAttribute.requireQualifiedAccess ]
|
|> SynComponentInfo.addAttributes [ SynAttribute.requireQualifiedAccess ]
|
||||||
|
|
||||||
@@ -1150,7 +1162,7 @@ module internal CataGenerator =
|
|||||||
|
|
||||||
[
|
[
|
||||||
for openStatement in opens do
|
for openStatement in opens do
|
||||||
yield SynModuleDecl.CreateOpen openStatement
|
yield SynModuleDecl.openAny openStatement
|
||||||
yield! cataStructures
|
yield! cataStructures
|
||||||
yield cataRecord
|
yield cataRecord
|
||||||
yield
|
yield
|
||||||
@@ -1162,53 +1174,7 @@ module internal CataGenerator =
|
|||||||
]
|
]
|
||||||
|> SynModuleOrNamespace.createNamespace ns
|
|> SynModuleOrNamespace.createNamespace ns
|
||||||
|
|
||||||
let generate (context : GeneratorContext) : Output =
|
open Myriad.Core
|
||||||
let ast, _ =
|
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
|
||||||
|
|
||||||
let types = Ast.extractTypeDefn ast
|
|
||||||
|
|
||||||
let opens = AstHelper.extractOpens ast
|
|
||||||
|
|
||||||
let namespaceAndTypes =
|
|
||||||
types
|
|
||||||
|> List.choose (fun (ns, types) ->
|
|
||||||
let typeWithAttr =
|
|
||||||
types
|
|
||||||
|> List.tryPick (fun ty ->
|
|
||||||
match Ast.getAttribute<CreateCatamorphismAttribute> ty with
|
|
||||||
| None -> None
|
|
||||||
| Some attr -> Some (attr.ArgExpr, ty)
|
|
||||||
)
|
|
||||||
|
|
||||||
match typeWithAttr with
|
|
||||||
| Some taggedType ->
|
|
||||||
let unions, records, others =
|
|
||||||
(([], [], []), types)
|
|
||||||
||> List.fold (fun
|
|
||||||
(unions, records, others)
|
|
||||||
(SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _) as ty) ->
|
|
||||||
match repr with
|
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union _, _) ->
|
|
||||||
ty :: unions, records, others
|
|
||||||
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record _, _) ->
|
|
||||||
unions, ty :: records, others
|
|
||||||
| _ -> unions, records, ty :: others
|
|
||||||
)
|
|
||||||
|
|
||||||
if not others.IsEmpty then
|
|
||||||
failwith
|
|
||||||
$"Error: all types recursively defined together with a CreateCatamorphism type must be discriminated unions or records. %+A{others}"
|
|
||||||
|
|
||||||
Some (ns, taggedType, unions, records)
|
|
||||||
| _ -> None
|
|
||||||
)
|
|
||||||
|
|
||||||
let modules =
|
|
||||||
namespaceAndTypes
|
|
||||||
|> List.map (fun (ns, taggedType, unions, records) -> createModule opens ns taggedType unions records)
|
|
||||||
|
|
||||||
Output.Ast modules
|
|
||||||
|
|
||||||
/// Myriad generator that provides a catamorphism for an algebraic data type.
|
/// Myriad generator that provides a catamorphism for an algebraic data type.
|
||||||
[<MyriadGenerator("create-catamorphism")>]
|
[<MyriadGenerator("create-catamorphism")>]
|
||||||
@@ -1217,4 +1183,52 @@ type CreateCatamorphismGenerator () =
|
|||||||
interface IMyriadGenerator with
|
interface IMyriadGenerator with
|
||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) = CataGenerator.generate context
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let ast, _ =
|
||||||
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
|
let types = Ast.extractTypeDefn ast
|
||||||
|
|
||||||
|
let opens = AstHelper.extractOpens ast
|
||||||
|
|
||||||
|
let namespaceAndTypes =
|
||||||
|
types
|
||||||
|
|> List.choose (fun (ns, types) ->
|
||||||
|
let typeWithAttr =
|
||||||
|
types
|
||||||
|
|> List.tryPick (fun ty ->
|
||||||
|
match SynTypeDefn.getAttribute typeof<CreateCatamorphismAttribute>.Name ty with
|
||||||
|
| None -> None
|
||||||
|
| Some attr -> Some (attr.ArgExpr, ty)
|
||||||
|
)
|
||||||
|
|
||||||
|
match typeWithAttr with
|
||||||
|
| Some taggedType ->
|
||||||
|
let unions, records, others =
|
||||||
|
(([], [], []), types)
|
||||||
|
||> List.fold (fun
|
||||||
|
(unions, records, others)
|
||||||
|
(SynTypeDefn.SynTypeDefn (_, repr, _, _, _, _) as ty) ->
|
||||||
|
match repr with
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union _, _) ->
|
||||||
|
ty :: unions, records, others
|
||||||
|
| SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record _, _) ->
|
||||||
|
unions, ty :: records, others
|
||||||
|
| _ -> unions, records, ty :: others
|
||||||
|
)
|
||||||
|
|
||||||
|
if not others.IsEmpty then
|
||||||
|
failwith
|
||||||
|
$"Error: all types recursively defined together with a CreateCatamorphism type must be discriminated unions or records. %+A{others}"
|
||||||
|
|
||||||
|
Some (ns, taggedType, unions, records)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
|
||||||
|
let modules =
|
||||||
|
namespaceAndTypes
|
||||||
|
|> List.map (fun (ns, taggedType, unions, records) ->
|
||||||
|
CataGenerator.createModule opens ns taggedType unions records
|
||||||
|
)
|
||||||
|
|
||||||
|
Output.Ast modules
|
||||||
|
@@ -1,7 +1,9 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System.IO
|
||||||
open System.Net.Http
|
open System.Net.Http
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
type internal HttpClientGeneratorOutputSpec =
|
type internal HttpClientGeneratorOutputSpec =
|
||||||
{
|
{
|
||||||
@@ -12,6 +14,17 @@ type internal HttpClientGeneratorOutputSpec =
|
|||||||
module internal HttpClientGenerator =
|
module internal HttpClientGenerator =
|
||||||
open Fantomas.FCS.Text.Range
|
open Fantomas.FCS.Text.Range
|
||||||
|
|
||||||
|
let outputFile = FileInfo "/tmp/output.txt"
|
||||||
|
|
||||||
|
// do
|
||||||
|
// use _ = File.Create outputFile.FullName
|
||||||
|
// ()
|
||||||
|
|
||||||
|
let log (line : string) =
|
||||||
|
// use w = outputFile.AppendText ()
|
||||||
|
// w.WriteLine line
|
||||||
|
()
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
type PathSpec =
|
type PathSpec =
|
||||||
| Verbatim of string
|
| Verbatim of string
|
||||||
@@ -60,6 +73,9 @@ module internal HttpClientGenerator =
|
|||||||
BaseAddress : SynExpr option
|
BaseAddress : SynExpr option
|
||||||
BasePath : SynExpr option
|
BasePath : SynExpr option
|
||||||
Accessibility : SynAccess option
|
Accessibility : SynAccess option
|
||||||
|
/// Headers which apply *only* to this endpoint.
|
||||||
|
/// For example, SynConst "Authorization" and SynConst "token BLAH".
|
||||||
|
Headers : (SynExpr * SynExpr) list
|
||||||
}
|
}
|
||||||
|
|
||||||
let httpMethodString (m : HttpMethod) : string =
|
let httpMethodString (m : HttpMethod) : string =
|
||||||
@@ -219,7 +235,7 @@ module internal HttpClientGenerator =
|
|||||||
SynExpr.CreateConst ("{" + substituteId + "}")
|
SynExpr.CreateConst ("{" + substituteId + "}")
|
||||||
SynExpr.callMethod "ToString" (SynExpr.createIdent' varName)
|
SynExpr.callMethod "ToString" (SynExpr.createIdent' varName)
|
||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (
|
||||||
SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
|
SynExpr.createLongIdent [ "System" ; "Uri" ; "EscapeDataString" ]
|
||||||
)
|
)
|
||||||
])
|
])
|
||||||
| _ -> template
|
| _ -> template
|
||||||
@@ -271,9 +287,7 @@ module internal HttpClientGenerator =
|
|||||||
SynExpr.createIdent' firstValueId
|
SynExpr.createIdent' firstValueId
|
||||||
|> SynExpr.toString firstValue.Type
|
|> SynExpr.toString firstValue.Type
|
||||||
|> SynExpr.paren
|
|> SynExpr.paren
|
||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Uri" ; "EscapeDataString" ])
|
||||||
SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
|
|
||||||
)
|
|
||||||
|> SynExpr.paren
|
|> SynExpr.paren
|
||||||
|> SynExpr.plus (SynExpr.plus urlSeparator (SynExpr.CreateConst (firstKey + "=")))
|
|> SynExpr.plus (SynExpr.plus urlSeparator (SynExpr.CreateConst (firstKey + "=")))
|
||||||
|
|
||||||
@@ -286,9 +300,7 @@ module internal HttpClientGenerator =
|
|||||||
|
|
||||||
SynExpr.toString paramValue.Type (SynExpr.createIdent' paramValueId)
|
SynExpr.toString paramValue.Type (SynExpr.createIdent' paramValueId)
|
||||||
|> SynExpr.paren
|
|> SynExpr.paren
|
||||||
|> SynExpr.pipeThroughFunction (
|
|> SynExpr.pipeThroughFunction (SynExpr.createLongIdent [ "System" ; "Uri" ; "EscapeDataString" ])
|
||||||
SynExpr.createLongIdent [ "System" ; "Web" ; "HttpUtility" ; "UrlEncode" ]
|
|
||||||
)
|
|
||||||
|> SynExpr.paren
|
|> SynExpr.paren
|
||||||
|> SynExpr.plus (SynExpr.plus uri (SynExpr.CreateConst ("&" + paramKey + "=")))
|
|> SynExpr.plus (SynExpr.plus uri (SynExpr.CreateConst ("&" + paramKey + "=")))
|
||||||
)
|
)
|
||||||
@@ -321,15 +333,33 @@ module internal HttpClientGenerator =
|
|||||||
|> SynExpr.createMatch baseAddress
|
|> SynExpr.createMatch baseAddress
|
||||||
|> SynExpr.paren
|
|> SynExpr.paren
|
||||||
|
|
||||||
|
let baseAddress =
|
||||||
|
match info.BasePath with
|
||||||
|
| None -> baseAddress
|
||||||
|
| Some basePath ->
|
||||||
|
[
|
||||||
|
yield baseAddress
|
||||||
|
|
||||||
|
yield
|
||||||
|
SynExpr.applyFunction
|
||||||
|
uriIdent
|
||||||
|
(SynExpr.tuple
|
||||||
|
[ basePath ; SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ] ])
|
||||||
|
]
|
||||||
|
|> SynExpr.tuple
|
||||||
|
|> SynExpr.applyFunction uriIdent
|
||||||
|
|
||||||
[
|
[
|
||||||
baseAddress
|
yield baseAddress
|
||||||
SynExpr.applyFunction
|
|
||||||
uriIdent
|
yield
|
||||||
(SynExpr.tuple
|
SynExpr.applyFunction
|
||||||
[
|
uriIdent
|
||||||
requestUriTrailer
|
(SynExpr.tuple
|
||||||
SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ]
|
[
|
||||||
])
|
requestUriTrailer
|
||||||
|
SynExpr.createLongIdent [ "System" ; "UriKind" ; "Relative" ]
|
||||||
|
])
|
||||||
]
|
]
|
||||||
|> SynExpr.tuple
|
|> SynExpr.tuple
|
||||||
|> SynExpr.applyFunction uriIdent
|
|> SynExpr.applyFunction uriIdent
|
||||||
@@ -404,14 +434,54 @@ module internal HttpClientGenerator =
|
|||||||
retType
|
retType
|
||||||
(SynExpr.createIdent "jsonNode")
|
(SynExpr.createIdent "jsonNode")
|
||||||
|
|
||||||
|
let contentTypeHeader, memberHeaders =
|
||||||
|
info.Headers
|
||||||
|
|> List.partition (fun (headerName, headerValue) ->
|
||||||
|
match headerName |> SynExpr.stripOptionalParen with
|
||||||
|
| SynExpr.Const (SynConst.String ("Content-Type", _, _), _) -> true
|
||||||
|
| _ -> false
|
||||||
|
)
|
||||||
|
|
||||||
|
let contentTypeHeader =
|
||||||
|
match contentTypeHeader with
|
||||||
|
| [] -> None
|
||||||
|
| [ _, ct ] -> Some (SynExpr.stripOptionalParen ct)
|
||||||
|
| _ -> failwith "Unexpectedly got multiple Content-Type headers"
|
||||||
|
|
||||||
|
let createStringContent (contents : SynExpr) =
|
||||||
|
SynExpr.createNew
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ])
|
||||||
|
(SynExpr.tupleNoParen
|
||||||
|
[
|
||||||
|
yield contents
|
||||||
|
match contentTypeHeader with
|
||||||
|
| None -> ()
|
||||||
|
| Some ch ->
|
||||||
|
yield SynExpr.createNull ()
|
||||||
|
// Sigh, Gitea in particular passes "json" here
|
||||||
|
match ch with
|
||||||
|
| SynExpr.Const (SynConst.String ("json", _, _), _) ->
|
||||||
|
yield SynExpr.CreateConst "application/json"
|
||||||
|
| SynExpr.Const (SynConst.String ("html", _, _), _) -> yield SynExpr.CreateConst "text/html"
|
||||||
|
| _ -> yield ch
|
||||||
|
])
|
||||||
|
|
||||||
let handleBodyParams =
|
let handleBodyParams =
|
||||||
match bodyParam with
|
match bodyParam with
|
||||||
| None -> []
|
| None -> []
|
||||||
| Some (bodyParamType, bodyParamName) ->
|
| Some (bodyParamType, bodyParamName) ->
|
||||||
match bodyParamType with
|
match bodyParamType with
|
||||||
| BodyParamMethods.StreamContent
|
|
||||||
| BodyParamMethods.ByteArrayContent
|
|
||||||
| BodyParamMethods.StringContent ->
|
| BodyParamMethods.StringContent ->
|
||||||
|
[
|
||||||
|
Let ("queryParams", createStringContent (SynExpr.createIdent' bodyParamName))
|
||||||
|
Do (
|
||||||
|
SynExpr.assign
|
||||||
|
(SynLongIdent.createS' [ "httpMessage" ; "Content" ])
|
||||||
|
(SynExpr.createIdent "queryParams")
|
||||||
|
)
|
||||||
|
]
|
||||||
|
| BodyParamMethods.StreamContent
|
||||||
|
| BodyParamMethods.ByteArrayContent ->
|
||||||
[
|
[
|
||||||
Let (
|
Let (
|
||||||
"queryParams",
|
"queryParams",
|
||||||
@@ -438,22 +508,22 @@ module internal HttpClientGenerator =
|
|||||||
[
|
[
|
||||||
Let (
|
Let (
|
||||||
"queryParams",
|
"queryParams",
|
||||||
SynExpr.createNew
|
createStringContent (
|
||||||
(SynType.createLongIdent' [ "System" ; "Net" ; "Http" ; "StringContent" ])
|
SynExpr.createIdent' bodyParamName
|
||||||
(SynExpr.createIdent' bodyParamName
|
|> SynExpr.pipeThroughFunction (fst (JsonSerializeGenerator.serializeNode ty))
|
||||||
|> SynExpr.pipeThroughFunction (fst (JsonSerializeGenerator.serializeNode ty))
|
|> SynExpr.pipeThroughFunction (
|
||||||
|> SynExpr.pipeThroughFunction (
|
SynExpr.createLambda
|
||||||
SynExpr.createLambda
|
"node"
|
||||||
"node"
|
(SynExpr.ifThenElse
|
||||||
(SynExpr.ifThenElse
|
(SynExpr.applyFunction
|
||||||
(SynExpr.applyFunction
|
(SynExpr.createIdent "isNull")
|
||||||
(SynExpr.createIdent "isNull")
|
(SynExpr.createIdent "node"))
|
||||||
(SynExpr.createIdent "node"))
|
(SynExpr.applyFunction
|
||||||
(SynExpr.applyFunction
|
(SynExpr.createLongIdent [ "node" ; "ToJsonString" ])
|
||||||
(SynExpr.createLongIdent [ "node" ; "ToJsonString" ])
|
(SynExpr.CreateConst ()))
|
||||||
(SynExpr.CreateConst ()))
|
(SynExpr.CreateConst "null"))
|
||||||
(SynExpr.CreateConst "null"))
|
)
|
||||||
))
|
)
|
||||||
)
|
)
|
||||||
Do (
|
Do (
|
||||||
SynExpr.assign
|
SynExpr.assign
|
||||||
@@ -522,6 +592,16 @@ module internal HttpClientGenerator =
|
|||||||
|> Do
|
|> Do
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let setMemberHeaders =
|
||||||
|
memberHeaders
|
||||||
|
|> List.map (fun (headerName, headerValue) ->
|
||||||
|
// Best-effort: assume this is a message header.
|
||||||
|
SynExpr.applyFunction
|
||||||
|
(SynExpr.createLongIdent [ "httpMessage" ; "Headers" ; "Add" ])
|
||||||
|
(SynExpr.tuple [ headerName ; headerValue ])
|
||||||
|
|> Do
|
||||||
|
)
|
||||||
|
|
||||||
[
|
[
|
||||||
yield LetBang ("ct", SynExpr.createLongIdent [ "Async" ; "CancellationToken" ])
|
yield LetBang ("ct", SynExpr.createLongIdent [ "Async" ; "CancellationToken" ])
|
||||||
yield Let ("uri", requestUri)
|
yield Let ("uri", requestUri)
|
||||||
@@ -537,6 +617,7 @@ module internal HttpClientGenerator =
|
|||||||
|
|
||||||
yield! setVariableHeaders
|
yield! setVariableHeaders
|
||||||
yield! setConstantHeaders
|
yield! setConstantHeaders
|
||||||
|
yield! setMemberHeaders
|
||||||
|
|
||||||
yield
|
yield
|
||||||
LetBang (
|
LetBang (
|
||||||
@@ -563,6 +644,9 @@ module internal HttpClientGenerator =
|
|||||||
yield jsonNode
|
yield jsonNode
|
||||||
| String -> yield responseString
|
| String -> yield responseString
|
||||||
| Stream -> yield responseStream
|
| Stream -> yield responseStream
|
||||||
|
| UnitType ->
|
||||||
|
// What we're returning doesn't depend on the content, so don't bother!
|
||||||
|
()
|
||||||
| _ ->
|
| _ ->
|
||||||
yield responseStream
|
yield responseStream
|
||||||
yield jsonNode
|
yield jsonNode
|
||||||
@@ -647,6 +731,15 @@ module internal HttpClientGenerator =
|
|||||||
| _ -> None
|
| _ -> None
|
||||||
)
|
)
|
||||||
|
|
||||||
|
let insertTrailingSlash (path : SynExpr) : SynExpr =
|
||||||
|
match path |> SynExpr.stripOptionalParen with
|
||||||
|
| SynExpr.Const (SynConst.String (s, _, _), _) ->
|
||||||
|
if s.EndsWith '/' then
|
||||||
|
path
|
||||||
|
else
|
||||||
|
SynExpr.CreateConst (s + "/")
|
||||||
|
| _ -> SynExpr.plus (SynExpr.paren path) (SynExpr.CreateConst "/")
|
||||||
|
|
||||||
let createModule
|
let createModule
|
||||||
(opens : SynOpenDeclTarget list)
|
(opens : SynOpenDeclTarget list)
|
||||||
(ns : LongIdent)
|
(ns : LongIdent)
|
||||||
@@ -676,8 +769,17 @@ module internal HttpClientGenerator =
|
|||||||
"Expected constant header parameters to be of the form [<Header (key, value)>], but got more than two args"
|
"Expected constant header parameters to be of the form [<Header (key, value)>], but got more than two args"
|
||||||
)
|
)
|
||||||
|
|
||||||
let baseAddress = extractBaseAddress interfaceType.Attributes
|
let baseAddress =
|
||||||
let basePath = extractBasePath interfaceType.Attributes
|
extractBaseAddress interfaceType.Attributes
|
||||||
|
// We artificially insert a trailing slash because this is almost certainly
|
||||||
|
// not meant to be an endpoint itself.
|
||||||
|
|> Option.map insertTrailingSlash
|
||||||
|
|
||||||
|
let basePath =
|
||||||
|
extractBasePath interfaceType.Attributes
|
||||||
|
// We artificially insert a trailing slash because this is almost certainly
|
||||||
|
// not meant to be an endpoint itself.
|
||||||
|
|> Option.map insertTrailingSlash
|
||||||
|
|
||||||
let properties =
|
let properties =
|
||||||
interfaceType.Properties
|
interfaceType.Properties
|
||||||
@@ -705,6 +807,16 @@ module internal HttpClientGenerator =
|
|||||||
|> List.map (fun mem ->
|
|> List.map (fun mem ->
|
||||||
let httpMethod, url = extractHttpInformation mem.Attributes
|
let httpMethod, url = extractHttpInformation mem.Attributes
|
||||||
|
|
||||||
|
let specificHeaders =
|
||||||
|
extractHeaderInformation mem.Attributes
|
||||||
|
|> List.map (fun l ->
|
||||||
|
match l with
|
||||||
|
| [ x ; y ] -> x, y
|
||||||
|
| _ ->
|
||||||
|
failwith
|
||||||
|
$"Expected Header attribute on member %s{mem.Identifier.idText} to have exactly two arguments."
|
||||||
|
)
|
||||||
|
|
||||||
let shouldEnsureSuccess = not (shouldAllowAnyStatusCode mem.Attributes)
|
let shouldEnsureSuccess = not (shouldAllowAnyStatusCode mem.Attributes)
|
||||||
|
|
||||||
let returnType =
|
let returnType =
|
||||||
@@ -745,6 +857,7 @@ module internal HttpClientGenerator =
|
|||||||
BaseAddress = baseAddress
|
BaseAddress = baseAddress
|
||||||
BasePath = basePath
|
BasePath = basePath
|
||||||
Accessibility = mem.Accessibility
|
Accessibility = mem.Accessibility
|
||||||
|
Headers = specificHeaders
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|> List.map (constructMember constantHeaders properties)
|
|> List.map (constructMember constantHeaders properties)
|
||||||
@@ -876,6 +989,10 @@ type HttpClientGenerator () =
|
|||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) =
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let targetedTypes =
|
||||||
|
MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|
||||||
let ast, _ =
|
let ast, _ =
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
@@ -888,13 +1005,33 @@ type HttpClientGenerator () =
|
|||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<HttpClientAttribute> typeDef with
|
match SynTypeDefn.getAttribute typeof<HttpClientAttribute>.Name typeDef with
|
||||||
| None -> None
|
| None ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| DesiredGenerator.HttpClient arg ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
ExtensionMethods =
|
||||||
|
arg
|
||||||
|
|> Option.defaultValue
|
||||||
|
HttpClientAttribute.DefaultIsExtensionMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
| _ -> None
|
||||||
| Some attr ->
|
| Some attr ->
|
||||||
let arg =
|
let arg =
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
| SynExpr.Const (SynConst.Bool value, _) -> value
|
| SynExpr.Const (SynConst.Bool value, _) -> value
|
||||||
| SynExpr.Const (SynConst.Unit, _) -> JsonParseAttribute.DefaultIsExtensionMethod
|
| SynExpr.Const (SynConst.Unit, _) -> HttpClientAttribute.DefaultIsExtensionMethod
|
||||||
| arg ->
|
| arg ->
|
||||||
failwith
|
failwith
|
||||||
$"Unrecognised argument %+A{arg} to [<%s{nameof HttpClientAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
$"Unrecognised argument %+A{arg} to [<%s{nameof HttpClientAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only."
|
||||||
|
@@ -3,6 +3,7 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
open System
|
open System
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
type internal GenerateMockOutputSpec =
|
type internal GenerateMockOutputSpec =
|
||||||
{
|
{
|
||||||
@@ -283,6 +284,10 @@ type InterfaceMockGenerator () =
|
|||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) =
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let targetedTypes =
|
||||||
|
MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|
||||||
let ast, _ =
|
let ast, _ =
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
@@ -294,7 +299,27 @@ type InterfaceMockGenerator () =
|
|||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<GenerateMockAttribute> typeDef with
|
match Ast.getAttribute<GenerateMockAttribute> typeDef with
|
||||||
| None -> None
|
| None ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| DesiredGenerator.InterfaceMock arg ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
IsInternal =
|
||||||
|
arg
|
||||||
|
|> Option.defaultValue GenerateMockAttribute.DefaultIsInternal
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
| Some attr ->
|
| Some attr ->
|
||||||
let arg =
|
let arg =
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
@@ -4,6 +4,7 @@ open System
|
|||||||
open System.Text
|
open System.Text
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
open Fantomas.FCS.SyntaxTrivia
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
type internal JsonParseOutputSpec =
|
type internal JsonParseOutputSpec =
|
||||||
{
|
{
|
||||||
@@ -280,7 +281,7 @@ module internal JsonParseGenerator =
|
|||||||
parseNumberType options propertyName node primType
|
parseNumberType options propertyName node primType
|
||||||
|> SynExpr.pipeThroughFunction (Measure.getLanguagePrimitivesMeasure primType)
|
|> SynExpr.pipeThroughFunction (Measure.getLanguagePrimitivesMeasure primType)
|
||||||
| JsonNode -> node
|
| JsonNode -> node
|
||||||
| Unit -> SynExpr.CreateConst ()
|
| UnitType -> SynExpr.CreateConst ()
|
||||||
| _ ->
|
| _ ->
|
||||||
// Let's just hope that we've also got our own type annotation!
|
// Let's just hope that we've also got our own type annotation!
|
||||||
let typeName =
|
let typeName =
|
||||||
@@ -702,6 +703,10 @@ type JsonParseGenerator () =
|
|||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) =
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let targetedTypes =
|
||||||
|
MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|
||||||
let ast, _ =
|
let ast, _ =
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
@@ -723,8 +728,29 @@ type JsonParseGenerator () =
|
|||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<JsonParseAttribute> typeDef with
|
match SynTypeDefn.getAttribute typeof<JsonParseAttribute>.Name typeDef with
|
||||||
| None -> None
|
| None ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| DesiredGenerator.JsonParse arg ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
ExtensionMethods =
|
||||||
|
arg
|
||||||
|
|> Option.defaultValue
|
||||||
|
JsonParseAttribute.DefaultIsExtensionMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
| Some attr ->
|
| Some attr ->
|
||||||
let arg =
|
let arg =
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
@@ -3,6 +3,7 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
open System
|
open System
|
||||||
open System.Text
|
open System.Text
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
type internal JsonSerializeOutputSpec =
|
type internal JsonSerializeOutputSpec =
|
||||||
{
|
{
|
||||||
@@ -147,7 +148,7 @@ module internal JsonSerializeGenerator =
|
|||||||
|> SynExpr.createLambda "field"
|
|> SynExpr.createLambda "field"
|
||||||
|> fun e -> e, false
|
|> fun e -> e, false
|
||||||
| JsonNode -> SynExpr.createIdent "id", true
|
| JsonNode -> SynExpr.createIdent "id", true
|
||||||
| Unit ->
|
| UnitType ->
|
||||||
SynExpr.createLambda
|
SynExpr.createLambda
|
||||||
"value"
|
"value"
|
||||||
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
(SynExpr.createLongIdent [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonObject" ]
|
||||||
@@ -519,6 +520,10 @@ type JsonSerializeGenerator () =
|
|||||||
member _.ValidInputExtensions = [ ".fs" ]
|
member _.ValidInputExtensions = [ ".fs" ]
|
||||||
|
|
||||||
member _.Generate (context : GeneratorContext) =
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let targetedTypes =
|
||||||
|
MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse)
|
||||||
|
|
||||||
let ast, _ =
|
let ast, _ =
|
||||||
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head
|
||||||
|
|
||||||
@@ -540,8 +545,29 @@ type JsonSerializeGenerator () =
|
|||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<JsonSerializeAttribute> typeDef with
|
match SynTypeDefn.getAttribute typeof<JsonSerializeAttribute>.Name typeDef with
|
||||||
| None -> None
|
| None ->
|
||||||
|
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
|
||||||
|
|
||||||
|
match Map.tryFind name targetedTypes with
|
||||||
|
| Some desired ->
|
||||||
|
desired
|
||||||
|
|> List.tryPick (fun generator ->
|
||||||
|
match generator with
|
||||||
|
| DesiredGenerator.JsonSerialize arg ->
|
||||||
|
let spec =
|
||||||
|
{
|
||||||
|
ExtensionMethods =
|
||||||
|
arg
|
||||||
|
|> Option.defaultValue
|
||||||
|
JsonSerializeAttribute.DefaultIsExtensionMethod
|
||||||
|
}
|
||||||
|
|
||||||
|
Some (typeDef, spec)
|
||||||
|
| _ -> None
|
||||||
|
)
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
| Some attr ->
|
| Some attr ->
|
||||||
let arg =
|
let arg =
|
||||||
match SynExpr.stripOptionalParen attr.ArgExpr with
|
match SynExpr.stripOptionalParen attr.ArgExpr with
|
||||||
|
@@ -12,3 +12,12 @@ module private List =
|
|||||||
)
|
)
|
||||||
|
|
||||||
List.rev xs, List.rev ys
|
List.rev xs, List.rev ys
|
||||||
|
|
||||||
|
let allSome<'a> (l : 'a option list) : 'a list option =
|
||||||
|
let rec go acc (l : 'a option list) =
|
||||||
|
match l with
|
||||||
|
| [] -> Some (List.rev acc)
|
||||||
|
| None :: _ -> None
|
||||||
|
| Some head :: tail -> go (head :: acc) tail
|
||||||
|
|
||||||
|
go [] l
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal Measure =
|
module internal Measure =
|
||||||
@@ -20,5 +21,4 @@ module internal Measure =
|
|||||||
| l ->
|
| l ->
|
||||||
let l = String.concat "." l
|
let l = String.concat "." l
|
||||||
failwith $"unrecognised type for measure: %s{l}"
|
failwith $"unrecognised type for measure: %s{l}"
|
||||||
|
|
||||||
|> SynExpr.createLongIdent
|
|> SynExpr.createLongIdent
|
||||||
|
64
WoofWare.Myriad.Plugins/MyriadParamParser.fs
Normal file
64
WoofWare.Myriad.Plugins/MyriadParamParser.fs
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System.Collections.Generic
|
||||||
|
|
||||||
|
type internal DesiredGenerator =
|
||||||
|
| InterfaceMock of isInternal : bool option
|
||||||
|
| JsonParse of extensionMethod : bool option
|
||||||
|
| JsonSerialize of extensionMethod : bool option
|
||||||
|
| HttpClient of extensionMethod : bool option
|
||||||
|
|
||||||
|
static member Parse (s : string) =
|
||||||
|
match s with
|
||||||
|
| "GenerateMock" -> DesiredGenerator.InterfaceMock None
|
||||||
|
| "GenerateMock(true)" -> DesiredGenerator.InterfaceMock (Some true)
|
||||||
|
| "GenerateMock(false)" -> DesiredGenerator.InterfaceMock (Some false)
|
||||||
|
| "JsonParse" -> DesiredGenerator.JsonParse None
|
||||||
|
| "JsonParse(true)" -> DesiredGenerator.JsonParse (Some true)
|
||||||
|
| "JsonParse(false)" -> DesiredGenerator.JsonParse (Some false)
|
||||||
|
| "JsonSerialize" -> DesiredGenerator.JsonSerialize None
|
||||||
|
| "JsonSerialize(true)" -> DesiredGenerator.JsonSerialize (Some true)
|
||||||
|
| "JsonSerialize(false)" -> DesiredGenerator.JsonSerialize (Some false)
|
||||||
|
| "HttpClient" -> DesiredGenerator.HttpClient None
|
||||||
|
| "HttpClient(true)" -> DesiredGenerator.HttpClient (Some true)
|
||||||
|
| "HttpClient(false)" -> DesiredGenerator.HttpClient (Some false)
|
||||||
|
| _ -> failwith $"Failed to parse as a generator specification: %s{s}"
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal MyriadParamParser =
|
||||||
|
(*
|
||||||
|
An apparent bug in Myriad's argument parsing means that this:
|
||||||
|
|
||||||
|
<MyriadParams>
|
||||||
|
<Foo>bar</Foo>
|
||||||
|
<Baz>quux</Baz>
|
||||||
|
</MyriadParams>
|
||||||
|
|
||||||
|
leads to this:
|
||||||
|
|
||||||
|
Foo = "bar;Baz=quux"
|
||||||
|
|
||||||
|
I'm not going to put effort into fixing Myriad, though, because I want
|
||||||
|
to build something much more powerful instead.
|
||||||
|
*)
|
||||||
|
|
||||||
|
/// Call this with `context.AdditionalParameters`.
|
||||||
|
let render (pars : IDictionary<string, string>) : Map<string, string> =
|
||||||
|
match pars.Count with
|
||||||
|
| 0 -> Map.empty
|
||||||
|
| 1 ->
|
||||||
|
let (KeyValue (key, value)) = pars |> Seq.exactlyOne
|
||||||
|
|
||||||
|
match value.Split ';' |> Seq.toList with
|
||||||
|
| [] -> failwith "LOGIC ERROR"
|
||||||
|
| value :: rest ->
|
||||||
|
rest
|
||||||
|
|> Seq.map (fun v ->
|
||||||
|
let split = v.Split '='
|
||||||
|
split.[0], String.concat "=" split.[1..]
|
||||||
|
)
|
||||||
|
|> Seq.append (Seq.singleton (key, value))
|
||||||
|
|> Map.ofSeq
|
||||||
|
| _ ->
|
||||||
|
// assume the Myriad bug is fixed!
|
||||||
|
pars |> Seq.map (fun (KeyValue (k, v)) -> k, v) |> Map.ofSeq
|
@@ -1,32 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal Primitives =
|
|
||||||
/// Given e.g. "byte", returns "System.Byte".
|
|
||||||
let qualifyType (typeName : string) : LongIdent option =
|
|
||||||
match typeName with
|
|
||||||
| "float32"
|
|
||||||
| "single" -> [ "System" ; "Single" ] |> Some
|
|
||||||
| "float"
|
|
||||||
| "double" -> [ "System" ; "Double" ] |> Some
|
|
||||||
| "byte"
|
|
||||||
| "uint8" -> [ "System" ; "Byte" ] |> Some
|
|
||||||
| "sbyte"
|
|
||||||
| "int8" -> [ "System" ; "SByte" ] |> Some
|
|
||||||
| "int16" -> [ "System" ; "Int16" ] |> Some
|
|
||||||
| "int"
|
|
||||||
| "int32" -> [ "System" ; "Int32" ] |> Some
|
|
||||||
| "int64" -> [ "System" ; "Int64" ] |> Some
|
|
||||||
| "uint16" -> [ "System" ; "UInt16" ] |> Some
|
|
||||||
| "uint"
|
|
||||||
| "uint32" -> [ "System" ; "UInt32" ] |> Some
|
|
||||||
| "uint64" -> [ "System" ; "UInt64" ] |> Some
|
|
||||||
| "char" -> [ "System" ; "Char" ] |> Some
|
|
||||||
| "decimal" -> [ "System" ; "Decimal" ] |> Some
|
|
||||||
| "string" -> [ "System" ; "String" ] |> Some
|
|
||||||
| "bool" -> [ "System" ; "Boolean" ] |> Some
|
|
||||||
| _ -> None
|
|
||||||
|> Option.map (List.map (fun i -> (Ident (i, range0))))
|
|
@@ -2,6 +2,7 @@ namespace WoofWare.Myriad.Plugins
|
|||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
open Fantomas.FCS.Syntax
|
||||||
open Fantomas.FCS.Xml
|
open Fantomas.FCS.Xml
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
[<RequireQualifiedAccess>]
|
||||||
module internal RemoveOptionsGenerator =
|
module internal RemoveOptionsGenerator =
|
||||||
@@ -150,7 +151,10 @@ type RemoveOptionsGenerator () =
|
|||||||
let namespaceAndRecords =
|
let namespaceAndRecords =
|
||||||
records
|
records
|
||||||
|> List.choose (fun (ns, types) ->
|
|> List.choose (fun (ns, types) ->
|
||||||
match types |> List.filter Ast.hasAttribute<RemoveOptionsAttribute> with
|
match
|
||||||
|
types
|
||||||
|
|> List.filter (SynTypeDefn.hasAttribute typeof<RemoveOptionsAttribute>.Name)
|
||||||
|
with
|
||||||
| [] -> None
|
| [] -> None
|
||||||
| types ->
|
| types ->
|
||||||
let types =
|
let types =
|
||||||
|
@@ -1,37 +1,295 @@
|
|||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties inherit obj, implements WoofWare.Myriad.Plugins.AdditionalProperties System.IEquatable, System.Collections.IStructuralEquatable - union type with 2 cases
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained inherit WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained.get_Item [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Constrained.Item [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags.Constrained [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties+Tags.Never [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Equals [method]: (WoofWare.Myriad.Plugins.AdditionalProperties, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_IsConstrained [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_IsNever [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_Never [static method]: unit -> WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.IsConstrained [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.IsNever [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Never [static property]: [read-only] WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.NewConstrained [static method]: WoofWare.Myriad.Plugins.Definition -> WoofWare.Myriad.Plugins.AdditionalProperties
|
||||||
|
WoofWare.Myriad.Plugins.AdditionalProperties.Tag [property]: [read-only] int
|
||||||
WoofWare.Myriad.Plugins.ArgParserGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.ArgParserGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.ArgParserGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.ArgParserGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition inherit obj, implements WoofWare.Myriad.Plugins.ArrayTypeDefinition System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition..ctor [constructor]: WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Equals [method]: (WoofWare.Myriad.Plugins.ArrayTypeDefinition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.get_Items [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Items [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.ArrayTypeDefinition.Parse [static method]: System.Text.Json.Nodes.JsonNode -> WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.CreateCatamorphismGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.Definition inherit obj, implements WoofWare.Myriad.Plugins.Definition System.IEquatable, System.Collections.IStructuralEquatable - union type with 8 cases
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array.get_Item [method]: unit -> WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Array.Item [property]: [read-only] WoofWare.Myriad.Plugins.ArrayTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Handle.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer.format [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Integer.get_format [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object inherit WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object.get_Item [method]: unit -> WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Object.Item [property]: [read-only] WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Array [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Boolean [static field]: int = 4
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.File [static field]: int = 7
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Handle [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Integer [static field]: int = 6
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Object [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.String [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.Definition+Tags.Unspecified [static field]: int = 5
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Boolean [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Equals [method]: (WoofWare.Myriad.Plugins.Definition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.File [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Boolean [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_File [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsArray [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsBoolean [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsFile [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsHandle [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsInteger [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsObject [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsString [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_IsUnspecified [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_String [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.Definition.get_Unspecified [static method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsArray [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsBoolean [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsFile [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsHandle [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsInteger [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsObject [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsString [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.IsUnspecified [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewArray [static method]: WoofWare.Myriad.Plugins.ArrayTypeDefinition -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewHandle [static method]: string -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewInteger [static method]: string option -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.NewObject [static method]: WoofWare.Myriad.Plugins.ObjectTypeDefinition -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.String [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.Definition.Unspecified [static property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.HttpClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.HttpClientGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.HttpClientGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod inherit obj, implements WoofWare.Myriad.Plugins.HttpMethod System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.HttpMethod System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 8 cases
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Delete [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Get [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Head [static field]: int = 5
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Options [static field]: int = 4
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Patch [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Post [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Put [static field]: int = 6
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod+Tags.Trace [static field]: int = 7
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Delete [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Equals [method]: (WoofWare.Myriad.Plugins.HttpMethod, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Get [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Delete [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Get [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Head [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsDelete [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsGet [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsHead [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsOptions [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPatch [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPost [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsPut [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_IsTrace [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Options [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Patch [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Post [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Put [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.get_Trace [static method]: unit -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Head [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsDelete [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsGet [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsHead [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsOptions [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPatch [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPost [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsPut [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.IsTrace [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Options [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Parse [static method]: string -> WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Patch [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Post [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Put [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.ToDotNet [method]: unit -> System.Net.Http.HttpMethod
|
||||||
|
WoofWare.Myriad.Plugins.HttpMethod.Trace [static property]: [read-only] WoofWare.Myriad.Plugins.HttpMethod
|
||||||
WoofWare.Myriad.Plugins.InterfaceMockGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.InterfaceMockGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.InterfaceMockGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.InterfaceMockGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.JsonParseGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.JsonParseGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonParseGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.JsonSerializeGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.MimeType inherit obj, implements WoofWare.Myriad.Plugins.MimeType System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.MimeType System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Equals [method]: (WoofWare.Myriad.Plugins.MimeType, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.NewMimeType [static method]: string -> WoofWare.Myriad.Plugins.MimeType
|
||||||
|
WoofWare.Myriad.Plugins.MimeType.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition inherit obj, implements WoofWare.Myriad.Plugins.ObjectTypeDefinition System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition..ctor [constructor]: (string option, Map<string, WoofWare.Myriad.Plugins.Definition> option, Map<string, System.Text.Json.Nodes.JsonNode>, string list option, WoofWare.Myriad.Plugins.AdditionalProperties option, System.Text.Json.Nodes.JsonObject option)
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.AdditionalProperties [property]: [read-only] WoofWare.Myriad.Plugins.AdditionalProperties option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Description [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Equals [method]: (WoofWare.Myriad.Plugins.ObjectTypeDefinition, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Example [property]: [read-only] System.Text.Json.Nodes.JsonObject option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Extras [property]: [read-only] Map<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_AdditionalProperties [method]: unit -> WoofWare.Myriad.Plugins.AdditionalProperties option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Description [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Example [method]: unit -> System.Text.Json.Nodes.JsonObject option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Extras [method]: unit -> Map<string, System.Text.Json.Nodes.JsonNode>
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Properties [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Definition> option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.get_Required [method]: unit -> string list option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.ObjectTypeDefinition
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Properties [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Definition> option
|
||||||
|
WoofWare.Myriad.Plugins.ObjectTypeDefinition.Required [property]: [read-only] string list option
|
||||||
|
WoofWare.Myriad.Plugins.OperationId inherit obj, implements WoofWare.Myriad.Plugins.OperationId System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.OperationId System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Equals [method]: (WoofWare.Myriad.Plugins.OperationId, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.get_Item [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Item [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.NewOperationId [static method]: string -> WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.OperationId.Tag [property]: [read-only] int
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn inherit obj, implements WoofWare.Myriad.Plugins.ParameterIn System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.ParameterIn System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 4 cases
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Path.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Query.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Body [static field]: int = 2
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Path [static field]: int = 0
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Query [static field]: int = 1
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Tags.Unrecognised [static field]: int = 3
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised inherit WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.get_name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.get_op [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn+Unrecognised.op [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Body [static property]: [read-only] WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Equals [method]: (WoofWare.Myriad.Plugins.ParameterIn, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_Body [static method]: unit -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsBody [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsPath [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsQuery [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_IsUnrecognised [method]: unit -> bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.get_Tag [method]: unit -> int
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsBody [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsPath [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsQuery [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.IsUnrecognised [property]: [read-only] bool
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewPath [static method]: string -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewQuery [static method]: string -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.NewUnrecognised [static method]: (string, string) -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.ParameterIn.Tag [property]: [read-only] int
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
WoofWare.Myriad.Plugins.RemoveOptionsGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
|
WoofWare.Myriad.Plugins.RemoveOptionsGenerator..ctor [constructor]: unit
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1 inherit obj
|
WoofWare.Myriad.Plugins.Response inherit obj, implements WoofWare.Myriad.Plugins.Response System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1..ctor [constructor]: (Fantomas.FCS.Syntax.SynAttribute list, 'Ident, Fantomas.FCS.Syntax.SynType)
|
WoofWare.Myriad.Plugins.Response..ctor [constructor]: (string, WoofWare.Myriad.Plugins.Definition)
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.Attrs [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list
|
WoofWare.Myriad.Plugins.Response.Description [property]: [read-only] string
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.get_Attrs [method]: unit -> Fantomas.FCS.Syntax.SynAttribute list
|
WoofWare.Myriad.Plugins.Response.Equals [method]: (WoofWare.Myriad.Plugins.Response, System.Collections.IEqualityComparer) -> bool
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.get_Ident [method]: unit -> 'Ident
|
WoofWare.Myriad.Plugins.Response.get_Description [method]: unit -> string
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.get_Type [method]: unit -> Fantomas.FCS.Syntax.SynType
|
WoofWare.Myriad.Plugins.Response.get_Schema [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.Ident [property]: [read-only] 'Ident
|
WoofWare.Myriad.Plugins.Response.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.Response
|
||||||
WoofWare.Myriad.Plugins.SynFieldData`1.Type [property]: [read-only] Fantomas.FCS.Syntax.SynType
|
WoofWare.Myriad.Plugins.Response.Schema [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
||||||
WoofWare.Myriad.Plugins.UnionCase inherit obj
|
WoofWare.Myriad.Plugins.Scheme inherit obj, implements WoofWare.Myriad.Plugins.Scheme System.IEquatable, System.Collections.IStructuralEquatable, WoofWare.Myriad.Plugins.Scheme System.IComparable, System.IComparable, System.Collections.IStructuralComparable - union type with 1 cases
|
||||||
WoofWare.Myriad.Plugins.UnionCase.mapIdentFields [static method]: ('a -> 'b) -> 'a WoofWare.Myriad.Plugins.UnionCase -> 'b WoofWare.Myriad.Plugins.UnionCase
|
WoofWare.Myriad.Plugins.Scheme.Equals [method]: (WoofWare.Myriad.Plugins.Scheme, System.Collections.IEqualityComparer) -> bool
|
||||||
WoofWare.Myriad.Plugins.UnionCase.ofSynUnionCase [static method]: Fantomas.FCS.Syntax.SynUnionCase -> Fantomas.FCS.Syntax.Ident option WoofWare.Myriad.Plugins.UnionCase
|
WoofWare.Myriad.Plugins.Scheme.get_Item [method]: unit -> string
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1 inherit obj
|
WoofWare.Myriad.Plugins.Scheme.get_Tag [method]: unit -> int
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1..ctor [constructor]: (Fantomas.FCS.Syntax.Ident, Fantomas.FCS.Xml.PreXmlDoc option, Fantomas.FCS.Syntax.SynAccess option, Fantomas.FCS.Syntax.SynAttribute list, 'ident WoofWare.Myriad.Plugins.SynFieldData list)
|
WoofWare.Myriad.Plugins.Scheme.Item [property]: [read-only] string
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.Access [property]: [read-only] Fantomas.FCS.Syntax.SynAccess option
|
WoofWare.Myriad.Plugins.Scheme.NewScheme [static method]: string -> WoofWare.Myriad.Plugins.Scheme
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.Attributes [property]: [read-only] Fantomas.FCS.Syntax.SynAttribute list
|
WoofWare.Myriad.Plugins.Scheme.Tag [property]: [read-only] int
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.Fields [property]: [read-only] 'ident WoofWare.Myriad.Plugins.SynFieldData list
|
WoofWare.Myriad.Plugins.Swagger inherit obj, implements WoofWare.Myriad.Plugins.Swagger System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.get_Access [method]: unit -> Fantomas.FCS.Syntax.SynAccess option
|
WoofWare.Myriad.Plugins.Swagger..ctor [constructor]: (WoofWare.Myriad.Plugins.MimeType list, WoofWare.Myriad.Plugins.MimeType list, WoofWare.Myriad.Plugins.Scheme list, System.Version, WoofWare.Myriad.Plugins.SwaggerInfo, string, Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>, Map<string, WoofWare.Myriad.Plugins.Definition>, Map<string, WoofWare.Myriad.Plugins.Response>)
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.get_Attributes [method]: unit -> Fantomas.FCS.Syntax.SynAttribute list
|
WoofWare.Myriad.Plugins.Swagger.BasePath [property]: [read-only] string
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.get_Fields [method]: unit -> 'ident WoofWare.Myriad.Plugins.SynFieldData list
|
WoofWare.Myriad.Plugins.Swagger.Consumes [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.get_Name [method]: unit -> Fantomas.FCS.Syntax.Ident
|
WoofWare.Myriad.Plugins.Swagger.Definitions [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Definition>
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.get_XmlDoc [method]: unit -> Fantomas.FCS.Xml.PreXmlDoc option
|
WoofWare.Myriad.Plugins.Swagger.Equals [method]: (WoofWare.Myriad.Plugins.Swagger, System.Collections.IEqualityComparer) -> bool
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.Name [property]: [read-only] Fantomas.FCS.Syntax.Ident
|
WoofWare.Myriad.Plugins.Swagger.get_BasePath [method]: unit -> string
|
||||||
WoofWare.Myriad.Plugins.UnionCase`1.XmlDoc [property]: [read-only] Fantomas.FCS.Xml.PreXmlDoc option
|
WoofWare.Myriad.Plugins.Swagger.get_Consumes [method]: unit -> WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Definitions [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Info [method]: unit -> WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Paths [method]: unit -> Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Produces [method]: unit -> WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Responses [method]: unit -> Map<string, WoofWare.Myriad.Plugins.Response>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Schemes [method]: unit -> WoofWare.Myriad.Plugins.Scheme list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.get_Swagger [method]: unit -> System.Version
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Info [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Paths [property]: [read-only] Map<string, Map<WoofWare.Myriad.Plugins.HttpMethod, WoofWare.Myriad.Plugins.SwaggerEndpoint>>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Produces [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Responses [property]: [read-only] Map<string, WoofWare.Myriad.Plugins.Response>
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Schemes [property]: [read-only] WoofWare.Myriad.Plugins.Scheme list
|
||||||
|
WoofWare.Myriad.Plugins.Swagger.Swagger [property]: [read-only] System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerClientGenerator inherit obj, implements Myriad.Core.IMyriadGenerator
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerClientGenerator..ctor [constructor]: unit
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint inherit obj, implements WoofWare.Myriad.Plugins.SwaggerEndpoint System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint..ctor [constructor]: (WoofWare.Myriad.Plugins.MimeType list option, WoofWare.Myriad.Plugins.MimeType list option, string list, string, WoofWare.Myriad.Plugins.OperationId, WoofWare.Myriad.Plugins.SwaggerParameter list option, Map<int, WoofWare.Myriad.Plugins.Definition>)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Consumes [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerEndpoint, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Consumes [method]: unit -> WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_OperationId [method]: unit -> WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Parameters [method]: unit -> WoofWare.Myriad.Plugins.SwaggerParameter list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Produces [method]: unit -> WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Responses [method]: unit -> Map<int, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Summary [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.get_Tags [method]: unit -> string list
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.OperationId [property]: [read-only] WoofWare.Myriad.Plugins.OperationId
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Parameters [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerParameter list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerEndpoint
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Produces [property]: [read-only] WoofWare.Myriad.Plugins.MimeType list option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Responses [property]: [read-only] Map<int, WoofWare.Myriad.Plugins.Definition>
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Summary [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerEndpoint.Tags [property]: [read-only] string list
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo inherit obj, implements WoofWare.Myriad.Plugins.SwaggerInfo System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo..ctor [constructor]: (string, string, WoofWare.Myriad.Plugins.SwaggerLicense, System.Version)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Description [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerInfo, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Description [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_License [method]: unit -> WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Title [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.get_Version [method]: unit -> System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.License [property]: [read-only] WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerInfo
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Title [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerInfo.Version [property]: [read-only] System.Version
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense inherit obj, implements WoofWare.Myriad.Plugins.SwaggerLicense System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense..ctor [constructor]: (string, System.Uri option, string option)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerLicense, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Identifier [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.get_Url [method]: unit -> System.Uri option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Identifier [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerLicense
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerLicense.Url [property]: [read-only] System.Uri option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerModule inherit obj
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerModule.parse [static method]: string -> WoofWare.Myriad.Plugins.Swagger
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter inherit obj, implements WoofWare.Myriad.Plugins.SwaggerParameter System.IEquatable, System.Collections.IStructuralEquatable
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter..ctor [constructor]: (WoofWare.Myriad.Plugins.Definition, string option, WoofWare.Myriad.Plugins.ParameterIn, string, bool option)
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Description [property]: [read-only] string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Equals [method]: (WoofWare.Myriad.Plugins.SwaggerParameter, System.Collections.IEqualityComparer) -> bool
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Description [method]: unit -> string option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_In [method]: unit -> WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Name [method]: unit -> string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Required [method]: unit -> bool option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.get_Type [method]: unit -> WoofWare.Myriad.Plugins.Definition
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.In [property]: [read-only] WoofWare.Myriad.Plugins.ParameterIn
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Name [property]: [read-only] string
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Parse [static method]: System.Text.Json.Nodes.JsonObject -> WoofWare.Myriad.Plugins.SwaggerParameter
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Required [property]: [read-only] bool option
|
||||||
|
WoofWare.Myriad.Plugins.SwaggerParameter.Type [property]: [read-only] WoofWare.Myriad.Plugins.Definition
|
576
WoofWare.Myriad.Plugins/Swagger.fs
Normal file
576
WoofWare.Myriad.Plugins/Swagger.fs
Normal file
@@ -0,0 +1,576 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
open System.Text.Json.Nodes
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal JsonHelpers =
|
||||||
|
let inline asString (n : JsonNode) (key : string) : string =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| s -> s.GetValue<string> ()
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asOpt<'ret> (n : JsonNode) (key : string) : 'ret option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| s -> s.GetValue<'ret> () |> Some
|
||||||
|
|
||||||
|
let inline asObj (n : JsonNode) (key : string) : JsonObject =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsObject ()
|
||||||
|
|
||||||
|
let inline asObjOpt (n : JsonNode) (key : string) : JsonObject option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsObject () |> Some
|
||||||
|
|
||||||
|
let inline asArr (n : JsonNode) (key : string) : JsonArray =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsArray ()
|
||||||
|
|
||||||
|
let inline asArrOpt (n : JsonNode) (key : string) : JsonArray option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsArray () |> Some
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asArr'<'v> (n : JsonNode) (key : string) : 'v list =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> failwith $"Expected node to have a key '%s{key}', but it did not: %s{n.ToJsonString ()}"
|
||||||
|
| o -> o.AsArray () |> Seq.map (fun v -> v.GetValue<'v> ()) |> Seq.toList
|
||||||
|
|
||||||
|
[<RequiresExplicitTypeArguments>]
|
||||||
|
let inline asArrOpt'<'v> (n : JsonNode) (key : string) : 'v list option =
|
||||||
|
match n.[key] with
|
||||||
|
| null -> None
|
||||||
|
| o -> o.AsArray () |> Seq.map (fun v -> v.GetValue<'v> ()) |> Seq.toList |> Some
|
||||||
|
|
||||||
|
/// A MIME type, like "application/json"
|
||||||
|
type MimeType =
|
||||||
|
/// A MIME type, like "application/json"
|
||||||
|
| MimeType of string
|
||||||
|
|
||||||
|
/// A URL scheme, like "https"
|
||||||
|
type Scheme =
|
||||||
|
/// A URL scheme, like "https"
|
||||||
|
| Scheme of string
|
||||||
|
|
||||||
|
/// "Licence information for the exposed API", whatever that means.
|
||||||
|
type SwaggerLicense =
|
||||||
|
{
|
||||||
|
/// "The license name used for the API", whatever that means.
|
||||||
|
Name : string
|
||||||
|
/// Link to the license used. Mutually exclusive with `Identifier`.
|
||||||
|
Url : Uri option
|
||||||
|
/// SPDX license identifier. Mutually exclusive with `Url`.
|
||||||
|
Identifier : string option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerLicense =
|
||||||
|
let name = asString node "name"
|
||||||
|
let url = asOpt<string> node "url" |> Option.map Uri
|
||||||
|
let identifier = asOpt<string> node "identifier"
|
||||||
|
|
||||||
|
match url, identifier with
|
||||||
|
| Some _, Some _ -> failwith "Invalid license spec: cannot supply both URL and identifier"
|
||||||
|
| _, _ -> ()
|
||||||
|
|
||||||
|
{
|
||||||
|
Name = name
|
||||||
|
Url = url
|
||||||
|
Identifier = identifier
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Overall information about the API described by this Swagger spec.
|
||||||
|
type SwaggerInfo =
|
||||||
|
{
|
||||||
|
/// Human-readable description of what this Swagger API is for.
|
||||||
|
/// Supports GitHub-flavoured markdown, apparently.
|
||||||
|
Description : string
|
||||||
|
/// Human-readable title of the service to which this is an API.
|
||||||
|
Title : string
|
||||||
|
/// The license applying to this schema. It's very unclear what this means.
|
||||||
|
/// The spec just says:
|
||||||
|
/// "Licence information for the exposed API"
|
||||||
|
License : SwaggerLicense
|
||||||
|
/// The version of this API (not the version of Swagger or the file defining the API!).
|
||||||
|
/// Strictly speaking this can be anything, but I am assuming it's roughly
|
||||||
|
/// SemVer.
|
||||||
|
Version : Version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerInfo =
|
||||||
|
let description = asString node "description"
|
||||||
|
let title = asString node "title"
|
||||||
|
let version = asString node "version" |> Version.Parse
|
||||||
|
let license = asObj node "license" |> SwaggerLicense.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = description
|
||||||
|
Title = title
|
||||||
|
License = license
|
||||||
|
Version = version
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "optional unique string used to describe an operation".
|
||||||
|
/// If present, these are assumed to be unique among all operations described
|
||||||
|
/// in the API.
|
||||||
|
type OperationId =
|
||||||
|
/// An "optional unique string used to describe an operation".
|
||||||
|
/// If present, these are assumed to be unique among all operations described
|
||||||
|
/// in the API.
|
||||||
|
| OperationId of string
|
||||||
|
|
||||||
|
/// Round-trip string representation.
|
||||||
|
override this.ToString () =
|
||||||
|
match this with
|
||||||
|
| OperationId.OperationId s -> s
|
||||||
|
|
||||||
|
/// Constraints on the `additionalProperties` (in the JSON schema sense).
|
||||||
|
/// "Additional properties" are properties of a JSON object which were not
|
||||||
|
/// listed in the schema.
|
||||||
|
type AdditionalProperties =
|
||||||
|
/// No additional properties are allowed: all properties must have been
|
||||||
|
/// mentioned in the schema.
|
||||||
|
| Never
|
||||||
|
/// Additional properties are permitted, but if they exist, they must
|
||||||
|
/// match this schema definition.
|
||||||
|
| Constrained of Definition
|
||||||
|
|
||||||
|
/// The Swagger schema lets you define types. An ObjectTypeDefinition
|
||||||
|
/// is specifically the information about types defined as `"type": "object"`.
|
||||||
|
and ObjectTypeDefinition =
|
||||||
|
{
|
||||||
|
/// Human-readable description of the purpose of this type.
|
||||||
|
Description : string option
|
||||||
|
/// Fields which any object must have to satisfy this type.
|
||||||
|
Properties : Map<string, Definition> option
|
||||||
|
/// Extra properties in the type description. In Gitea, these are
|
||||||
|
/// (for example) "x-go-package":"code.gitea.io/gitea/modules/structs".
|
||||||
|
Extras : Map<string, JsonNode>
|
||||||
|
/// List of fields which are required; all other fields are optional.
|
||||||
|
Required : string list option
|
||||||
|
/// Constraints, if any, placed on fields which are not mentioned in
|
||||||
|
/// the schema. If absent, there are no constraints.
|
||||||
|
AdditionalProperties : AdditionalProperties option
|
||||||
|
/// Example of an object which satisfies this schema.
|
||||||
|
Example : JsonObject option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (node : JsonObject) : ObjectTypeDefinition =
|
||||||
|
let description =
|
||||||
|
match asOpt<string> node "description", asOpt<string> node "title" with
|
||||||
|
| None, None -> None
|
||||||
|
| Some v, None
|
||||||
|
| None, Some v -> Some v
|
||||||
|
| Some v1, Some v2 -> failwith "both description and title were given"
|
||||||
|
|
||||||
|
let additionalProperties =
|
||||||
|
match node.["additionalProperties"] with
|
||||||
|
| null -> None
|
||||||
|
| :? JsonValue as p ->
|
||||||
|
if not (p.GetValue<bool> ()) then
|
||||||
|
Some AdditionalProperties.Never
|
||||||
|
else
|
||||||
|
failwith $"additionalProperties should be 'false' or an object, but was: %s{p.ToJsonString ()}"
|
||||||
|
| p ->
|
||||||
|
let p = p.AsObject ()
|
||||||
|
Definition.Parse p |> AdditionalProperties.Constrained |> Some
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
match node.["properties"] with
|
||||||
|
| null -> None
|
||||||
|
| p ->
|
||||||
|
p.AsObject ()
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
let example = asObjOpt node "example"
|
||||||
|
|
||||||
|
let required = asArrOpt'<string> node "required"
|
||||||
|
|
||||||
|
let extras =
|
||||||
|
node.AsObject ()
|
||||||
|
|> Seq.choose (fun (KeyValue (key, value)) ->
|
||||||
|
match key with
|
||||||
|
| "type"
|
||||||
|
| "description"
|
||||||
|
| "title"
|
||||||
|
| "additionalProperties"
|
||||||
|
| "example"
|
||||||
|
| "required"
|
||||||
|
| "properties" -> None
|
||||||
|
| _ -> Some (key, value)
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = description
|
||||||
|
Properties = properties
|
||||||
|
AdditionalProperties = additionalProperties
|
||||||
|
Required = required
|
||||||
|
Extras = extras
|
||||||
|
Example = example
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The Swagger schema lets you define types. An ArrayTypeDefinition
|
||||||
|
/// is specifically the information about types defined as `"type": "array"`.
|
||||||
|
and ArrayTypeDefinition =
|
||||||
|
{
|
||||||
|
/// The type is `'a array`; this field describes `'a`.
|
||||||
|
Items : Definition
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonNode into the strongly-typed version, performing sanity
|
||||||
|
/// checks and throwing on input that can't be parsed.
|
||||||
|
static member Parse (n : JsonNode) : ArrayTypeDefinition =
|
||||||
|
let items = asObj n "items" |> Definition.Parse
|
||||||
|
|
||||||
|
{
|
||||||
|
Items = items
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Any definition of a type in the Swagger document. This is basically any
|
||||||
|
/// information associated with the `"type": "blah"` field.
|
||||||
|
and Definition =
|
||||||
|
/// For example, if `"$ref": "#/responses/Blah", then this is "#/responses/Blah".
|
||||||
|
| Handle of string
|
||||||
|
/// A type definition with "type": "object".
|
||||||
|
| Object of ObjectTypeDefinition
|
||||||
|
/// A type definition with "type": "array".
|
||||||
|
| Array of ArrayTypeDefinition
|
||||||
|
/// A type definition with "type": "string".
|
||||||
|
| String
|
||||||
|
/// A type definition with "type": "boolean".
|
||||||
|
| Boolean
|
||||||
|
/// A response without a body has no "schema" specified.
|
||||||
|
| Unspecified
|
||||||
|
/// A type definition with "type": "integer".
|
||||||
|
/// The format is an optional hint which could be e.g. "int64" or "int32".
|
||||||
|
/// https://swagger.io/docs/specification/data-models/data-types/#numbers
|
||||||
|
| Integer of format : string option
|
||||||
|
/// Not a JSON schema type, but a Swagger 2.0 type.
|
||||||
|
| File
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (n : JsonObject) : Definition =
|
||||||
|
match n.["$ref"] |> Option.ofObj with
|
||||||
|
| Some ref -> Definition.Handle (ref.GetValue<string> ())
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let ty = asOpt<string> n "type"
|
||||||
|
|
||||||
|
match ty with
|
||||||
|
| None -> Definition.Unspecified
|
||||||
|
| Some "object" -> ObjectTypeDefinition.Parse n |> Definition.Object
|
||||||
|
| Some "array" -> ArrayTypeDefinition.Parse n |> Definition.Array
|
||||||
|
| Some "string" -> Definition.String
|
||||||
|
| Some "boolean" -> Definition.Boolean
|
||||||
|
| Some "file" -> Definition.File
|
||||||
|
| Some "integer" ->
|
||||||
|
let format = asOpt<string> n "format"
|
||||||
|
Definition.Integer format
|
||||||
|
| Some ty -> failwith $"Unrecognised type: %s{ty}"
|
||||||
|
|
||||||
|
/// REST APIs allow their parameters to be passed in various ways. This describes
|
||||||
|
/// how one single parameter is passed.
|
||||||
|
type ParameterIn =
|
||||||
|
/// The parameter is interpolated into the path, e.g. "/foo/{blah}".
|
||||||
|
/// The "name" is what we replace in the path: e.g. "/foo/{person}" would
|
||||||
|
/// have a name of "person".
|
||||||
|
| Path of name : string
|
||||||
|
/// The parameter is appended to the URL's query params, e.g. "?<name>=blah"
|
||||||
|
| Query of name : string
|
||||||
|
/// The parameter is passed in the body of the HTTP request.
|
||||||
|
| Body
|
||||||
|
/// Some spec that WoofWare.Myriad doesn't support.
|
||||||
|
| Unrecognised of op : string * name : string
|
||||||
|
|
||||||
|
/// Description of a single input parameter to an endpoint.
|
||||||
|
type SwaggerParameter =
|
||||||
|
{
|
||||||
|
/// The type schema to which this parameter must conform.
|
||||||
|
Type : Definition
|
||||||
|
/// Optional human-readable description of this parameter.
|
||||||
|
Description : string option
|
||||||
|
/// How this parameter is passed.
|
||||||
|
In : ParameterIn
|
||||||
|
/// Name of this parameter. For most `In` values, this name is the
|
||||||
|
/// name of the parameter as supplied to the API at runtime, and in WoofWare's
|
||||||
|
/// strongly-typed domain types this information is also contained in the `In` field.
|
||||||
|
/// For `Body` parameters, this is purely for dev-time information.
|
||||||
|
Name : string
|
||||||
|
/// Whether this parameter is required for validation to succeed.
|
||||||
|
/// I think this defaults to "no".
|
||||||
|
Required : bool option
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (node : JsonObject) : SwaggerParameter =
|
||||||
|
let ty =
|
||||||
|
match asObjOpt node "schema" with
|
||||||
|
| None -> Definition.Parse node
|
||||||
|
| Some node -> Definition.Parse node
|
||||||
|
|
||||||
|
let description = asOpt<string> node "description"
|
||||||
|
let name = asString node "name"
|
||||||
|
|
||||||
|
let paramIn =
|
||||||
|
match asString node "in" with
|
||||||
|
| "path" -> ParameterIn.Path name
|
||||||
|
| "query" -> ParameterIn.Query name
|
||||||
|
| "body" -> ParameterIn.Body
|
||||||
|
| f -> ParameterIn.Unrecognised (f, name)
|
||||||
|
|
||||||
|
let required = asOpt<bool> node "required"
|
||||||
|
|
||||||
|
{
|
||||||
|
Type = ty
|
||||||
|
Description = description
|
||||||
|
In = paramIn
|
||||||
|
Name = name
|
||||||
|
Required = required
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An "endpoint" is basically a single HTTP verb, applied to some path.
|
||||||
|
type SwaggerEndpoint =
|
||||||
|
{
|
||||||
|
/// The MIME types we should send our request body in.
|
||||||
|
/// This overrides (does not extend) any global definitions on the spec itself.
|
||||||
|
Consumes : MimeType list option
|
||||||
|
/// The MIME types we should expect to receive in response to this request.
|
||||||
|
/// This overrides (does not extend) any global definitions on the spec itself.
|
||||||
|
Produces : MimeType list option
|
||||||
|
/// Arbitrary list of [tags](https://swagger.io/docs/specification/2-0/grouping-operations-with-tags/).
|
||||||
|
Tags : string list
|
||||||
|
/// Human-readable description of the endpoint.
|
||||||
|
Summary : string
|
||||||
|
/// Arbitrary identifier of this endpoint; this must be unique across *all* endpoints
|
||||||
|
/// in this entire spec.
|
||||||
|
OperationId : OperationId
|
||||||
|
/// Parameters that must be supplied at HTTP-request-time to the endpoint.
|
||||||
|
/// (Each parameter knows how it needs to be supplied: e.g. if it's a query parameter or
|
||||||
|
/// if it's interpolated into the path.)
|
||||||
|
Parameters : SwaggerParameter list option
|
||||||
|
/// Map of HTTP response code to the type that we expect to receive in the body if we
|
||||||
|
/// get that response code back.
|
||||||
|
Responses : Map<int, Definition>
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (r : JsonObject) : SwaggerEndpoint =
|
||||||
|
let produces = asArrOpt'<string> r "produces" |> Option.map (List.map MimeType)
|
||||||
|
let consumes = asArrOpt'<string> r "consumes" |> Option.map (List.map MimeType)
|
||||||
|
let tags = asArr'<string> r "tags"
|
||||||
|
let summary = asString r "summary"
|
||||||
|
let operationId = asString r "operationId" |> OperationId
|
||||||
|
|
||||||
|
let responses =
|
||||||
|
asObj r "responses"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
Int32.Parse key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let parameters =
|
||||||
|
asArrOpt r "parameters"
|
||||||
|
|> Option.map (fun pars ->
|
||||||
|
pars
|
||||||
|
|> Seq.map (fun par -> par.AsObject () |> SwaggerParameter.Parse)
|
||||||
|
|> Seq.toList
|
||||||
|
)
|
||||||
|
|
||||||
|
{
|
||||||
|
Produces = produces
|
||||||
|
Consumes = consumes
|
||||||
|
Tags = tags
|
||||||
|
Summary = summary
|
||||||
|
OperationId = operationId
|
||||||
|
Parameters = parameters
|
||||||
|
Responses = responses
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Specifies the form a response to an endpoint will take if it's complying with this spec.
|
||||||
|
type Response =
|
||||||
|
{
|
||||||
|
/// Human-readable description.
|
||||||
|
Description : string
|
||||||
|
/// Specification of the type to which responses will conform under this spec.
|
||||||
|
Schema : Definition
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Render a JsonObject into this strongly-typed specification.
|
||||||
|
static member Parse (r : JsonObject) : Response =
|
||||||
|
let desc = asString r "description"
|
||||||
|
|
||||||
|
let schema =
|
||||||
|
match asObjOpt r "schema" with
|
||||||
|
| None -> Definition.Unspecified
|
||||||
|
| Some s -> Definition.Parse s
|
||||||
|
|
||||||
|
{
|
||||||
|
Description = desc
|
||||||
|
Schema = schema
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An HTTP method. This is System.Net.Http.HttpMethod, but
|
||||||
|
/// a proper discriminated union.
|
||||||
|
type HttpMethod =
|
||||||
|
/// HTTP Get
|
||||||
|
| Get
|
||||||
|
/// HTTP Post
|
||||||
|
| Post
|
||||||
|
/// HTTP Delete
|
||||||
|
| Delete
|
||||||
|
/// HTTP Patch
|
||||||
|
| Patch
|
||||||
|
/// HTTP Options
|
||||||
|
| Options
|
||||||
|
/// HTTP Head
|
||||||
|
| Head
|
||||||
|
/// HTTP Put
|
||||||
|
| Put
|
||||||
|
/// HTTP Trace
|
||||||
|
| Trace
|
||||||
|
|
||||||
|
/// Convert to the standard library's enum type.
|
||||||
|
member this.ToDotNet () : System.Net.Http.HttpMethod =
|
||||||
|
match this with
|
||||||
|
| HttpMethod.Get -> System.Net.Http.HttpMethod.Get
|
||||||
|
| HttpMethod.Post -> System.Net.Http.HttpMethod.Post
|
||||||
|
| HttpMethod.Delete -> System.Net.Http.HttpMethod.Delete
|
||||||
|
| HttpMethod.Patch -> System.Net.Http.HttpMethod.Patch
|
||||||
|
| HttpMethod.Options -> System.Net.Http.HttpMethod.Options
|
||||||
|
| HttpMethod.Head -> System.Net.Http.HttpMethod.Head
|
||||||
|
| HttpMethod.Put -> System.Net.Http.HttpMethod.Put
|
||||||
|
| HttpMethod.Trace -> System.Net.Http.HttpMethod.Trace
|
||||||
|
|
||||||
|
/// Human-readable string representation.
|
||||||
|
override this.ToString () : string =
|
||||||
|
match this with
|
||||||
|
| HttpMethod.Get -> "Get"
|
||||||
|
| HttpMethod.Post -> "Post"
|
||||||
|
| HttpMethod.Delete -> "Delete"
|
||||||
|
| HttpMethod.Patch -> "Post"
|
||||||
|
| HttpMethod.Options -> "Options"
|
||||||
|
| HttpMethod.Head -> "Head"
|
||||||
|
| HttpMethod.Put -> "Put"
|
||||||
|
| HttpMethod.Trace -> "Trace"
|
||||||
|
|
||||||
|
/// Throws on invalid inputs.
|
||||||
|
static member Parse (s : string) : HttpMethod =
|
||||||
|
if String.Equals (s, "get", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Get
|
||||||
|
elif String.Equals (s, "post", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Post
|
||||||
|
elif String.Equals (s, "patch", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Patch
|
||||||
|
elif String.Equals (s, "delete", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Delete
|
||||||
|
elif String.Equals (s, "head", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Head
|
||||||
|
elif String.Equals (s, "options", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Options
|
||||||
|
elif String.Equals (s, "put", StringComparison.OrdinalIgnoreCase) then
|
||||||
|
HttpMethod.Put
|
||||||
|
else
|
||||||
|
failwith $"Unrecognised method: %s{s}"
|
||||||
|
|
||||||
|
/// A Swagger API specification.
|
||||||
|
type Swagger =
|
||||||
|
{
|
||||||
|
/// Global collection of MIME types which any endpoint expects to consume its inputs in.
|
||||||
|
/// This may be overridden on any individual endpoint by that endpoint.
|
||||||
|
Consumes : MimeType list
|
||||||
|
/// Global collection of MIME types which any endpoint will produce.
|
||||||
|
/// This may be overridden on any individual endpoint by that endpoint.
|
||||||
|
Produces : MimeType list
|
||||||
|
/// HTTP or HTTPS, for example. Indicates which scheme to access the API on.
|
||||||
|
Schemes : Scheme list
|
||||||
|
/// The version of OpenAPI this specification is written against.
|
||||||
|
/// (As of this writing, we only support 2.0.)
|
||||||
|
Swagger : Version
|
||||||
|
/// General information about this API.
|
||||||
|
Info : SwaggerInfo
|
||||||
|
/// Path under the URI host, which should be prefixed (with trailing slash if necessary)
|
||||||
|
/// to all requests.
|
||||||
|
BasePath : string
|
||||||
|
/// Map from relative path to "what is served at that path".
|
||||||
|
Paths : Map<string, Map<HttpMethod, SwaggerEndpoint>>
|
||||||
|
/// Types defined in the schema. Requests may use these definitions just like in any other JSON schema.
|
||||||
|
/// Key is a domain type name, e.g. "APIError".
|
||||||
|
Definitions : Map<string, Definition>
|
||||||
|
/// Types of each response.
|
||||||
|
/// Key is a domain type name, e.g. "AccessToken".
|
||||||
|
Responses : Map<string, Response>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module Swagger =
|
||||||
|
/// Parse a JSON-schema-based specification of a Swagger 2.0 API and
|
||||||
|
/// build the strongly-typed version. Throws on invalid inputs.
|
||||||
|
let parse (s : string) : Swagger =
|
||||||
|
let node = JsonNode.Parse s
|
||||||
|
let consumes = asArr'<string> node "consumes" |> List.map MimeType
|
||||||
|
let produces = asArr'<string> node "produces" |> List.map MimeType
|
||||||
|
let schemes = asArr'<string> node "schemes" |> List.map Scheme
|
||||||
|
let swagger = asString node "swagger" |> Version.Parse
|
||||||
|
let info = asObj node "info" |> SwaggerInfo.Parse
|
||||||
|
let basePath = asString node "basePath"
|
||||||
|
|
||||||
|
let definitions =
|
||||||
|
asObj node "definitions"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Definition.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let paths =
|
||||||
|
asObj node "paths"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let contents =
|
||||||
|
value.AsObject ()
|
||||||
|
|> Seq.map (fun (KeyValue (endpoint, contents)) ->
|
||||||
|
let contents = contents.AsObject ()
|
||||||
|
HttpMethod.Parse endpoint, SwaggerEndpoint.Parse contents
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
key, contents
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
let responses =
|
||||||
|
asObj node "responses"
|
||||||
|
|> Seq.map (fun (KeyValue (key, value)) ->
|
||||||
|
let value = value.AsObject ()
|
||||||
|
key, Response.Parse value
|
||||||
|
)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
{
|
||||||
|
Consumes = consumes
|
||||||
|
Produces = produces
|
||||||
|
Schemes = schemes
|
||||||
|
Swagger = swagger
|
||||||
|
Info = info
|
||||||
|
BasePath = basePath
|
||||||
|
Paths = paths
|
||||||
|
Definitions = definitions
|
||||||
|
Responses = responses
|
||||||
|
}
|
725
WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs
Normal file
725
WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs
Normal file
@@ -0,0 +1,725 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System.Collections.Generic
|
||||||
|
open System.IO
|
||||||
|
open System.Threading
|
||||||
|
open Fantomas.FCS.Syntax
|
||||||
|
open Fantomas.FCS.Xml
|
||||||
|
open Fantomas.FCS.Text.Range
|
||||||
|
open WoofWare.Whippet.Fantomas
|
||||||
|
|
||||||
|
type internal SwaggerClientConfig =
|
||||||
|
{
|
||||||
|
/// Additionally create a mock with `InterfaceMockGenerator`, with the given boolean arg.
|
||||||
|
/// (`None` means "no mock".)
|
||||||
|
CreateMock : bool option
|
||||||
|
ClassName : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal Produces =
|
||||||
|
// TODO: this will cope with decoding JSON, plain text, etc
|
||||||
|
| Produces of string
|
||||||
|
|
||||||
|
type internal Endpoint =
|
||||||
|
{
|
||||||
|
DocString : PreXmlDoc
|
||||||
|
Produces : Produces
|
||||||
|
ReturnType : Definition
|
||||||
|
Method : WoofWare.Myriad.Plugins.HttpMethod
|
||||||
|
Operation : OperationId
|
||||||
|
Parameters : SwaggerParameter list
|
||||||
|
Endpoint : string
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal TypeEntry =
|
||||||
|
{
|
||||||
|
/// If we had to define a type for this, here it is.
|
||||||
|
FSharpDefinition : SynTypeDefn option
|
||||||
|
/// SynType you use in e.g. a type annotation to refer to this type in F# code.
|
||||||
|
Signature : SynType
|
||||||
|
}
|
||||||
|
|
||||||
|
type internal Types =
|
||||||
|
{
|
||||||
|
ByHandle : IReadOnlyDictionary<string, TypeEntry>
|
||||||
|
ByDefinition : IReadOnlyDictionary<Definition, TypeEntry>
|
||||||
|
}
|
||||||
|
|
||||||
|
[<RequireQualifiedAccess>]
|
||||||
|
module internal SwaggerClientGenerator =
|
||||||
|
let outputFile = FileInfo "/tmp/output.txt"
|
||||||
|
|
||||||
|
// do
|
||||||
|
// use _ = File.Create outputFile.FullName
|
||||||
|
// ()
|
||||||
|
|
||||||
|
let log (line : string) =
|
||||||
|
// use w = outputFile.AppendText ()
|
||||||
|
// w.WriteLine line
|
||||||
|
()
|
||||||
|
|
||||||
|
let renderType (types : Types) (defn : Definition) : SynType option =
|
||||||
|
match types.ByDefinition.TryGetValue defn with
|
||||||
|
| true, v -> Some v.Signature
|
||||||
|
| false, _ ->
|
||||||
|
|
||||||
|
match defn with
|
||||||
|
| Definition.Handle h ->
|
||||||
|
match types.ByHandle.TryGetValue h with
|
||||||
|
| false, _ -> None
|
||||||
|
| true, v -> Some v.Signature
|
||||||
|
| Definition.Object _ -> failwith "should not hit"
|
||||||
|
| Definition.Array _ -> failwith "should not hit"
|
||||||
|
| Definition.Unspecified -> failwith "should not hit"
|
||||||
|
| Definition.String -> SynType.string |> Some
|
||||||
|
| Definition.Boolean -> SynType.bool |> Some
|
||||||
|
| Definition.Integer _ -> SynType.int |> Some
|
||||||
|
| Definition.File -> SynType.createLongIdent' [ "System" ; "IO" ; "Stream" ] |> Some
|
||||||
|
|
||||||
|
/// Returns None if we lacked the information required to do this.
|
||||||
|
/// bigCache is a map of e.g. {"securityDefinition": {Defn : F# type}}.
|
||||||
|
let rec defnToType
|
||||||
|
(anonymousTypeCount : int ref)
|
||||||
|
(handlesMap : Dictionary<string, TypeEntry>)
|
||||||
|
(bigCache : Dictionary<string, Dictionary<Definition, TypeEntry>>)
|
||||||
|
(thisKey : string)
|
||||||
|
(typeName : string option)
|
||||||
|
(d : Definition)
|
||||||
|
: TypeEntry option
|
||||||
|
=
|
||||||
|
let cache =
|
||||||
|
match bigCache.TryGetValue thisKey with
|
||||||
|
| false, _ ->
|
||||||
|
let d = Dictionary ()
|
||||||
|
bigCache.Add (thisKey, d)
|
||||||
|
d
|
||||||
|
| true, d -> d
|
||||||
|
|
||||||
|
let handleKey =
|
||||||
|
match typeName with
|
||||||
|
| None -> None
|
||||||
|
| Some typeName -> $"#/%s{thisKey}/%s{typeName}" |> Some
|
||||||
|
|
||||||
|
match handleKey with
|
||||||
|
| Some hk when handlesMap.ContainsKey hk ->
|
||||||
|
let result = handlesMap.[hk]
|
||||||
|
cache.[d] <- result
|
||||||
|
Some result
|
||||||
|
|
||||||
|
| _ ->
|
||||||
|
|
||||||
|
match cache.TryGetValue d with
|
||||||
|
| true, v ->
|
||||||
|
match handleKey with
|
||||||
|
| None -> ()
|
||||||
|
| Some key -> handlesMap.Add (key, v)
|
||||||
|
|
||||||
|
Some v
|
||||||
|
| false, _ ->
|
||||||
|
|
||||||
|
let result =
|
||||||
|
match d with
|
||||||
|
| Definition.Object obj ->
|
||||||
|
let requiredFields = obj.Required |> Option.defaultValue [] |> Set.ofList
|
||||||
|
|
||||||
|
let namedProperties =
|
||||||
|
obj.Properties
|
||||||
|
|> Option.map Seq.cast
|
||||||
|
|> Option.defaultValue Seq.empty
|
||||||
|
|> Seq.map (fun (KeyValue (fieldName, defn)) ->
|
||||||
|
// TODO this is a horrible hack and is incomplete, e.g. if we contain an array of ourself
|
||||||
|
// Special case for when this is a reference to this very type
|
||||||
|
let isOurself =
|
||||||
|
match defn with
|
||||||
|
| Definition.Handle h ->
|
||||||
|
match h.Split '/' with
|
||||||
|
| [| "#" ; location ; ty |] when location = thisKey && Some ty = typeName ->
|
||||||
|
SynType.named ty |> Some
|
||||||
|
| _ -> None
|
||||||
|
| _ -> None
|
||||||
|
|
||||||
|
let jsonPropertyName =
|
||||||
|
SynExpr.CreateConst (fieldName : string)
|
||||||
|
|> SynAttribute.create (
|
||||||
|
SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonPropertyName" ]
|
||||||
|
)
|
||||||
|
|
||||||
|
match isOurself with
|
||||||
|
| Some alreadyDone ->
|
||||||
|
let ty =
|
||||||
|
if Set.contains fieldName requiredFields then
|
||||||
|
alreadyDone
|
||||||
|
else
|
||||||
|
SynType.option alreadyDone
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = [ jsonPropertyName ]
|
||||||
|
Type = ty
|
||||||
|
Ident = Some (Ident.createSanitisedTypeName fieldName)
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> Some
|
||||||
|
| None ->
|
||||||
|
|
||||||
|
let defn' = defnToType anonymousTypeCount handlesMap bigCache thisKey None defn
|
||||||
|
|
||||||
|
match defn' with
|
||||||
|
| None -> None
|
||||||
|
| Some defn' ->
|
||||||
|
let ty =
|
||||||
|
if Set.contains fieldName requiredFields then
|
||||||
|
defn'.Signature
|
||||||
|
else
|
||||||
|
defn'.Signature |> SynType.option
|
||||||
|
|
||||||
|
{
|
||||||
|
Attrs = [ jsonPropertyName ]
|
||||||
|
Ident = Ident.createSanitisedTypeName fieldName |> Some
|
||||||
|
Type = ty
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let additionalProperties =
|
||||||
|
match obj.AdditionalProperties with
|
||||||
|
| None ->
|
||||||
|
{
|
||||||
|
Attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonExtensionData" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
]
|
||||||
|
Ident = Ident.create "AdditionalProperties" |> Some
|
||||||
|
Type =
|
||||||
|
SynType.app'
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|
||||||
|
[
|
||||||
|
SynType.string
|
||||||
|
SynType.createLongIdent' [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
|> Some
|
||||||
|
| Some AdditionalProperties.Never -> Some []
|
||||||
|
| Some (AdditionalProperties.Constrained defn) ->
|
||||||
|
let defn' = defnToType anonymousTypeCount handlesMap bigCache thisKey None defn
|
||||||
|
|
||||||
|
match defn' with
|
||||||
|
| None -> None
|
||||||
|
| Some defn' ->
|
||||||
|
{
|
||||||
|
Attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS'
|
||||||
|
[ "System" ; "Text" ; "Json" ; "Serialization" ; "JsonExtensionData" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
]
|
||||||
|
Ident = Ident.create "AdditionalProperties" |> Some
|
||||||
|
Type =
|
||||||
|
SynType.app'
|
||||||
|
(SynType.createLongIdent'
|
||||||
|
[ "System" ; "Collections" ; "Generic" ; "Dictionary" ])
|
||||||
|
[ SynType.string ; defn'.Signature ]
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
|> Some
|
||||||
|
|
||||||
|
match additionalProperties with
|
||||||
|
| None -> None
|
||||||
|
| Some additionalProperties ->
|
||||||
|
|
||||||
|
match List.allSome namedProperties with
|
||||||
|
| None -> None
|
||||||
|
| Some namedProperties ->
|
||||||
|
|
||||||
|
let fSharpTypeName =
|
||||||
|
match typeName with
|
||||||
|
| None -> $"Type%i{Interlocked.Increment anonymousTypeCount}"
|
||||||
|
| Some typeName -> typeName
|
||||||
|
|
||||||
|
let properties = additionalProperties @ namedProperties
|
||||||
|
|
||||||
|
let properties =
|
||||||
|
if properties.IsEmpty then
|
||||||
|
// sigh, they didn't give us any properties at all; let's make one up
|
||||||
|
{
|
||||||
|
Attrs = []
|
||||||
|
Ident = Some (Ident.create "_SchemaUnspecified")
|
||||||
|
Type = SynType.obj
|
||||||
|
}
|
||||||
|
|> SynField.make
|
||||||
|
|> List.singleton
|
||||||
|
else
|
||||||
|
properties
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
let sci =
|
||||||
|
SynComponentInfo.create (Ident.createSanitisedTypeName fSharpTypeName)
|
||||||
|
|> SynComponentInfo.addAttributes
|
||||||
|
[
|
||||||
|
SynAttribute.create (SynLongIdent.createS' [ "JsonParse" ]) (SynExpr.CreateConst true)
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "JsonSerialize" ])
|
||||||
|
(SynExpr.CreateConst true)
|
||||||
|
]
|
||||||
|
|> fun sci ->
|
||||||
|
match obj.Description with
|
||||||
|
| None -> sci
|
||||||
|
| Some doc -> sci |> SynComponentInfo.withDocString (PreXmlDoc.create doc)
|
||||||
|
|
||||||
|
properties |> SynTypeDefnRepr.record |> SynTypeDefn.create sci
|
||||||
|
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
Signature = SynType.named fSharpTypeName
|
||||||
|
FSharpDefinition = Some defn
|
||||||
|
}
|
||||||
|
|
||||||
|
defn |> Some
|
||||||
|
|
||||||
|
| Definition.Array elt ->
|
||||||
|
let child = defnToType anonymousTypeCount handlesMap bigCache thisKey None elt.Items
|
||||||
|
|
||||||
|
match child with
|
||||||
|
| None -> None
|
||||||
|
| Some child ->
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
Signature = SynType.list child.Signature
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|
||||||
|
Some defn
|
||||||
|
| Definition.String ->
|
||||||
|
{
|
||||||
|
Signature = SynType.string
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Boolean ->
|
||||||
|
{
|
||||||
|
Signature = SynType.bool
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Unspecified ->
|
||||||
|
{
|
||||||
|
Signature = SynType.unit
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Integer _ ->
|
||||||
|
{
|
||||||
|
Signature = SynType.createLongIdent' [ "int" ]
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.File ->
|
||||||
|
{
|
||||||
|
Signature = SynType.createLongIdent' [ "System" ; "IO" ; "Stream" ]
|
||||||
|
FSharpDefinition = None
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
| Definition.Handle s ->
|
||||||
|
let split = s.Split '/' |> List.ofArray
|
||||||
|
|
||||||
|
match split with
|
||||||
|
| [ "#" ; _location ; _handle ] ->
|
||||||
|
match handlesMap.TryGetValue s with
|
||||||
|
| false, _ -> None
|
||||||
|
| true, computed ->
|
||||||
|
let defn =
|
||||||
|
{
|
||||||
|
FSharpDefinition = None
|
||||||
|
Signature = computed.Signature
|
||||||
|
}
|
||||||
|
|
||||||
|
defn |> Some
|
||||||
|
| _ -> failwith $"we don't know how to deal with object handle %s{s}"
|
||||||
|
|
||||||
|
match result with
|
||||||
|
| None -> None
|
||||||
|
| Some result ->
|
||||||
|
|
||||||
|
match handleKey with
|
||||||
|
| None -> ()
|
||||||
|
| Some handleKey -> handlesMap.Add (handleKey, result)
|
||||||
|
|
||||||
|
cache.Add (d, result)
|
||||||
|
Some result
|
||||||
|
|
||||||
|
let instantiateRequiredTypes (types : Types) : SynModuleDecl =
|
||||||
|
types.ByDefinition
|
||||||
|
|> Seq.choose (fun (KeyValue (_defn, typeEntry)) -> typeEntry.FSharpDefinition)
|
||||||
|
|> Seq.toList
|
||||||
|
|> SynModuleDecl.createTypes
|
||||||
|
|
||||||
|
type private IsIn =
|
||||||
|
| Path of str : string
|
||||||
|
| Query of str : string
|
||||||
|
| Body
|
||||||
|
|
||||||
|
let computeType
|
||||||
|
(options : SwaggerClientConfig)
|
||||||
|
(basePath : string)
|
||||||
|
(types : Types)
|
||||||
|
(clientDocString : PreXmlDoc)
|
||||||
|
(endpoints : Endpoint list)
|
||||||
|
: SynModuleDecl list
|
||||||
|
=
|
||||||
|
endpoints
|
||||||
|
|> List.choose (fun ep ->
|
||||||
|
let name = (Ident.createSanitisedTypeName (ep.Operation.ToString ())).idText
|
||||||
|
|
||||||
|
match renderType types ep.ReturnType with
|
||||||
|
| None ->
|
||||||
|
log $"Skipping %O{ep.Operation}: Couldn't render return type: %O{ep.ReturnType}"
|
||||||
|
None
|
||||||
|
| Some returnType ->
|
||||||
|
|
||||||
|
let pars =
|
||||||
|
ep.Parameters
|
||||||
|
|> List.map (fun par ->
|
||||||
|
let inParam =
|
||||||
|
match par.In with
|
||||||
|
| ParameterIn.Unrecognised (f, name) ->
|
||||||
|
log
|
||||||
|
$"Skipping %O{ep.Operation} at %s{ep.Endpoint}: unrecognised In parameter %s{f} with name %s{name}"
|
||||||
|
|
||||||
|
None
|
||||||
|
| ParameterIn.Body -> Some IsIn.Body
|
||||||
|
| ParameterIn.Query name -> Some (IsIn.Query name)
|
||||||
|
| ParameterIn.Path name -> Some (IsIn.Path name)
|
||||||
|
|
||||||
|
match inParam with
|
||||||
|
| None -> None
|
||||||
|
| Some inParam ->
|
||||||
|
|
||||||
|
match renderType types par.Type with
|
||||||
|
| None ->
|
||||||
|
// Couldn't render the return type
|
||||||
|
// failwith "Did not have a type here"
|
||||||
|
log $"Skipping %O{ep.Operation}: Couldn't render parameter: %O{par.Type}"
|
||||||
|
None
|
||||||
|
| Some v -> Some (Ident.createSanitisedParamName par.Name, inParam, v)
|
||||||
|
)
|
||||||
|
|> List.allSome
|
||||||
|
|
||||||
|
match pars with
|
||||||
|
| None -> None
|
||||||
|
| Some pars ->
|
||||||
|
|
||||||
|
let arity =
|
||||||
|
SynValInfo.SynValInfo (
|
||||||
|
[
|
||||||
|
ep.Parameters
|
||||||
|
|> List.map (fun par ->
|
||||||
|
let name = par.Name |> Ident.create |> Some
|
||||||
|
SynArgInfo.SynArgInfo ([], false, name)
|
||||||
|
)
|
||||||
|
|> fun l -> l @ [ SynArgInfo.SynArgInfo ([], true, Some (Ident.create "ct")) ]
|
||||||
|
],
|
||||||
|
SynArgInfo.SynArgInfo ([], false, None)
|
||||||
|
)
|
||||||
|
|
||||||
|
let domain =
|
||||||
|
let ctParam =
|
||||||
|
SynType.signatureParamOfType
|
||||||
|
[]
|
||||||
|
(SynType.createLongIdent' [ "System" ; "Threading" ; "CancellationToken" ])
|
||||||
|
true
|
||||||
|
(Some (Ident.create "ct"))
|
||||||
|
|
||||||
|
let argParams =
|
||||||
|
pars
|
||||||
|
|> List.map (fun (ident, isIn, t) ->
|
||||||
|
let attr : SynAttribute list =
|
||||||
|
match isIn with
|
||||||
|
| IsIn.Path name ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Path" ])
|
||||||
|
(SynExpr.CreateConst name)
|
||||||
|
|> List.singleton
|
||||||
|
| IsIn.Query name ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Query" ])
|
||||||
|
(SynExpr.CreateConst name)
|
||||||
|
|> List.singleton
|
||||||
|
| IsIn.Body ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Body" ])
|
||||||
|
(SynExpr.CreateConst ())
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
SynType.signatureParamOfType attr t false (Some ident)
|
||||||
|
)
|
||||||
|
|
||||||
|
SynType.tupleNoParen (argParams @ [ ctParam ]) |> Option.get
|
||||||
|
|
||||||
|
let attrs =
|
||||||
|
[
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; ep.Method.ToString () ])
|
||||||
|
// Gitea, at least, starts with a `/`, which `Uri` then takes to indicate an absolute path.
|
||||||
|
(SynExpr.CreateConst (ep.Endpoint.TrimStart '/'))
|
||||||
|
|
||||||
|
match ep.Produces with
|
||||||
|
| Produces.Produces contentType ->
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "Header" ])
|
||||||
|
// Gitea, at least, starts with a `/`, which `Uri` then takes to indicate an absolute path.
|
||||||
|
(SynExpr.tuple [ SynExpr.CreateConst "Content-Type" ; SynExpr.CreateConst contentType ])
|
||||||
|
]
|
||||||
|
|
||||||
|
returnType
|
||||||
|
|> SynType.task
|
||||||
|
|> SynType.toFun [ domain ]
|
||||||
|
|> SynMemberDefn.abstractMember attrs (SynIdent.createS name) None arity ep.DocString
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> SynTypeDefnRepr.interfaceType
|
||||||
|
|> SynTypeDefn.create (
|
||||||
|
let attrs =
|
||||||
|
[
|
||||||
|
yield SynAttribute.create (SynLongIdent.createS' [ "HttpClient" ]) (SynExpr.CreateConst false)
|
||||||
|
yield
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "RestEase" ; "BasePath" ])
|
||||||
|
(SynExpr.CreateConst basePath)
|
||||||
|
match options.CreateMock with
|
||||||
|
| None -> ()
|
||||||
|
| Some createMockValue ->
|
||||||
|
yield
|
||||||
|
SynAttribute.create
|
||||||
|
(SynLongIdent.createS' [ "GenerateMock" ])
|
||||||
|
(SynExpr.CreateConst createMockValue)
|
||||||
|
]
|
||||||
|
|
||||||
|
SynComponentInfo.create (Ident.create ("I" + options.ClassName))
|
||||||
|
|> SynComponentInfo.withDocString clientDocString
|
||||||
|
|> SynComponentInfo.addAttributes attrs
|
||||||
|
)
|
||||||
|
|> List.singleton
|
||||||
|
|> SynModuleDecl.createTypes
|
||||||
|
|> List.singleton
|
||||||
|
|
||||||
|
open Myriad.Core
|
||||||
|
|
||||||
|
/// Myriad generator that stamps out an interface and class to access a Swagger-specified API.
|
||||||
|
[<MyriadGenerator("swagger-client")>]
|
||||||
|
type SwaggerClientGenerator () =
|
||||||
|
|
||||||
|
interface IMyriadGenerator with
|
||||||
|
member _.ValidInputExtensions = [ ".json" ]
|
||||||
|
|
||||||
|
member _.Generate (context : GeneratorContext) =
|
||||||
|
let contents = File.ReadAllText context.InputFilename |> Swagger.parse
|
||||||
|
|
||||||
|
let scheme =
|
||||||
|
let preferred = Scheme "https"
|
||||||
|
|
||||||
|
if List.isEmpty contents.Schemes then
|
||||||
|
failwith "no schemes specified in API spec!"
|
||||||
|
|
||||||
|
if List.contains preferred contents.Schemes then
|
||||||
|
preferred
|
||||||
|
else
|
||||||
|
List.head contents.Schemes
|
||||||
|
|
||||||
|
let clientDocstring = contents.Info.Description |> PreXmlDoc.create
|
||||||
|
|
||||||
|
let basePath = contents.BasePath
|
||||||
|
|
||||||
|
let typeDefs =
|
||||||
|
let bigCache = Dictionary<_, Dictionary<_, _>> ()
|
||||||
|
|
||||||
|
let countAll () =
|
||||||
|
(0, bigCache) ||> Seq.fold (fun count (KeyValue (_, v)) -> count + v.Count)
|
||||||
|
|
||||||
|
let byHandle = Dictionary ()
|
||||||
|
let anonymousTypeCount = ref 0
|
||||||
|
|
||||||
|
let rec go (contents : ((string * Definition) * string) list) =
|
||||||
|
let lastRound = countAll ()
|
||||||
|
|
||||||
|
contents
|
||||||
|
|> List.filter (fun ((name, defn), defnClass) ->
|
||||||
|
let doIt =
|
||||||
|
SwaggerClientGenerator.defnToType
|
||||||
|
anonymousTypeCount
|
||||||
|
byHandle
|
||||||
|
bigCache
|
||||||
|
defnClass
|
||||||
|
(Some name)
|
||||||
|
defn
|
||||||
|
|
||||||
|
match doIt with
|
||||||
|
| None -> true
|
||||||
|
| Some _ -> false
|
||||||
|
)
|
||||||
|
|> fun remaining ->
|
||||||
|
if not remaining.IsEmpty then
|
||||||
|
let currentCount = countAll ()
|
||||||
|
|
||||||
|
if currentCount = lastRound then
|
||||||
|
for (name, remaining), kind in remaining do
|
||||||
|
SwaggerClientGenerator.log $"Remaining: %s{name} (%s{kind})"
|
||||||
|
|
||||||
|
SwaggerClientGenerator.log "--------"
|
||||||
|
|
||||||
|
for KeyValue (handle, defn) in byHandle do
|
||||||
|
SwaggerClientGenerator.log $"Known: %s{handle} %O{defn}"
|
||||||
|
|
||||||
|
// TODO: ohh noooooo the Gitea spec is genuinely circular,
|
||||||
|
// it's impossible to construct a Repository type
|
||||||
|
// we're going to have to somehow detect this case and break the cycle
|
||||||
|
// by artificially making a property optional
|
||||||
|
// :sob: Gitea why are you like this
|
||||||
|
// failwith "Made no further progress rendering types"
|
||||||
|
()
|
||||||
|
else
|
||||||
|
go remaining
|
||||||
|
|
||||||
|
seq {
|
||||||
|
for defnClass in [ "definitions" ; "responses" ] do
|
||||||
|
match defnClass with
|
||||||
|
| "definitions" ->
|
||||||
|
for KeyValue (k, v) in contents.Definitions do
|
||||||
|
yield (k, v), defnClass
|
||||||
|
| "responses" ->
|
||||||
|
for KeyValue (k, v) in contents.Responses do
|
||||||
|
yield (k, v.Schema), defnClass
|
||||||
|
| _ -> failwith "oh no"
|
||||||
|
}
|
||||||
|
|> Seq.toList
|
||||||
|
|> go
|
||||||
|
|
||||||
|
let result = Dictionary ()
|
||||||
|
|
||||||
|
for KeyValue (_container, types) in bigCache do
|
||||||
|
for KeyValue (defn, rendered) in types do
|
||||||
|
result.TryAdd (defn, rendered) |> ignore<bool>
|
||||||
|
|
||||||
|
{
|
||||||
|
ByHandle = byHandle
|
||||||
|
ByDefinition = result :> IReadOnlyDictionary<_, _>
|
||||||
|
}
|
||||||
|
|
||||||
|
let summary =
|
||||||
|
contents.Paths
|
||||||
|
|> Seq.collect (fun (KeyValue (path, endpoints)) ->
|
||||||
|
endpoints
|
||||||
|
|> Seq.choose (fun (KeyValue (method, endpoint)) ->
|
||||||
|
let docstring = endpoint.Summary |> PreXmlDoc.create
|
||||||
|
|
||||||
|
let produces =
|
||||||
|
match endpoint.Produces with
|
||||||
|
| None -> Produces "json"
|
||||||
|
| Some [] -> failwith $"API specified empty Produces: %s{path} (%O{method})"
|
||||||
|
| Some [ MimeType "application/json" ] -> Produces "json"
|
||||||
|
| Some [ MimeType (StartsWith "text/" t) ] -> Produces t
|
||||||
|
| Some [ MimeType s ] ->
|
||||||
|
failwithf
|
||||||
|
$"we don't support non-JSON Produces right now, got: %s{s} (%s{path} %O{method})"
|
||||||
|
| Some (_ :: _) ->
|
||||||
|
failwith $"we don't support multiple Produces right now, at %s{path} (%O{method})"
|
||||||
|
|
||||||
|
let returnType =
|
||||||
|
endpoint.Responses
|
||||||
|
|> Seq.choose (fun (KeyValue (response, defn)) ->
|
||||||
|
if 200 <= response && response < 300 then
|
||||||
|
Some defn
|
||||||
|
else
|
||||||
|
None
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let returnType =
|
||||||
|
match returnType with
|
||||||
|
| [ t ] -> Some t
|
||||||
|
| [] -> failwith $"got no successful response results, %s{path} %O{method}"
|
||||||
|
| _ ->
|
||||||
|
SwaggerClientGenerator.log
|
||||||
|
$"Ignoring %s{path} %O{method} due to multiple success responses"
|
||||||
|
// can't be bothered to work out how to deal with multiple success
|
||||||
|
// results right now
|
||||||
|
None
|
||||||
|
|
||||||
|
match returnType with
|
||||||
|
| None -> None
|
||||||
|
| Some returnType ->
|
||||||
|
|
||||||
|
{
|
||||||
|
Method = method
|
||||||
|
Produces = produces
|
||||||
|
DocString = docstring
|
||||||
|
ReturnType = returnType
|
||||||
|
Operation = endpoint.OperationId
|
||||||
|
Parameters = endpoint.Parameters |> Option.defaultValue []
|
||||||
|
Endpoint = path
|
||||||
|
}
|
||||||
|
|> Some
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
)
|
||||||
|
|> Seq.toList
|
||||||
|
|
||||||
|
let config =
|
||||||
|
let pars = MyriadParamParser.render context.AdditionalParameters
|
||||||
|
|
||||||
|
let pars =
|
||||||
|
pars
|
||||||
|
|> Map.toSeq
|
||||||
|
|> Seq.map (fun (k, v) -> k.ToUpperInvariant (), v)
|
||||||
|
|> Map.ofSeq
|
||||||
|
|
||||||
|
if pars.IsEmpty then
|
||||||
|
failwith "No parameters given. You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
|
||||||
|
let createMock =
|
||||||
|
match Map.tryFind "GENERATEMOCKVISIBILITY" pars with
|
||||||
|
| None -> None
|
||||||
|
| Some v ->
|
||||||
|
match v.ToLowerInvariant () with
|
||||||
|
| "internal" -> Some true
|
||||||
|
| "public" -> Some false
|
||||||
|
| _ ->
|
||||||
|
failwith
|
||||||
|
$"Expected GenerateMockVisibility parameter to be 'internal' or 'public', but was: '%s{v.ToLowerInvariant ()}'"
|
||||||
|
|
||||||
|
let className =
|
||||||
|
match Map.tryFind "CLASSNAME" pars with
|
||||||
|
| None -> failwith "You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
| Some v -> v
|
||||||
|
|
||||||
|
{
|
||||||
|
CreateMock = createMock
|
||||||
|
ClassName = className
|
||||||
|
}
|
||||||
|
|
||||||
|
let ty =
|
||||||
|
SwaggerClientGenerator.computeType config basePath typeDefs clientDocstring summary
|
||||||
|
|
||||||
|
[
|
||||||
|
yield
|
||||||
|
SynModuleDecl.Open (
|
||||||
|
SynOpenDeclTarget.ModuleOrNamespace (
|
||||||
|
SynLongIdent.createS' [ "WoofWare" ; "Myriad" ; "Plugins" ],
|
||||||
|
range0
|
||||||
|
),
|
||||||
|
range0
|
||||||
|
)
|
||||||
|
yield SwaggerClientGenerator.instantiateRequiredTypes typeDefs
|
||||||
|
yield! ty
|
||||||
|
]
|
||||||
|
|> SynModuleOrNamespace.createNamespace [ Ident.create config.ClassName ]
|
||||||
|
|> List.singleton
|
||||||
|
|> Output.Ast
|
@@ -1,49 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
|
|
||||||
type internal CompExprBinding =
|
|
||||||
| LetBang of varName : string * rhs : SynExpr
|
|
||||||
| Let of varName : string * rhs : SynExpr
|
|
||||||
| Use of varName : string * rhs : SynExpr
|
|
||||||
| Do of body : SynExpr
|
|
||||||
|
|
||||||
(*
|
|
||||||
Potential API!
|
|
||||||
type internal CompExprBindings =
|
|
||||||
private
|
|
||||||
{
|
|
||||||
/// These are stored in reverse.
|
|
||||||
Bindings : CompExprBinding list
|
|
||||||
CompExprName : string
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal CompExprBindings =
|
|
||||||
let make (name : string) : CompExprBindings =
|
|
||||||
{
|
|
||||||
Bindings = []
|
|
||||||
CompExprName = name
|
|
||||||
}
|
|
||||||
|
|
||||||
let thenDo (body : SynExpr) (bindings : CompExprBindings) =
|
|
||||||
{ bindings with
|
|
||||||
Bindings = (Do body :: bindings.Bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
let thenLet (varName : string) (value : SynExpr) (bindings : CompExprBindings) =
|
|
||||||
{ bindings with
|
|
||||||
Bindings = (Let (varName, value) :: bindings.Bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
let thenLetBang (varName : string) (value : SynExpr) (bindings : CompExprBindings) =
|
|
||||||
{ bindings with
|
|
||||||
Bindings = (LetBang (varName, value) :: bindings.Bindings)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
let thenUse (varName : string) (value : SynExpr) (bindings : CompExprBindings) =
|
|
||||||
{ bindings with
|
|
||||||
Bindings = (LetBang (varName, value) :: bindings.Bindings)
|
|
||||||
}
|
|
||||||
*)
|
|
@@ -1,65 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open System
|
|
||||||
open System.Text
|
|
||||||
open System.Text.RegularExpressions
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal Ident =
|
|
||||||
let inline create (s : string) = Ident (s, range0)
|
|
||||||
|
|
||||||
/// Fantomas bug, perhaps? "type" is not rendered as ``type``, although the ASTs are identical
|
|
||||||
/// apart from the ranges?
|
|
||||||
/// Awful hack: here is a function that does this sort of thing.
|
|
||||||
let createSanitisedParamName (s : string) =
|
|
||||||
match s with
|
|
||||||
| "type" -> create "type'"
|
|
||||||
| "private" -> create "private'"
|
|
||||||
| _ ->
|
|
||||||
|
|
||||||
let result = StringBuilder ()
|
|
||||||
|
|
||||||
for i = 0 to s.Length - 1 do
|
|
||||||
if Char.IsLetter s.[i] then
|
|
||||||
result.Append s.[i] |> ignore<StringBuilder>
|
|
||||||
elif Char.IsNumber s.[i] then
|
|
||||||
if result.Length > 0 then
|
|
||||||
result.Append s.[i] |> ignore<StringBuilder>
|
|
||||||
elif s.[i] = '_' || s.[i] = '-' then
|
|
||||||
result.Append '_' |> ignore<StringBuilder>
|
|
||||||
else
|
|
||||||
failwith $"could not convert to ident: %s{s}"
|
|
||||||
|
|
||||||
create (result.ToString ())
|
|
||||||
|
|
||||||
let private alnum = Regex @"^[a-zA-Z][a-zA-Z0-9]*$"
|
|
||||||
|
|
||||||
let createSanitisedTypeName (s : string) =
|
|
||||||
let result = StringBuilder ()
|
|
||||||
let mutable capitalize = true
|
|
||||||
|
|
||||||
for i = 0 to s.Length - 1 do
|
|
||||||
if Char.IsLetter s.[i] then
|
|
||||||
if capitalize then
|
|
||||||
result.Append (Char.ToUpperInvariant s.[i]) |> ignore<StringBuilder>
|
|
||||||
capitalize <- false
|
|
||||||
else
|
|
||||||
result.Append s.[i] |> ignore<StringBuilder>
|
|
||||||
elif Char.IsNumber s.[i] then
|
|
||||||
if result.Length > 0 then
|
|
||||||
result.Append s.[i] |> ignore<StringBuilder>
|
|
||||||
elif s.[i] = '_' then
|
|
||||||
capitalize <- true
|
|
||||||
|
|
||||||
if result.Length = 0 then
|
|
||||||
failwith $"String %s{s} was not suitable as a type identifier"
|
|
||||||
|
|
||||||
Ident (result.ToString (), range0)
|
|
||||||
|
|
||||||
let lowerFirstLetter (x : Ident) : Ident =
|
|
||||||
let result = StringBuilder x.idText.Length
|
|
||||||
result.Append (Char.ToLowerInvariant x.idText.[0]) |> ignore
|
|
||||||
result.Append x.idText.[1..] |> ignore
|
|
||||||
create ((result : StringBuilder).ToString ())
|
|
@@ -1,17 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal PreXmlDoc =
|
|
||||||
let create (s : string) : PreXmlDoc =
|
|
||||||
let s = s.Split "\n"
|
|
||||||
|
|
||||||
for i = 0 to s.Length - 1 do
|
|
||||||
s.[i] <- " " + s.[i]
|
|
||||||
|
|
||||||
PreXmlDoc.Create (s, range0)
|
|
||||||
|
|
||||||
let create' (s : string seq) : PreXmlDoc =
|
|
||||||
PreXmlDoc.Create (Array.ofSeq s, range0)
|
|
@@ -1,30 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynArgPats =
|
|
||||||
let createNamed (caseNames : string list) : SynArgPats =
|
|
||||||
match caseNames.Length with
|
|
||||||
| 0 -> SynArgPats.Pats []
|
|
||||||
| 1 ->
|
|
||||||
SynPat.Named (SynIdent.createS caseNames.[0], false, None, range0)
|
|
||||||
|> List.singleton
|
|
||||||
|> SynArgPats.Pats
|
|
||||||
| len ->
|
|
||||||
caseNames
|
|
||||||
|> List.map (fun name -> SynPat.Named (SynIdent.createS name, false, None, range0))
|
|
||||||
|> fun t -> SynPat.Tuple (false, t, List.replicate (len - 1) range0, range0)
|
|
||||||
|> fun t -> SynPat.Paren (t, range0)
|
|
||||||
|> List.singleton
|
|
||||||
|> SynArgPats.Pats
|
|
||||||
|
|
||||||
let create (pats : SynPat list) : SynArgPats =
|
|
||||||
match pats.Length with
|
|
||||||
| 0 -> SynArgPats.Pats []
|
|
||||||
| 1 -> [ pats.[0] ] |> SynArgPats.Pats
|
|
||||||
| len ->
|
|
||||||
SynPat.Paren (SynPat.Tuple (false, pats, List.replicate (len - 1) range0, range0), range0)
|
|
||||||
|> List.singleton
|
|
||||||
|> SynArgPats.Pats
|
|
@@ -1,27 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynAttribute =
|
|
||||||
let inline create (typeName : SynLongIdent) (arg : SynExpr) : SynAttribute =
|
|
||||||
{
|
|
||||||
TypeName = typeName
|
|
||||||
ArgExpr = arg
|
|
||||||
Target = None
|
|
||||||
AppliesToGetterAndSetter = false
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
|
|
||||||
let internal compilationRepresentation : SynAttribute =
|
|
||||||
[ "CompilationRepresentationFlags" ; "ModuleSuffix" ]
|
|
||||||
|> SynExpr.createLongIdent
|
|
||||||
|> SynExpr.paren
|
|
||||||
|> create (SynLongIdent.createS "CompilationRepresentation")
|
|
||||||
|
|
||||||
let internal requireQualifiedAccess : SynAttribute =
|
|
||||||
create (SynLongIdent.createS "RequireQualifiedAccess") (SynExpr.CreateConst ())
|
|
||||||
|
|
||||||
let internal autoOpen : SynAttribute =
|
|
||||||
create (SynLongIdent.createS "AutoOpen") (SynExpr.CreateConst ())
|
|
@@ -1,15 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynAttributes =
|
|
||||||
let ofAttrs (attrs : SynAttribute list) : SynAttributes =
|
|
||||||
attrs
|
|
||||||
|> List.map (fun a ->
|
|
||||||
{
|
|
||||||
Attributes = [ a ]
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
)
|
|
@@ -1,233 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynBinding =
|
|
||||||
|
|
||||||
let rec private stripParen (pat : SynPat) =
|
|
||||||
match pat with
|
|
||||||
| SynPat.Paren (p, _) -> stripParen p
|
|
||||||
| _ -> pat
|
|
||||||
|
|
||||||
let rec private getName (pat : SynPat) : Ident option =
|
|
||||||
match stripParen pat with
|
|
||||||
| SynPat.Named (SynIdent.SynIdent (name, _), _, _, _) -> Some name
|
|
||||||
| SynPat.Typed (pat, _, _) -> getName pat
|
|
||||||
| SynPat.LongIdent (SynLongIdent.SynLongIdent (longIdent, _, _), _, _, _, _, _) ->
|
|
||||||
match longIdent with
|
|
||||||
| [ x ] -> Some x
|
|
||||||
| _ -> failwithf "got long ident %O ; can only get the name of a long ident with one component" longIdent
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let private getArgInfo (pat : SynPat) : SynArgInfo list =
|
|
||||||
// TODO: this only copes with one layer of tupling
|
|
||||||
match stripParen pat with
|
|
||||||
| SynPat.Tuple (_, pats, _, _) -> pats |> List.map (fun pat -> SynArgInfo.SynArgInfo ([], false, getName pat))
|
|
||||||
| pat -> [ SynArgInfo.SynArgInfo (SynAttributes.Empty, false, getName pat) ]
|
|
||||||
|
|
||||||
let triviaZero (isMember : bool) =
|
|
||||||
{
|
|
||||||
SynBindingTrivia.EqualsRange = Some range0
|
|
||||||
InlineKeyword = None
|
|
||||||
LeadingKeyword =
|
|
||||||
if isMember then
|
|
||||||
SynLeadingKeyword.Member range0
|
|
||||||
else
|
|
||||||
SynLeadingKeyword.Let range0
|
|
||||||
}
|
|
||||||
|
|
||||||
let basic (name : LongIdent) (args : SynPat list) (body : SynExpr) : SynBinding =
|
|
||||||
let valInfo : SynValInfo =
|
|
||||||
args
|
|
||||||
|> List.map getArgInfo
|
|
||||||
|> fun x -> SynValInfo.SynValInfo (x, SynArgInfo.SynArgInfo ([], false, None))
|
|
||||||
|
|
||||||
SynBinding.SynBinding (
|
|
||||||
None,
|
|
||||||
SynBindingKind.Normal,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
[],
|
|
||||||
PreXmlDoc.Empty,
|
|
||||||
SynValData.SynValData (None, valInfo, None),
|
|
||||||
SynPat.identWithArgs name (SynArgPats.Pats args),
|
|
||||||
None,
|
|
||||||
body,
|
|
||||||
range0,
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
triviaZero false
|
|
||||||
)
|
|
||||||
|
|
||||||
let withMutability (mut : bool) (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (pat, kind, inl, _, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
|
|
||||||
SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
|
|
||||||
|
|
||||||
let withRecursion (isRec : bool) (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
|
|
||||||
let trivia =
|
|
||||||
{ trivia with
|
|
||||||
LeadingKeyword =
|
|
||||||
match trivia.LeadingKeyword with
|
|
||||||
| SynLeadingKeyword.Let _ ->
|
|
||||||
if isRec then
|
|
||||||
SynLeadingKeyword.LetRec (range0, range0)
|
|
||||||
else
|
|
||||||
trivia.LeadingKeyword
|
|
||||||
| SynLeadingKeyword.LetRec _ ->
|
|
||||||
if isRec then
|
|
||||||
trivia.LeadingKeyword
|
|
||||||
else
|
|
||||||
trivia.LeadingKeyword
|
|
||||||
| existing ->
|
|
||||||
failwith
|
|
||||||
$"WoofWare.Myriad doesn't yet let you adjust the recursion modifier on a binding with modifier %O{existing}"
|
|
||||||
}
|
|
||||||
|
|
||||||
SynBinding (pat, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
|
|
||||||
|
|
||||||
let withAccessibility (acc : SynAccess option) (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (_, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
|
|
||||||
let headPat =
|
|
||||||
match headPat with
|
|
||||||
| SynPat.LongIdent (ident, extra, options, argPats, _, range) ->
|
|
||||||
SynPat.LongIdent (ident, extra, options, argPats, acc, range)
|
|
||||||
| _ -> failwithf "unrecognised head pattern: %O" headPat
|
|
||||||
|
|
||||||
SynBinding (acc, kind, inl, mut, attrs, xml, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
|
|
||||||
|
|
||||||
let withXmlDoc (doc : PreXmlDoc) (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (acc, kind, inl, mut, attrs, _, valData, headPat, returnInfo, expr, range, debugPoint, trivia) ->
|
|
||||||
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, returnInfo, expr, range, debugPoint, trivia)
|
|
||||||
|
|
||||||
let withReturnAnnotation (ty : SynType) (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, _, expr, range, debugPoint, trivia) ->
|
|
||||||
let retInfo =
|
|
||||||
SynBindingReturnInfo.SynBindingReturnInfo (
|
|
||||||
ty,
|
|
||||||
range0,
|
|
||||||
[],
|
|
||||||
{
|
|
||||||
ColonRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SynBinding (
|
|
||||||
acc,
|
|
||||||
kind,
|
|
||||||
inl,
|
|
||||||
mut,
|
|
||||||
attrs,
|
|
||||||
doc,
|
|
||||||
valData,
|
|
||||||
headPat,
|
|
||||||
Some retInfo,
|
|
||||||
expr,
|
|
||||||
range,
|
|
||||||
debugPoint,
|
|
||||||
trivia
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline makeInline (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
|
||||||
SynBinding (
|
|
||||||
acc,
|
|
||||||
kind,
|
|
||||||
true,
|
|
||||||
mut,
|
|
||||||
attrs,
|
|
||||||
doc,
|
|
||||||
valData,
|
|
||||||
headPat,
|
|
||||||
ret,
|
|
||||||
expr,
|
|
||||||
range,
|
|
||||||
debugPoint,
|
|
||||||
{ trivia with
|
|
||||||
InlineKeyword = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline makeNotInline (binding : SynBinding) : SynBinding =
|
|
||||||
match binding with
|
|
||||||
| SynBinding (acc, kind, _, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
|
||||||
SynBinding (
|
|
||||||
acc,
|
|
||||||
kind,
|
|
||||||
false,
|
|
||||||
mut,
|
|
||||||
attrs,
|
|
||||||
doc,
|
|
||||||
valData,
|
|
||||||
headPat,
|
|
||||||
ret,
|
|
||||||
expr,
|
|
||||||
range,
|
|
||||||
debugPoint,
|
|
||||||
{ trivia with
|
|
||||||
InlineKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline setInline (isInline : bool) (binding : SynBinding) : SynBinding =
|
|
||||||
if isInline then
|
|
||||||
makeInline binding
|
|
||||||
else
|
|
||||||
makeNotInline binding
|
|
||||||
|
|
||||||
let makeStaticMember (binding : SynBinding) : SynBinding =
|
|
||||||
let memberFlags =
|
|
||||||
{
|
|
||||||
SynMemberFlags.IsInstance = false
|
|
||||||
SynMemberFlags.IsDispatchSlot = false
|
|
||||||
SynMemberFlags.IsOverrideOrExplicitImpl = false
|
|
||||||
SynMemberFlags.IsFinal = false
|
|
||||||
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
|
||||||
SynMemberFlags.MemberKind = SynMemberKind.Member
|
|
||||||
}
|
|
||||||
|
|
||||||
match binding with
|
|
||||||
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
|
||||||
let valData =
|
|
||||||
match valData with
|
|
||||||
| SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None)
|
|
||||||
|
|
||||||
let trivia =
|
|
||||||
{ trivia with
|
|
||||||
LeadingKeyword = SynLeadingKeyword.StaticMember (range0, range0)
|
|
||||||
}
|
|
||||||
|
|
||||||
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia)
|
|
||||||
|
|
||||||
let makeInstanceMember (binding : SynBinding) : SynBinding =
|
|
||||||
let memberFlags =
|
|
||||||
{
|
|
||||||
SynMemberFlags.IsInstance = true
|
|
||||||
SynMemberFlags.IsDispatchSlot = false
|
|
||||||
SynMemberFlags.IsOverrideOrExplicitImpl = true
|
|
||||||
SynMemberFlags.IsFinal = false
|
|
||||||
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
|
||||||
SynMemberFlags.MemberKind = SynMemberKind.Member
|
|
||||||
}
|
|
||||||
|
|
||||||
match binding with
|
|
||||||
| SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia) ->
|
|
||||||
let valData =
|
|
||||||
match valData with
|
|
||||||
| SynValData.SynValData (_, valInfo, _) -> SynValData.SynValData (Some memberFlags, valInfo, None)
|
|
||||||
|
|
||||||
let trivia =
|
|
||||||
{ trivia with
|
|
||||||
LeadingKeyword = SynLeadingKeyword.Member range0
|
|
||||||
}
|
|
||||||
|
|
||||||
SynBinding (acc, kind, inl, mut, attrs, doc, valData, headPat, ret, expr, range, debugPoint, trivia)
|
|
@@ -1,50 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynComponentInfo =
|
|
||||||
let inline createLong (name : LongIdent) =
|
|
||||||
SynComponentInfo.SynComponentInfo ([], None, [], name, PreXmlDoc.Empty, false, None, range0)
|
|
||||||
|
|
||||||
let inline create (name : Ident) = createLong [ name ]
|
|
||||||
|
|
||||||
let inline withDocString (doc : PreXmlDoc) (i : SynComponentInfo) : SynComponentInfo =
|
|
||||||
match i with
|
|
||||||
| SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, _, postfix, access, range) ->
|
|
||||||
SynComponentInfo (attrs, typars, constraints, name, doc, postfix, access, range)
|
|
||||||
|
|
||||||
let inline setGenerics (typars : SynTyparDecls option) (i : SynComponentInfo) : SynComponentInfo =
|
|
||||||
match i with
|
|
||||||
| SynComponentInfo.SynComponentInfo (attrs, _, constraints, name, doc, postfix, access, range) ->
|
|
||||||
SynComponentInfo (attrs, typars, constraints, name, doc, postfix, access, range)
|
|
||||||
|
|
||||||
let inline withGenerics (typars : SynTyparDecl list) (i : SynComponentInfo) : SynComponentInfo =
|
|
||||||
let inner =
|
|
||||||
if typars.IsEmpty then
|
|
||||||
None
|
|
||||||
else
|
|
||||||
Some (SynTyparDecls.PostfixList (typars, [], range0))
|
|
||||||
|
|
||||||
setGenerics inner i
|
|
||||||
|
|
||||||
let inline setAccessibility (acc : SynAccess option) (i : SynComponentInfo) : SynComponentInfo =
|
|
||||||
match i with
|
|
||||||
| SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, doc, postfix, _, range) ->
|
|
||||||
SynComponentInfo.SynComponentInfo (attrs, typars, constraints, name, doc, postfix, acc, range)
|
|
||||||
|
|
||||||
let inline withAccessibility (acc : SynAccess) (i : SynComponentInfo) : SynComponentInfo =
|
|
||||||
setAccessibility (Some acc) i
|
|
||||||
|
|
||||||
let inline addAttributes (attrs : SynAttribute list) (i : SynComponentInfo) : SynComponentInfo =
|
|
||||||
match i with
|
|
||||||
| SynComponentInfo.SynComponentInfo (oldAttrs, typars, constraints, name, doc, postfix, acc, range) ->
|
|
||||||
let attrs =
|
|
||||||
{
|
|
||||||
SynAttributeList.Attributes = attrs
|
|
||||||
SynAttributeList.Range = range0
|
|
||||||
}
|
|
||||||
|
|
||||||
SynComponentInfo.SynComponentInfo ((attrs :: oldAttrs), typars, constraints, name, doc, postfix, acc, range)
|
|
@@ -1,376 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Myriad.Core
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<AutoOpen>]
|
|
||||||
module internal SynExprExtensions =
|
|
||||||
type SynExpr with
|
|
||||||
static member CreateConst (s : string) : SynExpr =
|
|
||||||
SynExpr.Const (SynConst.String (s, SynStringKind.Regular, range0), range0)
|
|
||||||
|
|
||||||
static member CreateConst () : SynExpr = SynExpr.Const (SynConst.Unit, range0)
|
|
||||||
|
|
||||||
static member CreateConst (b : bool) : SynExpr = SynExpr.Const (SynConst.Bool b, range0)
|
|
||||||
|
|
||||||
static member CreateConst (c : char) : SynExpr =
|
|
||||||
// apparent Myriad bug: `IndexOf '?'` gets formatted as `IndexOf ?` which is clearly wrong
|
|
||||||
SynExpr.CreateApp (SynExpr.Ident (Ident.Create "char"), SynExpr.CreateConst (int c))
|
|
||||||
|> fun e -> SynExpr.Paren (e, range0, Some range0, range0)
|
|
||||||
|
|
||||||
static member CreateConst (i : int32) : SynExpr =
|
|
||||||
SynExpr.Const (SynConst.Int32 i, range0)
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynExpr =
|
|
||||||
|
|
||||||
/// {f} {x}
|
|
||||||
let applyFunction (f : SynExpr) (x : SynExpr) : SynExpr = SynExpr.CreateApp (f, x)
|
|
||||||
|
|
||||||
/// {f} {x}
|
|
||||||
let inline applyTo (x : SynExpr) (f : SynExpr) : SynExpr = applyFunction f x
|
|
||||||
|
|
||||||
/// {expr} |> {func}
|
|
||||||
let pipeThroughFunction (func : SynExpr) (expr : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.pipe, expr)
|
|
||||||
|> applyTo func
|
|
||||||
|
|
||||||
/// if {cond} then {trueBranch} else {falseBranch}
|
|
||||||
/// Note that this function puts the trueBranch last, for pipelining convenience:
|
|
||||||
/// we assume that the `else` branch is more like an error case and is less interesting.
|
|
||||||
let ifThenElse (cond : SynExpr) (falseBranch : SynExpr) (trueBranch : SynExpr) : SynExpr =
|
|
||||||
SynExpr.IfThenElse (
|
|
||||||
cond,
|
|
||||||
trueBranch,
|
|
||||||
Some falseBranch,
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
false,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
IfKeyword = range0
|
|
||||||
IsElif = false
|
|
||||||
ThenKeyword = range0
|
|
||||||
ElseKeyword = Some range0
|
|
||||||
IfToThenRange = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/// try {body} with | {exc} as exc -> {handler}
|
|
||||||
let pipeThroughTryWith (exc : SynPat) (handler : SynExpr) (body : SynExpr) : SynExpr =
|
|
||||||
let clause =
|
|
||||||
SynMatchClause.create (SynPat.As (exc, SynPat.named "exc", range0)) handler
|
|
||||||
|
|
||||||
SynExpr.TryWith (
|
|
||||||
body,
|
|
||||||
[ clause ],
|
|
||||||
range0,
|
|
||||||
DebugPointAtTry.Yes range0,
|
|
||||||
DebugPointAtWith.Yes range0,
|
|
||||||
{
|
|
||||||
TryKeyword = range0
|
|
||||||
TryToWithRange = range0
|
|
||||||
WithKeyword = range0
|
|
||||||
WithToEndRange = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
/// {a} = {b}
|
|
||||||
let equals (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.eq, a) |> applyTo b
|
|
||||||
|
|
||||||
/// {a} && {b}
|
|
||||||
let booleanAnd (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.booleanAnd, a)
|
|
||||||
|> applyTo b
|
|
||||||
|
|
||||||
/// {a} || {b}
|
|
||||||
let booleanOr (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.booleanOr, a)
|
|
||||||
|> applyTo b
|
|
||||||
|
|
||||||
/// {a} + {b}
|
|
||||||
let plus (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateAppInfix (
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
Ident.CreateLong "op_Addition",
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "+") ]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
a
|
|
||||||
)
|
|
||||||
|> applyTo b
|
|
||||||
|
|
||||||
/// {a} * {b}
|
|
||||||
let times (a : SynExpr) (b : SynExpr) =
|
|
||||||
SynExpr.CreateAppInfix (
|
|
||||||
SynExpr.CreateLongIdent (
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
Ident.CreateLong "op_Multiply",
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "*") ]
|
|
||||||
)
|
|
||||||
),
|
|
||||||
a
|
|
||||||
)
|
|
||||||
|> applyTo b
|
|
||||||
|
|
||||||
let rec stripOptionalParen (expr : SynExpr) : SynExpr =
|
|
||||||
match expr with
|
|
||||||
| SynExpr.Paren (expr, _, _, _) -> stripOptionalParen expr
|
|
||||||
| expr -> expr
|
|
||||||
|
|
||||||
let dotGet (field : string) (obj : SynExpr) : SynExpr =
|
|
||||||
SynExpr.DotGet (
|
|
||||||
obj,
|
|
||||||
range0,
|
|
||||||
SynLongIdent.SynLongIdent (id = [ Ident.create field ], dotRanges = [], trivia = [ None ]),
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
/// {obj}.{meth} {arg}
|
|
||||||
let callMethodArg (meth : string) (arg : SynExpr) (obj : SynExpr) : SynExpr = dotGet meth obj |> applyTo arg
|
|
||||||
|
|
||||||
/// {obj}.{meth}()
|
|
||||||
let callMethod (meth : string) (obj : SynExpr) : SynExpr =
|
|
||||||
callMethodArg meth (SynExpr.CreateConst ()) obj
|
|
||||||
|
|
||||||
let typeApp (types : SynType list) (operand : SynExpr) =
|
|
||||||
SynExpr.TypeApp (operand, range0, types, List.replicate (types.Length - 1) range0, Some range0, range0, range0)
|
|
||||||
|
|
||||||
/// {obj}.{meth}<types,...>()
|
|
||||||
let callGenericMethod (meth : SynLongIdent) (types : SynType list) (obj : SynExpr) : SynExpr =
|
|
||||||
SynExpr.DotGet (obj, range0, meth, range0)
|
|
||||||
|> typeApp types
|
|
||||||
|> applyTo (SynExpr.CreateConst ())
|
|
||||||
|
|
||||||
/// {obj}.{meth}<ty>()
|
|
||||||
let callGenericMethod' (meth : string) (ty : string) (obj : SynExpr) : SynExpr =
|
|
||||||
callGenericMethod (SynLongIdent.createS meth) [ SynType.createLongIdent' [ ty ] ] obj
|
|
||||||
|
|
||||||
let inline index (property : SynExpr) (obj : SynExpr) : SynExpr =
|
|
||||||
SynExpr.DotIndexedGet (obj, property, range0, range0)
|
|
||||||
|
|
||||||
let inline arrayIndexRange (start : SynExpr option) (endRange : SynExpr option) (arr : SynExpr) : SynExpr =
|
|
||||||
SynExpr.DotIndexedGet (
|
|
||||||
arr,
|
|
||||||
(SynExpr.IndexRange (start, range0, endRange, range0, range0, range0)),
|
|
||||||
range0,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline paren (e : SynExpr) : SynExpr =
|
|
||||||
SynExpr.Paren (e, range0, Some range0, range0)
|
|
||||||
|
|
||||||
/// (fun {varName} -> {body})
|
|
||||||
let createLambda (varName : string) (body : SynExpr) : SynExpr =
|
|
||||||
let parsedDataPat = [ SynPat.named varName ]
|
|
||||||
|
|
||||||
SynExpr.Lambda (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
SynSimplePats.Create [ SynSimplePat.CreateId (Ident.Create varName) ],
|
|
||||||
body,
|
|
||||||
Some (parsedDataPat, body),
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
ArrowRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> paren
|
|
||||||
|
|
||||||
let createThunk (body : SynExpr) : SynExpr =
|
|
||||||
SynExpr.Lambda (
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
SynSimplePats.Create [],
|
|
||||||
body,
|
|
||||||
Some ([ SynPat.unit ], body),
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
ArrowRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|> paren
|
|
||||||
|
|
||||||
let inline createIdent (s : string) : SynExpr = SynExpr.Ident (Ident (s, range0))
|
|
||||||
|
|
||||||
let inline createIdent' (i : Ident) : SynExpr = SynExpr.Ident i
|
|
||||||
|
|
||||||
let inline createLongIdent' (ident : Ident list) : SynExpr =
|
|
||||||
SynExpr.LongIdent (false, SynLongIdent.create ident, None, range0)
|
|
||||||
|
|
||||||
let inline createLongIdent (ident : string list) : SynExpr =
|
|
||||||
createLongIdent' (ident |> List.map Ident.create)
|
|
||||||
|
|
||||||
let tupleNoParen (args : SynExpr list) : SynExpr =
|
|
||||||
SynExpr.Tuple (false, args, List.replicate (args.Length - 1) range0, range0)
|
|
||||||
|
|
||||||
let inline tuple (args : SynExpr list) = args |> tupleNoParen |> paren
|
|
||||||
|
|
||||||
/// {body} |> fun a -> Async.StartAsTask (a, ?cancellationToken=ct)
|
|
||||||
let startAsTask (ct : Ident) (body : SynExpr) =
|
|
||||||
let lambda =
|
|
||||||
[
|
|
||||||
createIdent "a"
|
|
||||||
equals
|
|
||||||
(SynExpr.LongIdent (true, SynLongIdent.createS "cancellationToken", None, range0))
|
|
||||||
(createIdent' ct)
|
|
||||||
]
|
|
||||||
|> tuple
|
|
||||||
|> applyFunction (createLongIdent [ "Async" ; "StartAsTask" ])
|
|
||||||
|> createLambda "a"
|
|
||||||
|
|
||||||
pipeThroughFunction lambda body
|
|
||||||
|
|
||||||
let inline createForEach (pat : SynPat) (enumExpr : SynExpr) (body : SynExpr) : SynExpr =
|
|
||||||
SynExpr.ForEach (
|
|
||||||
DebugPointAtFor.No,
|
|
||||||
DebugPointAtInOrTo.No,
|
|
||||||
SeqExprOnly.SeqExprOnly false,
|
|
||||||
true,
|
|
||||||
pat,
|
|
||||||
enumExpr,
|
|
||||||
body,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline createLet (bindings : SynBinding list) (body : SynExpr) : SynExpr =
|
|
||||||
SynExpr.LetOrUse (false, false, bindings, body, range0, SynExprLetOrUseTrivia.empty)
|
|
||||||
|
|
||||||
let inline createDo (body : SynExpr) : SynExpr = SynExpr.Do (body, range0)
|
|
||||||
|
|
||||||
let inline createMatch (matchOn : SynExpr) (cases : SynMatchClause list) : SynExpr =
|
|
||||||
SynExpr.Match (
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
matchOn,
|
|
||||||
cases,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
MatchKeyword = range0
|
|
||||||
WithKeyword = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let typeAnnotate (ty : SynType) (expr : SynExpr) : SynExpr = SynExpr.Typed (expr, ty, range0)
|
|
||||||
|
|
||||||
let inline createNew (ty : SynType) (args : SynExpr) : SynExpr =
|
|
||||||
SynExpr.New (false, ty, paren args, range0)
|
|
||||||
|
|
||||||
let inline createWhile (cond : SynExpr) (body : SynExpr) : SynExpr =
|
|
||||||
SynExpr.While (DebugPointAtWhile.Yes range0, cond, body, range0)
|
|
||||||
|
|
||||||
let inline createNull () : SynExpr = SynExpr.Null range0
|
|
||||||
|
|
||||||
let reraise : SynExpr = createIdent "reraise" |> applyTo (SynExpr.CreateConst ())
|
|
||||||
|
|
||||||
let sequential (exprs : SynExpr list) : SynExpr =
|
|
||||||
exprs
|
|
||||||
|> List.reduce (fun a b -> SynExpr.Sequential (DebugPointAtSequential.SuppressNeither, false, a, b, range0))
|
|
||||||
|
|
||||||
let listLiteral (elts : SynExpr list) : SynExpr =
|
|
||||||
SynExpr.ArrayOrListComputed (false, sequential elts, range0)
|
|
||||||
|
|
||||||
let arrayLiteral (elts : SynExpr list) : SynExpr =
|
|
||||||
SynExpr.ArrayOrListComputed (true, sequential elts, range0)
|
|
||||||
|
|
||||||
/// {compExpr} { {lets} ; return {ret} }
|
|
||||||
let createCompExpr (compExpr : string) (retBody : SynExpr) (lets : CompExprBinding list) : SynExpr =
|
|
||||||
let retStatement = SynExpr.YieldOrReturn ((false, true), retBody, range0)
|
|
||||||
|
|
||||||
let contents : SynExpr =
|
|
||||||
(retStatement, List.rev lets)
|
|
||||||
||> List.fold (fun state binding ->
|
|
||||||
match binding with
|
|
||||||
| LetBang (lhs, rhs) ->
|
|
||||||
SynExpr.LetOrUseBang (
|
|
||||||
DebugPointAtBinding.Yes range0,
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
SynPat.named lhs,
|
|
||||||
rhs,
|
|
||||||
[],
|
|
||||||
state,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
EqualsRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
| Let (lhs, rhs) -> createLet [ SynBinding.basic [ Ident.create lhs ] [] rhs ] state
|
|
||||||
| Use (lhs, rhs) ->
|
|
||||||
SynExpr.LetOrUse (
|
|
||||||
false,
|
|
||||||
true,
|
|
||||||
[ SynBinding.basic [ Ident.create lhs ] [] rhs ],
|
|
||||||
state,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
SynExprLetOrUseTrivia.InKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
| Do body -> sequential [ SynExpr.Do (body, range0) ; state ]
|
|
||||||
)
|
|
||||||
|
|
||||||
applyFunction (createIdent compExpr) (SynExpr.ComputationExpr (false, contents, range0))
|
|
||||||
|
|
||||||
/// {expr} |> Async.AwaitTask
|
|
||||||
let awaitTask (expr : SynExpr) : SynExpr =
|
|
||||||
expr |> pipeThroughFunction (createLongIdent [ "Async" ; "AwaitTask" ])
|
|
||||||
|
|
||||||
/// {ident}.ToString ()
|
|
||||||
/// with special casing for some types like DateTime
|
|
||||||
let toString (ty : SynType) (ident : SynExpr) =
|
|
||||||
match ty with
|
|
||||||
| DateOnly -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-dd")
|
|
||||||
| DateTime -> ident |> callMethodArg "ToString" (SynExpr.CreateConst "yyyy-MM-ddTHH:mm:ss")
|
|
||||||
| _ -> callMethod "ToString" ident
|
|
||||||
|
|
||||||
let upcast' (ty : SynType) (e : SynExpr) = SynExpr.Upcast (e, ty, range0)
|
|
||||||
|
|
||||||
/// {ident} - {rhs}
|
|
||||||
let minus (ident : SynLongIdent) (rhs : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.sub, SynExpr.CreateLongIdent ident)
|
|
||||||
|> applyTo rhs
|
|
||||||
|
|
||||||
/// {ident} - {n}
|
|
||||||
let minusN (ident : SynLongIdent) (n : int) : SynExpr = minus ident (SynExpr.CreateConst n)
|
|
||||||
|
|
||||||
/// {y} > {x}
|
|
||||||
let greaterThan (x : SynExpr) (y : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.gt, y) |> applyTo x
|
|
||||||
|
|
||||||
/// {y} < {x}
|
|
||||||
let lessThan (x : SynExpr) (y : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.lt, y) |> applyTo x
|
|
||||||
|
|
||||||
/// {y} >= {x}
|
|
||||||
let greaterThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.geq, y)
|
|
||||||
|> applyTo x
|
|
||||||
|
|
||||||
/// {y} <= {x}
|
|
||||||
let lessThanOrEqual (x : SynExpr) (y : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (SynExpr.CreateLongIdent SynLongIdent.leq, y)
|
|
||||||
|> applyTo x
|
|
||||||
|
|
||||||
/// {x} :: {y}
|
|
||||||
let listCons (x : SynExpr) (y : SynExpr) : SynExpr =
|
|
||||||
SynExpr.CreateAppInfix (
|
|
||||||
SynExpr.LongIdent (
|
|
||||||
false,
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
[ Ident.create "op_ColonColon" ],
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "::") ]
|
|
||||||
),
|
|
||||||
None,
|
|
||||||
range0
|
|
||||||
),
|
|
||||||
tupleNoParen [ x ; y ]
|
|
||||||
)
|
|
||||||
|> paren
|
|
||||||
|
|
||||||
let assign (lhs : SynLongIdent) (rhs : SynExpr) : SynExpr = SynExpr.LongIdentSet (lhs, rhs, range0)
|
|
@@ -1,10 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynExprLetOrUseTrivia =
|
|
||||||
let empty : SynExprLetOrUseTrivia =
|
|
||||||
{
|
|
||||||
InKeyword = None
|
|
||||||
}
|
|
@@ -1,76 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
|
|
||||||
/// The data needed to reconstitute a single piece of data within a union field, or a single record field.
|
|
||||||
/// This is generic on whether the field is identified. For example, in `type Foo = Blah of int`, the `int`
|
|
||||||
/// field is not identified; whereas in `type Foo = Blah of baz : int`, it is identified.
|
|
||||||
type SynFieldData<'Ident> =
|
|
||||||
{
|
|
||||||
/// Attributes on this field. I think you can only get these if this is a *record* field.
|
|
||||||
Attrs : SynAttribute list
|
|
||||||
/// The identifier of this field (see docstring for SynFieldData).
|
|
||||||
Ident : 'Ident
|
|
||||||
/// The type of the data contained in this field. For example, `type Foo = { Blah : int }`
|
|
||||||
/// has this being `int`.
|
|
||||||
Type : SynType
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynField =
|
|
||||||
/// Get the useful information out of a SynField.
|
|
||||||
let extract (SynField (attrs, _, id, fieldType, _, _, _, _, _)) : SynFieldData<Ident option> =
|
|
||||||
{
|
|
||||||
Attrs = attrs |> List.collect (fun l -> l.Attributes)
|
|
||||||
Ident = id
|
|
||||||
Type = fieldType
|
|
||||||
}
|
|
||||||
|
|
||||||
let mapIdent<'a, 'b> (f : 'a -> 'b) (x : SynFieldData<'a>) : SynFieldData<'b> =
|
|
||||||
let ident = f x.Ident
|
|
||||||
|
|
||||||
{
|
|
||||||
Attrs = x.Attrs
|
|
||||||
Ident = ident
|
|
||||||
Type = x.Type
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Throws if the field has no identifier.
|
|
||||||
let extractWithIdent (f : SynField) : SynFieldData<Ident> =
|
|
||||||
f
|
|
||||||
|> extract
|
|
||||||
|> mapIdent (fun ident ->
|
|
||||||
match ident with
|
|
||||||
| None -> failwith "expected field identifier to have a value, but it did not"
|
|
||||||
| Some i -> i
|
|
||||||
)
|
|
||||||
|
|
||||||
let make (data : SynFieldData<Ident option>) : SynField =
|
|
||||||
let attrs : SynAttributeList list =
|
|
||||||
data.Attrs
|
|
||||||
|> List.map (fun l ->
|
|
||||||
{
|
|
||||||
Attributes = [ l ]
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SynField.SynField (
|
|
||||||
attrs,
|
|
||||||
false,
|
|
||||||
data.Ident,
|
|
||||||
data.Type,
|
|
||||||
false,
|
|
||||||
PreXmlDoc.Empty,
|
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
SynFieldTrivia.Zero
|
|
||||||
)
|
|
||||||
|
|
||||||
let withDocString (doc : PreXmlDoc) (f : SynField) : SynField =
|
|
||||||
match f with
|
|
||||||
| SynField (attributes, isStatic, idOpt, fieldType, isMutable, _, accessibility, range, trivia) ->
|
|
||||||
SynField (attributes, isStatic, idOpt, fieldType, isMutable, doc, accessibility, range, trivia)
|
|
@@ -1,10 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynIdent =
|
|
||||||
let inline createI (i : Ident) : SynIdent = SynIdent.SynIdent (i, None)
|
|
||||||
|
|
||||||
let inline createS (i : string) : SynIdent =
|
|
||||||
SynIdent.SynIdent (Ident.create i, None)
|
|
@@ -1,134 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynLongIdent =
|
|
||||||
|
|
||||||
let geq =
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
[ Ident.create "op_GreaterThanOrEqual" ],
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation ">=") ]
|
|
||||||
)
|
|
||||||
|
|
||||||
let leq =
|
|
||||||
SynLongIdent.SynLongIdent (
|
|
||||||
[ Ident.create "op_LessThanOrEqual" ],
|
|
||||||
[],
|
|
||||||
[ Some (IdentTrivia.OriginalNotation "<=") ]
|
|
||||||
)
|
|
||||||
|
|
||||||
let gt =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_GreaterThan" ], [], [ Some (IdentTrivia.OriginalNotation ">") ])
|
|
||||||
|
|
||||||
let lt =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_LessThan" ], [], [ Some (IdentTrivia.OriginalNotation "<") ])
|
|
||||||
|
|
||||||
let sub =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_Subtraction" ], [], [ Some (IdentTrivia.OriginalNotation "-") ])
|
|
||||||
|
|
||||||
let eq =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_Equality" ], [], [ Some (IdentTrivia.OriginalNotation "=") ])
|
|
||||||
|
|
||||||
let booleanAnd =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_BooleanAnd" ], [], [ Some (IdentTrivia.OriginalNotation "&&") ])
|
|
||||||
|
|
||||||
let booleanOr =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_BooleanOr" ], [], [ Some (IdentTrivia.OriginalNotation "||") ])
|
|
||||||
|
|
||||||
let pipe =
|
|
||||||
SynLongIdent.SynLongIdent ([ Ident.create "op_PipeRight" ], [], [ Some (IdentTrivia.OriginalNotation "|>") ])
|
|
||||||
|
|
||||||
let toString (sli : SynLongIdent) : string =
|
|
||||||
sli.LongIdent |> List.map _.idText |> String.concat "."
|
|
||||||
|
|
||||||
let create (ident : LongIdent) : SynLongIdent =
|
|
||||||
let commas =
|
|
||||||
match ident with
|
|
||||||
| [] -> []
|
|
||||||
| _ :: commas -> commas |> List.map (fun _ -> range0)
|
|
||||||
|
|
||||||
SynLongIdent.SynLongIdent (ident, commas, List.replicate ident.Length None)
|
|
||||||
|
|
||||||
let inline createI (i : Ident) : SynLongIdent = create [ i ]
|
|
||||||
|
|
||||||
let inline createS (s : string) : SynLongIdent = createI (Ident (s, range0))
|
|
||||||
|
|
||||||
let inline createS' (s : string list) : SynLongIdent =
|
|
||||||
create (s |> List.map (fun i -> Ident (i, range0)))
|
|
||||||
|
|
||||||
let isUnit (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when System.String.Equals (i.idText, "unit", System.StringComparison.OrdinalIgnoreCase) -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isList (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when System.String.Equals (i.idText, "list", System.StringComparison.OrdinalIgnoreCase) -> true
|
|
||||||
// TODO: consider FSharpList or whatever it is
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isArray (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when
|
|
||||||
System.String.Equals (i.idText, "array", System.StringComparison.OrdinalIgnoreCase)
|
|
||||||
|| System.String.Equals (i.idText, "[]", System.StringComparison.Ordinal)
|
|
||||||
->
|
|
||||||
true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isOption (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when System.String.Equals (i.idText, "option", System.StringComparison.OrdinalIgnoreCase) -> true
|
|
||||||
// TODO: consider Microsoft.FSharp.Option or whatever it is
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isChoice (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] when System.String.Equals (i.idText, "Choice", System.StringComparison.Ordinal) -> true
|
|
||||||
// TODO: consider Microsoft.FSharp.Choice or whatever it is
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isNullable (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "System" ; "Nullable" ]
|
|
||||||
| [ "Nullable" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isResponse (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "Response" ]
|
|
||||||
| [ "RestEase" ; "Response" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isMap (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "Map" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isReadOnlyDictionary (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "IReadOnlyDictionary" ]
|
|
||||||
| [ "Generic" ; "IReadOnlyDictionary" ]
|
|
||||||
| [ "Collections" ; "Generic" ; "IReadOnlyDictionary" ]
|
|
||||||
| [ "System" ; "Collections" ; "Generic" ; "IReadOnlyDictionary" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isDictionary (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "Dictionary" ]
|
|
||||||
| [ "Generic" ; "Dictionary" ]
|
|
||||||
| [ "Collections" ; "Generic" ; "Dictionary" ]
|
|
||||||
| [ "System" ; "Collections" ; "Generic" ; "Dictionary" ] -> true
|
|
||||||
| _ -> false
|
|
||||||
|
|
||||||
let isIDictionary (ident : SynLongIdent) : bool =
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "IDictionary" ]
|
|
||||||
| [ "Generic" ; "IDictionary" ]
|
|
||||||
| [ "Collections" ; "Generic" ; "IDictionary" ]
|
|
||||||
| [ "System" ; "Collections" ; "Generic" ; "IDictionary" ] -> true
|
|
||||||
| _ -> false
|
|
@@ -1,24 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynMatchClause =
|
|
||||||
let create (lhs : SynPat) (rhs : SynExpr) : SynMatchClause =
|
|
||||||
SynMatchClause.SynMatchClause (
|
|
||||||
lhs,
|
|
||||||
None,
|
|
||||||
rhs,
|
|
||||||
range0,
|
|
||||||
DebugPointAtTarget.Yes,
|
|
||||||
{
|
|
||||||
ArrowRange = Some range0
|
|
||||||
BarRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let withWhere (where : SynExpr) (m : SynMatchClause) : SynMatchClause =
|
|
||||||
match m with
|
|
||||||
| SynMatchClause (synPat, _, resultExpr, range, debugPointAtTarget, synMatchClauseTrivia) ->
|
|
||||||
SynMatchClause (synPat, Some where, resultExpr, range, debugPointAtTarget, synMatchClauseTrivia)
|
|
@@ -1,71 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynMemberDefn =
|
|
||||||
let private interfaceMemberSlotFlags =
|
|
||||||
{
|
|
||||||
SynMemberFlags.IsInstance = true
|
|
||||||
SynMemberFlags.IsDispatchSlot = true
|
|
||||||
SynMemberFlags.IsOverrideOrExplicitImpl = false
|
|
||||||
SynMemberFlags.IsFinal = false
|
|
||||||
SynMemberFlags.GetterOrSetterIsCompilerGenerated = false
|
|
||||||
SynMemberFlags.MemberKind = SynMemberKind.Member
|
|
||||||
}
|
|
||||||
|
|
||||||
let abstractMember
|
|
||||||
(attrs : SynAttribute list)
|
|
||||||
(ident : SynIdent)
|
|
||||||
(typars : SynTyparDecls option)
|
|
||||||
(arity : SynValInfo)
|
|
||||||
(xmlDoc : PreXmlDoc)
|
|
||||||
(returnType : SynType)
|
|
||||||
: SynMemberDefn
|
|
||||||
=
|
|
||||||
let slot =
|
|
||||||
SynValSig.SynValSig (
|
|
||||||
attrs
|
|
||||||
|> List.map (fun attr ->
|
|
||||||
{
|
|
||||||
Attributes = [ attr ]
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
),
|
|
||||||
ident,
|
|
||||||
SynValTyparDecls.SynValTyparDecls (typars, true),
|
|
||||||
returnType,
|
|
||||||
arity,
|
|
||||||
false,
|
|
||||||
false,
|
|
||||||
xmlDoc,
|
|
||||||
None,
|
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
EqualsRange = None
|
|
||||||
WithKeyword = None
|
|
||||||
InlineKeyword = None
|
|
||||||
LeadingKeyword = SynLeadingKeyword.Abstract range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
SynMemberDefn.AbstractSlot (
|
|
||||||
slot,
|
|
||||||
interfaceMemberSlotFlags,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
GetSetKeywords = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let staticMember (binding : SynBinding) : SynMemberDefn =
|
|
||||||
let binding = SynBinding.makeStaticMember binding
|
|
||||||
SynMemberDefn.Member (binding, range0)
|
|
||||||
|
|
||||||
let memberImplementation (binding : SynBinding) : SynMemberDefn =
|
|
||||||
let binding = SynBinding.makeInstanceMember binding
|
|
||||||
SynMemberDefn.Member (binding, range0)
|
|
@@ -1,30 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynModuleDecl =
|
|
||||||
|
|
||||||
let inline openAny (ident : SynOpenDeclTarget) : SynModuleDecl = SynModuleDecl.Open (ident, range0)
|
|
||||||
|
|
||||||
let inline createLets (bindings : SynBinding list) : SynModuleDecl =
|
|
||||||
SynModuleDecl.Let (false, bindings, range0)
|
|
||||||
|
|
||||||
let inline createLet (binding : SynBinding) : SynModuleDecl = createLets [ binding ]
|
|
||||||
|
|
||||||
let inline createTypes (tys : SynTypeDefn list) : SynModuleDecl = SynModuleDecl.Types (tys, range0)
|
|
||||||
|
|
||||||
let nestedModule (info : SynComponentInfo) (decls : SynModuleDecl list) : SynModuleDecl =
|
|
||||||
SynModuleDecl.NestedModule (
|
|
||||||
info,
|
|
||||||
false,
|
|
||||||
decls,
|
|
||||||
false,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
ModuleKeyword = Some range0
|
|
||||||
EqualsRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
@@ -1,24 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynModuleOrNamespace =
|
|
||||||
|
|
||||||
let createNamespace (name : LongIdent) (decls : SynModuleDecl list) =
|
|
||||||
SynModuleOrNamespace.SynModuleOrNamespace (
|
|
||||||
name,
|
|
||||||
false,
|
|
||||||
SynModuleOrNamespaceKind.DeclaredNamespace,
|
|
||||||
decls,
|
|
||||||
PreXmlDoc.Empty,
|
|
||||||
[],
|
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
LeadingKeyword = SynModuleOrNamespaceLeadingKeyword.Namespace range0
|
|
||||||
}
|
|
||||||
)
|
|
@@ -1,54 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynPat =
|
|
||||||
let inline paren (pat : SynPat) : SynPat = SynPat.Paren (pat, range0)
|
|
||||||
|
|
||||||
let anon : SynPat = SynPat.Wild range0
|
|
||||||
|
|
||||||
let inline annotateTypeNoParen (ty : SynType) (pat : SynPat) = SynPat.Typed (pat, ty, range0)
|
|
||||||
|
|
||||||
let inline annotateType (ty : SynType) (pat : SynPat) = paren (annotateTypeNoParen ty pat)
|
|
||||||
|
|
||||||
let inline named (s : string) : SynPat =
|
|
||||||
SynPat.Named (SynIdent.SynIdent (Ident (s, range0), None), false, None, range0)
|
|
||||||
|
|
||||||
let inline namedI (i : Ident) : SynPat =
|
|
||||||
SynPat.Named (SynIdent.SynIdent (i, None), false, None, range0)
|
|
||||||
|
|
||||||
let inline identWithArgs (i : LongIdent) (args : SynArgPats) : SynPat =
|
|
||||||
SynPat.LongIdent (SynLongIdent.create i, None, None, args, None, range0)
|
|
||||||
|
|
||||||
let inline nameWithArgs (i : string) (args : SynPat list) : SynPat =
|
|
||||||
identWithArgs [ Ident.create i ] (SynArgPats.create args)
|
|
||||||
|
|
||||||
let inline tupleNoParen (elements : SynPat list) : SynPat =
|
|
||||||
match elements with
|
|
||||||
| [] -> failwith "Can't tuple no elements in a pattern"
|
|
||||||
| [ p ] -> p
|
|
||||||
| elements -> SynPat.Tuple (false, elements, List.replicate (elements.Length - 1) range0, range0)
|
|
||||||
|
|
||||||
let inline tuple (elements : SynPat list) : SynPat = tupleNoParen elements |> paren
|
|
||||||
|
|
||||||
let inline createConst (c : SynConst) = SynPat.Const (c, range0)
|
|
||||||
|
|
||||||
let unit = createConst SynConst.Unit
|
|
||||||
|
|
||||||
let createNull = SynPat.Null range0
|
|
||||||
|
|
||||||
let emptyList = SynPat.ArrayOrList (false, [], range0)
|
|
||||||
|
|
||||||
let listCons (lhs : SynPat) (rhs : SynPat) =
|
|
||||||
SynPat.ListCons (
|
|
||||||
lhs,
|
|
||||||
rhs,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
ColonColonRange = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let emptyArray = SynPat.ArrayOrList (true, [], range0)
|
|
@@ -1,539 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open System
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<AutoOpen>]
|
|
||||||
module internal SynTypePatterns =
|
|
||||||
let (|OptionType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isOption ident ->
|
|
||||||
Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|ChoiceType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, inner, _, _, _, _) when SynLongIdent.isChoice ident -> Some inner
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|NullableType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isNullable ident ->
|
|
||||||
Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|UnitType|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident when SynLongIdent.isUnit ident -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|ListType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isList ident ->
|
|
||||||
Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|ArrayType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isArray ident ->
|
|
||||||
Some innerType
|
|
||||||
| SynType.Array (1, innerType, _) -> Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|RestEaseResponseType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ innerType ], _, _, _, _) when SynLongIdent.isResponse ident ->
|
|
||||||
Some innerType
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DictionaryType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isDictionary ident ->
|
|
||||||
Some (key, value)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|IDictionaryType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isIDictionary ident ->
|
|
||||||
Some (key, value)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|IReadOnlyDictionaryType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when
|
|
||||||
SynLongIdent.isReadOnlyDictionary ident
|
|
||||||
->
|
|
||||||
Some (key, value)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|MapType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent ident, _, [ key ; value ], _, _, _, _) when SynLongIdent.isMap ident ->
|
|
||||||
Some (key, value)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|BigInt|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map _.idText with
|
|
||||||
| [ "bigint" ]
|
|
||||||
| [ "BigInteger" ]
|
|
||||||
| [ "Numerics" ; "BigInteger" ]
|
|
||||||
| [ "System" ; "Numerics" ; "BigInteger" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
/// Returns the type, qualified as in e.g. `System.Boolean`.
|
|
||||||
let (|PrimitiveType|_|) (fieldType : SynType) : LongIdent option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] -> Primitives.qualifyType i.idText
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|String|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] ->
|
|
||||||
[ "string" ]
|
|
||||||
|> List.tryFind (fun s -> s = i.idText)
|
|
||||||
|> Option.map ignore<string>
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Byte|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] -> [ "byte" ] |> List.tryFind (fun s -> s = i.idText) |> Option.map ignore<string>
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Guid|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Guid" ]
|
|
||||||
| [ "Guid" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|HttpResponseMessage|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Net" ; "Http" ; "HttpResponseMessage" ]
|
|
||||||
| [ "Net" ; "Http" ; "HttpResponseMessage" ]
|
|
||||||
| [ "Http" ; "HttpResponseMessage" ]
|
|
||||||
| [ "HttpResponseMessage" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|HttpContent|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Net" ; "Http" ; "HttpContent" ]
|
|
||||||
| [ "Net" ; "Http" ; "HttpContent" ]
|
|
||||||
| [ "Http" ; "HttpContent" ]
|
|
||||||
| [ "HttpContent" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Stream|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "IO" ; "Stream" ]
|
|
||||||
| [ "IO" ; "Stream" ]
|
|
||||||
| [ "Stream" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|NumberType|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent ident ->
|
|
||||||
match ident.LongIdent with
|
|
||||||
| [ i ] ->
|
|
||||||
// We won't bother with the case that the user has done e.g. `Single` (relying on `System` being open).
|
|
||||||
match Primitives.qualifyType i.idText with
|
|
||||||
| Some qualified ->
|
|
||||||
match i.idText with
|
|
||||||
| "char"
|
|
||||||
| "string" -> None
|
|
||||||
| _ -> Some qualified
|
|
||||||
| None -> None
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
/// Returns the name of the measure, and the outer type.
|
|
||||||
let (|Measure|_|) (fieldType : SynType) : (Ident * LongIdent) option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (NumberType outer,
|
|
||||||
_,
|
|
||||||
[ SynType.LongIdent (SynLongIdent.SynLongIdent ([ ident ], _, _)) ],
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_,
|
|
||||||
_) -> Some (ident, outer)
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|JsonNode|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
|
||||||
| [ "Text" ; "Json" ; "Nodes" ; "JsonNode" ]
|
|
||||||
| [ "Json" ; "Nodes" ; "JsonNode" ]
|
|
||||||
| [ "Nodes" ; "JsonNode" ]
|
|
||||||
| [ "JsonNode" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Unit|_|) (fieldType : SynType) : unit option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText.ToLowerInvariant ()) with
|
|
||||||
| [ "microsoft" ; "fsharp" ; "core" ; "unit" ]
|
|
||||||
| [ "fsharp" ; "core" ; "unit" ]
|
|
||||||
| [ "core" ; "unit" ]
|
|
||||||
| [ "unit" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DateOnly|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "DateOnly" ]
|
|
||||||
| [ "DateOnly" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DateTime|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "DateTime" ]
|
|
||||||
| [ "DateTime" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DateTimeOffset|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "DateTimeOffset" ]
|
|
||||||
| [ "DateTimeOffset" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Uri|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "Uri" ]
|
|
||||||
| [ "Uri" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|Task|_|) (fieldType : SynType) : SynType option =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.App (SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)), _, args, _, _, _, _) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "Task" ]
|
|
||||||
| [ "Tasks" ; "Task" ]
|
|
||||||
| [ "Threading" ; "Tasks" ; "Task" ]
|
|
||||||
| [ "System" ; "Threading" ; "Tasks" ; "Task" ] ->
|
|
||||||
match args with
|
|
||||||
| [ arg ] -> Some arg
|
|
||||||
| _ -> failwithf "Expected Task to be applied to exactly one arg, but got: %+A" args
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|DirectoryInfo|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "IO" ; "DirectoryInfo" ]
|
|
||||||
| [ "IO" ; "DirectoryInfo" ]
|
|
||||||
| [ "DirectoryInfo" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|FileInfo|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "IO" ; "FileInfo" ]
|
|
||||||
| [ "IO" ; "FileInfo" ]
|
|
||||||
| [ "FileInfo" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
let (|TimeSpan|_|) (fieldType : SynType) =
|
|
||||||
match fieldType with
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) ->
|
|
||||||
match ident |> List.map (fun i -> i.idText) with
|
|
||||||
| [ "System" ; "TimeSpan" ]
|
|
||||||
| [ "TimeSpan" ] -> Some ()
|
|
||||||
| _ -> None
|
|
||||||
| _ -> None
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynType =
|
|
||||||
let rec stripOptionalParen (ty : SynType) : SynType =
|
|
||||||
match ty with
|
|
||||||
| SynType.Paren (ty, _) -> stripOptionalParen ty
|
|
||||||
| ty -> ty
|
|
||||||
|
|
||||||
let inline paren (ty : SynType) : SynType = SynType.Paren (ty, range0)
|
|
||||||
|
|
||||||
let inline createLongIdent (ident : LongIdent) : SynType =
|
|
||||||
SynType.LongIdent (SynLongIdent.create ident)
|
|
||||||
|
|
||||||
let inline createLongIdent' (ident : string list) : SynType =
|
|
||||||
SynType.LongIdent (SynLongIdent.createS' ident)
|
|
||||||
|
|
||||||
let inline named (name : string) = createLongIdent' [ name ]
|
|
||||||
|
|
||||||
let inline app' (name : SynType) (args : SynType list) : SynType =
|
|
||||||
if args.IsEmpty then
|
|
||||||
failwith "Type cannot be applied to no arguments"
|
|
||||||
|
|
||||||
SynType.App (name, Some range0, args, List.replicate (args.Length - 1) range0, Some range0, false, range0)
|
|
||||||
|
|
||||||
let inline app (name : string) (args : SynType list) : SynType = app' (named name) args
|
|
||||||
|
|
||||||
/// Returns None if the input list was empty.
|
|
||||||
let inline tupleNoParen (ty : SynType list) : SynType option =
|
|
||||||
match List.rev ty with
|
|
||||||
| [] -> None
|
|
||||||
| [ t ] -> Some t
|
|
||||||
| t :: rest ->
|
|
||||||
([ SynTupleTypeSegment.Type t ], rest)
|
|
||||||
||> List.fold (fun ty nextArg -> SynTupleTypeSegment.Type nextArg :: SynTupleTypeSegment.Star range0 :: ty)
|
|
||||||
|> fun segs -> SynType.Tuple (false, segs, range0)
|
|
||||||
|> Some
|
|
||||||
|
|
||||||
let inline appPostfix (name : string) (arg : SynType) : SynType =
|
|
||||||
SynType.App (named name, None, [ arg ], [], None, true, range0)
|
|
||||||
|
|
||||||
let inline appPostfix' (name : string list) (arg : SynType) : SynType =
|
|
||||||
SynType.App (createLongIdent' name, None, [ arg ], [], None, true, range0)
|
|
||||||
|
|
||||||
let inline funFromDomain (domain : SynType) (range : SynType) : SynType =
|
|
||||||
SynType.Fun (
|
|
||||||
domain,
|
|
||||||
range,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
ArrowRange = range0
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline signatureParamOfType
|
|
||||||
(attrs : SynAttribute list)
|
|
||||||
(ty : SynType)
|
|
||||||
(optional : bool)
|
|
||||||
(name : Ident option)
|
|
||||||
: SynType
|
|
||||||
=
|
|
||||||
SynType.SignatureParameter (
|
|
||||||
attrs
|
|
||||||
|> List.map (fun attr ->
|
|
||||||
{
|
|
||||||
Attributes = [ attr ]
|
|
||||||
Range = range0
|
|
||||||
}
|
|
||||||
),
|
|
||||||
optional,
|
|
||||||
name,
|
|
||||||
ty,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline var (ty : SynTypar) : SynType = SynType.Var (ty, range0)
|
|
||||||
|
|
||||||
let unit : SynType = named "unit"
|
|
||||||
let obj : SynType = named "obj"
|
|
||||||
let bool : SynType = named "bool"
|
|
||||||
let int : SynType = named "int"
|
|
||||||
let array (elt : SynType) : SynType = SynType.Array (1, elt, range0)
|
|
||||||
|
|
||||||
let list (elt : SynType) : SynType =
|
|
||||||
SynType.App (named "list", None, [ elt ], [], None, true, range0)
|
|
||||||
|
|
||||||
let option (elt : SynType) : SynType =
|
|
||||||
SynType.App (named "option", None, [ elt ], [], None, true, range0)
|
|
||||||
|
|
||||||
let anon : SynType = SynType.Anon range0
|
|
||||||
|
|
||||||
let task (elt : SynType) : SynType =
|
|
||||||
SynType.App (
|
|
||||||
createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "Task" ],
|
|
||||||
None,
|
|
||||||
[ elt ],
|
|
||||||
[],
|
|
||||||
None,
|
|
||||||
true,
|
|
||||||
range0
|
|
||||||
)
|
|
||||||
|
|
||||||
let string : SynType = named "string"
|
|
||||||
|
|
||||||
/// Given ['a1, 'a2] and 'ret, returns 'a1 -> 'a2 -> 'ret.
|
|
||||||
let toFun (inputs : SynType list) (ret : SynType) : SynType =
|
|
||||||
(ret, List.rev inputs) ||> List.fold (fun ty input -> funFromDomain input ty)
|
|
||||||
|
|
||||||
let primitiveToHumanReadableString (name : LongIdent) : string =
|
|
||||||
match name |> List.map _.idText with
|
|
||||||
| [ "System" ; "Single" ] -> "single"
|
|
||||||
| [ "System" ; "Double" ] -> "double"
|
|
||||||
| [ "System" ; "Byte" ] -> "byte"
|
|
||||||
| [ "System" ; "SByte" ] -> "signed byte"
|
|
||||||
| [ "System" ; "Int16" ] -> "int16"
|
|
||||||
| [ "System" ; "Int32" ] -> "int32"
|
|
||||||
| [ "System" ; "Int64" ] -> "int64"
|
|
||||||
| [ "System" ; "UInt16" ] -> "uint16"
|
|
||||||
| [ "System" ; "UInt32" ] -> "uint32"
|
|
||||||
| [ "System" ; "UInt64" ] -> "uint64"
|
|
||||||
| [ "System" ; "Char" ] -> "char"
|
|
||||||
| [ "System" ; "Decimal" ] -> "decimal"
|
|
||||||
| [ "System" ; "String" ] -> "string"
|
|
||||||
| [ "System" ; "Boolean" ] -> "bool"
|
|
||||||
| ty ->
|
|
||||||
ty
|
|
||||||
|> String.concat "."
|
|
||||||
|> failwithf "could not create human-readable string for primitive type %s"
|
|
||||||
|
|
||||||
let rec toHumanReadableString (ty : SynType) : string =
|
|
||||||
match ty with
|
|
||||||
| PrimitiveType t1 -> primitiveToHumanReadableString t1
|
|
||||||
| OptionType t1 -> toHumanReadableString t1 + " option"
|
|
||||||
| NullableType t1 -> toHumanReadableString t1 + " Nullable"
|
|
||||||
| ChoiceType ts ->
|
|
||||||
ts
|
|
||||||
|> List.map toHumanReadableString
|
|
||||||
|> String.concat ", "
|
|
||||||
|> sprintf "Choice<%s>"
|
|
||||||
| MapType (k, v)
|
|
||||||
| DictionaryType (k, v)
|
|
||||||
| IDictionaryType (k, v)
|
|
||||||
| IReadOnlyDictionaryType (k, v) -> sprintf "map<%s, %s>" (toHumanReadableString k) (toHumanReadableString v)
|
|
||||||
| ListType t1 -> toHumanReadableString t1 + " list"
|
|
||||||
| ArrayType t1 -> toHumanReadableString t1 + " array"
|
|
||||||
| Task t1 -> toHumanReadableString t1 + " Task"
|
|
||||||
| UnitType -> "unit"
|
|
||||||
| FileInfo -> "FileInfo"
|
|
||||||
| DirectoryInfo -> "DirectoryInfo"
|
|
||||||
| Uri -> "URI"
|
|
||||||
| Stream -> "Stream"
|
|
||||||
| Guid -> "GUID"
|
|
||||||
| BigInt -> "bigint"
|
|
||||||
| DateTimeOffset -> "DateTimeOffset"
|
|
||||||
| DateOnly -> "DateOnly"
|
|
||||||
| TimeSpan -> "TimeSpan"
|
|
||||||
| SynType.LongIdent (SynLongIdent.SynLongIdent (ident, _, _)) -> ident |> List.map _.idText |> String.concat "."
|
|
||||||
| ty -> failwithf "could not compute human-readable string for type: %O" ty
|
|
||||||
|
|
||||||
/// Guess whether the types are equal. We err on the side of saying "no, they're different".
|
|
||||||
let rec provablyEqual (ty1 : SynType) (ty2 : SynType) : bool =
|
|
||||||
if Object.ReferenceEquals (ty1, ty2) then
|
|
||||||
true
|
|
||||||
else
|
|
||||||
|
|
||||||
match ty1 with
|
|
||||||
| PrimitiveType t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| PrimitiveType t2 -> (t1 |> List.map _.idText) = (t2 |> List.map _.idText)
|
|
||||||
| _ -> false
|
|
||||||
| OptionType t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| OptionType t2 -> provablyEqual t1 t2
|
|
||||||
| _ -> false
|
|
||||||
| NullableType t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| NullableType t2 -> provablyEqual t1 t2
|
|
||||||
| _ -> false
|
|
||||||
| ChoiceType t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| ChoiceType t2 ->
|
|
||||||
t1.Length = t2.Length
|
|
||||||
&& List.forall (fun (a, b) -> provablyEqual a b) (List.zip t1 t2)
|
|
||||||
| _ -> false
|
|
||||||
| DictionaryType (k1, v1) ->
|
|
||||||
match ty2 with
|
|
||||||
| DictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
|
|
||||||
| _ -> false
|
|
||||||
| IDictionaryType (k1, v1) ->
|
|
||||||
match ty2 with
|
|
||||||
| IDictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
|
|
||||||
| _ -> false
|
|
||||||
| IReadOnlyDictionaryType (k1, v1) ->
|
|
||||||
match ty2 with
|
|
||||||
| IReadOnlyDictionaryType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
|
|
||||||
| _ -> false
|
|
||||||
| MapType (k1, v1) ->
|
|
||||||
match ty2 with
|
|
||||||
| MapType (k2, v2) -> provablyEqual k1 k2 && provablyEqual v1 v2
|
|
||||||
| _ -> false
|
|
||||||
| ListType t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| ListType t2 -> provablyEqual t1 t2
|
|
||||||
| _ -> false
|
|
||||||
| ArrayType t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| ArrayType t2 -> provablyEqual t1 t2
|
|
||||||
| _ -> false
|
|
||||||
| Task t1 ->
|
|
||||||
match ty2 with
|
|
||||||
| Task t2 -> provablyEqual t1 t2
|
|
||||||
| _ -> false
|
|
||||||
| UnitType ->
|
|
||||||
match ty2 with
|
|
||||||
| UnitType -> true
|
|
||||||
| _ -> false
|
|
||||||
| FileInfo ->
|
|
||||||
match ty2 with
|
|
||||||
| FileInfo -> true
|
|
||||||
| _ -> false
|
|
||||||
| DirectoryInfo ->
|
|
||||||
match ty2 with
|
|
||||||
| DirectoryInfo -> true
|
|
||||||
| _ -> false
|
|
||||||
| Uri ->
|
|
||||||
match ty2 with
|
|
||||||
| Uri -> true
|
|
||||||
| _ -> false
|
|
||||||
| Stream ->
|
|
||||||
match ty2 with
|
|
||||||
| Stream -> true
|
|
||||||
| _ -> false
|
|
||||||
| Guid ->
|
|
||||||
match ty2 with
|
|
||||||
| Guid -> true
|
|
||||||
| _ -> false
|
|
||||||
| BigInt ->
|
|
||||||
match ty2 with
|
|
||||||
| BigInt -> true
|
|
||||||
| _ -> false
|
|
||||||
| DateTimeOffset ->
|
|
||||||
match ty2 with
|
|
||||||
| DateTimeOffset -> true
|
|
||||||
| _ -> false
|
|
||||||
| DateOnly ->
|
|
||||||
match ty2 with
|
|
||||||
| DateOnly -> true
|
|
||||||
| _ -> false
|
|
||||||
| _ ->
|
|
||||||
|
|
||||||
match ty1, ty2 with
|
|
||||||
| SynType.LongIdent (SynLongIdent (ident1, _, _)), SynType.LongIdent (SynLongIdent (ident2, _, _)) ->
|
|
||||||
let ident1 = ident1 |> List.map _.idText
|
|
||||||
let ident2 = ident2 |> List.map _.idText
|
|
||||||
ident1 = ident2
|
|
||||||
| _, _ -> false
|
|
@@ -1,27 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynTypeDefn =
|
|
||||||
|
|
||||||
let inline create (componentInfo : SynComponentInfo) (repr : SynTypeDefnRepr) : SynTypeDefn =
|
|
||||||
SynTypeDefn.SynTypeDefn (
|
|
||||||
componentInfo,
|
|
||||||
repr,
|
|
||||||
[],
|
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
LeadingKeyword = SynTypeDefnLeadingKeyword.Type range0
|
|
||||||
EqualsRange = Some range0
|
|
||||||
WithKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
|
|
||||||
let inline withMemberDefns (members : SynMemberDefn list) (r : SynTypeDefn) : SynTypeDefn =
|
|
||||||
match r with
|
|
||||||
| SynTypeDefn (typeInfo, typeRepr, _, ctor, range, trivia) ->
|
|
||||||
SynTypeDefn.SynTypeDefn (typeInfo, typeRepr, members, ctor, range, trivia)
|
|
@@ -1,24 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynTypeDefnRepr =
|
|
||||||
|
|
||||||
let inline interfaceType (mems : SynMemberDefns) : SynTypeDefnRepr =
|
|
||||||
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Unspecified, mems, range0)
|
|
||||||
|
|
||||||
/// Indicates the body of a `type Foo with {body}` extension type declaration.
|
|
||||||
let inline augmentation () : SynTypeDefnRepr =
|
|
||||||
SynTypeDefnRepr.ObjectModel (SynTypeDefnKind.Augmentation range0, [], range0)
|
|
||||||
|
|
||||||
let inline unionWithAccess (implAccess : SynAccess option) (cases : SynUnionCase list) : SynTypeDefnRepr =
|
|
||||||
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Union (implAccess, cases, range0), range0)
|
|
||||||
|
|
||||||
let inline union (cases : SynUnionCase list) : SynTypeDefnRepr = unionWithAccess None cases
|
|
||||||
|
|
||||||
let inline recordWithAccess (implAccess : SynAccess option) (fields : SynField list) : SynTypeDefnRepr =
|
|
||||||
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (implAccess, fields, range0), range0)
|
|
||||||
|
|
||||||
let inline record (fields : SynField list) : SynTypeDefnRepr = recordWithAccess None fields
|
|
@@ -1,55 +0,0 @@
|
|||||||
namespace WoofWare.Myriad.Plugins
|
|
||||||
|
|
||||||
open Fantomas.FCS.Syntax
|
|
||||||
open Fantomas.FCS.Text.Range
|
|
||||||
open Fantomas.FCS.Xml
|
|
||||||
open Fantomas.FCS.SyntaxTrivia
|
|
||||||
|
|
||||||
/// Represents everything you need to know about a union case.
|
|
||||||
/// This is generic on whether each field of this case must be named.
|
|
||||||
type UnionCase<'ident> =
|
|
||||||
{
|
|
||||||
/// The name of the case: e.g. `| Foo of blah` has this being `Foo`.
|
|
||||||
Name : Ident
|
|
||||||
/// Any docstring associated with this case.
|
|
||||||
XmlDoc : PreXmlDoc option
|
|
||||||
/// Any accessibility modifier: e.g. `type Foo = private | Blah`.
|
|
||||||
Access : SynAccess option
|
|
||||||
/// Attributes on the case: for example, `| [<Attr>] Foo of blah`.
|
|
||||||
Attributes : SynAttribute list
|
|
||||||
/// The data contained within the case: for example, `[blah]` in `| Foo of blah`.
|
|
||||||
Fields : SynFieldData<'ident> list
|
|
||||||
}
|
|
||||||
|
|
||||||
[<RequireQualifiedAccess>]
|
|
||||||
module internal SynUnionCase =
|
|
||||||
let create (case : UnionCase<Ident>) : SynUnionCase =
|
|
||||||
let fields =
|
|
||||||
case.Fields
|
|
||||||
|> List.map (fun field ->
|
|
||||||
SynField.SynField (
|
|
||||||
SynAttributes.ofAttrs field.Attrs,
|
|
||||||
false,
|
|
||||||
Some field.Ident,
|
|
||||||
field.Type,
|
|
||||||
false,
|
|
||||||
PreXmlDoc.Empty,
|
|
||||||
None,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
LeadingKeyword = None
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
SynUnionCase.SynUnionCase (
|
|
||||||
SynAttributes.ofAttrs case.Attributes,
|
|
||||||
SynIdent.createI case.Name,
|
|
||||||
SynUnionCaseKind.Fields fields,
|
|
||||||
case.XmlDoc |> Option.defaultValue PreXmlDoc.Empty,
|
|
||||||
case.Access,
|
|
||||||
range0,
|
|
||||||
{
|
|
||||||
BarRange = Some range0
|
|
||||||
}
|
|
||||||
)
|
|
11
WoofWare.Myriad.Plugins/Text.fs
Normal file
11
WoofWare.Myriad.Plugins/Text.fs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
namespace WoofWare.Myriad.Plugins
|
||||||
|
|
||||||
|
open System
|
||||||
|
|
||||||
|
[<AutoOpen>]
|
||||||
|
module internal Text =
|
||||||
|
let (|StartsWith|_|) (prefix : string) (s : string) : string option =
|
||||||
|
if s.StartsWith (prefix, StringComparison.Ordinal) then
|
||||||
|
Some (s.Substring prefix.Length)
|
||||||
|
else
|
||||||
|
None
|
@@ -15,49 +15,32 @@
|
|||||||
<WarnOn>FS3559</WarnOn>
|
<WarnOn>FS3559</WarnOn>
|
||||||
<PackageId>WoofWare.Myriad.Plugins</PackageId>
|
<PackageId>WoofWare.Myriad.Plugins</PackageId>
|
||||||
<PackageIcon>logo.png</PackageIcon>
|
<PackageIcon>logo.png</PackageIcon>
|
||||||
|
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Myriad.Core" Version="0.8.3" />
|
<PackageReference Include="Myriad.Core" Version="0.8.3" />
|
||||||
|
<PackageReference Include="WoofWare.Whippet.Fantomas" Version="0.2.1" />
|
||||||
<!-- the lowest version allowed by Myriad.Core -->
|
<!-- the lowest version allowed by Myriad.Core -->
|
||||||
<PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/>
|
<PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<Compile Include="List.fs"/>
|
<Compile Include="List.fs"/>
|
||||||
|
<Compile Include="Text.fs" />
|
||||||
<Compile Include="Teq.fs" />
|
<Compile Include="Teq.fs" />
|
||||||
<Compile Include="Primitives.fs" />
|
|
||||||
<Compile Include="SynExpr\SynAttributes.fs" />
|
|
||||||
<Compile Include="SynExpr\PreXmlDoc.fs" />
|
|
||||||
<Compile Include="SynExpr\Ident.fs" />
|
|
||||||
<Compile Include="SynExpr\SynIdent.fs" />
|
|
||||||
<Compile Include="SynExpr\SynLongIdent.fs" />
|
|
||||||
<Compile Include="SynExpr\SynExprLetOrUseTrivia.fs" />
|
|
||||||
<Compile Include="SynExpr\SynArgPats.fs" />
|
|
||||||
<Compile Include="SynExpr\SynPat.fs" />
|
|
||||||
<Compile Include="SynExpr\SynBinding.fs" />
|
|
||||||
<Compile Include="SynExpr\SynType.fs" />
|
|
||||||
<Compile Include="SynExpr\SynMatchClause.fs" />
|
|
||||||
<Compile Include="SynExpr\CompExpr.fs" />
|
|
||||||
<Compile Include="SynExpr\SynExpr.fs" />
|
|
||||||
<Compile Include="SynExpr\SynField.fs" />
|
|
||||||
<Compile Include="SynExpr\SynUnionCase.fs" />
|
|
||||||
<Compile Include="SynExpr\SynTypeDefnRepr.fs" />
|
|
||||||
<Compile Include="SynExpr\SynTypeDefn.fs" />
|
|
||||||
<Compile Include="SynExpr\SynComponentInfo.fs" />
|
|
||||||
<Compile Include="SynExpr\SynMemberDefn.fs" />
|
|
||||||
<Compile Include="SynExpr\SynAttribute.fs" />
|
|
||||||
<Compile Include="SynExpr\SynModuleDecl.fs" />
|
|
||||||
<Compile Include="SynExpr\SynModuleOrNamespace.fs" />
|
|
||||||
<Compile Include="Measure.fs" />
|
<Compile Include="Measure.fs" />
|
||||||
<Compile Include="AstHelper.fs" />
|
<Compile Include="AstHelper.fs" />
|
||||||
<Compile Include="RemoveOptionsGenerator.fs"/>
|
<Compile Include="RemoveOptionsGenerator.fs"/>
|
||||||
|
<Compile Include="MyriadParamParser.fs" />
|
||||||
<Compile Include="InterfaceMockGenerator.fs"/>
|
<Compile Include="InterfaceMockGenerator.fs"/>
|
||||||
<Compile Include="JsonSerializeGenerator.fs"/>
|
<Compile Include="JsonSerializeGenerator.fs"/>
|
||||||
<Compile Include="JsonParseGenerator.fs"/>
|
<Compile Include="JsonParseGenerator.fs"/>
|
||||||
<Compile Include="HttpClientGenerator.fs"/>
|
<Compile Include="HttpClientGenerator.fs"/>
|
||||||
<Compile Include="CataGenerator.fs" />
|
<Compile Include="CataGenerator.fs" />
|
||||||
<Compile Include="ArgParserGenerator.fs" />
|
<Compile Include="ArgParserGenerator.fs" />
|
||||||
|
<Compile Include="Swagger.fs" />
|
||||||
|
<Compile Include="SwaggerClientGenerator.fs" />
|
||||||
<None Include="TeqLicence.txt" />
|
<None Include="TeqLicence.txt" />
|
||||||
<EmbeddedResource Include="version.json"/>
|
<EmbeddedResource Include="version.json"/>
|
||||||
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
<EmbeddedResource Include="SurfaceBaseline.txt"/>
|
||||||
|
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "2.3",
|
"version": "4.0",
|
||||||
"publicReleaseRefSpec": [
|
"publicReleaseRefSpec": [
|
||||||
"^refs/heads/main$"
|
"^refs/heads/main$"
|
||||||
],
|
],
|
||||||
@@ -11,4 +11,4 @@
|
|||||||
":/README.md",
|
":/README.md",
|
||||||
":/Directory.Build.props"
|
":/Directory.Build.props"
|
||||||
]
|
]
|
||||||
}
|
}
|
12
flake.lock
generated
12
flake.lock
generated
@@ -5,11 +5,11 @@
|
|||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1710146030,
|
"lastModified": 1726560853,
|
||||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
"narHash": "sha256-X6rJYSESBVr3hBoH0WbKE5KvhPU5bloyZ2L4K60/fPQ=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "flake-utils",
|
"repo": "flake-utils",
|
||||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
"rev": "c1dfcf08411b08f6b8615f7d8971a2bfa81d5e8a",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -20,11 +20,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1725534445,
|
"lastModified": 1728093190,
|
||||||
"narHash": "sha256-Yd0FK9SkWy+ZPuNqUgmVPXokxDgMJoGuNpMEtkfcf84=",
|
"narHash": "sha256-CAZF2NRuHmqTtRTNAruWpHA43Gg2UvuCNEIzabP0l6M=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "9bb1e7571aadf31ddb4af77fc64b2d59580f9a39",
|
"rev": "e2f08f4d8b3ecb5cf5c9fd9cb2d53bb3c71807da",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
|
@@ -52,7 +52,7 @@
|
|||||||
projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj";
|
projectFile = "./WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj";
|
||||||
testProjectFile = "./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj";
|
testProjectFile = "./WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj";
|
||||||
disabledTests = ["WoofWare.Myriad.Plugins.Test.TestSurface.CheckVersionAgainstRemote"];
|
disabledTests = ["WoofWare.Myriad.Plugins.Test.TestSurface.CheckVersionAgainstRemote"];
|
||||||
nugetDeps = ./nix/deps.nix; # `nix build .#default.passthru.fetch-deps && ./result` and put the result here
|
nugetDeps = ./nix/deps.nix; # `nix build .#default.passthru.fetch-deps && ./result nix/deps.nix`
|
||||||
doCheck = true;
|
doCheck = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
13
nix/deps.nix
13
nix/deps.nix
@@ -8,8 +8,8 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "fantomas";
|
pname = "fantomas";
|
||||||
version = "6.3.12";
|
version = "6.3.15";
|
||||||
hash = "sha256-LFZn2cO72FlsmLI0vTLz52Bn4XBeGILTOr8rz/EuXeg=";
|
hash = "sha256-Gjw7MxjUNckMWSfnOye4UTe5fZWnor6RHCls3PNsuG8=";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Fantomas.Core";
|
pname = "Fantomas.Core";
|
||||||
@@ -48,8 +48,8 @@
|
|||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "FsUnit";
|
pname = "FsUnit";
|
||||||
version = "6.0.0";
|
version = "6.0.1";
|
||||||
hash = "sha256-q87WQf6MqGhzvaQ7WkkUlCdoE94DY0CD5PaXEj64A6M=";
|
hash = "sha256-vka/aAgWhDCl5tu+kgO7GtSaHOOvlSaWxG+tExwGXpI=";
|
||||||
})
|
})
|
||||||
(fetchNuGet {
|
(fetchNuGet {
|
||||||
pname = "Microsoft.AspNetCore.App.Ref";
|
pname = "Microsoft.AspNetCore.App.Ref";
|
||||||
@@ -326,4 +326,9 @@
|
|||||||
version = "7.0.3";
|
version = "7.0.3";
|
||||||
hash = "sha256-aSJZ17MjqaZNQkprfxm/09LaCoFtpdWmqU9BTROzWX4=";
|
hash = "sha256-aSJZ17MjqaZNQkprfxm/09LaCoFtpdWmqU9BTROzWX4=";
|
||||||
})
|
})
|
||||||
|
(fetchNuGet {
|
||||||
|
pname = "WoofWare.Whippet.Fantomas";
|
||||||
|
version = "0.2.1";
|
||||||
|
hash = "sha256-38LPop5tQ14oqzi0dSzBp2m53fugIMyWed67xnZmJqk=";
|
||||||
|
})
|
||||||
]
|
]
|
||||||
|
Reference in New Issue
Block a user