Skip to content

Commit 1da2194

Browse files
committed
Make ArgumentConvertible failable
1 parent 24da216 commit 1da2194

File tree

4 files changed

+108
-77
lines changed

4 files changed

+108
-77
lines changed
Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,46 +1,74 @@
1+
public enum ArgumentError : ErrorType, CustomStringConvertible {
2+
case MissingValue(String?)
3+
4+
/// Value is not convertible to type
5+
case InvalidType(value:String, type:String)
6+
7+
public var description:String {
8+
switch self {
9+
case .MissingValue:
10+
return "Missing argument"
11+
case .InvalidType(let value, let type):
12+
return "\(value) is not a \(type)"
13+
}
14+
}
15+
}
16+
117
public protocol ArgumentConvertible {
218
/// Initialise the type with an ArgumentParser
3-
init?(parser: ArgumentParser)
19+
init(parser: ArgumentParser) throws
420
}
521

622
extension String : ArgumentConvertible {
7-
public init?(parser: ArgumentParser) {
23+
public init(parser: ArgumentParser) throws {
824
if let value = parser.shift() {
925
self.init(value)
1026
} else {
11-
return nil
27+
throw ArgumentError.MissingValue(nil)
1228
}
1329
}
1430
}
1531

1632
extension Int : ArgumentConvertible {
17-
public init?(parser: ArgumentParser) {
33+
public init(parser: ArgumentParser) throws {
1834
if let value = parser.shift() {
19-
self.init(value)
35+
if let value = Int(value) {
36+
self.init(value)
37+
} else {
38+
throw ArgumentError.InvalidType(value: value, type: "number")
39+
}
2040
} else {
21-
return nil
41+
throw ArgumentError.MissingValue(nil)
2242
}
2343
}
2444
}
2545

2646

2747
extension Float : ArgumentConvertible {
28-
public init?(parser: ArgumentParser) {
48+
public init(parser: ArgumentParser) throws {
2949
if let value = parser.shift() {
30-
self.init(value)
50+
if let value = Float(value) {
51+
self.init(value)
52+
} else {
53+
throw ArgumentError.InvalidType(value: value, type: "number")
54+
}
3155
} else {
32-
return nil
56+
throw ArgumentError.MissingValue(nil)
3357
}
3458
}
3559
}
3660

3761

3862
extension Double : ArgumentConvertible {
39-
public init?(parser: ArgumentParser) {
63+
public init(parser: ArgumentParser) throws {
4064
if let value = parser.shift() {
41-
self.init(value)
65+
if let value = Double(value) {
66+
self.init(value)
67+
} else {
68+
throw ArgumentError.InvalidType(value: value, type: "number")
69+
}
4270
} else {
43-
return nil
71+
throw ArgumentError.MissingValue(nil)
4472
}
4573
}
4674
}

Commander/ArgumentParser.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public final class ArgumentParser : ArgumentConvertible {
6262
}
6363
}
6464

65-
public init?(parser: ArgumentParser) {
65+
public init(parser: ArgumentParser) throws {
6666
arguments = parser.arguments
6767
}
6868

Commander/Command.swift

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -25,38 +25,21 @@ public func command(closure:() -> ()) -> CommandType {
2525
/// Create a command which takes one argument using a closure
2626
public func command<A : ArgumentConvertible>(closure:A -> ()) -> CommandType {
2727
return AnonymousCommand { parser in
28-
if let argument = A(parser: parser) {
29-
closure(argument)
30-
} else {
31-
throw CommandError.InvalidArgument
32-
}
28+
closure(try A(parser: parser))
3329
}
3430
}
3531

3632
/// Create a command which takes two arguments using a closure
3733
public func command<A : ArgumentConvertible, B : ArgumentConvertible>(closure:(A, B) -> ()) -> CommandType {
3834
return AnonymousCommand { parser in
39-
if let argumentA = A(parser: parser),
40-
argumentB = B(parser: parser)
41-
{
42-
closure(argumentA, argumentB)
43-
} else {
44-
throw CommandError.InvalidArgument
45-
}
35+
closure(try A(parser: parser), try B(parser: parser))
4636
}
4737
}
4838

4939

5040
/// Create a command which takes three arguments using a closure
5141
public func command<A : ArgumentConvertible, B : ArgumentConvertible, C : ArgumentConvertible>(closure:(A, B, C) -> ()) -> CommandType {
5242
return AnonymousCommand { parser in
53-
if let argumentA = A(parser: parser),
54-
argumentB = B(parser: parser),
55-
argumentC = C(parser: parser)
56-
{
57-
closure(argumentA, argumentB, argumentC)
58-
} else {
59-
throw CommandError.InvalidArgument
60-
}
43+
closure(try A(parser: parser), try B(parser: parser), try C(parser: parser))
6144
}
6245
}

CommanderTests/ArgumentConvertibleTests.swift

Lines changed: 64 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -2,88 +2,108 @@ import XCTest
22
import Commander
33

44

5+
class ArgumentErrorTests : XCTestCase {
6+
func testMissingValueDescription() {
7+
let error = ArgumentError.MissingValue(nil)
8+
XCTAssertEqual(error.description, "Missing argument")
9+
}
10+
11+
func testNoValue() {
12+
let error = ArgumentError.InvalidType(value: "five", type: "number")
13+
XCTAssertEqual(error.description, "five is not a number")
14+
}
15+
}
16+
517
class StringArgumentConvertibleTests : XCTestCase {
618
func testValue() {
719
let parser = ArgumentParser(arguments: ["argument"])
8-
let value = String(parser: parser)
20+
let value = try? String(parser: parser)
921

10-
XCTAssertEqual(value, "argument")
22+
XCTAssertEqual(value!, "argument")
1123
}
1224

1325
func testNoValue() {
14-
let parser = ArgumentParser(arguments: [])
15-
let value = String(parser: parser)
16-
17-
XCTAssertNil(value)
26+
testMissingValue { try String(parser: $0) }
1827
}
1928
}
2029

2130

2231
class IntArgumentConvertibleTests : XCTestCase {
2332
func testValue() {
24-
let parser = ArgumentParser(arguments: ["5"])
25-
let value = Int(parser: parser)
26-
27-
XCTAssertEqual(value, 5)
33+
testValidValue("5", value: 5) { try Int(parser: $0) }
2834
}
2935

30-
func testInvalidValue() {
31-
let parser = ArgumentParser(arguments: ["five"])
32-
let value = Int(parser: parser)
33-
34-
XCTAssertNil(value)
36+
func testInvalidInput() {
37+
testInvalidValue("five") { try Int(parser: $0) }
3538
}
3639

3740
func testNoValue() {
38-
let parser = ArgumentParser(arguments: [])
39-
let value = Int(parser: parser)
40-
41-
XCTAssertNil(value)
41+
testMissingValue { try Int(parser: $0) }
4242
}
4343
}
4444

45+
4546
class FloatArgumentConvertibleTests : XCTestCase {
4647
func testValue() {
47-
let parser = ArgumentParser(arguments: ["5"])
48-
let value = Float(parser: parser)
49-
50-
XCTAssertEqual(value, 5)
48+
testValidValue("5", value: 5) { try Float(parser: $0) }
5149
}
5250

53-
func testInvalidValue() {
54-
let parser = ArgumentParser(arguments: ["five"])
55-
let value = Float(parser: parser)
56-
57-
XCTAssertNil(value)
51+
func testInvalidInput() {
52+
testInvalidValue("five") { try Float(parser: $0) }
5853
}
5954

6055
func testNoValue() {
61-
let parser = ArgumentParser(arguments: [])
62-
let value = Float(parser: parser)
63-
64-
XCTAssertNil(value)
56+
testMissingValue { try Float(parser: $0) }
6557
}
6658
}
6759

60+
6861
class DoubleArgumentConvertibleTests : XCTestCase {
6962
func testValue() {
70-
let parser = ArgumentParser(arguments: ["5"])
71-
let value = Double(parser: parser)
63+
testValidValue("5", value: 5) { try Double(parser: $0) }
64+
}
7265

73-
XCTAssertEqual(value, 5)
66+
func testInvalidInput() {
67+
testInvalidValue("five") { try Double(parser: $0) }
68+
}
69+
70+
func testNoValue() {
71+
testMissingValue { try Double(parser: $0) }
72+
}
73+
}
74+
75+
76+
func testMissingValue<T>(closure:((ArgumentParser) throws -> (T))) {
77+
let parser = ArgumentParser(arguments: [])
78+
79+
do {
80+
try closure(parser)
81+
XCTFail("Unexpected success")
82+
} catch ArgumentError.MissingValue {
83+
} catch {
84+
XCTFail("Unexpected error: \(error)")
7485
}
86+
}
7587

76-
func testInvalidValue() {
77-
let parser = ArgumentParser(arguments: ["five"])
78-
let value = Double(parser: parser)
88+
func testInvalidValue<T>(value:String, closure:((ArgumentParser) throws -> (T))) {
89+
let parser = ArgumentParser(arguments: [value])
7990

80-
XCTAssertNil(value)
91+
do {
92+
try closure(parser)
93+
XCTFail("Unexpected success")
94+
} catch ArgumentError.InvalidType {
95+
} catch {
96+
XCTFail("Unexpected error: \(error)")
8197
}
98+
}
8299

83-
func testNoValue() {
84-
let parser = ArgumentParser(arguments: [])
85-
let value = Double(parser: parser)
100+
func testValidValue<T>(argument:String, value:T, closure:((ArgumentParser) throws -> (T))) {
101+
let parser = ArgumentParser(arguments: ["5"])
86102

87-
XCTAssertNil(value)
103+
do {
104+
let value = try Int(parser: parser)
105+
XCTAssertEqual(value, 5)
106+
} catch {
107+
XCTFail("Unexpected error: \(error)")
88108
}
89-
}
109+
}

0 commit comments

Comments
 (0)