Skip to content

Commit 2daf9a1

Browse files
Smaug123claude
andcommitted
ArgParser: support positional args inside union cases
Removes the last restriction of the positional-args-with-unions plan: a [<PositionalArgs>] field may now live inside a union case's payload record, so each alternative can convert the positional stream at its own field's type. Two sinks in mutually exclusive cases may share their addressing forms (a keyed --rest token means the same thing whichever case wins); a form still may not collide with any named argument. Generation rejects only what is genuinely unsound: two sinks in one product (argv holds a single positional stream), Collect-mode sinks anywhere a union is in play (a mistyped case-selecting argument would be silently collected), and unprovable flag-like settings. Selection remains purely structural, before any conversion: named arguments select cases; a keyed positional form unique to one alternative selects it structurally; bare tokens never select (except that a sink present in only one case makes bare tokens pin that case); and parseability at some case's element type never influences the choice. Help renders each case's positional args inside its group. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent e4b89d7 commit 2daf9a1

8 files changed

Lines changed: 834 additions & 50 deletions

File tree

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
Notable changes are recorded here.
22

3+
# Unreleased
4+
5+
The `ArgParserGenerator` now supports positional args together with discriminated-union args, as long as the positional args reject unrecognised flag-like tokens (the default; `[<PositionalArgs true>]` remains banned in combination with a union).
6+
A `[<PositionalArgs>]` field may sit beside the union-typed field (the positional stream is shared by every alternative), or inside the cases' payload records (each alternative converts the stream at its own field's type).
7+
Case selection is purely structural and happens before any value conversion: named arguments (and, where unique to one alternative, the keyed `--rest=value` form of a positional field) select the case, bare positional tokens never do, and whether a token happens to parse at some case's element type never influences which case wins.
8+
39
# WoofWare.Myriad.Plugins 10.2.3
410

511
The `ArgParserGenerator` now permits a `[<PositionalArgs>]` field at the top level alongside (though not within) a discriminated-union arg, as long as the positional sink rejects unrecognised flag-like tokens (the default; `[<PositionalArgs true>]` remains banned beside a union, because that would make it very confusing when you typo a DU-case-selecting flag).

ConsumePlugin/DuArgs.fs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,3 +105,50 @@ type CommandAndPositionals =
105105
[<PositionalArgs false>]
106106
Paths : string list
107107
}
108+
109+
type FooModeArgs =
110+
{
111+
Foo : int
112+
113+
[<PositionalArgs>]
114+
Rest : int list
115+
}
116+
117+
type BarModeArgs =
118+
{
119+
Bar : int
120+
121+
[<PositionalArgs>]
122+
Rest : string list
123+
}
124+
125+
/// The motivating example of per-case positional args: both cases collect the positional
126+
/// stream (addressable under the same default `--rest` form, which is fine because the cases
127+
/// are mutually exclusive), but convert it at different types. Selection happens before any
128+
/// conversion, so whether a token parses as int never influences which case wins.
129+
[<ArgParser>]
130+
type FooBarMode =
131+
| FooMode of FooModeArgs
132+
| BarMode of BarModeArgs
133+
134+
type PullArgs =
135+
{
136+
[<ArgumentLongForm "source">]
137+
From : string
138+
139+
[<PositionalArgs>]
140+
Refs : string list
141+
}
142+
143+
type StatusArgs =
144+
{
145+
Verbose : bool option
146+
}
147+
148+
/// Positional args in only one case: the other (all-optional) case is the empty command
149+
/// line's fallback, and a bare token structurally selects Pull because only Pull can consume
150+
/// it.
151+
[<ArgParser>]
152+
type GitLike =
153+
| Pull of PullArgs
154+
| Status of StatusArgs

0 commit comments

Comments
 (0)