Skip to content

Commit b2c5551

Browse files
committed
[Group] Raise relevant mising command errors
1 parent 4c519da commit b2c5551

File tree

5 files changed

+151
-6
lines changed

5 files changed

+151
-6
lines changed

Commander.xcodeproj/project.pbxproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
279256B41BB3260D00E66B9E /* Group.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279256B31BB3260D00E66B9E /* Group.swift */; settings = {ASSET_TAGS = (); }; };
1919
279256B61BB32A8E00E66B9E /* ArgumentConvertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279256B51BB32A8E00E66B9E /* ArgumentConvertible.swift */; settings = {ASSET_TAGS = (); }; };
2020
279256B81BB32B3F00E66B9E /* ArgumentConvertibleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 279256B71BB32B3F00E66B9E /* ArgumentConvertibleTests.swift */; settings = {ASSET_TAGS = (); }; };
21+
27A119101BB337A30005318E /* Utilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27A1190F1BB337A30005318E /* Utilities.swift */; settings = {ASSET_TAGS = (); }; };
2122
27B1A0FF1BACD69500B1260F /* ArgumentParserTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 27B1A0FE1BACD69500B1260F /* ArgumentParserTests.swift */; settings = {ASSET_TAGS = (); }; };
2223
/* End PBXBuildFile section */
2324

@@ -50,6 +51,7 @@
5051
279256B51BB32A8E00E66B9E /* ArgumentConvertible.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgumentConvertible.swift; sourceTree = "<group>"; };
5152
279256B71BB32B3F00E66B9E /* ArgumentConvertibleTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgumentConvertibleTests.swift; sourceTree = "<group>"; };
5253
279256B91BB32E4200E66B9E /* Commander.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = Commander.playground; sourceTree = "<group>"; };
54+
27A1190F1BB337A30005318E /* Utilities.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Utilities.swift; sourceTree = "<group>"; };
5355
27B1A0FE1BACD69500B1260F /* ArgumentParserTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgumentParserTests.swift; sourceTree = "<group>"; };
5456
/* End PBXFileReference section */
5557

@@ -124,6 +126,7 @@
124126
279256AD1BB3237300E66B9E /* CommandTypeTests.swift */,
125127
279256AF1BB3237D00E66B9E /* CommandTests.swift */,
126128
279256B11BB325F400E66B9E /* GroupTests.swift */,
129+
27A1190F1BB337A30005318E /* Utilities.swift */,
127130
2768A2311BACC38C00F994EE /* Info.plist */,
128131
);
129132
path = CommanderTests;
@@ -249,6 +252,7 @@
249252
isa = PBXSourcesBuildPhase;
250253
buildActionMask = 2147483647;
251254
files = (
255+
27A119101BB337A30005318E /* Utilities.swift in Sources */,
252256
279256B81BB32B3F00E66B9E /* ArgumentConvertibleTests.swift in Sources */,
253257
279256B01BB3237D00E66B9E /* CommandTests.swift in Sources */,
254258
27B1A0FF1BACD69500B1260F /* ArgumentParserTests.swift in Sources */,

Commander/CommandType.swift

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,17 @@ extension CommandType {
1212
}
1313

1414
/// Run the command using the `Process.argument`, removing the executable name
15-
public func run() throws {
15+
@noreturn public func run() {
1616
let parser = ArgumentParser(arguments: Process.arguments)
1717
parser.shift() // Executable Name
18-
try run(parser)
18+
19+
do {
20+
try run(parser)
21+
} catch {
22+
fputs("\(error)\n", stderr)
23+
exit(1)
24+
}
25+
26+
exit(0)
1927
}
2028
}

Commander/Group.swift

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,30 @@
1+
public enum GroupError : ErrorType, CustomStringConvertible {
2+
/// No-subcommand was found with the given name
3+
case UnknownCommand(String)
4+
5+
/// No command was given
6+
/// :param: The current path to the command (i.e, all the group names)
7+
/// :param: The group raising the error
8+
case NoCommand(String?, Group)
9+
10+
public var description:String {
11+
switch self {
12+
case .UnknownCommand(let name):
13+
return "Unknown command: `\(name)`"
14+
case .NoCommand(let path, let group):
15+
let available = group.commands.keys.joinWithSeparator(", ")
16+
if let path = path {
17+
return "Usage: \(path) COMMAND\n\nCommands: \(available)"
18+
} else {
19+
return "Commands: \(available)"
20+
}
21+
}
22+
}
23+
}
24+
125
/// Represents a group of commands
226
public class Group : CommandType {
3-
private var commands = [String:CommandType]()
27+
var commands = [String:CommandType]()
428

529
/// Create a new group
630
public init() {}
@@ -14,8 +38,22 @@ public class Group : CommandType {
1438
public func run(parser:ArgumentParser) throws {
1539
if let name = parser.shift() {
1640
if let command = commands[name] {
17-
try command.run(parser)
41+
do {
42+
try command.run(parser)
43+
} catch GroupError.UnknownCommand(let childName) {
44+
throw GroupError.UnknownCommand("\(name) \(childName)")
45+
} catch GroupError.NoCommand(let path, let group) {
46+
if let path = path {
47+
throw GroupError.NoCommand("\(name) \(path)", group)
48+
}
49+
50+
throw GroupError.NoCommand(name, group)
51+
}
52+
} else {
53+
throw GroupError.UnknownCommand(name)
1854
}
55+
} else {
56+
throw GroupError.NoCommand(nil, self)
1957
}
2058
}
2159
}

CommanderTests/GroupTests.swift

Lines changed: 87 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,76 @@ class GroupTests : XCTestCase {
1313

1414
XCTAssertFalse(didRunHelpCommand)
1515

16-
try! group.run(["unknown"])
16+
assertRaises(try group.run(["unknown"]))
1717
XCTAssertFalse(didRunHelpCommand)
1818

1919
try! group.run(["help"])
2020
XCTAssertTrue(didRunHelpCommand)
2121
}
2222

23+
func testRunningCommandReRaisesCommandName() {
24+
let group = Group {
25+
$0.group("subgroup") {
26+
$0.command("command") {}
27+
}
28+
}
29+
30+
do {
31+
try group.run(["subgroup", "yo"])
32+
XCTFail("Didn't raise error")
33+
} catch GroupError.UnknownCommand(let command) {
34+
XCTAssertEqual(command, "subgroup yo")
35+
} catch {
36+
XCTFail("Unexpected error")
37+
}
38+
}
39+
40+
func testRunningCommandMissingCommandNameRaises() {
41+
let group = Group()
42+
43+
do {
44+
try group.run([])
45+
XCTFail("Didn't raise error")
46+
} catch GroupError.NoCommand(let path, let raisedGroup) {
47+
XCTAssertNil(path)
48+
XCTAssertTrue(raisedGroup === group)
49+
} catch {
50+
XCTFail("Unexpected error")
51+
}
52+
}
53+
54+
55+
func testRunningCommandWithSubGroupMissingCommandNameReraises() {
56+
let subgroup = Group()
57+
let group = Group { $0.addCommand("group", subgroup) }
58+
59+
do {
60+
try group.run(["group"])
61+
XCTFail("Didn't raise error")
62+
} catch GroupError.NoCommand(let path, let raisedGroup) {
63+
XCTAssertEqual(path, "group")
64+
XCTAssertTrue(raisedGroup === subgroup)
65+
} catch {
66+
XCTFail("Unexpected error")
67+
}
68+
}
69+
70+
func testRunningCommandWithSubSubGroupMissingCommandNameReraises() {
71+
let subsubgroup = Group()
72+
let subgroup = Group { $0.addCommand("g2", subsubgroup) }
73+
let group = Group { $0.addCommand("g1", subgroup) }
74+
75+
do {
76+
try group.run(["g1", "g2"])
77+
XCTFail("Didn't raise error")
78+
} catch GroupError.NoCommand(let path, let raisedGroup) {
79+
XCTAssertEqual(path, "g1 g2")
80+
XCTAssertTrue(raisedGroup === subsubgroup)
81+
} catch {
82+
XCTFail("Unexpected error")
83+
}
84+
}
85+
2386
// MARK: Extension
2487

2588
func testClosureConvinienceInitialiser() {
@@ -31,7 +94,7 @@ class GroupTests : XCTestCase {
3194
})
3295
}
3396

34-
try! group.run(["unknown"])
97+
assertRaises(try group.run(["unknown"]))
3598
XCTAssertFalse(didRunHelpCommand)
3699

37100
try! group.run(["help"])
@@ -76,3 +139,25 @@ class GroupTests : XCTestCase {
76139
XCTAssertEqual(givenName, "kyle")
77140
}
78141
}
142+
143+
class GroupErrorTests : XCTestCase {
144+
let group = Group {
145+
$0.command("create") {}
146+
$0.command("lint") {}
147+
}
148+
149+
func testUnknownCommandDescription() {
150+
let error = GroupError.UnknownCommand("pod spec create")
151+
XCTAssertEqual(error.description, "Unknown command: `pod spec create`")
152+
}
153+
154+
func testNoCommandDescription() {
155+
let error = GroupError.NoCommand("pod lib", group)
156+
XCTAssertEqual(error.description, "Usage: pod lib COMMAND\n\nCommands: lint, create")
157+
}
158+
159+
func testNoCommandWithoutPathDescription() {
160+
let error = GroupError.NoCommand(nil, group)
161+
XCTAssertEqual(error.description, "Commands: lint, create")
162+
}
163+
}

CommanderTests/Utilities.swift

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
import XCTest
2+
3+
func assertRaises(@autoclosure closure:() throws -> ()) {
4+
do {
5+
try closure()
6+
XCTFail("Expected error")
7+
} catch {
8+
// Success
9+
}
10+
}

0 commit comments

Comments
 (0)