🔥 전체 코드
843자
5분
//===----------------------------------------------------------*- swift -*-===// // // This source file is part of the Swift Argument Parser open source project // // Copyright (c) 2020 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See <https://swift.org/LICENSE.txt> for license information // //===----------------------------------------------------------------------===// import ArgumentParser @main struct Math: ParsableCommand { // Customize your command's help and subcommands by implementing the // `configuration` property. static let configuration = CommandConfiguration( // Optional abstracts and discussions are used for help output. abstract: "A utility for performing maths.", // Commands can define a version for automatic '--version' support. version: "1.0.0", // Pass an array to `subcommands` to set up a nested tree of subcommands. // With language support for type-level introspection, this could be // provided by automatically finding nested `ParsableCommand` types. subcommands: [Add.self, Multiply.self, Statistics.self], // A default subcommand, when provided, is automatically selected if a // subcommand is not given on the command line. defaultSubcommand: Add.self) } struct Options: ParsableArguments { @Flag(name: [.customLong("hex-output"), .customShort("x")], help: "Use hexadecimal notation for the result.") var hexadecimalOutput = false @Argument( help: "A group of integers to operate on.") var values: [Int] = [] } extension Math { static func format(_ result: Int, usingHex: Bool) -> String { usingHex ? String(result, radix: 16) : String(result) } struct Add: ParsableCommand { static let configuration = CommandConfiguration(abstract: "Print the sum of the values.") // The `@OptionGroup` attribute includes the flags, options, and // arguments defined by another `ParsableArguments` type. @OptionGroup var options: Options mutating func run() { let result = options.values.reduce(0, +) print(format(result, usingHex: options.hexadecimalOutput)) } } struct Multiply: ParsableCommand { static let configuration = CommandConfiguration(abstract: "Print the product of the values.") @OptionGroup var options: Options mutating func run() { let result = options.values.reduce(1, *) print(format(result, usingHex: options.hexadecimalOutput)) } } } // In practice, these nested types could be broken out into different files. extension Math { struct Statistics: ParsableCommand { static let configuration = CommandConfiguration( // Command names are automatically generated from the type name // by default; you can specify an override here. commandName: "stats", abstract: "Calculate descriptive statistics.", subcommands: [Average.self, StandardDeviation.self, Quantiles.self]) } } extension Math.Statistics { struct Average: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Print the average of the values.", version: "1.5.0-alpha") enum Kind: String, ExpressibleByArgument, CaseIterable { case mean, median, mode } @Option(help: "The kind of average to provide.") var kind: Kind = .mean @Argument(help: "A group of floating-point values to operate on.") var values: [Double] = [] func validate() throws { if (kind == .median || kind == .mode) && values.isEmpty { throw ValidationError("Please provide at least one value to calculate the \(kind).") } } func calculateMean() -> Double { guard !values.isEmpty else { return 0 } let sum = values.reduce(0, +) return sum / Double(values.count) } func calculateMedian() -> Double { guard !values.isEmpty else { return 0 } let sorted = values.sorted() let mid = sorted.count / 2 if sorted.count.isMultiple(of: 2) { return (sorted[mid - 1] + sorted[mid]) / 2 } else { return sorted[mid] } } func calculateMode() -> [Double] { guard !values.isEmpty else { return [] } let grouped = Dictionary(grouping: values, by: { $0 }) let highestFrequency = grouped.lazy.map { $0.value.count }.max()! return grouped.filter { _, v in v.count == highestFrequency } .map { k, _ in k } } mutating func run() { switch kind { case .mean: print(calculateMean()) case .median: print(calculateMedian()) case .mode: let result = calculateMode() .map(String.init(describing:)) .joined(separator: " ") print(result) } } } struct StandardDeviation: ParsableCommand { static let configuration = CommandConfiguration( commandName: "stdev", abstract: "Print the standard deviation of the values.") @Argument(help: "A group of floating-point values to operate on.") var values: [Double] = [] mutating func run() { if values.isEmpty { print(0.0) } else { let sum = values.reduce(0, +) let mean = sum / Double(values.count) let squaredErrors = values .map { $0 - mean } .map { $0 * $0 } let variance = squaredErrors.reduce(0, +) / Double(values.count) let result = variance.squareRoot() print(result) } } } struct Quantiles: ParsableCommand { static let configuration = CommandConfiguration( abstract: "Print the quantiles of the values (TBD).") @Argument(completion: .list(["alphabet", "alligator", "branch", "braggart"])) var oneOfFour: String? @Argument(completion: .custom { _ in ["alabaster", "breakfast", "crunch", "crash"] }) var customArg: String? @Argument(help: "A group of floating-point values to operate on.") var values: [Double] = [] // These args and the validation method are for testing exit codes: @Flag(help: .hidden) var testSuccessExitCode = false @Flag(help: .hidden) var testFailureExitCode = false @Flag(help: .hidden) var testValidationExitCode = false @Option(help: .hidden) var testCustomExitCode: Int32? // These args are for testing custom completion scripts: @Option(completion: .file(extensions: ["txt", "md"])) var file: String? @Option(completion: .directory) var directory: String? @Option(completion: .shellCommand("head -100 /usr/share/dict/words | tail -50")) var shell: String? @Option(completion: .custom(customCompletion)) var custom: String? func validate() throws { if testSuccessExitCode { throw ExitCode.success } if testFailureExitCode { throw ExitCode.failure } if testValidationExitCode { throw ExitCode.validationFailure } if let exitCode = testCustomExitCode { throw ExitCode(exitCode) } } } } func customCompletion(_ s: [String]) -> [String] { return (s.last ?? "").starts(with: "a") ? ["aardvark", "aaaaalbert"] : ["hello", "helicopter", "heliotrope"] }
swift