Compare commits

..

10 Commits

Author SHA1 Message Date
patrick-conscriptus[bot]
8225df5673 Automated commit (#466)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-12-07 01:49:21 +00:00
dependabot[bot]
1982b69a56 Bump woofware.nunittestrunner from 0.3.9 to 0.3.10 (#464)
* Bump woofware.nunittestrunner from 0.3.9 to 0.3.10

---
updated-dependencies:
- dependency-name: woofware.nunittestrunner
  dependency-version: 0.3.10
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* Bump WoofWare.Whippet.Fantomas from 0.6.4 to 0.7.1

---
updated-dependencies:
- dependency-name: WoofWare.Whippet.Fantomas
  dependency-version: 0.7.1
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

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

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-12-01 12:43:50 +00:00
Patrick Stevens
6b886bff09 Migrate to slnx (#463) 2025-11-30 12:31:18 +00:00
patrick-conscriptus[bot]
a73466f0ff Automated commit (#462)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-11-30 01:49:06 +00:00
Patrick Stevens
95f5ceab03 Add no-attribute support for CreateCatamorphism (#461) 2025-11-28 18:58:27 +00:00
dependabot[bot]
2a7b5822b8 Bump WoofWare.Expect from 0.8.4 to 0.8.5 (#460)
* Bump WoofWare.Expect from 0.8.4 to 0.8.5

---
updated-dependencies:
- dependency-name: WoofWare.Expect
  dependency-version: 0.8.5
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

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

* Deps

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Smaug123 <3138005+Smaug123@users.noreply.github.com>
2025-11-25 11:04:43 +00:00
dependabot[bot]
d344d9a7e9 Bump actions/checkout from 5 to 6 (#459)
Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6.
- [Release notes](https://github.com/actions/checkout/releases)
- [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md)
- [Commits](https://github.com/actions/checkout/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/checkout
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-11-24 16:36:55 +00:00
patrick-conscriptus[bot]
fab8c0854a Automated commit (#458)
Co-authored-by: patrick-conscriptus[bot] <175414948+patrick-conscriptus[bot]@users.noreply.github.com>
2025-11-23 01:50:13 +00:00
Patrick Stevens
038b424906 Add type-level help text support to ArgParser generator (#457)
The ArgumentHelpText attribute can now be applied to the record type itself
to display help text at the top of the --help output, before field descriptions.
This enables better documentation of command-line argument parsers.

Features:
- Type-level help text appears before argument list
- Multiline help text is supported
- Backward compatible with existing code
- Help text appears in both --help and error messages

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-11-21 06:32:55 +00:00
Patrick Stevens
2fc8ba958c Add IAsyncDisposable support in mock generators (#456)
* Add IAsyncDisposable support to mock generators

Extend both GenerateMock and GenerateCapturingMock to support interfaces
that inherit IAsyncDisposable, mirroring the existing IDisposable pattern.

Changes:
- Add IAsyncDisposable to KnownInheritance type
- Detect IAsyncDisposable inheritance in interface parsing
- Generate DisposeAsync field with ValueTask return type
- Initialize DisposeAsync with completed ValueTask() in Empty mock
- Implement System.IAsyncDisposable interface explicitly
- Support interfaces inheriting both IDisposable and IAsyncDisposable

Test cases added:
- TypeWithAsyncDisposable: interface with only IAsyncDisposable
- TypeWithBothDisposables: interface with both disposal interfaces

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2025-11-20 08:59:30 +00:00
31 changed files with 1343 additions and 159 deletions

View File

@@ -13,6 +13,12 @@
"commands": [
"fsharp-analyzers"
]
},
"woofware.nunittestrunner": {
"version": "0.3.10",
"commands": [
"woofware.nunittestrunner"
]
}
}
}

View File

@@ -25,7 +25,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
@@ -46,7 +46,7 @@ jobs:
security-events: write
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
@@ -65,7 +65,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
@@ -80,7 +80,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
@@ -93,7 +93,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
@@ -114,7 +114,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
@@ -152,7 +152,7 @@ jobs:
nuget-pack:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
with:
fetch-depth: 0 # so that NerdBank.GitVersioning has access to history
- name: Install Nix
@@ -207,7 +207,7 @@ jobs:
runs-on: ubuntu-latest
needs: [nuget-pack]
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Download NuGet artifact
uses: actions/download-artifact@v6
with:
@@ -287,7 +287,7 @@ jobs:
attestations: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
@@ -325,7 +325,7 @@ jobs:
attestations: write
contents: read
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Install Nix
uses: cachix/install-nix-action@v31
with:
@@ -366,7 +366,7 @@ jobs:
permissions:
contents: write
steps:
- uses: actions/checkout@v5
- uses: actions/checkout@v6
- name: Download NuGet artifact
uses: actions/download-artifact@v6
with:

View File

@@ -11,7 +11,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Check out repository
uses: actions/checkout@v5
uses: actions/checkout@v6
- name: Install Nix
uses: DeterminateSystems/nix-installer-action@main

View File

@@ -235,3 +235,27 @@ type FlagsIntoPositionalArgs' =
[<PositionalArgs false>]
DontGrabEverything : string list
}
[<ArgParser>]
[<ArgumentHelpText "Parse command-line arguments for a basic configuration. This help text appears before the argument list.">]
type WithTypeHelp =
{
[<ArgumentHelpText "The configuration file path">]
ConfigFile : string
[<ArgumentHelpText "Enable verbose output">]
Verbose : bool
Port : int
}
[<ArgParser>]
[<ArgumentHelpText "This is a multiline help text example.
It spans multiple lines to test that multiline strings work correctly.
You can use this to provide detailed documentation for your argument parser.">]
type WithMultilineTypeHelp =
{
[<ArgumentHelpText "Input file to process">]
InputFile : string
[<ArgumentHelpText "Output directory">]
OutputDir : string
Force : bool
}

View File

@@ -55,3 +55,15 @@ type TypeWithProperties =
abstract Mem1 : string option -> string[] Async
abstract Prop1 : int
abstract Prop2 : unit Async
[<GenerateCapturingMock>]
type TypeWithAsyncDisposable =
inherit IAsyncDisposable
abstract Mem1 : string option -> string[] Async
abstract Mem2 : unit -> string[] Async
[<GenerateCapturingMock>]
type TypeWithBothDisposables =
inherit IDisposable
inherit IAsyncDisposable
abstract Mem1 : string -> int

View File

@@ -0,0 +1,19 @@
namespace ConsumePluginNoAttr
type ConstNoAttr<'a> =
| Verbatim of 'a
| String of string
type PairOpKindNoAttr =
| NormalSeq
| ThenDoSeq
type TreeNoAttr<'a, 'b> =
| Const of ConstNoAttr<'a> * 'b
| Pair of TreeNoAttr<'a, 'b> * TreeNoAttr<'a, 'b> * PairOpKindNoAttr
| Sequential of TreeNoAttr<'a, 'b> list
| Builder of TreeNoAttr<'a, 'b> * TreeBuilderNoAttr<'b, 'a>
and TreeBuilderNoAttr<'b, 'a> =
| Child of TreeBuilderNoAttr<'b, 'a>
| Parent of TreeNoAttr<'a, 'b>

View File

@@ -77,6 +77,13 @@
<Compile Include="GeneratedCatamorphism.fs">
<MyriadFile>Catamorphism.fs</MyriadFile>
</Compile>
<Compile Include="CatamorphismNoAttribute.fs" />
<Compile Include="GeneratedCatamorphismNoAttribute.fs">
<MyriadFile>CatamorphismNoAttribute.fs</MyriadFile>
<MyriadParams>
<TreeNoAttr>CreateCatamorphism(TreeNoAttrCata)</TreeNoAttr>
</MyriadParams>
</Compile>
<Compile Include="FSharpForFunAndProfitCata.fs" />
<Compile Include="GeneratedFileSystem.fs">
<MyriadFile>FSharpForFunAndProfitCata.fs</MyriadFile>

View File

@@ -4346,3 +4346,438 @@ module FlagsIntoPositionalArgs'ArgParse =
static member parse (args : string list) : FlagsIntoPositionalArgs' =
FlagsIntoPositionalArgs'.parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
/// Methods to parse arguments for the type WithTypeHelp
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module WithTypeHelp =
type private ParseState_WithTypeHelp =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
let parse' (getEnvironmentVariable : string -> string option) (args : string list) : WithTypeHelp =
let ArgParser_errors = ResizeArray ()
let helpText () =
[
"Parse command-line arguments for a basic configuration. This help text appears before the argument list."
""
(sprintf
"%s string%s%s"
(sprintf "--%s" "config-file")
""
(sprintf " : %s" ("The configuration file path")))
(sprintf "%s bool%s%s" (sprintf "--%s" "verbose") "" (sprintf " : %s" ("Enable verbose output")))
(sprintf "%s int32%s%s" (sprintf "--%s" "port") "" "")
]
|> String.concat "\n"
let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
let mutable arg_0 : string option = None
let mutable arg_1 : bool option = None
let mutable arg_2 : int option = None
/// Processes the key-value pair, returning Error if no key was matched.
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
let processKeyValue (key : string) (value : string) : Result<unit, string option> =
if System.String.Equals (key, sprintf "--%s" "port", System.StringComparison.OrdinalIgnoreCase) then
match arg_2 with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "port")
(x.ToString ())
(value.ToString ())
|> ArgParser_errors.Add
Ok ()
| None ->
try
arg_2 <- value |> (fun x -> System.Int32.Parse x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if System.String.Equals (key, sprintf "--%s" "verbose", System.StringComparison.OrdinalIgnoreCase) then
match arg_1 with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "verbose")
(x.ToString ())
(value.ToString ())
|> ArgParser_errors.Add
Ok ()
| None ->
try
arg_1 <- value |> (fun x -> System.Boolean.Parse x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if
System.String.Equals (key, sprintf "--%s" "config-file", System.StringComparison.OrdinalIgnoreCase)
then
match arg_0 with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "config-file")
(x.ToString ())
(value.ToString ())
|> ArgParser_errors.Add
Ok ()
| None ->
try
arg_0 <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else
Error None
/// Returns false if we didn't set a value.
let setFlagValue (key : string) : bool =
if System.String.Equals (key, sprintf "--%s" "verbose", System.StringComparison.OrdinalIgnoreCase) then
match arg_1 with
| Some x ->
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "verbose")
|> ArgParser_errors.Add
true
| None ->
arg_1 <- true |> Some
true
else
false
let rec go (state : ParseState_WithTypeHelp) (args : string list) =
match args with
| [] ->
match state with
| ParseState_WithTypeHelp.AwaitingKey -> ()
| ParseState_WithTypeHelp.AwaitingValue key ->
if setFlagValue key then
()
else
sprintf
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
key
|> ArgParser_errors.Add
| "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
| arg :: args ->
match state with
| ParseState_WithTypeHelp.AwaitingKey ->
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
if arg = "--help" then
helpText () |> failwithf "Help text requested.\n%s"
else
let equals = arg.IndexOf (char 61)
if equals < 0 then
args |> go (ParseState_WithTypeHelp.AwaitingValue arg)
else
let key = arg.[0 .. equals - 1]
let value = arg.[equals + 1 ..]
match processKeyValue key value with
| Ok () -> go ParseState_WithTypeHelp.AwaitingKey args
| Error x ->
match x with
| None ->
failwithf "Unable to process argument %s as key %s and value %s" arg key value
| Some msg ->
sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
go ParseState_WithTypeHelp.AwaitingKey args
else
arg |> (fun x -> x) |> parser_LeftoverArgs.Add
go ParseState_WithTypeHelp.AwaitingKey args
| ParseState_WithTypeHelp.AwaitingValue key ->
match processKeyValue key arg with
| Ok () -> go ParseState_WithTypeHelp.AwaitingKey args
| Error exc ->
if setFlagValue key then
go ParseState_WithTypeHelp.AwaitingKey (arg :: args)
else
match exc with
| None ->
failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
| Some msg -> msg |> ArgParser_errors.Add
go ParseState_WithTypeHelp.AwaitingKey args
let parser_LeftoverArgs =
if 0 = parser_LeftoverArgs.Count then
()
else
parser_LeftoverArgs
|> String.concat " "
|> sprintf "There were leftover args: %s"
|> ArgParser_errors.Add
Unchecked.defaultof<_>
let arg_0 =
match arg_0 with
| None ->
sprintf "Required argument '%s' received no value" (sprintf "--%s" "config-file")
|> ArgParser_errors.Add
Unchecked.defaultof<_>
| Some x -> x
let arg_1 =
match arg_1 with
| None ->
sprintf "Required argument '%s' received no value" (sprintf "--%s" "verbose")
|> ArgParser_errors.Add
Unchecked.defaultof<_>
| Some x -> x
let arg_2 =
match arg_2 with
| None ->
sprintf "Required argument '%s' received no value" (sprintf "--%s" "port")
|> ArgParser_errors.Add
Unchecked.defaultof<_>
| Some x -> x
if 0 = ArgParser_errors.Count then
{
ConfigFile = arg_0
Port = arg_2
Verbose = arg_1
}
else
ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
let parse (args : string list) : WithTypeHelp =
parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args
namespace ConsumePlugin
open System
open System.IO
open WoofWare.Myriad.Plugins
/// Methods to parse arguments for the type WithMultilineTypeHelp
[<RequireQualifiedAccess ; CompilationRepresentation(CompilationRepresentationFlags.ModuleSuffix)>]
module WithMultilineTypeHelp =
type private ParseState_WithMultilineTypeHelp =
/// Ready to consume a key or positional arg
| AwaitingKey
/// Waiting to receive a value for the key we've already consumed
| AwaitingValue of key : string
let parse' (getEnvironmentVariable : string -> string option) (args : string list) : WithMultilineTypeHelp =
let ArgParser_errors = ResizeArray ()
let helpText () =
[
"This is a multiline help text example.
It spans multiple lines to test that multiline strings work correctly.
You can use this to provide detailed documentation for your argument parser."
""
(sprintf "%s string%s%s" (sprintf "--%s" "input-file") "" (sprintf " : %s" ("Input file to process")))
(sprintf "%s string%s%s" (sprintf "--%s" "output-dir") "" (sprintf " : %s" ("Output directory")))
(sprintf "%s bool%s%s" (sprintf "--%s" "force") "" "")
]
|> String.concat "\n"
let parser_LeftoverArgs : string ResizeArray = ResizeArray ()
let mutable arg_0 : string option = None
let mutable arg_1 : string option = None
let mutable arg_2 : bool option = None
/// Processes the key-value pair, returning Error if no key was matched.
/// If the key is an arg which can have arity 1, but throws when consuming that arg, we return Error(<the message>).
/// This can nevertheless be a successful parse, e.g. when the key may have arity 0.
let processKeyValue (key : string) (value : string) : Result<unit, string option> =
if System.String.Equals (key, sprintf "--%s" "force", System.StringComparison.OrdinalIgnoreCase) then
match arg_2 with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "force")
(x.ToString ())
(value.ToString ())
|> ArgParser_errors.Add
Ok ()
| None ->
try
arg_2 <- value |> (fun x -> System.Boolean.Parse x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if
System.String.Equals (key, sprintf "--%s" "output-dir", System.StringComparison.OrdinalIgnoreCase)
then
match arg_1 with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "output-dir")
(x.ToString ())
(value.ToString ())
|> ArgParser_errors.Add
Ok ()
| None ->
try
arg_1 <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else if
System.String.Equals (key, sprintf "--%s" "input-file", System.StringComparison.OrdinalIgnoreCase)
then
match arg_0 with
| Some x ->
sprintf
"Argument '%s' was supplied multiple times: %s and %s"
(sprintf "--%s" "input-file")
(x.ToString ())
(value.ToString ())
|> ArgParser_errors.Add
Ok ()
| None ->
try
arg_0 <- value |> (fun x -> x) |> Some
Ok ()
with _ as exc ->
exc.Message |> Some |> Error
else
Error None
/// Returns false if we didn't set a value.
let setFlagValue (key : string) : bool =
if System.String.Equals (key, sprintf "--%s" "force", System.StringComparison.OrdinalIgnoreCase) then
match arg_2 with
| Some x ->
sprintf "Flag '%s' was supplied multiple times" (sprintf "--%s" "force")
|> ArgParser_errors.Add
true
| None ->
arg_2 <- true |> Some
true
else
false
let rec go (state : ParseState_WithMultilineTypeHelp) (args : string list) =
match args with
| [] ->
match state with
| ParseState_WithMultilineTypeHelp.AwaitingKey -> ()
| ParseState_WithMultilineTypeHelp.AwaitingValue key ->
if setFlagValue key then
()
else
sprintf
"Trailing argument %s had no value. Use a double-dash to separate positional args from key-value args."
key
|> ArgParser_errors.Add
| "--" :: rest -> parser_LeftoverArgs.AddRange (rest |> Seq.map (fun x -> x))
| arg :: args ->
match state with
| ParseState_WithMultilineTypeHelp.AwaitingKey ->
if arg.StartsWith ("--", System.StringComparison.Ordinal) then
if arg = "--help" then
helpText () |> failwithf "Help text requested.\n%s"
else
let equals = arg.IndexOf (char 61)
if equals < 0 then
args |> go (ParseState_WithMultilineTypeHelp.AwaitingValue arg)
else
let key = arg.[0 .. equals - 1]
let value = arg.[equals + 1 ..]
match processKeyValue key value with
| Ok () -> go ParseState_WithMultilineTypeHelp.AwaitingKey args
| Error x ->
match x with
| None ->
failwithf "Unable to process argument %s as key %s and value %s" arg key value
| Some msg ->
sprintf "%s (at arg %s)" msg arg |> ArgParser_errors.Add
go ParseState_WithMultilineTypeHelp.AwaitingKey args
else
arg |> (fun x -> x) |> parser_LeftoverArgs.Add
go ParseState_WithMultilineTypeHelp.AwaitingKey args
| ParseState_WithMultilineTypeHelp.AwaitingValue key ->
match processKeyValue key arg with
| Ok () -> go ParseState_WithMultilineTypeHelp.AwaitingKey args
| Error exc ->
if setFlagValue key then
go ParseState_WithMultilineTypeHelp.AwaitingKey (arg :: args)
else
match exc with
| None ->
failwithf "Unable to process supplied arg %s. Help text follows.\n%s" key (helpText ())
| Some msg -> msg |> ArgParser_errors.Add
go ParseState_WithMultilineTypeHelp.AwaitingKey args
let parser_LeftoverArgs =
if 0 = parser_LeftoverArgs.Count then
()
else
parser_LeftoverArgs
|> String.concat " "
|> sprintf "There were leftover args: %s"
|> ArgParser_errors.Add
Unchecked.defaultof<_>
let arg_0 =
match arg_0 with
| None ->
sprintf "Required argument '%s' received no value" (sprintf "--%s" "input-file")
|> ArgParser_errors.Add
Unchecked.defaultof<_>
| Some x -> x
let arg_1 =
match arg_1 with
| None ->
sprintf "Required argument '%s' received no value" (sprintf "--%s" "output-dir")
|> ArgParser_errors.Add
Unchecked.defaultof<_>
| Some x -> x
let arg_2 =
match arg_2 with
| None ->
sprintf "Required argument '%s' received no value" (sprintf "--%s" "force")
|> ArgParser_errors.Add
Unchecked.defaultof<_>
| Some x -> x
if 0 = ArgParser_errors.Count then
{
Force = arg_2
InputFile = arg_0
OutputDir = arg_1
}
else
ArgParser_errors |> String.concat "\n" |> failwithf "Errors during parse!\n%s"
let parse (args : string list) : WithMultilineTypeHelp =
parse' (System.Environment.GetEnvironmentVariable >> Option.ofObj) args

View File

@@ -36,7 +36,7 @@ type internal PublicTypeMock =
Mem3 : int * System.Threading.CancellationToken option -> string
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PublicTypeMock =
{
Calls = PublicTypeMockCalls.Calls.Empty ()
@@ -89,7 +89,7 @@ type public PublicTypeInternalFalseMock =
Mem3 : int * System.Threading.CancellationToken option -> string
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PublicTypeInternalFalseMock =
{
Calls = PublicTypeInternalFalseMockCalls.Calls.Empty ()
@@ -139,7 +139,7 @@ type internal InternalTypeMock =
Mem2 : string -> int
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : InternalTypeMock =
{
Calls = InternalTypeMockCalls.Calls.Empty ()
@@ -184,7 +184,7 @@ type private PrivateTypeMock =
Mem2 : string -> int
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PrivateTypeMock =
{
Calls = PrivateTypeMockCalls.Calls.Empty ()
@@ -229,7 +229,7 @@ type private PrivateTypeInternalFalseMock =
Mem2 : string -> int
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PrivateTypeInternalFalseMock =
{
Calls = PrivateTypeInternalFalseMockCalls.Calls.Empty ()
@@ -271,7 +271,7 @@ type internal VeryPublicTypeMock<'a, 'b> =
Mem1 : 'a -> 'b
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : VeryPublicTypeMock<'a, 'b> =
{
Calls = VeryPublicTypeMockCalls.Calls.Empty ()
@@ -365,7 +365,7 @@ type internal CurriedMock<'a> =
Mem6 : int * string -> 'a * int -> string
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : CurriedMock<'a> =
{
Calls = CurriedMockCalls.Calls.Empty ()
@@ -486,7 +486,7 @@ type internal TypeWithInterfaceMock =
Mem2 : unit -> string[] Async
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : TypeWithInterfaceMock =
{
Calls = TypeWithInterfaceMockCalls.Calls.Empty ()
@@ -540,7 +540,7 @@ type internal TypeWithPropertiesMock =
Prop2 : unit -> unit Async
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : TypeWithPropertiesMock =
{
Calls = TypeWithPropertiesMockCalls.Calls.Empty ()
@@ -560,3 +560,103 @@ type internal TypeWithPropertiesMock =
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
namespace SomeNamespace.CapturingMock
open System
open WoofWare.Myriad.Plugins
[<RequireQualifiedAccess>]
module internal TypeWithAsyncDisposableMockCalls =
/// All the calls made to a TypeWithAsyncDisposableMock mock
type internal Calls =
{
Mem1 : ResizeArray<string option>
Mem2 : ResizeArray<unit>
}
/// A fresh calls object which has not yet had any calls made.
static member Empty () : Calls =
{
Mem1 = ResizeArray ()
Mem2 = ResizeArray ()
}
/// Mock record type for an interface
type internal TypeWithAsyncDisposableMock =
{
Calls : TypeWithAsyncDisposableMockCalls.Calls
/// Implementation of IAsyncDisposable.DisposeAsync
DisposeAsync : unit -> System.Threading.Tasks.ValueTask
Mem1 : string option -> string[] Async
Mem2 : unit -> string[] Async
}
/// An implementation where every non-disposal method throws.
static member Empty () : TypeWithAsyncDisposableMock =
{
Calls = TypeWithAsyncDisposableMockCalls.Calls.Empty ()
DisposeAsync = (fun () -> (System.Threading.Tasks.ValueTask ()))
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
}
interface TypeWithAsyncDisposable with
member this.Mem1 arg_0_0 =
lock this.Calls.Mem1 (fun _ -> this.Calls.Mem1.Add (arg_0_0))
this.Mem1 (arg_0_0)
member this.Mem2 () =
lock this.Calls.Mem2 (fun _ -> this.Calls.Mem2.Add (()))
this.Mem2 (())
interface System.IAsyncDisposable with
member this.DisposeAsync () : System.Threading.Tasks.ValueTask = this.DisposeAsync ()
namespace SomeNamespace.CapturingMock
open System
open WoofWare.Myriad.Plugins
[<RequireQualifiedAccess>]
module internal TypeWithBothDisposablesMockCalls =
/// All the calls made to a TypeWithBothDisposablesMock mock
type internal Calls =
{
Mem1 : ResizeArray<string>
}
/// A fresh calls object which has not yet had any calls made.
static member Empty () : Calls =
{
Mem1 = ResizeArray ()
}
/// Mock record type for an interface
type internal TypeWithBothDisposablesMock =
{
Calls : TypeWithBothDisposablesMockCalls.Calls
/// Implementation of IDisposable.Dispose
Dispose : unit -> unit
/// Implementation of IAsyncDisposable.DisposeAsync
DisposeAsync : unit -> System.Threading.Tasks.ValueTask
Mem1 : string -> int
}
/// An implementation where every non-disposal method throws.
static member Empty () : TypeWithBothDisposablesMock =
{
Calls = TypeWithBothDisposablesMockCalls.Calls.Empty ()
Dispose = (fun () -> ())
DisposeAsync = (fun () -> (System.Threading.Tasks.ValueTask ()))
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
}
interface TypeWithBothDisposables with
member this.Mem1 arg_0_0 =
lock this.Calls.Mem1 (fun _ -> this.Calls.Mem1.Add (arg_0_0))
this.Mem1 (arg_0_0)
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
interface System.IAsyncDisposable with
member this.DisposeAsync () : System.Threading.Tasks.ValueTask = this.DisposeAsync ()

View File

@@ -35,7 +35,7 @@ type internal PublicTypeNoAttrMock =
Mem3 : int * System.Threading.CancellationToken option -> string
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PublicTypeNoAttrMock =
{
Calls = PublicTypeNoAttrMockCalls.Calls.Empty ()
@@ -87,7 +87,7 @@ type public PublicTypeInternalFalseNoAttrMock =
Mem3 : int * System.Threading.CancellationToken option -> string
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PublicTypeInternalFalseNoAttrMock =
{
Calls = PublicTypeInternalFalseNoAttrMockCalls.Calls.Empty ()
@@ -136,7 +136,7 @@ type internal InternalTypeNoAttrMock =
Mem2 : string -> int
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : InternalTypeNoAttrMock =
{
Calls = InternalTypeNoAttrMockCalls.Calls.Empty ()
@@ -180,7 +180,7 @@ type private PrivateTypeNoAttrMock =
Mem2 : string -> int
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PrivateTypeNoAttrMock =
{
Calls = PrivateTypeNoAttrMockCalls.Calls.Empty ()
@@ -224,7 +224,7 @@ type private PrivateTypeInternalFalseNoAttrMock =
Mem2 : string -> int
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : PrivateTypeInternalFalseNoAttrMock =
{
Calls = PrivateTypeInternalFalseNoAttrMockCalls.Calls.Empty ()
@@ -265,7 +265,7 @@ type internal VeryPublicTypeNoAttrMock<'a, 'b> =
Mem1 : 'a -> 'b
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : VeryPublicTypeNoAttrMock<'a, 'b> =
{
Calls = VeryPublicTypeNoAttrMockCalls.Calls.Empty ()
@@ -358,7 +358,7 @@ type internal CurriedNoAttrMock<'a> =
Mem6 : int * string -> 'a * int -> string
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : CurriedNoAttrMock<'a> =
{
Calls = CurriedNoAttrMockCalls.Calls.Empty ()
@@ -478,7 +478,7 @@ type internal TypeWithInterfaceNoAttrMock =
Mem2 : unit -> string[] Async
}
/// An implementation where every non-unit method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : TypeWithInterfaceNoAttrMock =
{
Calls = TypeWithInterfaceNoAttrMockCalls.Calls.Empty ()

View File

@@ -0,0 +1,144 @@
//------------------------------------------------------------------------------
// This code was generated by myriad.
// Changes to this file will be lost when the code is regenerated.
//------------------------------------------------------------------------------
namespace ConsumePluginNoAttr
/// Description of how to combine cases during a fold
type TreeBuilderNoAttrCataCase<'b, 'a, 'TreeBuilderNoAttr, 'TreeNoAttr> =
/// How to operate on the Child case
abstract Child : 'TreeBuilderNoAttr -> 'TreeBuilderNoAttr
/// How to operate on the Parent case
abstract Parent : 'TreeNoAttr -> 'TreeBuilderNoAttr
/// Description of how to combine cases during a fold
type TreeNoAttrCataCase<'a, 'b, 'TreeBuilderNoAttr, 'TreeNoAttr> =
/// How to operate on the Const case
abstract Const : ConstNoAttr<'a> -> 'b -> 'TreeNoAttr
/// How to operate on the Pair case
abstract Pair : 'TreeNoAttr -> 'TreeNoAttr -> PairOpKindNoAttr -> 'TreeNoAttr
/// How to operate on the Sequential case
abstract Sequential : 'TreeNoAttr list -> 'TreeNoAttr
/// How to operate on the Builder case
abstract Builder : 'TreeNoAttr -> 'TreeBuilderNoAttr -> 'TreeNoAttr
/// Specifies how to perform a fold (catamorphism) over the type TreeNoAttr and its friends.
type TreeNoAttrCata<'b, 'a, 'TreeBuilderNoAttr, 'TreeNoAttr> =
{
/// How to perform a fold (catamorphism) over the type TreeBuilderNoAttr
TreeBuilderNoAttr : TreeBuilderNoAttrCataCase<'b, 'a, 'TreeBuilderNoAttr, 'TreeNoAttr>
/// How to perform a fold (catamorphism) over the type TreeNoAttr
TreeNoAttr : TreeNoAttrCataCase<'a, 'b, 'TreeBuilderNoAttr, 'TreeNoAttr>
}
/// Methods to perform a catamorphism over the type TreeNoAttr
[<RequireQualifiedAccess>]
module TreeNoAttrCata =
[<RequireQualifiedAccess>]
type private Instruction<'b, 'a> =
| Process__TreeBuilderNoAttr of TreeBuilderNoAttr<'b, 'a>
| Process__TreeNoAttr of TreeNoAttr<'a, 'b>
| TreeBuilderNoAttr_Child
| TreeBuilderNoAttr_Parent
| TreeNoAttr_Pair of PairOpKindNoAttr
| TreeNoAttr_Sequential of int
| TreeNoAttr_Builder
let private loop
(cata : TreeNoAttrCata<'b, 'a, 'TreeBuilderNoAttr, 'TreeNoAttr>)
(instructions : ResizeArray<Instruction<'b, 'a>>)
=
let treeNoAttrStack = ResizeArray<'TreeNoAttr> ()
let treeBuilderNoAttrStack = ResizeArray<'TreeBuilderNoAttr> ()
while instructions.Count > 0 do
let currentInstruction = instructions.[instructions.Count - 1]
instructions.RemoveAt (instructions.Count - 1)
match currentInstruction with
| Instruction.Process__TreeBuilderNoAttr x ->
match x with
| TreeBuilderNoAttr.Child (arg0_0) ->
instructions.Add Instruction.TreeBuilderNoAttr_Child
instructions.Add (Instruction.Process__TreeBuilderNoAttr arg0_0)
| TreeBuilderNoAttr.Parent (arg0_0) ->
instructions.Add Instruction.TreeBuilderNoAttr_Parent
instructions.Add (Instruction.Process__TreeNoAttr arg0_0)
| Instruction.Process__TreeNoAttr x ->
match x with
| TreeNoAttr.Const (arg0_0, arg1_0) -> cata.TreeNoAttr.Const arg0_0 arg1_0 |> treeNoAttrStack.Add
| TreeNoAttr.Pair (arg0_0, arg1_0, arg2_0) ->
instructions.Add (Instruction.TreeNoAttr_Pair (arg2_0))
instructions.Add (Instruction.Process__TreeNoAttr arg0_0)
instructions.Add (Instruction.Process__TreeNoAttr arg1_0)
| TreeNoAttr.Sequential (arg0_0) ->
instructions.Add (Instruction.TreeNoAttr_Sequential ((List.length arg0_0)))
for elt in arg0_0 do
instructions.Add (Instruction.Process__TreeNoAttr elt)
| TreeNoAttr.Builder (arg0_0, arg1_0) ->
instructions.Add Instruction.TreeNoAttr_Builder
instructions.Add (Instruction.Process__TreeNoAttr arg0_0)
instructions.Add (Instruction.Process__TreeBuilderNoAttr arg1_0)
| Instruction.TreeBuilderNoAttr_Child ->
let arg0_0 = treeBuilderNoAttrStack.[treeBuilderNoAttrStack.Count - 1]
treeBuilderNoAttrStack.RemoveAt (treeBuilderNoAttrStack.Count - 1)
cata.TreeBuilderNoAttr.Child arg0_0 |> treeBuilderNoAttrStack.Add
| Instruction.TreeBuilderNoAttr_Parent ->
let arg0_0 = treeNoAttrStack.[treeNoAttrStack.Count - 1]
treeNoAttrStack.RemoveAt (treeNoAttrStack.Count - 1)
cata.TreeBuilderNoAttr.Parent arg0_0 |> treeBuilderNoAttrStack.Add
| Instruction.TreeNoAttr_Pair arg2_0 ->
let arg0_0 = treeNoAttrStack.[treeNoAttrStack.Count - 1]
treeNoAttrStack.RemoveAt (treeNoAttrStack.Count - 1)
let arg1_0 = treeNoAttrStack.[treeNoAttrStack.Count - 1]
treeNoAttrStack.RemoveAt (treeNoAttrStack.Count - 1)
cata.TreeNoAttr.Pair arg0_0 arg1_0 arg2_0 |> treeNoAttrStack.Add
| Instruction.TreeNoAttr_Sequential arg0_0 ->
let arg0_0_len = arg0_0
let arg0_0 =
seq {
for i = treeNoAttrStack.Count - 1 downto treeNoAttrStack.Count - arg0_0 do
yield treeNoAttrStack.[i]
}
|> Seq.toList
treeNoAttrStack.RemoveRange (treeNoAttrStack.Count - arg0_0_len, arg0_0_len)
cata.TreeNoAttr.Sequential arg0_0 |> treeNoAttrStack.Add
| Instruction.TreeNoAttr_Builder ->
let arg0_0 = treeNoAttrStack.[treeNoAttrStack.Count - 1]
treeNoAttrStack.RemoveAt (treeNoAttrStack.Count - 1)
let arg1_0 = treeBuilderNoAttrStack.[treeBuilderNoAttrStack.Count - 1]
treeBuilderNoAttrStack.RemoveAt (treeBuilderNoAttrStack.Count - 1)
cata.TreeNoAttr.Builder arg0_0 arg1_0 |> treeNoAttrStack.Add
treeBuilderNoAttrStack, treeNoAttrStack
/// Execute the catamorphism.
let runTreeBuilderNoAttr
(cata : TreeNoAttrCata<'b, 'a, 'TreeBuilderNoAttrRet, 'TreeNoAttrRet>)
(x : TreeBuilderNoAttr<'b, 'a>)
: 'TreeBuilderNoAttrRet
=
let instructions = ResizeArray ()
instructions.Add (Instruction.Process__TreeBuilderNoAttr x)
let treeBuilderNoAttrRetStack, treeNoAttrRetStack = loop cata instructions
Seq.exactlyOne treeBuilderNoAttrRetStack
/// Execute the catamorphism.
let runTreeNoAttr
(cata : TreeNoAttrCata<'b, 'a, 'TreeBuilderNoAttrRet, 'TreeNoAttrRet>)
(x : TreeNoAttr<'a, 'b>)
: 'TreeNoAttrRet
=
let instructions = ResizeArray ()
instructions.Add (Instruction.Process__TreeNoAttr x)
let treeBuilderNoAttrRetStack, treeNoAttrRetStack = loop cata instructions
Seq.exactlyOne treeNoAttrRetStack

View File

@@ -16,7 +16,7 @@ type internal PublicTypeMock =
Mem3 : int * option<System.Threading.CancellationToken> -> string
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PublicTypeMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -41,7 +41,7 @@ type public PublicTypeInternalFalseMock =
Mem3 : int * option<System.Threading.CancellationToken> -> string
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PublicTypeInternalFalseMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -65,7 +65,7 @@ type internal InternalTypeMock =
Mem2 : string -> int
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : InternalTypeMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -87,7 +87,7 @@ type private PrivateTypeMock =
Mem2 : string -> int
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PrivateTypeMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -109,7 +109,7 @@ type private PrivateTypeInternalFalseMock =
Mem2 : string -> int
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PrivateTypeInternalFalseMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -130,7 +130,7 @@ type internal VeryPublicTypeMock<'a, 'b> =
Mem1 : 'a -> 'b
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : VeryPublicTypeMock<'a, 'b> =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -154,7 +154,7 @@ type internal CurriedMock<'a> =
Mem6 : int * string -> 'a * int -> string
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : CurriedMock<'a> =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -192,7 +192,7 @@ type internal TypeWithInterfaceMock =
Mem2 : unit -> string[] Async
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : TypeWithInterfaceMock =
{
Dispose = (fun () -> ())
@@ -221,7 +221,7 @@ type internal TypeWithPropertiesMock =
Mem1 : string option -> string[] Async
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : TypeWithPropertiesMock =
{
Dispose = (fun () -> ())
@@ -237,3 +237,62 @@ type internal TypeWithPropertiesMock =
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins
/// Mock record type for an interface
type internal TypeWithAsyncDisposableMock =
{
/// Implementation of IAsyncDisposable.DisposeAsync
DisposeAsync : unit -> System.Threading.Tasks.ValueTask
Mem1 : string option -> string[] Async
Mem2 : unit -> string[] Async
}
/// An implementation where every non-disposal method throws.
static member Empty : TypeWithAsyncDisposableMock =
{
DisposeAsync = (fun () -> (System.Threading.Tasks.ValueTask ()))
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
Mem2 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem2"))
}
interface TypeWithAsyncDisposable with
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
member this.Mem2 () = this.Mem2 (())
interface System.IAsyncDisposable with
member this.DisposeAsync () : System.Threading.Tasks.ValueTask = this.DisposeAsync ()
namespace SomeNamespace
open System
open WoofWare.Myriad.Plugins
/// Mock record type for an interface
type internal TypeWithBothDisposablesMock =
{
/// Implementation of IDisposable.Dispose
Dispose : unit -> unit
/// Implementation of IAsyncDisposable.DisposeAsync
DisposeAsync : unit -> System.Threading.Tasks.ValueTask
Mem1 : string -> int
}
/// An implementation where every non-disposal method throws.
static member Empty : TypeWithBothDisposablesMock =
{
Dispose = (fun () -> ())
DisposeAsync = (fun () -> (System.Threading.Tasks.ValueTask ()))
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
}
interface TypeWithBothDisposables with
member this.Mem1 arg_0_0 = this.Mem1 (arg_0_0)
interface System.IDisposable with
member this.Dispose () : unit = this.Dispose ()
interface System.IAsyncDisposable with
member this.DisposeAsync () : System.Threading.Tasks.ValueTask = this.DisposeAsync ()

View File

@@ -15,7 +15,7 @@ type internal PublicTypeNoAttrMock =
Mem3 : int * option<System.Threading.CancellationToken> -> string
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PublicTypeNoAttrMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -39,7 +39,7 @@ type public PublicTypeInternalFalseNoAttrMock =
Mem3 : int * option<System.Threading.CancellationToken> -> string
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PublicTypeInternalFalseNoAttrMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -62,7 +62,7 @@ type internal InternalTypeNoAttrMock =
Mem2 : string -> int
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : InternalTypeNoAttrMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -83,7 +83,7 @@ type private PrivateTypeNoAttrMock =
Mem2 : string -> int
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PrivateTypeNoAttrMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -104,7 +104,7 @@ type private PrivateTypeInternalFalseNoAttrMock =
Mem2 : string -> int
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : PrivateTypeInternalFalseNoAttrMock =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -124,7 +124,7 @@ type internal VeryPublicTypeNoAttrMock<'a, 'b> =
Mem1 : 'a -> 'b
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : VeryPublicTypeNoAttrMock<'a, 'b> =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -147,7 +147,7 @@ type internal CurriedNoAttrMock<'a> =
Mem6 : int * string -> 'a * int -> string
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty () : CurriedNoAttrMock<'a> =
{
Mem1 = (fun _ -> raise (System.NotImplementedException "Unimplemented mock function: Mem1"))
@@ -184,7 +184,7 @@ type internal TypeWithInterfaceNoAttrMock =
Mem2 : unit -> string[] Async
}
/// An implementation where every method throws.
/// An implementation where every non-disposal method throws.
static member Empty : TypeWithInterfaceNoAttrMock =
{
Dispose = (fun () -> ())

View File

@@ -55,3 +55,15 @@ type TypeWithProperties =
abstract Mem1 : string option -> string[] Async
abstract Prop1 : int
abstract Prop2 : unit Async
[<GenerateMock>]
type TypeWithAsyncDisposable =
inherit IAsyncDisposable
abstract Mem1 : string option -> string[] Async
abstract Mem2 : unit -> string[] Async
[<GenerateMock>]
type TypeWithBothDisposables =
inherit IDisposable
inherit IAsyncDisposable
abstract Mem1 : string -> int

View File

@@ -62,8 +62,10 @@ type ArgumentDefaultFunctionAttribute () =
type ArgumentDefaultEnvironmentVariableAttribute (envVar : string) =
inherit Attribute ()
/// Attribute indicating that this field shall have the given help text, when `--help` is invoked
/// Attribute indicating that this field or type shall have the given help text, when `--help` is invoked
/// or when a parse error causes us to print help text.
/// When applied to a record type, the help text appears at the top of the help output, before the field descriptions.
/// When applied to a field, the help text appears next to that field's description.
type ArgumentHelpTextAttribute (helpText : string) =
inherit Attribute ()

View File

@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>

View File

@@ -444,7 +444,7 @@ Required argument '--exact' received no value"""
]
|> List.map TestCaseData
[<TestCaseSource(nameof (boolCases))>]
[<TestCaseSource(nameof boolCases)>]
let ``Bool env vars can be populated`` (envValue : string, boolValue : bool) =
let getEnvVar (s : string) =
s |> shouldEqual "CONSUMEPLUGIN_THINGS"
@@ -704,3 +704,87 @@ Required argument '--exact' received no value"""
// Again, we don't try to detect that the user has missed out the desired argument to `--a`.
exc.Message
|> shouldEqual """Unable to process argument --c=hi as key --c and value hi"""
[<Test>]
let ``Type-level help text appears in help output`` () =
let getEnvVar (_ : string) = None
let exc =
Assert.Throws<exn> (fun () -> WithTypeHelp.parse' getEnvVar [ "--help" ] |> ignore<WithTypeHelp>)
exc.Message
|> shouldContainText
"Parse command-line arguments for a basic configuration. This help text appears before the argument list."
exc.Message
|> shouldContainText "--config-file string : The configuration file path"
exc.Message |> shouldContainText "--verbose bool : Enable verbose output"
exc.Message |> shouldContainText "--port int32"
[<Test>]
let ``Type-level help text appears before field help`` () =
let getEnvVar (_ : string) = None
let exc =
Assert.Throws<exn> (fun () -> WithTypeHelp.parse' getEnvVar [ "--help" ] |> ignore<WithTypeHelp>)
// Verify that the type help appears before the field help
let typeHelpIndex =
exc.Message.IndexOf "Parse command-line arguments for a basic configuration"
let fieldHelpIndex = exc.Message.IndexOf "--config-file"
typeHelpIndex |> shouldBeSmallerThan fieldHelpIndex
[<Test>]
let ``Multiline type-level help text works`` () =
let getEnvVar (_ : string) = None
let exc =
Assert.Throws<exn> (fun () ->
WithMultilineTypeHelp.parse' getEnvVar [ "--help" ]
|> ignore<WithMultilineTypeHelp>
)
exc.Message |> shouldContainText "This is a multiline help text example."
exc.Message
|> shouldContainText "It spans multiple lines to test that multiline strings work correctly."
exc.Message
|> shouldContainText "You can use this to provide detailed documentation for your argument parser."
exc.Message |> shouldContainText "--input-file string : Input file to process"
exc.Message |> shouldContainText "--output-dir string : Output directory"
exc.Message |> shouldContainText "--force bool"
[<Test>]
let ``Type-level help text appears in error messages`` () =
let getEnvVar (_ : string) = None
let exc =
Assert.Throws<exn> (fun () ->
WithTypeHelp.parse' getEnvVar [ "--unknown-arg" ; "value" ]
|> ignore<WithTypeHelp>
)
// Verify that the type help appears in error messages too
exc.Message
|> shouldContainText
"Parse command-line arguments for a basic configuration. This help text appears before the argument list."
exc.Message |> shouldContainText "--config-file"
[<Test>]
let ``Types without type-level help still work`` () =
let getEnvVar (_ : string) = None
let exc =
Assert.Throws<exn> (fun () -> Basic.parse' getEnvVar [ "--help" ] |> ignore<Basic>)
// Should not contain any type-level help, just the field help
exc.Message |> shouldContainText "--foo int32 : This is a foo!"
exc.Message |> shouldContainText "--bar string"
// Make sure there's no extra blank line at the beginning
exc.Message.StartsWith '\n' |> shouldEqual false

View File

@@ -70,3 +70,63 @@ module TestCapturingMockGenerator =
bar = 3
Arg1 = "hello"
}
[<Test>]
let ``Example of use IAsyncDisposable`` () =
let mock' =
{ TypeWithAsyncDisposableMock.Empty () with
Mem1 = fun i -> async { return Option.toArray i }
Mem2 = fun () -> async { return [||] }
}
let mock = mock' :> TypeWithAsyncDisposable
mock.Mem1 (Some "hi") |> Async.RunSynchronously |> shouldEqual [| "hi" |]
mock.Mem2 () |> Async.RunSynchronously |> shouldEqual [||]
// Test that DisposeAsync returns a completed ValueTask
let asyncDisposable = mock :> IAsyncDisposable
let valueTask = asyncDisposable.DisposeAsync ()
valueTask.IsCompleted |> shouldEqual true
// Verify calls were captured
lock mock'.Calls.Mem1 (fun () -> Seq.toList mock'.Calls.Mem1)
|> List.exactlyOne
|> shouldEqual (Some "hi")
lock mock'.Calls.Mem2 (fun () -> Seq.toList mock'.Calls.Mem2)
|> List.exactlyOne
|> shouldEqual ()
[<Test>]
let ``Example of use: Both IDisposable and IAsyncDisposable`` () =
let mutable disposed = false
let mutable disposedAsync = false
let mock' =
{ TypeWithBothDisposablesMock.Empty () with
Dispose = fun () -> disposed <- true
DisposeAsync =
fun () ->
disposedAsync <- true
System.Threading.Tasks.ValueTask ()
Mem1 = fun s -> s.Length
}
let mock = mock' :> TypeWithBothDisposables
mock.Mem1 "hello" |> shouldEqual 5
mock.Mem1 "world" |> shouldEqual 5
// Test IDisposable.Dispose
(mock :> IDisposable).Dispose ()
disposed |> shouldEqual true
// Test IAsyncDisposable.DisposeAsync
let valueTask = (mock :> IAsyncDisposable).DisposeAsync ()
valueTask.IsCompleted |> shouldEqual true
disposedAsync |> shouldEqual true
// Verify calls were captured
lock mock'.Calls.Mem1 (fun () -> Seq.toList mock'.Calls.Mem1)
|> shouldEqual [ "hello" ; "world" ]

View File

@@ -0,0 +1,51 @@
namespace WoofWare.Myriad.Plugins.Test
open System.Threading
open NUnit.Framework
open FsUnitTyped
open ConsumePluginNoAttr
open FsCheck
[<TestFixture>]
module TestCataGeneratorNoAttr =
let idCata<'a, 'b> : TreeNoAttrCata<'a, 'b, _, _> =
{
TreeNoAttr =
{ new TreeNoAttrCataCase<_, _, _, _> with
member _.Const x y = Const (x, y)
member _.Pair x y z = Pair (x, y, z)
member _.Sequential xs = Sequential xs
member _.Builder x b = Builder (x, b)
}
TreeBuilderNoAttr =
{ new TreeBuilderNoAttrCataCase<_, _, _, _> with
member _.Child x = Child x
member _.Parent x = Parent x
}
}
[<Test>]
let ``Example`` () =
let x =
TreeNoAttr.Pair (
TreeNoAttr.Const (ConstNoAttr.Verbatim 0, "hi"),
TreeNoAttr.Const (ConstNoAttr.String "", "bye"),
PairOpKindNoAttr.ThenDoSeq
)
TreeNoAttrCata.runTreeNoAttr idCata x |> shouldEqual x
[<Test>]
let ``Cata works`` () =
let builderCases = ref 0
let property (x : TreeNoAttr<int, string>) =
match x with
| TreeNoAttr.Builder _ -> Interlocked.Increment builderCases |> ignore
| _ -> ()
TreeNoAttrCata.runTreeNoAttr idCata x = x
Check.QuickThrowOnFailure property
builderCases.Value |> shouldBeGreaterThan 10

View File

@@ -47,3 +47,45 @@ module TestMockGenerator =
mock.Mem1 (Some "hi") |> Async.RunSynchronously |> shouldEqual [| "hi" |]
mock.Prop1 |> shouldEqual 44
[<Test>]
let ``Example of use: IAsyncDisposable`` () =
let mock : TypeWithAsyncDisposable =
{ TypeWithAsyncDisposableMock.Empty with
Mem1 = fun i -> async { return Option.toArray i }
}
:> _
mock.Mem1 (Some "hi") |> Async.RunSynchronously |> shouldEqual [| "hi" |]
// Test that DisposeAsync returns a completed ValueTask
let asyncDisposable = mock :> IAsyncDisposable
let valueTask = asyncDisposable.DisposeAsync ()
valueTask.IsCompleted |> shouldEqual true
[<Test>]
let ``Example of use: Both IDisposable and IAsyncDisposable`` () =
let mutable disposed = false
let mutable disposedAsync = false
let mock : TypeWithBothDisposables =
{ TypeWithBothDisposablesMock.Empty with
Dispose = fun () -> disposed <- true
DisposeAsync =
fun () ->
disposedAsync <- true
System.Threading.Tasks.ValueTask ()
Mem1 = fun s -> s.Length
}
:> _
mock.Mem1 "hello" |> shouldEqual 5
// Test IDisposable.Dispose
(mock :> IDisposable).Dispose ()
disposed |> shouldEqual true
// Test IAsyncDisposable.DisposeAsync
let valueTask = (mock :> IAsyncDisposable).DisposeAsync ()
valueTask.IsCompleted |> shouldEqual true
disposedAsync |> shouldEqual true

View File

@@ -9,7 +9,6 @@
I have not yet seen a single instance where I care about this warning
-->
<NoWarn>$(NoWarn),NU1903</NoWarn>
<TestingPlatformDotnetTestSupport>true</TestingPlatformDotnetTestSupport>
</PropertyGroup>
<ItemGroup>
@@ -33,6 +32,7 @@
<Compile Include="TestCapturingMockGenerator\TestCapturingMockGeneratorNoAttr.fs" />
<Compile Include="TestJsonSerialize\TestJsonSerde.fs" />
<Compile Include="TestCataGenerator\TestCataGenerator.fs" />
<Compile Include="TestCataGenerator\TestCataGeneratorNoAttr.fs" />
<Compile Include="TestCataGenerator\TestDirectory.fs" />
<Compile Include="TestCataGenerator\TestGift.fs" />
<Compile Include="TestCataGenerator\TestMyList.fs" />
@@ -62,7 +62,7 @@
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="NUnit" Version="4.3.2" />
<PackageReference Include="NUnit3TestAdapter" Version="5.2.0" />
<PackageReference Include="WoofWare.Expect" Version="0.8.4" />
<PackageReference Include="WoofWare.Expect" Version="0.8.5" />
</ItemGroup>
<ItemGroup>

View File

@@ -769,6 +769,7 @@ module internal ArgParserGenerator =
/// let helpText : string = ...
let private helpText
(typeHelp : SynExpr option)
(typeName : Ident)
(positional : ParseFunctionPositional option)
(args : ParseFunctionNonPositional list)
@@ -850,12 +851,24 @@ module internal ArgParserGenerator =
|> SynExpr.applyTo helpText
|> SynExpr.paren
args
|> List.map (toPrintable describeNonPositional)
|> fun l ->
match positional with
| None -> l
| Some pos -> l @ [ toPrintable describePositional pos ]
let fieldHelp =
args
|> List.map (toPrintable describeNonPositional)
|> fun l ->
match positional with
| None -> l
| Some pos -> l @ [ toPrintable describePositional pos ]
let allHelp =
match typeHelp with
| Some helpExpr ->
// Prepend type help, followed by blank line, then field help
[ helpExpr ; SynExpr.CreateConst "" ] @ fieldHelp
| None ->
// No type help, just field help
fieldHelp
allHelp
|> SynExpr.listLiteral
|> SynExpr.pipeThroughFunction (
SynExpr.applyFunction (SynExpr.createLongIdent [ "String" ; "concat" ]) (SynExpr.CreateConst @"\n")
@@ -1560,6 +1573,7 @@ module internal ArgParserGenerator =
/// Takes a single argument, `args : string list`, and returns something of the type indicated by `recordType`.
let createRecordParse
(typeHelpText : SynExpr option)
(parseState : Ident)
(flagDus : FlagDu list)
(ambientRecords : RecordType list)
@@ -1626,7 +1640,7 @@ module internal ArgParserGenerator =
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic [ argParseErrors ] []
let helpText = helpText recordType.Name pos nonPos
let helpText = helpText typeHelpText recordType.Name pos nonPos
let bindings = errorCollection :: helpText :: bindings
@@ -1923,14 +1937,25 @@ module internal ArgParserGenerator =
| _ -> None
)
let taggedType =
let taggedType, typeHelpText =
match taggedType with
| SynTypeDefn.SynTypeDefn (sci,
| SynTypeDefn.SynTypeDefn (SynComponentInfo.SynComponentInfo (attributes = attrs) as sci,
SynTypeDefnRepr.Simple (SynTypeDefnSimpleRepr.Record (access, fields, _), _),
smd,
_,
_,
_) -> RecordType.OfRecord sci smd access fields
_) ->
let typeHelp =
attrs
|> SynAttributes.toAttrs
|> List.tryPick (fun a ->
match (List.last a.TypeName.LongIdent).idText with
| "ArgumentHelpTextAttribute"
| "ArgumentHelpText" -> Some a.ArgExpr
| _ -> None
)
RecordType.OfRecord sci smd access fields, typeHelp
| _ -> failwith "[<ArgParser>] currently only supports being placed on records."
let modAttrs, modName =
@@ -1988,7 +2013,7 @@ module internal ArgParserGenerator =
|> SynPat.annotateType (SynType.appPostfix "list" SynType.string)
let parsePrime =
createRecordParse parseStateIdent flagDus allRecordTypes taggedType
createRecordParse typeHelpText parseStateIdent flagDus allRecordTypes taggedType
|> SynBinding.basic
[ Ident.create "parse'" ]
[

View File

@@ -19,7 +19,9 @@ module internal CapturingInterfaceMockGenerator =
open Fantomas.FCS.Text.Range
[<RequireQualifiedAccess>]
type private KnownInheritance = | IDisposable
type private KnownInheritance =
| IDisposable
| IAsyncDisposable
/// Expects the input `args` list to have more than one element.
let private createTypeForArgs
@@ -209,6 +211,8 @@ module internal CapturingInterfaceMockGenerator =
| [] -> failwith "Unexpected empty identifier in inheritance declaration"
| [ "IDisposable" ]
| [ "System" ; "IDisposable" ] -> KnownInheritance.IDisposable
| [ "IAsyncDisposable" ]
| [ "System" ; "IAsyncDisposable" ] -> KnownInheritance.IAsyncDisposable
| _ -> failwithf $"Unrecognised inheritance identifier: %+A{name}"
| x -> failwithf $"Unrecognised type in inheritance: %+A{x}"
)
@@ -250,12 +254,26 @@ module internal CapturingInterfaceMockGenerator =
let emptyRecordFieldInstantiations =
let interfaceExtras =
if inherits.Contains KnownInheritance.IDisposable then
let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
let disposable =
if inherits.Contains KnownInheritance.IDisposable then
let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
[ SynLongIdent.createS "Dispose", unitFun ]
else
[]
[ SynLongIdent.createS "Dispose", unitFun ]
else
[]
let asyncDisposable =
if inherits.Contains KnownInheritance.IAsyncDisposable then
let valueTaskCtor =
SynExpr.createLongIdent [ "System" ; "Threading" ; "Tasks" ; "ValueTask" ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynExpr.paren
|> SynExpr.createLambda "()"
[ SynLongIdent.createS "DisposeAsync", valueTaskCtor ]
else
[]
disposable @ asyncDisposable
let originalMembers =
fields
@@ -275,23 +293,42 @@ module internal CapturingInterfaceMockGenerator =
[ Ident.create "Empty" ]
[ SynPat.unit ]
(SynExpr.createRecord None emptyRecordFieldInstantiations)
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every non-unit method throws.")
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every non-disposal method throws.")
|> SynBinding.withReturnAnnotation constructorReturnType
|> SynMemberDefn.staticMember
let recordFields =
let extras =
if inherits.Contains KnownInheritance.IDisposable then
{
Attrs = []
Ident = Some (Ident.create "Dispose")
Type = SynType.funFromDomain SynType.unit SynType.unit
}
|> SynField.make
|> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|> List.singleton
else
[]
let disposable =
if inherits.Contains KnownInheritance.IDisposable then
{
Attrs = []
Ident = Some (Ident.create "Dispose")
Type = SynType.funFromDomain SynType.unit SynType.unit
}
|> SynField.make
|> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|> List.singleton
else
[]
let asyncDisposable =
if inherits.Contains KnownInheritance.IAsyncDisposable then
{
Attrs = []
Ident = Some (Ident.create "DisposeAsync")
Type =
SynType.funFromDomain
SynType.unit
(SynType.createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "ValueTask" ])
}
|> SynField.make
|> SynField.withDocString (PreXmlDoc.create "Implementation of IAsyncDisposable.DisposeAsync")
|> List.singleton
else
[]
disposable @ asyncDisposable
let nonExtras =
fields |> Map.toSeq |> Seq.map (fun (_, (field, _)) -> field) |> Seq.toList
@@ -528,6 +565,23 @@ module internal CapturingInterfaceMockGenerator =
Some [ mem ],
range0
)
| KnownInheritance.IAsyncDisposable ->
let mem =
SynExpr.createLongIdent [ "this" ; "DisposeAsync" ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic [ Ident.create "this" ; Ident.create "DisposeAsync" ] [ SynPat.unit ]
|> SynBinding.withReturnAnnotation (
SynType.createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "ValueTask" ]
)
|> SynMemberDefn.memberImplementation
SynMemberDefn.Interface (
SynType.createLongIdent' [ "System" ; "IAsyncDisposable" ],
Some range0,
Some [ mem ],
range0
)
)
|> Seq.toList

View File

@@ -1209,6 +1209,10 @@ type CreateCatamorphismGenerator () =
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
@@ -1218,17 +1222,26 @@ type CreateCatamorphismGenerator () =
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)
)
|> List.collect (fun (ns, types) ->
types
|> List.choose (fun typeDef ->
match SynTypeDefn.getAttribute typeof<CreateCatamorphismAttribute>.Name typeDef with
| None ->
let name = SynTypeDefn.getName typeDef |> List.map _.idText |> String.concat "."
match typeWithAttr with
| Some taggedType ->
match Map.tryFind name targetedTypes with
| Some desired ->
desired
|> List.tryPick (fun generator ->
match generator with
| DesiredGenerator.CreateCatamorphism cataOutputName ->
Some (SynExpr.CreateConst cataOutputName, typeDef)
| _ -> None
)
| None -> None
| Some attr -> Some (attr.ArgExpr, typeDef)
)
|> List.map (fun (typeName, taggedType) ->
let unions, records, others =
(([], [], []), types)
||> List.fold (fun
@@ -1246,8 +1259,8 @@ type CreateCatamorphismGenerator () =
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
(ns, (typeName, taggedType), unions, records)
)
)
let modules =

View File

@@ -20,7 +20,9 @@ module internal InterfaceMockGenerator =
| Some id -> id
[<RequireQualifiedAccess>]
type private KnownInheritance = | IDisposable
type private KnownInheritance =
| IDisposable
| IAsyncDisposable
let createType
(spec : GenerateMockOutputSpec)
@@ -39,6 +41,8 @@ module internal InterfaceMockGenerator =
| [] -> failwith "Unexpected empty identifier in inheritance declaration"
| [ "IDisposable" ]
| [ "System" ; "IDisposable" ] -> KnownInheritance.IDisposable
| [ "IAsyncDisposable" ]
| [ "System" ; "IAsyncDisposable" ] -> KnownInheritance.IAsyncDisposable
| _ -> failwithf "Unrecognised inheritance identifier: %+A" name
| x -> failwithf "Unrecognised type in inheritance: %+A" x
)
@@ -69,12 +73,26 @@ module internal InterfaceMockGenerator =
let constructorFields =
let extras =
if inherits.Contains KnownInheritance.IDisposable then
let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
let disposable =
if inherits.Contains KnownInheritance.IDisposable then
let unitFun = SynExpr.createThunk (SynExpr.CreateConst ())
[ SynLongIdent.createS "Dispose", unitFun ]
else
[]
[ SynLongIdent.createS "Dispose", unitFun ]
else
[]
let asyncDisposable =
if inherits.Contains KnownInheritance.IAsyncDisposable then
let valueTaskCtor =
SynExpr.createLongIdent [ "System" ; "Threading" ; "Tasks" ; "ValueTask" ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynExpr.paren
|> SynExpr.createLambda "()"
[ SynLongIdent.createS "DisposeAsync", valueTaskCtor ]
else
[]
disposable @ asyncDisposable
let nonExtras =
fields
@@ -90,23 +108,42 @@ module internal InterfaceMockGenerator =
else
[ SynPat.unit ])
(SynExpr.createRecord None constructorFields)
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every method throws.")
|> SynBinding.withXmlDoc (PreXmlDoc.create "An implementation where every non-disposal method throws.")
|> SynBinding.withReturnAnnotation constructorReturnType
|> SynMemberDefn.staticMember
let fields =
let extras =
if inherits.Contains KnownInheritance.IDisposable then
{
Attrs = []
Ident = Some (Ident.create "Dispose")
Type = SynType.funFromDomain SynType.unit SynType.unit
}
|> SynField.make
|> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|> List.singleton
else
[]
let disposable =
if inherits.Contains KnownInheritance.IDisposable then
{
Attrs = []
Ident = Some (Ident.create "Dispose")
Type = SynType.funFromDomain SynType.unit SynType.unit
}
|> SynField.make
|> SynField.withDocString (PreXmlDoc.create "Implementation of IDisposable.Dispose")
|> List.singleton
else
[]
let asyncDisposable =
if inherits.Contains KnownInheritance.IAsyncDisposable then
{
Attrs = []
Ident = Some (Ident.create "DisposeAsync")
Type =
SynType.funFromDomain
SynType.unit
(SynType.createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "ValueTask" ])
}
|> SynField.make
|> SynField.withDocString (PreXmlDoc.create "Implementation of IAsyncDisposable.DisposeAsync")
|> List.singleton
else
[]
disposable @ asyncDisposable
extras @ fields
@@ -212,6 +249,23 @@ module internal InterfaceMockGenerator =
Some [ mem ],
range0
)
| KnownInheritance.IAsyncDisposable ->
let mem =
SynExpr.createLongIdent [ "this" ; "DisposeAsync" ]
|> SynExpr.applyTo (SynExpr.CreateConst ())
|> SynBinding.basic [ Ident.create "this" ; Ident.create "DisposeAsync" ] [ SynPat.unit ]
|> SynBinding.withReturnAnnotation (
SynType.createLongIdent' [ "System" ; "Threading" ; "Tasks" ; "ValueTask" ]
)
|> SynMemberDefn.memberImplementation
SynMemberDefn.Interface (
SynType.createLongIdent' [ "System" ; "IAsyncDisposable" ],
Some range0,
Some [ mem ],
range0
)
)
|> Seq.toList

View File

@@ -1,11 +1,14 @@
namespace WoofWare.Myriad.Plugins
open System
type internal DesiredGenerator =
| InterfaceMock of isInternal : bool option
| CapturingInterfaceMock of isInternal : bool option
| JsonParse of extensionMethod : bool option
| JsonSerialize of extensionMethod : bool option
| HttpClient of extensionMethod : bool option
| CreateCatamorphism of typeName : string
static member Parse (s : string) =
match s with
@@ -24,4 +27,10 @@ type internal DesiredGenerator =
| "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}"
| _ ->
let prefix = "CreateCatamorphism("
if s.StartsWith (prefix, StringComparison.Ordinal) && s.EndsWith ')' then
DesiredGenerator.CreateCatamorphism (s.Substring (prefix.Length, s.Length - prefix.Length - 1))
else
failwith $"Failed to parse as a generator specification: %s{s}"

View File

@@ -22,7 +22,7 @@
<ItemGroup>
<PackageReference Include="Myriad.Core" Version="0.8.3" />
<PackageReference Include="TypeEquality" Version="0.4.2" />
<PackageReference Include="WoofWare.Whippet.Fantomas" Version="0.6.4" />
<PackageReference Include="WoofWare.Whippet.Fantomas" Version="0.7.1" />
<!-- the lowest version allowed by Myriad.Core -->
<PackageReference Update="FSharp.Core" Version="6.0.1" PrivateAssets="all"/>
</ItemGroup>

View File

@@ -1,40 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "ConsumePlugin", "ConsumePlugin\ConsumePlugin.fsproj", "{0D174482-9CB2-448A-8BA8-846FAEC65579}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins", "WoofWare.Myriad.Plugins\WoofWare.Myriad.Plugins.fsproj", "{DB86C53B-4090-4791-884B-024C5759855F}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Test", "WoofWare.Myriad.Plugins.Test\WoofWare.Myriad.Plugins.Test.fsproj", "{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Attributes", "WoofWare.Myriad.Plugins.Attributes\WoofWare.Myriad.Plugins.Attributes.fsproj", "{17548737-9BAB-4B1E-B680-76D47C343AAC}"
EndProject
Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "WoofWare.Myriad.Plugins.Attributes.Test", "WoofWare.Myriad.Plugins.Attributes\Test\WoofWare.Myriad.Plugins.Attributes.Test.fsproj", "{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0D174482-9CB2-448A-8BA8-846FAEC65579}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{0D174482-9CB2-448A-8BA8-846FAEC65579}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0D174482-9CB2-448A-8BA8-846FAEC65579}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0D174482-9CB2-448A-8BA8-846FAEC65579}.Release|Any CPU.Build.0 = Release|Any CPU
{DB86C53B-4090-4791-884B-024C5759855F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DB86C53B-4090-4791-884B-024C5759855F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DB86C53B-4090-4791-884B-024C5759855F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DB86C53B-4090-4791-884B-024C5759855F}.Release|Any CPU.Build.0 = Release|Any CPU
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EBFFA5D3-7F74-4824-8795-B6194E6FE0CB}.Release|Any CPU.Build.0 = Release|Any CPU
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{17548737-9BAB-4B1E-B680-76D47C343AAC}.Release|Any CPU.Build.0 = Release|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{26DC0C94-85F2-45B4-8FA1-1B27201F7AFB}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

7
WoofWare.Myriad.slnx Normal file
View File

@@ -0,0 +1,7 @@
<Solution>
<Project Path="ConsumePlugin/ConsumePlugin.fsproj" />
<Project Path="WoofWare.Myriad.Plugins.Attributes/Test/WoofWare.Myriad.Plugins.Attributes.Test.fsproj" />
<Project Path="WoofWare.Myriad.Plugins.Attributes/WoofWare.Myriad.Plugins.Attributes.fsproj" />
<Project Path="WoofWare.Myriad.Plugins.Test/WoofWare.Myriad.Plugins.Test.fsproj" />
<Project Path="WoofWare.Myriad.Plugins/WoofWare.Myriad.Plugins.fsproj" />
</Solution>

6
flake.lock generated
View File

@@ -20,11 +20,11 @@
},
"nixpkgs": {
"locked": {
"lastModified": 1763191728,
"narHash": "sha256-esRhOS0APE6k40Hs/jjReXg+rx+J5LkWw7cuWFKlwYA=",
"lastModified": 1764947035,
"narHash": "sha256-EYHSjVM4Ox4lvCXUMiKKs2vETUSL5mx+J2FfutM7T9w=",
"owner": "NixOS",
"repo": "nixpkgs",
"rev": "1d4c88323ac36805d09657d13a5273aea1b34f0c",
"rev": "a672be65651c80d3f592a89b3945466584a22069",
"type": "github"
},
"original": {

View File

@@ -386,12 +386,17 @@
},
{
"pname": "WoofWare.Expect",
"version": "0.8.4",
"hash": "sha256-UI7f2nt4g4Gg1Ke/IChrA4fpVOYAChXpvR6zkKfkmzE="
"version": "0.8.5",
"hash": "sha256-rMlkk1osadQYwxmb0KAHqsB51hinTf7NzI0zyovpx04="
},
{
"pname": "WoofWare.NUnitTestRunner",
"version": "0.3.10",
"hash": "sha256-SvLLK9nZq/yMEcUoLx08Zw+0Z/lj02zebrBa6ATb2ug="
},
{
"pname": "WoofWare.Whippet.Fantomas",
"version": "0.6.4",
"hash": "sha256-ScZ7EEcxLvXyam2ZVqDK58QlK3RcePWghzRvtLLLdZI="
"version": "0.7.1",
"hash": "sha256-Y9H42NjZx238Y73FaoPraJ9JqCOf5+2MAFetjPNlNXY="
}
]