論証


コマンドラインツールの引数を解析するいくつかの機能的ヘルパーを提供することを目的とした迅速な遊び場.それはOvertureを使用して、高い作曲性、柔軟性と進化するプロジェクトの自由に少しの影響のために構築されています.
中央使用例はArchery sであった.実際に解釈された議論だけが消費される.他のいずれかを収集する(remaining)または実行を防ぐ必要があります(exhaust)、現在のコマンドに応じて.
すべての例を使用します.
// Experiment
do {
    let (isVerbose, whoToGreet, language, _) = try with(["-v", "hi", "Some string", "--language", "en"], chain(
        flag("verbose", "v"),
        positional("Name"),
        argument("language", "l"),
        exhaust
    ))
} catch {
    print("Command failed:", error)
}
マイクロライブラリ自体の実装
// Errors
private func quoted(_ string: String) -> String {
    return "\"\(string)\""
}

public protocol StringConvertibleError: Error, CustomStringConvertible {}

public struct MissingArgumentError: StringConvertibleError {
    public let name: String?
    public var description: String {
        return with(name, pipe(
            map(quoted),
            map { "Missing argument \($0)" },
            ??"Missing argument"
        ))
    }
}

public struct NonExhaustiveArgumentsError: StringConvertibleError {
    public let remaining: [String]
    public var description: String {
        return "Unused arguments: \(remaining)"
    }
}

// Domain
private func isArgument(named name: String) -> (String) -> Bool {
    return { $0 == "-\(name)" || $0 == "--\(name)" }
}

private func isOneArgument(of names: [String]) -> (String) -> Bool {
    return { passed in
        names.lazy
        .map(isArgument(named:))
        .reduce(false, { $0 || $1(passed) })
    }
}

private func indexOfArguments(named names: [String]) -> ([String]) -> Array<String>.Index? {
    return { passed in
        passed.index(where: isOneArgument(of: names))
    }
}

public func flag(_ names: String...) -> ([String]) -> ([String], Bool) {
    return { arguments in
        if let index = arguments.index(where: isOneArgument(of: names)) {
            var result = arguments
            result.remove(at: index)
            return (result, true)
        } else {
            return (arguments, false)
        }
    }
}

public func positional(_ name: String? = nil) -> ([String]) throws -> ([String], String) {
    return { arguments in
        let index = arguments.index(where: { !$0.starts(with: "-")})
        if let index = index {
            var result = arguments
            result.remove(at: index)
            return (result, arguments[index])
        } else {
            throw MissingArgumentError(name: name)
        }
    }
}

public func optional<A>(_ f: @escaping ([String]) throws -> ([String], A)) -> ([String]) throws -> ([String], A?) {
    return { arguments in
        do {
            let (remaining, parsed) = try f(arguments)
            return (remaining, .some(parsed))
        } catch is MissingArgumentError {
            return (arguments, nil)
        } catch {
            throw error
        }
    }
}

public func argument(_ names: String...) -> ([String]) throws -> ([String], String) {
    return { arguments in
        if let flagIndex = indexOfArguments(named: names)(arguments), arguments.count >= flagIndex + 2 {
            let value = arguments[flagIndex + 1]
            var rest = arguments
            rest.remove(at: flagIndex + 1)
            rest.remove(at: flagIndex)
            return (rest, value)
        } else {
            throw MissingArgumentError(name: names.first)
        }
    }
}

public func exhaust(_ arguments: [String]) throws -> ([String], Void) {
    if arguments.isEmpty {
        return ([], ())
    } else {
        throw NonExhaustiveArgumentsError(remaining: arguments)
    }
}

public func remaining(_ arguments: [String]) -> ([String], [String]) {
    return ([], arguments)
}

次は何ですか.
同様の概念はプロセス、特に割り込みの処理にも適用できる.
with(Process(), concat(
    setWorkingDir("tmp"),
    passInterrupt(),
    setBashCommand("") // is { concat(setLaunchPath("/bin/bash"), setArguments(["-c", $0]) }
))