介绍 Swift ArgumentParser 库

Apple 最近新开源了一个库,叫 ArgumentParser,它是由 Swift 实现的,用于解析命令行参数(command-line arguments)的。

构建命令行工具

为了演示如何使用 ArgumentParser 库,我们将以一个生成随机数为示例。

> random 20
17
> random 100
89
> random
Error: Missing expected argument '<high-value>'
Usage: random <high-value>

定义了一个 Random 类型,并且需要一个整数类型的参数(highValue),然后会生成一个介于 1 和 highValue 之间的随机数。

源码如下:

import ArgumentParser

struct Random: ParsableCommand {
    @Argument() var highValue: Int

    func run() {
        print(Int.random(in: 1...highValue))
    }
}

Random.main()

实现就是这么简单。

  • @Argument 表明该属性是作为命令行参数(command-line argument)的。
  • 调用 main() 方法,将会开始解析,如果成功将运行命令行工具(command-line tool)。
  • 该库也会提供一些引导性的信息给用户,比如 highValue 属性的名称以及类型等。
  • highValue 被定义成 Int 类型,只有输入合法的值才可以,否则将会报错。

> random ZZZ
Error: The value 'ZZZ' is invalid for '<high-value>'
Usage: random <high-value>

自定义 Validation 和 Help

一个好的命令行工具会检查输入值的。接下来我们通过 validate() 方法来实现。

struct Random: ParsableCommand {
    static let configuration = CommandConfiguration(
        abstract: "Chooses a random number between 1 and your input.")

    @Argument(help: "The highest value to pick.")
    var highValue: Int

    func validate() throws {
        guard highValue >= 1 else {
            throw ValidationError("'<high-value>' must be at least 1.")
        }
    }

    func run() {
        print(Int.random(in: 1...highValue))
    }
}

现在我们的程序就会检查输入值是否符合要求,并给出相应的提示信息。

> random 0
Error: '<high-value>' must be at least 1.
Usage: random <high-value>
> random --help
OVERVIEW: Chooses a random number between 1 and your input.

USAGE: random <high-value>

ARGUMENTS:
  <high-value>            The highest value to pick. 

OPTIONS:
  -h, --help              Show help information.

使用子命令(Subcommands)

现代命令行工具(比如 GitSwift Package Manager)都支持子命令。让我们看看在 ArgumentParser 中如何实现的。

将已有的代码逻辑迁移到 Number 类型中。

extension Random {
    struct Number: ParsableCommand {
        static let configuration = CommandConfiguration(
            abstract: "Chooses a random number between 1 and your input.")

        @Argument(help: "The highest value to pick.")
        var highValue: Int

        func validate() throws {
            guard highValue >= 1 else {
                throw ValidationError("'<high-value>' must be at least 1.")
            }
        }

        func run() {
            print(Int.random(in: 1...highValue))
        }
    }
}

然后在 configuration 中列出子命令(subcommand)。

struct Random: ParsableCommand {
    static let configuration = CommandConfiguration(
        abstract: "Randomness utilities.",
        subcommands: [Number.self])
}

效果如下:

> random number 100
79

增加子命令

接下来,我们增加第二个子命令 pick

> random pick Fuji Gala Cameo Honeycrisp McIntosh Braeburn
McIntosh
> random pick --count 3 Fuji Gala Cameo Honeycrisp McIntosh Braeburn
Honeycrisp
Cameo
Braeburn

pick 命令接收一个 count 选项和一串 array类型的数据。

struct Random: ParsableCommand {
    static let configuration = CommandConfiguration(
        abstract: "Randomness utilities.",
        subcommands: [Number.self, Pick.self])

    // ...

    struct Pick: ParsableCommand {
        static let configuration = CommandConfiguration(
            abstract: "Picks random elements from your input.")

        @Option(default: 1, help: "The number of elements to choose.")
        var count: Int

        @Argument(help: "The elements to choose from.")
        var elements: [String]

        func validate() throws {
            guard !elements.isEmpty else {
                throw ValidationError("Must provide at least one element.")
            }
        }

        func run() {
            let picks = elements.shuffled().prefix(count)
            print(picks.joined(separator: "\n"))
        }
    }
}

其中,@Option 表明该属性是一个可选参数。

最终效果如下:

> random
OVERVIEW: Randomness utilities.

USAGE: random <subcommand>

OPTIONS:
  -h, --help              Show help information.

SUBCOMMANDS:
  number                  Chooses a random number between 1 and your input.
  pick                    Picks random elements from your input.

总结

目前 indexstore-dbswift-format 开源库已经开始准备采用 ArgumentParser 了,在未来还会有更多的项目开始采用 ArgumentParser 的,比如 SwiftPM

发表评论

电子邮件地址不会被公开。 必填项已用*标注