Skip to content

Commit 24da216

Browse files
committed
Add argument option parsing
1 parent 1f184ae commit 24da216

File tree

2 files changed

+122
-3
lines changed

2 files changed

+122
-3
lines changed

Commander/ArgumentParser.swift

Lines changed: 79 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
private enum Arg {
1+
private enum Arg : CustomStringConvertible {
22
/// A positional argument
33
case Argument(String)
44

@@ -7,6 +7,37 @@ private enum Arg {
77

88
/// A flag
99
case Flag(Set<Character>)
10+
11+
var description:String {
12+
switch self {
13+
case .Argument(let value):
14+
return value
15+
case .Option(let key):
16+
return "--\(key)"
17+
case .Flag(let flags):
18+
return "-\(String(flags))"
19+
}
20+
}
21+
22+
var type:String {
23+
switch self {
24+
case .Argument:
25+
return "argument"
26+
case .Option:
27+
return "option"
28+
case .Flag:
29+
return "flag"
30+
}
31+
}
32+
}
33+
34+
35+
public struct ArgumentParserError : ErrorType, CustomStringConvertible {
36+
public let description:String
37+
38+
init(description:String) {
39+
self.description = description
40+
}
1041
}
1142

1243

@@ -51,6 +82,53 @@ public final class ArgumentParser : ArgumentConvertible {
5182
return nil
5283
}
5384

85+
/// Returns the value for an option (--name Kyle, --name=Kyle)
86+
public func shiftValueForOption(name:String) throws -> String? {
87+
return try shiftValuesForOption(name)?.first
88+
}
89+
90+
/// Returns the value for an option (--name Kyle, --name=Kyle)
91+
public func shiftValuesForOption(name:String, count:Int = 1) throws -> [String]? {
92+
var index = 0
93+
var hasOption = false
94+
95+
for argument in arguments {
96+
switch argument {
97+
case .Option(let option):
98+
if option == name {
99+
hasOption = true
100+
break
101+
}
102+
fallthrough
103+
default:
104+
++index
105+
}
106+
107+
if hasOption {
108+
break
109+
}
110+
}
111+
112+
if hasOption {
113+
arguments.removeAtIndex(index) // Pop option
114+
return try (0..<count).map { i in
115+
if arguments.count > index {
116+
let argument = arguments.removeAtIndex(index)
117+
switch argument {
118+
case .Argument(let value):
119+
return value
120+
default:
121+
throw ArgumentParserError(description: "Unexpected \(argument.type) `\(argument)` as a value for `--\(name)`")
122+
}
123+
}
124+
125+
throw ArgumentParserError(description: "Missing value for `--\(name)`")
126+
}
127+
}
128+
129+
return nil
130+
}
131+
54132
/// Returns whether an option was specified in the arguments
55133
public func hasOption(name:String) -> Bool {
56134
for argument in arguments {

CommanderTests/ArgumentParserTests.swift

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import Commander
33

44

55
class ArgumentParserTests : XCTestCase {
6-
let parser = ArgumentParser(arguments: ["first", "-f", "--verbose", "last"])
6+
let parser = ArgumentParser(arguments: ["first", "-f", "--verbose", "middle", "end"])
77

88
func testShiftingReturnsFirstPositionalArgument() {
99
XCTAssertEqual(parser.shift(), "first")
@@ -12,7 +12,7 @@ class ArgumentParserTests : XCTestCase {
1212
func testShiftingRemovesPositionalArgument() {
1313
parser.shift()
1414

15-
XCTAssertEqual(parser.shift(), "last")
15+
XCTAssertEqual(parser.shift(), "middle")
1616
}
1717

1818
func testHasOption() {
@@ -30,4 +30,45 @@ class ArgumentParserTests : XCTestCase {
3030
func testDoesNotHaveFlag() {
3131
XCTAssertFalse(parser.hasFlag("v"))
3232
}
33+
34+
func testValueForOptionMissing() {
35+
let value = try! parser.shiftValueForOption("unknown")
36+
XCTAssertNil(value)
37+
}
38+
39+
func testValueForOption() {
40+
let value = try! parser.shiftValueForOption("verbose")
41+
XCTAssertEqual(value, "middle")
42+
}
43+
44+
func testValueForOptionThrowsWhenValueIsNotPositionalArgument() {
45+
let parser = ArgumentParser(arguments: ["--verbose", "-t"])
46+
47+
do {
48+
try parser.shiftValueForOption("verbose")
49+
XCTFail("Unexpected Success")
50+
} catch let error as CustomStringConvertible {
51+
XCTAssertEqual(error.description, "Unexpected flag `-t` as a value for `--verbose`")
52+
} catch {
53+
XCTFail("Unexpected error")
54+
}
55+
}
56+
57+
func testValueForOptionThrowsWhenValueIsMissing() {
58+
let parser = ArgumentParser(arguments: ["--verbose"])
59+
60+
do {
61+
try parser.shiftValueForOption("verbose")
62+
XCTFail("Unexpected Success")
63+
} catch let error as CustomStringConvertible {
64+
XCTAssertEqual(error.description, "Missing value for `--verbose`")
65+
} catch {
66+
XCTFail("Unexpected error")
67+
}
68+
}
69+
70+
func testValuesForOption() {
71+
let value = try! parser.shiftValuesForOption("verbose", count: 2)!
72+
XCTAssertEqual(value, ["middle", "end"])
73+
}
3374
}

0 commit comments

Comments
 (0)