Skip to content

Commit cfac15f

Browse files
authored
Add ParsableArguments/Command.usageString (#634)
Adds a new API to ParsableArguments and ParsableCommand for getting the usage string. This allows clients to use argument-parser in a more piecemeal way to construct their own error screens.
1 parent 2b96293 commit cfac15f

File tree

3 files changed

+149
-22
lines changed

3 files changed

+149
-22
lines changed

Sources/ArgumentParser/Parsable Types/ParsableArguments.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,18 @@ extension ParsableArguments {
231231
exit(withError: error)
232232
}
233233
}
234+
235+
/// Returns the usage text for this type.
236+
///
237+
/// - Parameters:
238+
/// - includeHidden: Include hidden help information in the generated
239+
/// message.
240+
/// - Returns: The usage text for this type.
241+
public static func usageString(
242+
includeHidden: Bool = false
243+
) -> String {
244+
HelpGenerator(self, visibility: includeHidden ? .hidden : .default).usage
245+
}
234246
}
235247

236248
/// Unboxes the given value if it is a `nil` value.

Sources/ArgumentParser/Parsable Types/ParsableCommand.swift

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,22 @@ extension ParsableCommand {
113113
.rendered(screenWidth: columns)
114114
}
115115

116+
/// Returns the usage text for the given subcommand of this command.
117+
///
118+
/// - Parameters:
119+
/// - includeHidden: Include hidden help information in the generated
120+
/// message.
121+
/// - Returns: The usage text for this type.
122+
public static func usageString(
123+
for subcommand: ParsableCommand.Type,
124+
includeHidden: Bool = false
125+
) -> String {
126+
HelpGenerator(
127+
commandStack: CommandParser(self).commandStack(for: subcommand),
128+
visibility: includeHidden ? .hidden : .default)
129+
.usage
130+
}
131+
116132
/// Executes this command, or one of its subcommands, with the given
117133
/// arguments.
118134
///

Tests/ArgumentParserUnitTests/HelpGenerationTests.swift

Lines changed: 121 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -680,7 +680,7 @@ extension HelpGenerationTests {
680680
static let configuration = CommandConfiguration(
681681
commandName: "parserBug",
682682
subcommands: [Sub.self])
683-
683+
684684
struct CommonOptions: ParsableCommand {
685685
@Flag(help: "example flag")
686686
var example: Bool = false
@@ -689,12 +689,12 @@ extension HelpGenerationTests {
689689
struct Sub: ParsableCommand {
690690
@OptionGroup()
691691
var commonOptions: CommonOptions
692-
692+
693693
@Argument(help: "Non-mandatory argument")
694694
var argument: String?
695695
}
696696
}
697-
697+
698698
func testIssue278() {
699699
AssertHelp(.default, for: ParserBug.Sub.self, root: ParserBug.self, equals: """
700700
USAGE: parserBug sub [--example] [<argument>]
@@ -708,42 +708,87 @@ extension HelpGenerationTests {
708708
709709
""")
710710
}
711+
}
712+
713+
extension HelpGenerationTests {
714+
struct NonCustomUsage: ParsableCommand {
715+
struct ExampleSubcommand: ParsableCommand {
716+
static let configuration = CommandConfiguration()
717+
@Argument var output: String
718+
}
719+
720+
static let configuration = CommandConfiguration(
721+
subcommands: [ExampleSubcommand.self])
722+
723+
@Argument var file: String
724+
@Flag var verboseMode = false
725+
}
711726

712727
struct CustomUsageShort: ParsableCommand {
713-
static var configuration: CommandConfiguration {
714-
CommandConfiguration(usage: """
728+
static let configuration = CommandConfiguration(
729+
usage: """
715730
example [--verbose] <file-name>
716731
""")
717-
}
718-
732+
719733
@Argument var file: String
720734
@Flag var verboseMode = false
721735
}
722736

723737
struct CustomUsageLong: ParsableCommand {
724-
static var configuration: CommandConfiguration {
725-
CommandConfiguration(usage: """
738+
static let configuration = CommandConfiguration(
739+
usage: """
726740
example <file-name>
727741
example --verbose <file-name>
728742
example --help
729743
""")
730-
}
731-
744+
732745
@Argument var file: String
733746
@Flag var verboseMode = false
734747
}
735748

736749
struct CustomUsageHidden: ParsableCommand {
737-
static var configuration: CommandConfiguration {
738-
CommandConfiguration(usage: "")
739-
}
750+
static let configuration = CommandConfiguration(usage: "")
740751

741752
@Argument var file: String
742753
@Flag var verboseMode = false
743754
}
744755

745-
func testCustomUsageHelp() {
746-
XCTAssertEqual(CustomUsageShort.helpMessage(columns: 80), """
756+
func test_usageCustomization_helpMessage() {
757+
AssertEqualStrings(
758+
actual: NonCustomUsage.helpMessage(columns: 80),
759+
expected: """
760+
USAGE: non-custom-usage <file> [--verbose-mode] <subcommand>
761+
762+
ARGUMENTS:
763+
<file>
764+
765+
OPTIONS:
766+
--verbose-mode
767+
-h, --help Show help information.
768+
769+
SUBCOMMANDS:
770+
example-subcommand
771+
772+
See 'non-custom-usage help <subcommand>' for detailed help.
773+
""")
774+
775+
AssertEqualStrings(
776+
actual: NonCustomUsage.helpMessage(
777+
for: NonCustomUsage.ExampleSubcommand.self, columns: 80),
778+
expected: """
779+
USAGE: non-custom-usage example-subcommand <output>
780+
781+
ARGUMENTS:
782+
<output>
783+
784+
OPTIONS:
785+
-h, --help Show help information.
786+
787+
""")
788+
789+
AssertEqualStrings(
790+
actual: CustomUsageShort.helpMessage(columns: 80),
791+
expected: """
747792
USAGE: example [--verbose] <file-name>
748793
749794
ARGUMENTS:
@@ -755,7 +800,9 @@ extension HelpGenerationTests {
755800
756801
""")
757802

758-
XCTAssertEqual(CustomUsageLong.helpMessage(columns: 80), """
803+
AssertEqualStrings(
804+
actual: CustomUsageLong.helpMessage(columns: 80),
805+
expected: """
759806
USAGE: example <file-name>
760807
example --verbose <file-name>
761808
example --help
@@ -769,7 +816,9 @@ extension HelpGenerationTests {
769816
770817
""")
771818

772-
XCTAssertEqual(CustomUsageHidden.helpMessage(columns: 80), """
819+
AssertEqualStrings(
820+
actual: CustomUsageHidden.helpMessage(columns: 80),
821+
expected: """
773822
ARGUMENTS:
774823
<file>
775824
@@ -780,24 +829,74 @@ extension HelpGenerationTests {
780829
""")
781830
}
782831

783-
func testCustomUsageError() {
784-
XCTAssertEqual(CustomUsageShort.fullMessage(for: ValidationError("Test")), """
832+
func test_usageCustomization_fullMessage() {
833+
AssertEqualStrings(
834+
actual: NonCustomUsage.fullMessage(for: ValidationError("Test")),
835+
expected: """
836+
Error: Test
837+
Usage: non-custom-usage <file> [--verbose-mode] <subcommand>
838+
See 'non-custom-usage --help' for more information.
839+
""")
840+
841+
AssertEqualStrings(
842+
actual: CustomUsageShort.fullMessage(for: ValidationError("Test")),
843+
expected: """
785844
Error: Test
786845
Usage: example [--verbose] <file-name>
787846
See 'custom-usage-short --help' for more information.
788847
""")
789-
XCTAssertEqual(CustomUsageLong.fullMessage(for: ValidationError("Test")), """
848+
849+
AssertEqualStrings(
850+
actual: CustomUsageLong.fullMessage(for: ValidationError("Test")),
851+
expected: """
790852
Error: Test
791853
Usage: example <file-name>
792854
example --verbose <file-name>
793855
example --help
794856
See 'custom-usage-long --help' for more information.
795857
""")
796-
XCTAssertEqual(CustomUsageHidden.fullMessage(for: ValidationError("Test")), """
858+
859+
AssertEqualStrings(
860+
actual: CustomUsageHidden.fullMessage(for: ValidationError("Test")),
861+
expected: """
797862
Error: Test
798863
See 'custom-usage-hidden --help' for more information.
799864
""")
800865
}
866+
867+
func test_usageCustomization_usageString() {
868+
AssertEqualStrings(
869+
actual: NonCustomUsage.usageString(),
870+
expected: """
871+
non-custom-usage <file> [--verbose-mode] <subcommand>
872+
""")
873+
874+
AssertEqualStrings(
875+
actual: NonCustomUsage.usageString(
876+
for: NonCustomUsage.ExampleSubcommand.self),
877+
expected: """
878+
non-custom-usage example-subcommand <output>
879+
""")
880+
881+
AssertEqualStrings(
882+
actual: CustomUsageShort.usageString(),
883+
expected: """
884+
example [--verbose] <file-name>
885+
""")
886+
887+
AssertEqualStrings(
888+
actual: CustomUsageLong.usageString(),
889+
expected: """
890+
example <file-name>
891+
example --verbose <file-name>
892+
example --help
893+
""")
894+
895+
AssertEqualStrings(
896+
actual: CustomUsageHidden.usageString(),
897+
expected: """
898+
""")
899+
}
801900
}
802901

803902
extension HelpGenerationTests {

0 commit comments

Comments
 (0)