I am using ArgumentParser package for command-line parsing and want to use it with an async API of Swift concurrency:
struct Foo: ParsableCommand {
@Argument(
help: "File to be parsed. If not present, parses stdin.",
transform: URL.init(fileURLWithPath:)
)
var file: URL?
mutating func run() async throws {
let handle: FileHandle
if let file {
handle = try .init(forReadingFrom: file)
} else {
handle = .standardInput
}
for try await line in handle.bytes.lines {
// do something with each line
}
try handle.close()
}
}
But when I do this, I always see the “USAGE” text:
USAGE: foo [<file>]
ARGUMENTS:
<file> File to be parsed. If not present, parses stdin.
OPTIONS:
-h, --help Show help information.
I get no compilation errors, but I always see the “USAGE” text whether I supply a parameter or not.
The issue is the use of
ParsableCommandin conjunction withrun() async throws.Use
AsyncParsableCommandinstead. As its documentation says:Unfortunately, while the broader documentation makes an occasional reference to supporting
async, you have to dig into the code samples (specifically,count-lines) or pour through the entire class library to stumble acrossAsyncParsableCommand.So, use
AsyncParsableCommandwithasyncrendition ofrun:But, unfortunately, if you accidentally use
asyncrendition ofrunwithParsableCommand, it compiles without error but just produces the “USAGE” text whenever you run it, without any diagnostic information about why it is not working.In short,
ParsableCommandrequires a non-asyncrendition ofrun. Theasyncrendition requiresAsyncParsableCommand.