mirror of
https://github.com/Smaug123/WoofWare.Myriad
synced 2025-12-15 05:15:40 +00:00
Allow using fsproj annotations instead of attributes (#275)
This commit is contained in:
@@ -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>
|
||||||
|
|||||||
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 ()
|
||||||
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
|
||||||
30
README.md
30
README.md
@@ -604,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.
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -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" />
|
||||||
|
|||||||
@@ -992,6 +992,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
|
||||||
|
|
||||||
@@ -1005,12 +1009,32 @@ type HttpClientGenerator () =
|
|||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<HttpClientAttribute> typeDef with
|
match Ast.getAttribute<HttpClientAttribute> 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."
|
||||||
|
|||||||
@@ -283,6 +283,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 +298,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
|
||||||
|
|||||||
@@ -702,6 +702,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
|
||||||
|
|
||||||
@@ -724,7 +728,28 @@ type JsonParseGenerator () =
|
|||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<JsonParseAttribute> typeDef with
|
match Ast.getAttribute<JsonParseAttribute> 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
|
||||||
|
|||||||
@@ -519,6 +519,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
|
||||||
|
|
||||||
@@ -541,7 +545,28 @@ type JsonSerializeGenerator () =
|
|||||||
types
|
types
|
||||||
|> List.choose (fun typeDef ->
|
|> List.choose (fun typeDef ->
|
||||||
match Ast.getAttribute<JsonSerializeAttribute> typeDef with
|
match Ast.getAttribute<JsonSerializeAttribute> 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
|
||||||
|
|||||||
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
|
||||||
@@ -672,31 +672,16 @@ type SwaggerClientGenerator () =
|
|||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
|
|
||||||
let config =
|
let config =
|
||||||
// Bug in Myriad, their arg parsing is borked.
|
let pars = MyriadParamParser.render context.AdditionalParameters
|
||||||
let pars =
|
|
||||||
context.AdditionalParameters
|
|
||||||
|> Seq.map (fun (KeyValue (k, v)) -> k, v)
|
|
||||||
|> Seq.toList
|
|
||||||
|
|
||||||
let pars =
|
let pars =
|
||||||
match pars with
|
pars
|
||||||
| [] ->
|
|> Map.toSeq
|
||||||
failwith "No parameters given. You must supply the <ClassName /> parameter in <MyriadParams />."
|
|> Seq.map (fun (k, v) -> k.ToUpperInvariant (), v)
|
||||||
| [ key, value ] ->
|
|> Map.ofSeq
|
||||||
let semicolon = value.IndexOf ';'
|
|
||||||
|
|
||||||
if semicolon >= 0 then
|
if pars.IsEmpty then
|
||||||
let equals = value.IndexOf ('=', semicolon)
|
failwith "No parameters given. You must supply the <ClassName /> parameter in <MyriadParams />."
|
||||||
|
|
||||||
[
|
|
||||||
key, value.Substring (0, semicolon)
|
|
||||||
value.Substring (semicolon + 1, equals - semicolon - 1), value.Substring (equals + 1)
|
|
||||||
]
|
|
||||||
else
|
|
||||||
[ key, value ]
|
|
||||||
| rest -> rest
|
|
||||||
|> List.map (fun (key, value) -> key.ToUpperInvariant (), value)
|
|
||||||
|> Map.ofList
|
|
||||||
|
|
||||||
let createMock =
|
let createMock =
|
||||||
match Map.tryFind "GENERATEMOCKVISIBILITY" pars with
|
match Map.tryFind "GENERATEMOCKVISIBILITY" pars with
|
||||||
|
|||||||
@@ -25,3 +25,7 @@ module internal SynTypeDefn =
|
|||||||
match r with
|
match r with
|
||||||
| SynTypeDefn (typeInfo, typeRepr, _, ctor, range, trivia) ->
|
| SynTypeDefn (typeInfo, typeRepr, _, ctor, range, trivia) ->
|
||||||
SynTypeDefn.SynTypeDefn (typeInfo, typeRepr, members, ctor, range, trivia)
|
SynTypeDefn.SynTypeDefn (typeInfo, typeRepr, members, ctor, range, trivia)
|
||||||
|
|
||||||
|
let getName (defn : SynTypeDefn) : LongIdent =
|
||||||
|
match defn with
|
||||||
|
| SynTypeDefn (SynComponentInfo.SynComponentInfo (_, _, _, id, _, _, _, _), _, _, _, _, _) -> id
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
<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"/>
|
||||||
|
|||||||
Reference in New Issue
Block a user