[Swift] Processを使おう！(4)

### Processってなに？
ちょっと前までNSTaskと呼ばれていた、主にコマンドラインツールを実行してデータのやり取りを行うためのクラスです。
SwiftでできることはSwiftでやってしまってもいいのですが、swiftcを呼びたいとかそういう時に使います。

の4回目。

[第１回](https://qiita.com/masakihori/items/0639526a3baf0d6cb621)
[第２回](https://qiita.com/masakihori/items/a85d1667b0af86d6f6e7)
[第３回](https://qiita.com/masakihori/items/fe094ca1f66d56f38d92)

### 振り返り
前回出力を得るカスタムオペレーションを追加して下のようになりました。

```swift
Process() <<< "/bin/echo" <<< ["Hello, World."] >>> { output in
    
    output.string.map { print($0) }
}
```

完璧です。

ところで皆さん。コマンドラインにおけるパイプラインってご存知ですか？
そうです。`|`です。

これを今までの成果を利用して書いてみましょう。

```swift
let echo = Process() <<< "/bin/echo" <<< ["Hello, World.\nこんにちは世界。"]
let grep = Process() <<< "/usr/bin/grep" <<< ["Hello"]

let pipe = Pipe()
echo.standardOutput = pipe
grep.standardInput = pipe

echo.launch()

grep >>> { output in
   output.string.map { print($0) }
}
```

**また`Pipe`と`launch()`が現れた！！！**

## パイプも簡単に

パイプを行うカスタムオペレーションを追加します。

```swift
func | (lhs: Process, rhs: Process) -> Process {

    let pipe = Pipe()

    lhs.standardOutput = pipe
    rhs.standardInput = pipe
    lhs.launch()

    return rhs
}
```
ふたつの`Process`をパイプし新しい`Process`を取り出します。
右側が返ってくるだけですが、中身が見えているからそう思うだけです！
ふたつの`Process`がパイプされて新しい`Process`が生まれたのです。

### 使用法

```swift
Process() <<< "/bin/echo" <<< ["Hello, World.\nこんにちは世界。"]
    | Process() <<< "/usr/bin/grep" <<< ["Hello"]
    >>> { output in
    
    output.string.map { print($0) }
}
```

短い！　わかりやすい！


## まとめ
今まで作ったカスタムな何かをまとめたものがこちらです。

```swift
/// 出力を簡単に扱うための補助的な型。FileHandleを隠蔽する。
struct Output {
    
    private let fileHandle: FileHandle
    
    init(fileHandle: FileHandle) {
        self.fileHandle = fileHandle
    }
    
    var data: Data {
        return fileHandle.readDataToEndOfFile()
    }
    
    var string: String? {
        return String(data: data, encoding: .utf8)
    }
    
    var lines: [String] {
        return string?.components(separatedBy: "\n") ?? []
    }
}

precedencegroup ArgumentPrecedence {
    
    associativity: left
    higherThan: AdditionPrecedence
}
infix operator <<< : ArgumentPrecedence

/// Processにexecutable pathを設定する。
func <<< (lhs: Process, rhs: String) -> Process {
    
    lhs.executableURL = URL(fileURLWithPath: rhs)
    return lhs
}

/// Processに引数を設定する。
func <<< (lhs: Process, rhs: [String]) -> Process {
    
    lhs.arguments = rhs
    return lhs
}

/// Processをパイプする。
func | (lhs: Process, rhs: Process) -> Process {

    let pipe = Pipe()

    lhs.standardOutput = pipe
    rhs.standardInput = pipe
    lhs.launch()

    return rhs
}

precedencegroup RedirectPrecedence {
    
    associativity: left
    lowerThan: LogicalDisjunctionPrecedence
}
infix operator >>> : RedirectPrecedence

/// Processの出力をOutput型で受け取り加工などができる。
/// ジェネリクスを利用しているのでどのような型にでも変換して返せる。
func >>> <T>(lhs: Process, rhs: (Output) -> T) -> T {
    
    let pipe = Pipe()
    lhs.standardOutput = pipe
    lhs.launch()
    
    return rhs(Output(fileHandle: pipe.fileHandleForReading))
}
```

`Process`クラスには直接手を加えず、カスタムオペレーションと補助的な型を追加することで、`Process`クラスをとてもわかりやすく、そして短い記述で使用できるようになりました。

オペレータの右側を関数にすることを思いついた時は思わず飛び上がりそうになりました。
こういうのを考えてるととても楽しくなりますね。

それではみなさん

```swift
let string = Process() <<< "/bin/echo" <<< ["Hello, World.\nこんにちは世界。"]
    | Process() <<< "/usr/bin/grep" <<< [","]
    | Process() <<< "/usr/bin/sed" <<< ["-e", "s/Hel/We/"]
    | Process() <<< "/usr/bin/sed" <<< ["-e", "s/,/ to New/"]
    | Process() <<< "/usr/bin/sed" <<< ["-e", "s/o/come/"]
    >>> { $0.string }

string.map { print($0) }
```