Skip to content

Commit 9f90286

Browse files
committed
[benchmark][Gardening] Extracted ArgumentParser
Moved the argument parsing logic into new class `ArgumentParser`. The `checked` function is also moved to the ArgParse.swift, next to the parser.
1 parent 1841dde commit 9f90286

File tree

2 files changed

+67
-51
lines changed

2 files changed

+67
-51
lines changed

benchmark/utils/ArgParse.swift

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,3 +97,55 @@ public func parseArgs(_ validOptions: [String]? = nil)
9797

9898
return Arguments(progName, positionalArgs, optionalArgsMap)
9999
}
100+
101+
/// Returns the argument value converted to the type T using the parser.
102+
/// If the parser cannot create the value of specified type, throw an invalid
103+
/// type argument error.
104+
func checked<T>(
105+
_ parse: (String) throws -> T?,
106+
_ value: String,
107+
argument: String? = nil
108+
) throws -> T {
109+
if let t = try parse(value) { return t }
110+
var type = "\(T.self)"
111+
if type.starts(with: "Optional<") {
112+
let s = type.index(after: type.index(of:"<")!)
113+
let e = type.index(before: type.endIndex) // ">"
114+
type = String(type[s..<e]) // strip Optional< >
115+
}
116+
throw ArgumentError.invalidType(
117+
value: value, type: type, argument: argument)
118+
}
119+
120+
class ArgumentParser<U> {
121+
var result: U
122+
let validOptions: [String]
123+
private let benchArgs: Arguments
124+
125+
init(into result: U, validOptions: [String]) throws {
126+
self.result = result
127+
self.validOptions = validOptions
128+
guard let benchArgs = parseArgs(validOptions) else {
129+
throw ArgumentError.general("Failed to parse arguments")
130+
}
131+
self.benchArgs = benchArgs
132+
}
133+
134+
func parseArg<T>(
135+
_ name: String?,
136+
_ property: WritableKeyPath<U, T>,
137+
defaultValue: T? = nil,
138+
parser parse: (String) throws -> T? = { _ in nil }
139+
) throws {
140+
if let name = name, let value = benchArgs.optionalArgsMap[name] {
141+
guard !value.isEmpty || defaultValue != nil
142+
else { throw ArgumentError.missingValue(name) }
143+
144+
result[keyPath: property] = (value.isEmpty)
145+
? defaultValue!
146+
: try checked(parse, value, argument:name)
147+
} else if name == nil {
148+
result[keyPath: property] = benchArgs.positionalArgs as! T
149+
}
150+
}
151+
}

benchmark/utils/DriverUtils.swift

Lines changed: 15 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -71,50 +71,12 @@ struct TestConfig {
7171
var action: TestAction?
7272
var tests: [String]?
7373
}
74-
var c = PartialTestConfig()
7574

76-
let validOptions = [
75+
let p = try ArgumentParser(into: PartialTestConfig(), validOptions: [
7776
"--iter-scale", "--num-samples", "--num-iters",
7877
"--verbose", "--delim", "--list", "--sleep",
7978
"--tags", "--skip-tags", "--help"
80-
]
81-
guard let benchArgs = parseArgs(validOptions) else {
82-
throw ArgumentError.general("Failed to parse arguments")
83-
}
84-
85-
func checked<T>(
86-
_ parse: (String) throws -> T?,
87-
_ value: String,
88-
argument: String? = nil
89-
) throws -> T {
90-
if let t = try parse(value) { return t }
91-
var type = "\(T.self)"
92-
if type.starts(with: "Optional<") {
93-
let s = type.index(after: type.index(of:"<")!)
94-
let e = type.index(before: type.endIndex) // ">"
95-
type = String(type[s..<e]) // strip Optional< >
96-
}
97-
throw ArgumentError.invalidType(
98-
value: value, type: type, argument: argument)
99-
}
100-
101-
func parseArg<T>(
102-
_ name: String?,
103-
_ property: WritableKeyPath<PartialTestConfig, T>,
104-
defaultValue: T? = nil,
105-
parser parse: (String) throws -> T? = { _ in nil }
106-
) throws {
107-
if let name = name, let value = benchArgs.optionalArgsMap[name] {
108-
guard !value.isEmpty || defaultValue != nil
109-
else { throw ArgumentError.missingValue(name) }
110-
111-
c[keyPath: property] = (value.isEmpty)
112-
? defaultValue!
113-
: try checked(parse, value, argument:name)
114-
} else if name == nil {
115-
c[keyPath: property] = benchArgs.positionalArgs as! T
116-
}
117-
}
79+
])
11880

11981
func tags(tags: String) throws -> Set<BenchmarkCategory> {
12082
// We support specifying multiple tags by splitting on comma, i.e.:
@@ -126,18 +88,20 @@ struct TestConfig {
12688
}
12789

12890
// Parse command line arguments
129-
try parseArg("--iter-scale", \.iterationScale) { Int($0) }
130-
try parseArg("--num-iters", \.fixedNumIters) { UInt($0) }
131-
try parseArg("--num-samples", \.numSamples) { Int($0) }
132-
try parseArg("--verbose", \.verbose, defaultValue: true)
133-
try parseArg("--delim", \.delim) { $0 }
134-
try parseArg("--tags", \PartialTestConfig.tags, parser: tags)
135-
try parseArg("--skip-tags", \PartialTestConfig.skipTags,
91+
try p.parseArg("--iter-scale", \.iterationScale) { Int($0) }
92+
try p.parseArg("--num-iters", \.fixedNumIters) { UInt($0) }
93+
try p.parseArg("--num-samples", \.numSamples) { Int($0) }
94+
try p.parseArg("--verbose", \.verbose, defaultValue: true)
95+
try p.parseArg("--delim", \.delim) { $0 }
96+
try p.parseArg("--tags", \PartialTestConfig.tags, parser: tags)
97+
try p.parseArg("--skip-tags", \PartialTestConfig.skipTags,
13698
defaultValue: [], parser: tags)
137-
try parseArg("--sleep", \.afterRunSleep) { Int($0) }
138-
try parseArg("--list", \.action, defaultValue: .listTests)
139-
try parseArg("--help", \.action, defaultValue: .help(validOptions))
140-
try parseArg(nil, \.tests) // positional arguments
99+
try p.parseArg("--sleep", \.afterRunSleep) { Int($0) }
100+
try p.parseArg("--list", \.action, defaultValue: .listTests)
101+
try p.parseArg("--help", \.action, defaultValue: .help(p.validOptions))
102+
try p.parseArg(nil, \.tests) // positional arguments
103+
104+
let c = p.result
141105

142106
// Configure from the command line arguments, filling in the defaults.
143107
delim = c.delim ?? ","

0 commit comments

Comments
 (0)