在Swift并发中使用ArgumentParser



我正在使用ArgumentParser包进行命令行解析,并希望将其与Swift并发的asyncAPI一起使用:

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"文本。

问题在于ParsableCommandrun() async throws的结合使用。

使用AsyncParsableCommand代替。正如它的文档所说:

要在命令的run()方法实现中使用async/await代码,请遵循以下步骤:

  1. 对于命令行工具中的根命令,声明符合AsyncParsableCommand,无论该命令是否使用异步代码。
  2. @main属性应用于root命令。(注意:如果您的根命令在main.Swift文件中,请将该文件重命名为命令的名称。)
  3. 对于任何需要使用异步代码的命令,声明符合AsyncParsableCommand,并将run()方法标记为async。不使用异步代码的子命令不需要修改。

不幸的是,虽然更广泛的文档偶尔会提到支持async,但您必须深入研究代码示例(特别是count-lines)或遍历整个类库才能偶然发现AsyncParsableCommand

所以,使用AsyncParsableCommandrunasync版本:

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()
}
}

但是,不幸的是,如果您不小心将runasync版本与ParsableCommand一起使用,它编译时不会出错,但无论何时运行它都只生成"USAGE"文本,而没有关于为什么不工作的任何诊断信息。

简而言之,ParsableCommand需要run的非async版本。async版本需要AsyncParsableCommand

最新更新