Skip to content

Commit 8fd0f61

Browse files
Smaug123claude
andauthored
ArgParser: read ArgumentHelpText from nested records and unions (#602)
The type-level help lookup ran only against the [<ArgParser>]-tagged root, so an [<ArgumentHelpText>] on a nested argument record or union was silently ignored, despite the attribute documenting itself as applying to a record type generally. RecordType.Attributes and UnionType.Attributes were already to hand at the nesting site; nothing was reading them. The nested type's help now heads the group its arguments form, wherever that type is embedded. A field's own [<ArgumentHelpText>] overrides it: the field is the more specific placement. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent 96986ed commit 8fd0f61

12 files changed

Lines changed: 1247 additions & 68 deletions

File tree

CHANGELOG.md

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

3+
# WoofWare.Myriad.Plugins 10.7.2
4+
5+
`ArgParserGenerator` now reads `[<ArgumentHelpText>]` from a nested argument record or union of alternative argument sets, and not only from the `[<ArgParser>]`-tagged root, and from a discriminated union's case (and that case's payload record).
6+
The nested type's help heads the group of arguments that type contributes, wherever it is embedded, and a case's help heads its group the same way.
7+
8+
An `[<ArgumentHelpText>]` on the more specific placement overrides the more general one: a field's overrides its nested type's, and a case's overrides its payload record's.
9+
One type, or one payload record, may be embedded or reused at several sites for different purposes, so the more specific placement is the one which can say what a particular occurrence is for.
10+
311
# WoofWare.Myriad.Plugins 10.7.1
412

513
`ArgParserGenerator` help text now groups the arguments contributed by a field whose type is another argument record, or a union of alternative argument sets, under a header line naming that field.

ConsumePlugin/Args.fs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,36 @@ type ParentRecordWithGroupHelp =
158158
AndAnother : bool
159159
}
160160

161+
/// A nested type may describe itself, for the benefit of every site which embeds it.
162+
[<ArgumentHelpText "How to talk to the database">]
163+
type DescribedChild =
164+
{
165+
Host : string
166+
Port : int
167+
}
168+
169+
/// `Primary` takes the type's own description; `Secondary` overrides it, because the field is the
170+
/// more specific placement and one type may be embedded for different purposes.
171+
[<ArgParser true>]
172+
type ParentRecordWithTypeHelp =
173+
{
174+
[<ArgumentPrefix "primary">]
175+
Primary : DescribedChild
176+
[<ArgumentPrefix "secondary">]
177+
[<ArgumentHelpText "Where to fail over to">]
178+
Secondary : DescribedChild
179+
}
180+
181+
/// Help text may contain characters which need escaping to survive being reproduced in the
182+
/// generated file: FCS decodes the literal before Myriad ever sees it, so a backslash, a quote,
183+
/// and a control character must all be re-escaped rather than passed through as the decoded text.
184+
[<ArgParser true>]
185+
type ParentRecordWithEscapedHelp =
186+
{
187+
[<ArgumentHelpText "Path is C:\\temp, quote is \" and tab is \t.">]
188+
Child : ChildRecord
189+
}
190+
161191
[<ArgParser true>]
162192
type ChoicePositionals =
163193
{

ConsumePlugin/DuArgs.fs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,55 @@ type WithModeHelpArgs =
7676
Mode : Mode
7777
}
7878

79+
/// A union of alternative argument sets may describe itself, for the benefit of every field which
80+
/// embeds it.
81+
[<ArgumentHelpText "Which transport to use">]
82+
type Transport =
83+
| Tcp of TcpArgs
84+
| Unix of UnixArgs
85+
86+
and TcpArgs =
87+
{
88+
TcpPort : int
89+
}
90+
91+
and UnixArgs =
92+
{
93+
SocketPath : string
94+
}
95+
96+
/// `Fallback` takes the union's own description; `Preferred` overrides it from the field.
97+
[<ArgParser>]
98+
type WithTransportArgs =
99+
{
100+
[<ArgumentPrefix "preferred">]
101+
[<ArgumentHelpText "Try this one first">]
102+
Preferred : Transport
103+
[<ArgumentPrefix "fallback">]
104+
Fallback : Transport
105+
}
106+
107+
[<ArgumentHelpText "Fetch a URL">]
108+
type FetchWithHelpArgs =
109+
{
110+
Url : string
111+
}
112+
113+
[<ArgumentHelpText "Push args, not that you'd know it from the case header">]
114+
type PushWithHelpArgs =
115+
{
116+
Remote : string
117+
}
118+
119+
/// A case's payload record may describe itself, for the benefit of that case; a case is not
120+
/// reached through a field, so there is no field-level attribute to check first, but the case
121+
/// itself is a more specific placement than its payload record and so overrides it, exactly as
122+
/// a field overrides a nested record's own description.
123+
[<ArgParser>]
124+
type CommandWithHelp =
125+
| FetchCase of FetchWithHelpArgs
126+
| [<ArgumentHelpText "Push to a remote">] PushCase of PushWithHelpArgs
127+
79128
/// A union beside a positional sink (default, i.e. Reject-mode): named arguments select the
80129
/// union case, and every bare token is routed to the sink whichever case wins. An unrecognised
81130
/// `--key`-shaped token remains fatal. The sink converts, so selection must not depend on the

0 commit comments

Comments
 (0)