Skip to content

Commit 5f1e43e

Browse files
committed
Split implementation into files by concept
1 parent 1744ba8 commit 5f1e43e

File tree

6 files changed

+495
-468
lines changed

6 files changed

+495
-468
lines changed

Commandant.xcodeproj/project.pbxproj

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,10 @@
1414
D00CCE241A2073DA00109F8C /* Nimble.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D00CCE221A2073DA00109F8C /* Nimble.framework */; };
1515
D00CCE251A2073DA00109F8C /* Quick.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D00CCE231A2073DA00109F8C /* Quick.framework */; };
1616
D00CCE271A20741300109F8C /* Command.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CCE261A20741300109F8C /* Command.swift */; };
17-
D00CCE291A20741C00109F8C /* CommandSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CCE281A20741C00109F8C /* CommandSpec.swift */; };
17+
D00CCE291A20741C00109F8C /* OptionSpec.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CCE281A20741C00109F8C /* OptionSpec.swift */; };
1818
D00CCE2B1A20748500109F8C /* Errors.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CCE2A1A20748500109F8C /* Errors.swift */; };
19+
D00CCE2D1A2075ED00109F8C /* ArgumentParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CCE2C1A2075ED00109F8C /* ArgumentParser.swift */; };
20+
D00CCE2F1A2075F700109F8C /* Option.swift in Sources */ = {isa = PBXBuildFile; fileRef = D00CCE2E1A2075F700109F8C /* Option.swift */; };
1921
/* End PBXBuildFile section */
2022

2123
/* Begin PBXContainerItemProxy section */
@@ -56,8 +58,10 @@
5658
D00CCE221A2073DA00109F8C /* Nimble.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Nimble.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5759
D00CCE231A2073DA00109F8C /* Quick.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Quick.framework; sourceTree = BUILT_PRODUCTS_DIR; };
5860
D00CCE261A20741300109F8C /* Command.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Command.swift; sourceTree = "<group>"; };
59-
D00CCE281A20741C00109F8C /* CommandSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CommandSpec.swift; sourceTree = "<group>"; };
61+
D00CCE281A20741C00109F8C /* OptionSpec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionSpec.swift; sourceTree = "<group>"; };
6062
D00CCE2A1A20748500109F8C /* Errors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Errors.swift; sourceTree = "<group>"; };
63+
D00CCE2C1A2075ED00109F8C /* ArgumentParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ArgumentParser.swift; sourceTree = "<group>"; };
64+
D00CCE2E1A2075F700109F8C /* Option.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Option.swift; sourceTree = "<group>"; };
6165
/* End PBXFileReference section */
6266

6367
/* Begin PBXFrameworksBuildPhase section */
@@ -106,8 +110,10 @@
106110
isa = PBXGroup;
107111
children = (
108112
D00CCDDE1A20717400109F8C /* Commandant.h */,
113+
D00CCE2C1A2075ED00109F8C /* ArgumentParser.swift */,
109114
D00CCE261A20741300109F8C /* Command.swift */,
110115
D00CCE2A1A20748500109F8C /* Errors.swift */,
116+
D00CCE2E1A2075F700109F8C /* Option.swift */,
111117
D00CCDDC1A20717400109F8C /* Supporting Files */,
112118
);
113119
path = Commandant;
@@ -125,7 +131,7 @@
125131
D00CCDE81A20717400109F8C /* CommandantTests */ = {
126132
isa = PBXGroup;
127133
children = (
128-
D00CCE281A20741C00109F8C /* CommandSpec.swift */,
134+
D00CCE281A20741C00109F8C /* OptionSpec.swift */,
129135
D00CCDE91A20717400109F8C /* Supporting Files */,
130136
);
131137
path = CommandantTests;
@@ -314,16 +320,18 @@
314320
isa = PBXSourcesBuildPhase;
315321
buildActionMask = 2147483647;
316322
files = (
323+
D00CCE2F1A2075F700109F8C /* Option.swift in Sources */,
317324
D00CCE2B1A20748500109F8C /* Errors.swift in Sources */,
318325
D00CCE271A20741300109F8C /* Command.swift in Sources */,
326+
D00CCE2D1A2075ED00109F8C /* ArgumentParser.swift in Sources */,
319327
);
320328
runOnlyForDeploymentPostprocessing = 0;
321329
};
322330
D00CCDE01A20717400109F8C /* Sources */ = {
323331
isa = PBXSourcesBuildPhase;
324332
buildActionMask = 2147483647;
325333
files = (
326-
D00CCE291A20741C00109F8C /* CommandSpec.swift in Sources */,
334+
D00CCE291A20741C00109F8C /* OptionSpec.swift in Sources */,
327335
);
328336
runOnlyForDeploymentPostprocessing = 0;
329337
};

Commandant/ArgumentParser.swift

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
//
2+
// ArgumentParser.swift
3+
// Commandant
4+
//
5+
// Created by Justin Spahr-Summers on 2014-11-21.
6+
// Copyright (c) 2014 Carthage. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import LlamaKit
11+
12+
/// Represents an argument passed on the command line.
13+
private enum RawArgument: Equatable {
14+
/// A key corresponding to an option (e.g., `verbose` for `--verbose`).
15+
case Key(String)
16+
17+
/// A value, either associated with an option or passed as a positional
18+
/// argument.
19+
case Value(String)
20+
}
21+
22+
private func ==(lhs: RawArgument, rhs: RawArgument) -> Bool {
23+
switch (lhs, rhs) {
24+
case let (.Key(left), .Key(right)):
25+
return left == right
26+
27+
case let (.Value(left), .Value(right)):
28+
return left == right
29+
30+
default:
31+
return false
32+
}
33+
}
34+
35+
extension RawArgument: Printable {
36+
private var description: String {
37+
switch self {
38+
case let .Key(key):
39+
return "--\(key)"
40+
41+
case let .Value(value):
42+
return "\"\(value)\""
43+
}
44+
}
45+
}
46+
47+
/// Destructively parses a list of command-line arguments.
48+
public final class ArgumentParser {
49+
/// The remaining arguments to be extracted, in their raw form.
50+
private var rawArguments: [RawArgument] = []
51+
52+
/// Initializes the generator from a simple list of command-line arguments.
53+
public init(_ arguments: [String]) {
54+
var permitKeys = true
55+
56+
for arg in arguments {
57+
// Check whether this is a keyed argument.
58+
if permitKeys && arg.hasPrefix("--") {
59+
// Check for -- by itself, which should terminate the keyed
60+
// argument list.
61+
let keyStartIndex = arg.startIndex.successor().successor()
62+
if keyStartIndex == arg.endIndex {
63+
permitKeys = false
64+
} else {
65+
let key = arg.substringFromIndex(keyStartIndex)
66+
rawArguments.append(.Key(key))
67+
}
68+
} else {
69+
rawArguments.append(.Value(arg))
70+
}
71+
}
72+
}
73+
74+
/// Returns whether the given key was enabled or disabled, or nil if it
75+
/// was not given at all.
76+
///
77+
/// If the key is found, it is then removed from the list of arguments
78+
/// remaining to be parsed.
79+
internal func consumeBooleanKey(key: String) -> Bool? {
80+
let oldArguments = rawArguments
81+
rawArguments.removeAll()
82+
83+
var result: Bool?
84+
for arg in oldArguments {
85+
if arg == .Key(key) {
86+
result = true
87+
} else if arg == .Key("no-\(key)") {
88+
result = false
89+
} else {
90+
rawArguments.append(arg)
91+
}
92+
}
93+
94+
return result
95+
}
96+
97+
/// Returns the value associated with the given flag, or nil if the flag was
98+
/// not specified. If the key is presented, but no value was given, an error
99+
/// is returned.
100+
///
101+
/// If a value is found, the key and the value are both removed from the
102+
/// list of arguments remaining to be parsed.
103+
internal func consumeValueForKey(key: String) -> Result<String?> {
104+
let oldArguments = rawArguments
105+
rawArguments.removeAll()
106+
107+
var foundValue: String?
108+
argumentLoop: for var index = 0; index < oldArguments.count; index++ {
109+
let arg = oldArguments[index]
110+
111+
if arg == .Key(key) {
112+
if ++index < oldArguments.count {
113+
switch oldArguments[index] {
114+
case let .Value(value):
115+
foundValue = value
116+
continue argumentLoop
117+
118+
default:
119+
break
120+
}
121+
}
122+
123+
return failure(missingArgumentError("--\(key)"))
124+
} else {
125+
rawArguments.append(arg)
126+
}
127+
}
128+
129+
return success(foundValue)
130+
}
131+
132+
/// Returns the next positional argument that hasn't yet been returned, or
133+
/// nil if there are no more positional arguments.
134+
internal func consumePositionalArgument() -> String? {
135+
for var index = 0; index < rawArguments.count; index++ {
136+
switch rawArguments[index] {
137+
case let .Value(value):
138+
rawArguments.removeAtIndex(index)
139+
return value
140+
141+
default:
142+
break
143+
}
144+
}
145+
146+
return nil
147+
}
148+
}

0 commit comments

Comments
 (0)