我正在使用ArgumentParser
包进行命令行解析,并希望将其与Swift并发的async
API一起使用:
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()
}
}
但是当我这样做的时候,我总是看到" USAGE "文本:
USAGE: foo [<file>]
ARGUMENTS:
<file> File to be parsed. If not present, parses stdin.
OPTIONS:
-h, --help Show help information.
我没有得到编译错误,但无论我是否提供参数,我总是看到"USAGE"文本。
问题在于ParsableCommand
与run() async throws
的结合使用。
使用AsyncParsableCommand
代替。正如它的文档所说:
要在命令的
run()
方法实现中使用async
/await
代码,请遵循以下步骤:
- 对于命令行工具中的根命令,声明符合
AsyncParsableCommand
,无论该命令是否使用异步代码。- 将
@main
属性应用于root命令。(注意:如果您的根命令在main.Swift
文件中,请将该文件重命名为命令的名称。)- 对于任何需要使用异步代码的命令,声明符合
AsyncParsableCommand
,并将run()
方法标记为async
。不使用异步代码的子命令不需要修改。
不幸的是,虽然更广泛的文档偶尔会提到支持async
,但您必须深入研究代码示例(特别是count-lines
)或遍历整个类库才能偶然发现AsyncParsableCommand
。
所以,使用AsyncParsableCommand
和run
的async
版本:
import ArgumentParser
@main
struct Foo: AsyncParsableCommand {
@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()
}
}
但是,不幸的是,如果您不小心将run
的async
版本与ParsableCommand
一起使用,它编译时不会出错,但无论何时运行它都只生成"USAGE"文本,而没有关于为什么不工作的任何诊断信息。
简而言之,ParsableCommand
需要run
的非async
版本。async
版本需要AsyncParsableCommand
。