Skip to content

Commit dcd0ae5

Browse files
committed
Introduce tool info version 1
Fork the tool info version 0 to a new stable version 1. Introduce a new '--help-dump-tool-info-v1' flag that functions similarly to '--experimental-dump-help'. Create a new ToolInfoV1 to keep the new types for the new schema. Update the tool info serialization version to 1. Add a JSON schema for the version 1 tool info format.
1 parent 04695ec commit dcd0ae5

File tree

20 files changed

+848
-59
lines changed

20 files changed

+848
-59
lines changed

Schemas/tool-info-v1.json

Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
{
2+
"$schema": "http://json-schema.org/draft-07/schema#",
3+
"title": "ToolInfo V1 Schema",
4+
"description": "JSON schema for Swift Argument Parser ToolInfo V1 structure",
5+
"type": "object",
6+
"required": ["serializationVersion", "command"],
7+
"properties": {
8+
"serializationVersion": {
9+
"type": "integer",
10+
"const": 1,
11+
"description": "A sentinel value indicating the version of the ToolInfo struct used to generate the serialized form"
12+
},
13+
"command": {
14+
"$ref": "#/definitions/CommandInfo"
15+
}
16+
},
17+
"definitions": {
18+
"CommandInfo": {
19+
"type": "object",
20+
"required": ["commandName"],
21+
"properties": {
22+
"superCommands": {
23+
"type": "array",
24+
"items": {
25+
"type": "string"
26+
},
27+
"description": "Super commands and tools"
28+
},
29+
"shouldDisplay": {
30+
"type": "boolean",
31+
"default": true,
32+
"description": "Command should appear in help displays"
33+
},
34+
"commandName": {
35+
"type": "string",
36+
"description": "Name used to invoke the command"
37+
},
38+
"abstract": {
39+
"type": "string",
40+
"description": "Short description of the command's functionality"
41+
},
42+
"discussion": {
43+
"type": "string",
44+
"description": "Extended description of the command's functionality"
45+
},
46+
"defaultSubcommand": {
47+
"type": "string",
48+
"description": "Optional name of the subcommand invoked when the command is invoked with no arguments"
49+
},
50+
"subcommands": {
51+
"type": "array",
52+
"items": {
53+
"$ref": "#/definitions/CommandInfo"
54+
},
55+
"description": "List of nested commands"
56+
},
57+
"arguments": {
58+
"type": "array",
59+
"items": {
60+
"$ref": "#/definitions/ArgumentInfo"
61+
},
62+
"description": "List of supported arguments"
63+
}
64+
}
65+
},
66+
"ArgumentInfo": {
67+
"type": "object",
68+
"required": ["kind", "shouldDisplay", "isOptional", "isRepeating", "parsingStrategy"],
69+
"properties": {
70+
"kind": {
71+
"$ref": "#/definitions/ArgumentKind"
72+
},
73+
"shouldDisplay": {
74+
"type": "boolean",
75+
"description": "Argument should appear in help displays"
76+
},
77+
"sectionTitle": {
78+
"type": "string",
79+
"description": "Custom name of argument's section"
80+
},
81+
"isOptional": {
82+
"type": "boolean",
83+
"description": "Argument can be omitted"
84+
},
85+
"isRepeating": {
86+
"type": "boolean",
87+
"description": "Argument can be specified multiple times"
88+
},
89+
"parsingStrategy": {
90+
"$ref": "#/definitions/ParsingStrategy"
91+
},
92+
"names": {
93+
"type": "array",
94+
"items": {
95+
"$ref": "#/definitions/NameInfo"
96+
},
97+
"description": "All names of the argument"
98+
},
99+
"preferredName": {
100+
"$ref": "#/definitions/NameInfo",
101+
"description": "The best name to use when referring to the argument in help displays"
102+
},
103+
"valueName": {
104+
"type": "string",
105+
"description": "Name of argument's value"
106+
},
107+
"defaultValue": {
108+
"type": "string",
109+
"description": "Default value of the argument is none is specified on the command line"
110+
},
111+
"allValues": {
112+
"type": "array",
113+
"items": {
114+
"type": "string"
115+
},
116+
"description": "List of all valid values"
117+
},
118+
"allValueDescriptions": {
119+
"type": "object",
120+
"additionalProperties": {
121+
"type": "string"
122+
},
123+
"description": "Mapping of valid values to descriptions of the value"
124+
},
125+
"completionKind": {
126+
"$ref": "#/definitions/CompletionKind",
127+
"description": "The type of completion to use for an argument or an option value"
128+
},
129+
"abstract": {
130+
"type": "string",
131+
"description": "Short description of the argument's functionality"
132+
},
133+
"discussion": {
134+
"type": "string",
135+
"description": "Extended description of the argument's functionality"
136+
}
137+
}
138+
},
139+
"NameInfo": {
140+
"type": "object",
141+
"required": ["kind", "name"],
142+
"properties": {
143+
"kind": {
144+
"$ref": "#/definitions/NameInfoKind"
145+
},
146+
"name": {
147+
"type": "string",
148+
"description": "Single or multi-character name of the argument"
149+
}
150+
}
151+
},
152+
"NameInfoKind": {
153+
"type": "string",
154+
"enum": ["long", "short", "longWithSingleDash"],
155+
"description": "Kind of prefix of an argument's name"
156+
},
157+
"ArgumentKind": {
158+
"type": "string",
159+
"enum": ["positional", "option", "flag"],
160+
"description": "Kind of argument"
161+
},
162+
"ParsingStrategy": {
163+
"type": "string",
164+
"enum": ["default", "scanningForValue", "unconditional", "upToNextOption", "allRemainingInput", "postTerminator", "allUnrecognized"],
165+
"description": "Parsing strategy of the ArgumentInfo"
166+
},
167+
"CompletionKind": {
168+
"oneOf": [
169+
{
170+
"type": "object",
171+
"required": ["list"],
172+
"properties": {
173+
"list": {
174+
"type": "object",
175+
"required": ["values"],
176+
"properties": {
177+
"values": {
178+
"type": "array",
179+
"items": {
180+
"type": "string"
181+
}
182+
}
183+
}
184+
}
185+
},
186+
"description": "Use the specified list of completion strings"
187+
},
188+
{
189+
"type": "object",
190+
"required": ["file"],
191+
"properties": {
192+
"file": {
193+
"type": "object",
194+
"required": ["extensions"],
195+
"properties": {
196+
"extensions": {
197+
"type": "array",
198+
"items": {
199+
"type": "string"
200+
}
201+
}
202+
}
203+
}
204+
},
205+
"description": "Complete file names with the specified extensions"
206+
},
207+
{
208+
"type": "object",
209+
"required": ["directory"],
210+
"properties": {
211+
"directory": {
212+
"type": "object"
213+
}
214+
},
215+
"description": "Complete directory names that match the specified pattern"
216+
},
217+
{
218+
"type": "object",
219+
"required": ["shellCommand"],
220+
"properties": {
221+
"shellCommand": {
222+
"type": "object",
223+
"required": ["command"],
224+
"properties": {
225+
"command": {
226+
"type": "string"
227+
}
228+
}
229+
}
230+
},
231+
"description": "Call the given shell command to generate completions"
232+
},
233+
{
234+
"type": "object",
235+
"required": ["custom"],
236+
"properties": {
237+
"custom": {
238+
"type": "object"
239+
}
240+
},
241+
"description": "Generate completions using the given three-parameter closure"
242+
},
243+
{
244+
"type": "object",
245+
"required": ["customAsync"],
246+
"properties": {
247+
"customAsync": {
248+
"type": "object"
249+
}
250+
},
251+
"description": "Generate completions using the given async three-parameter closure"
252+
},
253+
{
254+
"type": "object",
255+
"required": ["customDeprecated"],
256+
"properties": {
257+
"customDeprecated": {
258+
"type": "object"
259+
}
260+
},
261+
"description": "Generate completions using the given one-parameter closure (deprecated)"
262+
}
263+
]
264+
}
265+
}
266+
}

Sources/ArgumentParser/Parsable Properties/Errors.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public struct CleanExit: Error, CustomStringConvertible {
7171
internal enum Representation {
7272
case helpRequest(ParsableCommand.Type? = nil)
7373
case message(String)
74-
case dumpRequest(ParsableCommand.Type? = nil)
74+
case dumpRequest(ParsableCommand.Type? = nil, DumpHelpVersion)
7575
}
7676

7777
internal var base: Representation
@@ -113,7 +113,7 @@ public struct CleanExit: Error, CustomStringConvertible {
113113
switch self.base {
114114
case .helpRequest: return "--help"
115115
case .message(let message): return message
116-
case .dumpRequest: return "--experimental-dump-help"
116+
case .dumpRequest(_, let version): return "--\(version.flagName)"
117117
}
118118
}
119119
}

Sources/ArgumentParser/Parsable Types/ParsableArguments.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,8 @@ extension ParsableArguments {
163163
}
164164

165165
/// Returns the JSON representation of this type.
166-
public static func _dumpHelp() -> String {
167-
DumpHelpGenerator(self).rendered()
166+
public static func _dumpHelp(version: DumpHelpVersion) -> String {
167+
version.render(self)
168168
}
169169

170170
/// Returns the exit code for the given error.

Sources/ArgumentParser/Parsing/CommandParser.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,10 +129,12 @@ extension CommandParser {
129129
throw HelpRequested(visibility: .hidden)
130130
}
131131

132-
// Look for dump-help flag
133-
guard !split.contains(Name.long("experimental-dump-help")) else {
134-
throw CommandError(
135-
commandStack: commandStack, parserError: .dumpHelpRequested)
132+
// Look for dump-help flags
133+
for version in DumpHelpVersion.allCases {
134+
guard !split.contains(Name.long(version.flagName)) else {
135+
throw CommandError(
136+
commandStack: commandStack, parserError: .dumpHelpRequested(version))
137+
}
136138
}
137139

138140
// Look for a version flag if any commands in the stack define a version

Sources/ArgumentParser/Parsing/ParserError.swift

Lines changed: 37 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,47 @@
99
//
1010
//===----------------------------------------------------------------------===//
1111

12+
/// Represents supported OpenCLI schema versions.
13+
public enum DumpHelpVersion: String, CaseIterable, Sendable {
14+
// swift-format-ignore: AlwaysUseLowerCamelCase
15+
case v0 = "v0"
16+
17+
// swift-format-ignore: AlwaysUseLowerCamelCase
18+
case v1 = "v1"
19+
20+
public var flagName: String {
21+
return switch self {
22+
case .v0:
23+
"experimental-dump-help"
24+
default:
25+
"help-dump-tool-info-\(self.rawValue)"
26+
}
27+
}
28+
29+
public func render(commandStack: [ParsableCommand.Type]) -> String {
30+
return switch self {
31+
case .v0:
32+
DumpHelpGeneratorV0(commandStack: commandStack).rendered()
33+
case .v1:
34+
DumpHelpGeneratorV1(commandStack: commandStack).rendered()
35+
}
36+
}
37+
38+
public func render(_ type: any ParsableArguments.Type) -> String {
39+
return switch self {
40+
case .v0:
41+
DumpHelpGeneratorV0(type).rendered()
42+
case .v1:
43+
DumpHelpGeneratorV1(type).rendered()
44+
}
45+
}
46+
}
47+
1248
/// Gets thrown while parsing and will be handled by the error output generation.
1349
enum ParserError: Error {
1450
case helpRequested(visibility: ArgumentVisibility)
1551
case versionRequested
16-
case dumpHelpRequested
52+
case dumpHelpRequested(DumpHelpVersion)
1753

1854
case completionScriptRequested(shell: String?)
1955
case completionScriptCustomResponse(String)

Sources/ArgumentParser/Usage/DumpHelpGenerator.swift renamed to Sources/ArgumentParser/Usage/DumpHelpGeneratorV0.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ internal import ArgumentParserToolInfo
1515
import ArgumentParserToolInfo
1616
#endif
1717

18-
internal struct DumpHelpGenerator {
18+
internal struct DumpHelpGeneratorV0 {
1919
private var toolInfo: ToolInfoV0
2020

2121
init(_ type: ParsableArguments.Type) {

0 commit comments

Comments
 (0)