# 機能設計書 72-パイプライン

## 概要

本ドキュメントは、Julia言語におけるプロセス間パイプライン接続機能の設計を記述する。`pipeline`、`Pipe`、`PipeBuffer` によるプロセス間のI/O接続を提供する機能である。

### 本機能の処理概要

パイプライン機能は、複数の外部プロセスの標準入出力をパイプで接続し、UNIXシェルのパイプライン（`cmd1 | cmd2 | cmd3`）と同等の機能をJuliaから利用可能にする。`|>` 演算子やバッククォートリテラルの `pipeline` 関数を通じて、データストリームのチェーン処理を実現する。

**業務上の目的・背景**：データ処理パイプラインはUNIXの基本的な設計哲学であり、小さなツールを組み合わせて複雑な処理を実現する手法は広く使われている。Juliaがシステムスクリプティング言語としても利用されるために、シェルと同等のパイプライン構築能力が必要である。また、標準出力だけでなく標準エラー出力のリダイレクトやファイルへの出力など、柔軟なI/Oルーティングを提供する。

**機能の利用シーン**：外部コマンドのチェーン実行（grep | sort | uniq等）、プロセスの出力をファイルにリダイレクト、プロセスの標準エラー出力を別プロセスにパイプ、Juliaプログラムと外部プロセス間の双方向通信などで使用される。

**主要な処理内容**：
1. `pipeline(cmd1, cmd2)` - 2つのコマンドの標準出力→標準入力を接続
2. `pipeline(cmd; stdin, stdout, stderr)` - コマンドのI/Oを指定先にリダイレクト
3. `|>` 演算子によるパイプライン構築（OrCmds型）
4. `|>` 演算子による標準エラーパイプライン（ErrOrCmds型）
5. `&` 演算子による並列コマンド実行（AndCmds型）
6. `Pipe` / `PipeEndpoint` によるプロセス間パイプの管理
7. `link_pipe` / `link_pipe!` によるパイプペアの作成と接続

**関連システム・外部連携**：libuvのパイプAPI（uv_pipe_t）を使用してOSレベルのパイプを管理する。

**権限による制御**：特別な権限制御は不要。OSレベルのファイルアクセス権限に従う。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 4 | シェルモード（shell>） | 補助機能 | pipelineによるシェルコマンドのパイプライン接続 |

## 機能種別

プロセス間通信・データ連携処理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| cmds | AbstractCmd | Yes | パイプラインに接続するコマンド群 | 空でないこと |
| stdin | Redirectable | No | 標準入力のリダイレクト先 | IO/RawFD/String(ファイル名) |
| stdout | Redirectable | No | 標準出力のリダイレクト先 | IO/RawFD/String(ファイル名) |
| stderr | Redirectable | No | 標準エラー出力のリダイレクト先 | IO/RawFD/String(ファイル名) |
| append | Bool | No | ファイルへの追記モード | true/false |

### 入力データソース

- Juliaプログラムからの直接呼び出し
- バッククォートリテラル（`cmd`）からの構築
- REPLシェルモードからの間接的な利用

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| process | Process/ProcessChain | 生成されたプロセスまたはプロセスチェーン |
| stdout_data | Vector{UInt8}/String | read()で読み取ったプロセス出力 |

### 出力先

- ProcessChain オブジェクトとして返却
- パイプ接続されたプロセスの標準入出力を通じてデータが流れる

## 処理フロー

### 処理シーケンス

```
1. パイプライン構築（pipeline関数 / |> 演算子）
   └─ OrCmds / ErrOrCmds / AndCmds 型の生成
2. パイプペアの作成（link_pipe）
   └─ libuvのuv_pipe_tを2つ作成して接続
3. 各コマンドのスポーン（_spawn）
   └─ OrCmds: 左コマンドのstdout→パイプ→右コマンドのstdin
   └─ ErrOrCmds: 左コマンドのstderr→パイプ→右コマンドのstdin
   └─ AndCmds: 両コマンドを同じstdioで順次スポーン
4. プロセスチェーンへの登録
   └─ 各Processをchain.processesに追加
5. パイプのクローズ
   └─ 子プロセス側のパイプエンドをクローズ
6. データ転送
   └─ プロセス間でデータがパイプを通じて流れる
```

### フローチャート

```mermaid
flowchart TD
    A[pipeline構築] --> B{コマンドの種類}
    B -->|OrCmds a|b| C[link_pipe作成]
    B -->|ErrOrCmds| D[link_pipe作成]
    B -->|AndCmds a&b| E[同一stdioで両方spawn]
    C --> F[左cmdのstdout→pipe_out]
    F --> G[右cmdのstdin→pipe_in]
    D --> H[左cmdのstderr→pipe_out]
    H --> I[右cmdのstdin→pipe_in]
    G --> J[ProcessChainに登録]
    I --> J
    E --> J
    J --> K[パイプクローズ]
    K --> L[データ転送開始]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-01 | パイプ方向 | OrCmds(a\|b): aのstdout→bのstdin | `\|>` 演算子使用時 |
| BR-72-02 | エラーパイプ | ErrOrCmds: aのstderr→bのstdin | エラーリダイレクト使用時 |
| BR-72-03 | 並列実行 | AndCmds(a&b): 同じstdioで両方実行 | `&` 演算子使用時 |
| BR-72-04 | ファイルリダイレクト | FileRedirectでファイル名指定時、読み/書き/追記モードを自動判定 | pipeline(cmd, stdout="file") |
| BR-72-05 | パイプクローズ | 子プロセス側のパイプはspawn後にクローズされる | finally ブロック内 |

### 計算ロジック

パイプラインの構築はコマンドの型に基づく再帰的なディスパッチで実現される。`_spawn(cmds::OrCmds, stdios, chain)` は左右のコマンドにパイプで接続されたstdioを渡して再帰的にspawnする。

## データベース操作仕様

### 操作別データベース影響一覧

該当なし（本機能はデータベースを使用しない）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| ArgumentError | 例外 | 空のコマンドをspawn | コマンドが空でないことを確認 |
| ProcessFailedException | 例外 | パイプライン内のプロセスが非ゼロで終了 | ignorestatus設定またはエラーハンドリング |
| _UVError | UVError | パイプ作成やプロセススポーンが失敗 | OS権限やリソース確認 |
| UV_EPIPE | UVError | open(do)でプロセス出力を消費し切れなかった | 出力を完全に読み取る |

### リトライ仕様

パイプラインにリトライ機構は組み込まれていない。

## トランザクション仕様

該当なし

## パフォーマンス要件

- パイプラインはOSレベルのパイプを使用するため、データ転送はカーネルバッファ経由で効率的に行われる
- 大量データの転送時はバッファサイズがボトルネックになる可能性がある
- SyncCloseFDによるIOコピータスクは@asyncで非同期実行される

## セキュリティ考慮事項

- ファイルリダイレクト先のパス検証はOS任せ
- パイプライン内のプロセスは親プロセスの権限を継承

## 備考

- `pipeline` 関数は `base/cmd.jl` で定義されるが、実際のスポーン処理は `base/process.jl` にある
- CmdRedirect型でI/Oリダイレクトのメタデータを保持
- SpawnIOs（Memory{SpawnIO}）で各fd用のI/Oハンドルを管理

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

パイプラインを構成するコマンド型とI/Oリダイレクト型を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | cmd.jl | `base/cmd.jl` | OrCmds / ErrOrCmds / AndCmds / CmdRedirect / FileRedirect の定義。パイプライン構造がコマンド型の合成で表現されることを理解 |
| 1-2 | process.jl | `base/process.jl` | ProcessChain構造体（27-35行目）、SpawnIO/SpawnIOs型エイリアス（87-88行目）、SyncCloseFD（42-45行目） |

**読解のコツ**: Juliaのパイプライン構築は型システムを活用している。`a |> b` は `OrCmds(a, b)` を生成し、`_spawn` のメソッドディスパッチで異なるパイプ接続戦略が選択される。

#### Step 2: パイプライン構築APIを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | cmd.jl | `base/cmd.jl` | pipeline関数の定義。stdioリダイレクトの指定方法を確認 |

#### Step 3: スポーン処理でのパイプ接続を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | process.jl | `base/process.jl` | _spawn(cmds::OrCmds)（193-205行目）: 標準出力パイプラインの実装。link_pipeでパイプペアを作成し、左cmdのfd2にout_pipe、右cmdのfd1にin_pipeを設定 |
| 3-2 | process.jl | `base/process.jl` | _spawn(cmds::ErrOrCmds)（207-219行目）: 標準エラーパイプラインの実装。左cmdのfd3(stderr)にout_pipeを設定 |
| 3-3 | process.jl | `base/process.jl` | _spawn(cmds::AndCmds)（221-225行目）: 並列実行。同じstdiosで両方をspawn |
| 3-4 | process.jl | `base/process.jl` | _spawn(redirect::CmdRedirect)（182-191行目）: I/Oリダイレクトの適用 |

**主要処理フロー**:
- **193-205行目**: OrCmdsのspawn - link_pipeで(in_pipe, out_pipe)を作成、左cmdのstdout→out_pipe、右cmdのstdin→in_pipe
- **236-251行目**: setup_stdios - 全stdioのオープンとクローズを管理

#### Step 4: I/Oセットアップを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | process.jl | `base/process.jl` | setup_stdio の各オーバーロード（253-338行目）: PipeEndpoint, Pipe, IOStream, FileRedirect, 汎用IO 各種のセットアップ方法 |

**主要処理フロー**:
- **253-271行目**: PipeEndpointのsetup - 未初期化ならlink_pipeで接続
- **289-300行目**: FileRedirectのsetup - ファイルをオープンしてfd返却
- **306-334行目**: 汎用IOのsetup - @asyncで中間コピータスクを起動

### プログラム呼び出し階層図

```
pipeline(cmd1, cmd2) / cmd1 |> cmd2
    │
    ├─ OrCmds(cmd1, cmd2) 生成
    │
    └─ run() / _spawn()
           │
           ├─ _spawn(cmds::OrCmds, stdios, chain)
           │      ├─ link_pipe(false, false)  // パイプペア作成
           │      ├─ _stdio_copy(stdios, 2, out_pipe)
           │      ├─ _spawn(cmds.a, stdios_left, chain)  // 左コマンド
           │      ├─ _stdio_copy(stdios, 1, in_pipe)
           │      ├─ _spawn(cmds.b, stdios_right, chain) // 右コマンド
           │      └─ close_pipe_sync(out_pipe, in_pipe)
           │
           └─ _spawn(cmd::Cmd, stdios, chain)
                  └─ _spawn_primitive(file, cmd, stdios)
                         └─ ccall(:jl_spawn, ...)
```

### データフロー図

```
[入力]               [処理]                    [出力]

cmd1 ─────────▶ _spawn(OrCmds)
                     │
                     ├─ cmd1.stdout ──▶ [pipe] ──▶ cmd2.stdin
                     │
                     └─ ProcessChain ─────────────▶ 結合されたプロセス群

FileRedirect ──▶ setup_stdio ──▶ Filesystem.open ──▶ RawFD

IO (generic) ──▶ setup_stdio ──▶ @async write(in,out) ──▶ SyncCloseFD
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| cmd.jl | `base/cmd.jl` | ソース | Cmd/OrCmds/ErrOrCmds/AndCmds/CmdRedirect型定義、pipeline関数 |
| process.jl | `base/process.jl` | ソース | _spawn各種オーバーロード、setup_stdio、パイプ接続処理 |
| pipe.jl | `base/pipe.jl` | ソース | Pipe/PipeEndpoint構造体、link_pipe/link_pipe!関数 |
| stream.jl | `base/stream.jl` | ソース | libuvストリーム基盤、open_pipe! |
| filesystem.jl | `base/filesystem.jl` | ソース | FileRedirectのファイルオープン処理 |
