From 0652744c5757fe1d075739377917c97416b87e98 Mon Sep 17 00:00:00 2001 From: Patrick Stevens <3138005+Smaug123@users.noreply.github.com> Date: Wed, 2 Oct 2024 20:30:21 +0100 Subject: [PATCH] Allow using fsproj annotations instead of attributes (#275) --- ConsumePlugin/ConsumePlugin.fsproj | 14 ++ ConsumePlugin/GeneratedMockNoAttributes.fs | 200 ++++++++++++++++++ ConsumePlugin/MockExampleNoAttributes.fs | 41 ++++ README.md | 30 +++ .../TestMockGeneratorNoAttr.fs | 36 ++++ .../WoofWare.Myriad.Plugins.Test.fsproj | 1 + .../HttpClientGenerator.fs | 28 ++- .../InterfaceMockGenerator.fs | 26 ++- WoofWare.Myriad.Plugins/JsonParseGenerator.fs | 27 ++- .../JsonSerializeGenerator.fs | 27 ++- WoofWare.Myriad.Plugins/MyriadParamParser.fs | 64 ++++++ .../SwaggerClientGenerator.fs | 29 +-- .../SynExpr/SynTypeDefn.fs | 4 + .../WoofWare.Myriad.Plugins.fsproj | 1 + 14 files changed, 501 insertions(+), 27 deletions(-) create mode 100644 ConsumePlugin/GeneratedMockNoAttributes.fs create mode 100644 ConsumePlugin/MockExampleNoAttributes.fs create mode 100644 WoofWare.Myriad.Plugins.Test/TestMockGenerator/TestMockGeneratorNoAttr.fs create mode 100644 WoofWare.Myriad.Plugins/MyriadParamParser.fs diff --git a/ConsumePlugin/ConsumePlugin.fsproj b/ConsumePlugin/ConsumePlugin.fsproj index 5b99cc1..d509e3b 100644 --- a/ConsumePlugin/ConsumePlugin.fsproj +++ b/ConsumePlugin/ConsumePlugin.fsproj @@ -32,6 +32,20 @@ MockExample.fs + + + MockExampleNoAttributes.fs + + GenerateMock + GenerateMock(false) + GenerateMock + GenerateMock + GenerateMock(false) + GenerateMock + GenerateMock + GenerateMock + + Vault.fs diff --git a/ConsumePlugin/GeneratedMockNoAttributes.fs b/ConsumePlugin/GeneratedMockNoAttributes.fs new file mode 100644 index 0000000..f9ea47c --- /dev/null +++ b/ConsumePlugin/GeneratedMockNoAttributes.fs @@ -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 -> 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 -> 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 () diff --git a/ConsumePlugin/MockExampleNoAttributes.fs b/ConsumePlugin/MockExampleNoAttributes.fs new file mode 100644 index 0000000..3d181fb --- /dev/null +++ b/ConsumePlugin/MockExampleNoAttributes.fs @@ -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 diff --git a/README.md b/README.md index 3cbacb9..9c8139e 100644 --- a/README.md +++ b/README.md @@ -604,6 +604,36 @@ For example, this specifies that Myriad is to use the contents of `Client.fs` to ``` +## 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 + + + + + Client.fs + + GenerateMock(false)!JsonParse + GenerateMock + + + + + + + + +``` + +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 * MsBuild doesn't always realise that it needs to invoke Myriad during rebuild. diff --git a/WoofWare.Myriad.Plugins.Test/TestMockGenerator/TestMockGeneratorNoAttr.fs b/WoofWare.Myriad.Plugins.Test/TestMockGenerator/TestMockGeneratorNoAttr.fs new file mode 100644 index 0000000..bff8aba --- /dev/null +++ b/WoofWare.Myriad.Plugins.Test/TestMockGenerator/TestMockGeneratorNoAttr.fs @@ -0,0 +1,36 @@ +namespace WoofWare.Myriad.Plugins.Test + +open System +open SomeNamespace +open NUnit.Framework +open FsUnitTyped + +[] +module TestMockGeneratorNoAttr = + + [] + let ``Example of use: IPublicType`` () = + let mock : IPublicTypeNoAttr = + { PublicTypeNoAttrMock.Empty with + Mem1 = fun (s, count) -> List.replicate count s + } + :> _ + + let _ = + Assert.Throws (fun () -> mock.Mem2 "hi" |> ignore) + + mock.Mem1 ("hi", 3) |> shouldEqual [ "hi" ; "hi" ; "hi" ] + + [] + 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" diff --git a/WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj b/WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj index 4fd67a4..110a31e 100644 --- a/WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj +++ b/WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj @@ -26,6 +26,7 @@ + diff --git a/WoofWare.Myriad.Plugins/HttpClientGenerator.fs b/WoofWare.Myriad.Plugins/HttpClientGenerator.fs index 561f068..57cab3e 100644 --- a/WoofWare.Myriad.Plugins/HttpClientGenerator.fs +++ b/WoofWare.Myriad.Plugins/HttpClientGenerator.fs @@ -992,6 +992,10 @@ type HttpClientGenerator () = member _.ValidInputExtensions = [ ".fs" ] member _.Generate (context : GeneratorContext) = + let targetedTypes = + MyriadParamParser.render context.AdditionalParameters + |> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse) + let ast, _ = Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head @@ -1005,12 +1009,32 @@ type HttpClientGenerator () = types |> List.choose (fun typeDef -> match Ast.getAttribute 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 -> let arg = match SynExpr.stripOptionalParen attr.ArgExpr with | SynExpr.Const (SynConst.Bool value, _) -> value - | SynExpr.Const (SynConst.Unit, _) -> JsonParseAttribute.DefaultIsExtensionMethod + | SynExpr.Const (SynConst.Unit, _) -> HttpClientAttribute.DefaultIsExtensionMethod | arg -> failwith $"Unrecognised argument %+A{arg} to [<%s{nameof HttpClientAttribute}>]. Literals are not supported. Use `true` or `false` (or unit) only." diff --git a/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs b/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs index a433767..b8fc07e 100644 --- a/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs +++ b/WoofWare.Myriad.Plugins/InterfaceMockGenerator.fs @@ -283,6 +283,10 @@ type InterfaceMockGenerator () = member _.ValidInputExtensions = [ ".fs" ] member _.Generate (context : GeneratorContext) = + let targetedTypes = + MyriadParamParser.render context.AdditionalParameters + |> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse) + let ast, _ = Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head @@ -294,7 +298,27 @@ type InterfaceMockGenerator () = types |> List.choose (fun typeDef -> match Ast.getAttribute 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 -> let arg = match SynExpr.stripOptionalParen attr.ArgExpr with diff --git a/WoofWare.Myriad.Plugins/JsonParseGenerator.fs b/WoofWare.Myriad.Plugins/JsonParseGenerator.fs index a06c942..14f5f36 100644 --- a/WoofWare.Myriad.Plugins/JsonParseGenerator.fs +++ b/WoofWare.Myriad.Plugins/JsonParseGenerator.fs @@ -702,6 +702,10 @@ type JsonParseGenerator () = member _.ValidInputExtensions = [ ".fs" ] member _.Generate (context : GeneratorContext) = + let targetedTypes = + MyriadParamParser.render context.AdditionalParameters + |> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse) + let ast, _ = Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head @@ -724,7 +728,28 @@ type JsonParseGenerator () = types |> List.choose (fun typeDef -> match Ast.getAttribute 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 -> let arg = match SynExpr.stripOptionalParen attr.ArgExpr with diff --git a/WoofWare.Myriad.Plugins/JsonSerializeGenerator.fs b/WoofWare.Myriad.Plugins/JsonSerializeGenerator.fs index d6c72f5..594c45a 100644 --- a/WoofWare.Myriad.Plugins/JsonSerializeGenerator.fs +++ b/WoofWare.Myriad.Plugins/JsonSerializeGenerator.fs @@ -519,6 +519,10 @@ type JsonSerializeGenerator () = member _.ValidInputExtensions = [ ".fs" ] member _.Generate (context : GeneratorContext) = + let targetedTypes = + MyriadParamParser.render context.AdditionalParameters + |> Map.map (fun _ v -> v.Split '!' |> Array.toList |> List.map DesiredGenerator.Parse) + let ast, _ = Ast.fromFilename context.InputFilename |> Async.RunSynchronously |> Array.head @@ -541,7 +545,28 @@ type JsonSerializeGenerator () = types |> List.choose (fun typeDef -> match Ast.getAttribute 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 -> let arg = match SynExpr.stripOptionalParen attr.ArgExpr with diff --git a/WoofWare.Myriad.Plugins/MyriadParamParser.fs b/WoofWare.Myriad.Plugins/MyriadParamParser.fs new file mode 100644 index 0000000..82c4e3b --- /dev/null +++ b/WoofWare.Myriad.Plugins/MyriadParamParser.fs @@ -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}" + +[] +module internal MyriadParamParser = + (* + An apparent bug in Myriad's argument parsing means that this: + + + bar + quux + + + 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) : Map = + 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 diff --git a/WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs b/WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs index 746734a..a5b1359 100644 --- a/WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs +++ b/WoofWare.Myriad.Plugins/SwaggerClientGenerator.fs @@ -672,31 +672,16 @@ type SwaggerClientGenerator () = |> Seq.toList let config = - // Bug in Myriad, their arg parsing is borked. - let pars = - context.AdditionalParameters - |> Seq.map (fun (KeyValue (k, v)) -> k, v) - |> Seq.toList + let pars = MyriadParamParser.render context.AdditionalParameters let pars = - match pars with - | [] -> - failwith "No parameters given. You must supply the parameter in ." - | [ key, value ] -> - let semicolon = value.IndexOf ';' + pars + |> Map.toSeq + |> Seq.map (fun (k, v) -> k.ToUpperInvariant (), v) + |> Map.ofSeq - if semicolon >= 0 then - let equals = value.IndexOf ('=', semicolon) - - [ - 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 + if pars.IsEmpty then + failwith "No parameters given. You must supply the parameter in ." let createMock = match Map.tryFind "GENERATEMOCKVISIBILITY" pars with diff --git a/WoofWare.Myriad.Plugins/SynExpr/SynTypeDefn.fs b/WoofWare.Myriad.Plugins/SynExpr/SynTypeDefn.fs index 966e3d6..529352a 100644 --- a/WoofWare.Myriad.Plugins/SynExpr/SynTypeDefn.fs +++ b/WoofWare.Myriad.Plugins/SynExpr/SynTypeDefn.fs @@ -25,3 +25,7 @@ module internal SynTypeDefn = match r with | SynTypeDefn (typeInfo, typeRepr, _, ctor, range, trivia) -> SynTypeDefn.SynTypeDefn (typeInfo, typeRepr, members, ctor, range, trivia) + + let getName (defn : SynTypeDefn) : LongIdent = + match defn with + | SynTypeDefn (SynComponentInfo.SynComponentInfo (_, _, _, id, _, _, _, _), _, _, _, _, _) -> id diff --git a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj index 08aabe6..9af3bb2 100644 --- a/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj +++ b/WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj @@ -53,6 +53,7 @@ +