# 機能設計書 39-パス解析

## 概要

本ドキュメントは、Nushellにおけるパス解析機能（path parse, path split, path join）の設計を記述する。これらのコマンドはパス文字列を構造化データに変換したり、構造化データからパス文字列を構築したりする。

### 本機能の処理概要

**業務上の目的・背景**：
パスの操作は、ファイル名の変更、拡張子の取得、ディレクトリ構造の解析など多くの場面で必要とされる。Nushellのパス解析コマンドは、パスを構造化データ（レコード/リスト）に分解し、操作後に再構築することを可能にする。

**機能の利用シーン**：
- ファイルパスから拡張子を取得・変更
- ディレクトリパスを構成要素に分解
- 複数のパス要素を結合
- パス構造の分析とレポート生成

**主要な処理内容**：
1. `path parse`: パスを構造化レコード（parent, stem, extension）に変換
2. `path split`: パスを構成要素のリストに分割
3. `path join`: 構造化データまたはリストからパス文字列を構築

**関連システム・外部連携**：
- std::path（Rust標準ライブラリ）

**権限による制御**：
特になし。パス文字列の操作のみで、ファイルシステムへのアクセスなし。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 1 | メインREPL | 参照画面 | コマンド入力・結果表示 |

## 機能種別

パス文字列操作

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input (parse) | String | Yes | 解析対象のパス文字列 | - |
| --extension (parse) | String | No | 手動指定する拡張子 | - |
| input (split) | String | Yes | 分割対象のパス文字列 | - |
| input (join) | String/List/Record | Yes | 結合対象のデータ | - |
| append (join) | String | No | 追加するパス要素 | - |

### 入力データソース

パイプライン入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| parse 出力 | Record | parent, stem, extension（Windowsはprefix追加） |
| split 出力 | List<String> | パス構成要素のリスト |
| join 出力 | String | 結合されたパス文字列 |

### path parse 出力カラム

| カラム名 | 型 | 説明 | 条件 |
|----------|-----|------|------|
| prefix | String | ドライブレター等 | Windows のみ |
| parent | String | 親ディレクトリ | 常に |
| stem | String | 拡張子を除いたファイル名 | 常に |
| extension | String | 拡張子（ドットなし） | 常に |

### 出力先

パイプライン出力

## 処理フロー

### 処理シーケンス

```
[path parse]
1. パス文字列の取得
   └─ パイプライン入力から取得
2. 拡張子オプションの確認
   └─ --extensionで手動指定可能
3. パスの分解
   └─ parent, stem, extensionを抽出
4. レコードの構築
   └─ Windowsの場合はprefixも追加

[path split]
1. パス文字列の取得
   └─ パイプライン入力から取得
2. コンポーネントの抽出
   └─ Path::components()でイテレート
3. リストの構築
   └─ 各コンポーネントを文字列化

[path join]
1. 入力データの取得
   └─ String, List, Recordのいずれか
2. パスの構築
   └─ 入力型に応じた処理
3. 追加パスの結合
   └─ append引数があれば結合
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{コマンド種別}
    B -->|parse| C[パス取得]
    C --> D{--extension指定?}
    D -->|Yes| E[指定拡張子で分解]
    D -->|No| F[自動拡張子検出]
    E --> G[レコード構築]
    F --> G
    G --> H{Windows?}
    H -->|Yes| I[prefix追加]
    H -->|No| J[出力]
    I --> J
    B -->|split| K[パス取得]
    K --> L[components()でイテレート]
    L --> M[リスト構築]
    M --> J
    B -->|join| N{入力型?}
    N -->|String| O[直接結合]
    N -->|List| P[要素を結合]
    N -->|Record| Q[フィールドから構築]
    O --> R{append引数?}
    P --> R
    Q --> R
    R -->|Yes| S[追加結合]
    R -->|No| J
    S --> J
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-39-1 | Windows prefix | WindowsではC:等のprefixを分離 | Windows |
| BR-39-2 | 複合拡張子 | --extensionで複合拡張子（tar.gz等）を指定可能 | --extension指定時 |
| BR-39-3 | 空拡張子 | --extension ''で拡張子なしとして扱う | --extension ''時 |
| BR-39-4 | ルート保持 | splitは/や\をルートとして保持 | 常に |
| BR-39-5 | 相対パス | joinは..等の相対パスを解決しない | 常に |
| BR-39-6 | 絶対パス優先 | joinで絶対パスを追加すると上書き | 常に |

### 許可されるレコードカラム

| プラットフォーム | 許可カラム |
|-----------------|-----------|
| Windows | prefix, parent, stem, extension |
| Unix/Mac | parent, stem, extension |

### 計算ロジック

- parent: `Path::parent()`
- stem: `Path::file_stem()`
- extension: `Path::extension()`
- prefix (Windows): `Component::Prefix`

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

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

該当なし（パス文字列操作のみ）

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| PipelineEmpty | 入力エラー | パイプライン入力が空 | 入力を提供 |
| OnlySupportsThisInputType | 型エラー | String/List/Record以外の入力 | 正しい型を入力 |
| UnsupportedInput | 入力エラー | 不正なレコードカラム | 許可されたカラムを使用 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- const実行をサポート（コンパイル時評価可能）
- リスト入力に対してはmap処理で効率的に実行

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

- ファイルシステムへのアクセスなし
- パス文字列の変換のみ

## 備考

- path parseの出力はpath joinで再構築可能
- path splitの出力はpath joinで再構築可能
- プラットフォーム固有のパスセパレータを使用

---

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

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

### 推奨読解順序

#### Step 1: 共通処理を理解する

pathモジュールの共通処理とプラットフォーム差異を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | mod.rs | `crates/nu-command/src/path/mod.rs` | ALLOWED_COLUMNS定義（28-31行目） |
| 1-2 | mod.rs | `crates/nu-command/src/path/mod.rs` | operate関数（35-45行目） |

**読解のコツ**: ALLOWED_COLUMNSはプラットフォーム別にコンパイル時に決定される。operate関数は共通のエラーハンドリングを提供。

#### Step 2: path parseを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | parse.rs | `crates/nu-command/src/path/parse.rs` | PathParseコマンドの実装 |

**主要処理フロー**:
1. **48-68行目**: run関数でパイプライン処理
2. **186-245行目**: parse関数でパス分解
3. **189-200行目**: Windows prefixの処理
4. **214-242行目**: --extension指定時の処理

#### Step 3: path splitを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | split.rs | `crates/nu-command/src/path/split.rs` | PathSplitコマンドの実装 |

**主要処理フロー**:
1. **38-56行目**: run関数でパイプライン処理
2. **158-168行目**: split関数でcomponents()をイテレート
3. **170-186行目**: process_component（プラットフォーム別処理）

#### Step 4: path joinを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | join.rs | `crates/nu-command/src/path/join.rs` | PathJoinコマンドの実装 |

**主要処理フロー**:
1. **49-61行目**: run関数でパイプライン処理
2. **184-193行目**: handle_value関数で入力型判定
3. **195-202行目**: join_single（パス結合）
4. **240-245行目**: join_record（レコードからパス構築）

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

```
PathParse::run()
    │
    ├─ call.get_flag("extension") [拡張子オプション]
    │
    └─ input.map(operate(&parse))
           │
           └─ parse()
                  │
                  ├─ path.parent()
                  ├─ path.file_stem()
                  ├─ path.extension()
                  └─ [Windows] Component::Prefix

PathSplit::run()
    │
    └─ input.map(operate(&split))
           │
           └─ split()
                  │
                  └─ path.components()
                         │
                         └─ process_component()

PathJoin::run()
    │
    ├─ call.rest("append") [追加パス]
    │
    └─ handle_value()
           │
           ├─ [String] → join_single()
           ├─ [List] → join_list()
           └─ [Record] → join_record()
                              │
                              └─ merge_record()
```

### データフロー図

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

String ────────▶ PathParse::run() ──────▶ Record
    │                  │                    │
    │                  └─ parse()           ├─ parent
    │                                       ├─ stem
    │                                       ├─ extension
    │                                       └─ [prefix]

String ────────▶ PathSplit::run() ──────▶ List<String>
    │                  │
    │                  └─ split()
    │                         │
    │                         └─ components()

String/List/  ──▶ PathJoin::run() ───────▶ String
Record              │
    │               ├─ join_single()
    │               ├─ join_list()
    │               └─ join_record()
    │
append ─────────────┘
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| parse.rs | `crates/nu-command/src/path/parse.rs` | ソース | path parse コマンド実装 |
| split.rs | `crates/nu-command/src/path/split.rs` | ソース | path split コマンド実装 |
| join.rs | `crates/nu-command/src/path/join.rs` | ソース | path join コマンド実装 |
| mod.rs | `crates/nu-command/src/path/mod.rs` | ソース | pathモジュール定義・共通処理 |
