# 機能設計書 40-パス情報取得

## 概要

本ドキュメントは、Nushellにおけるパス情報取得機能（path basename, path dirname, path exists, path type）の設計を記述する。これらのコマンドはパスの各種情報を取得・検査する。

### 本機能の処理概要

**業務上の目的・背景**：
パスの構成要素の取得や存在確認は、ファイル操作の前処理として頻繁に必要とされる。これらのコマンドは、パスの最後の要素（basename）、親ディレクトリ（dirname）、存在確認（exists）、種別判定（type）を提供する。

**機能の利用シーン**：
- ファイル名のみを抽出する（basename）
- ファイルの親ディレクトリを取得する（dirname）
- ファイル/ディレクトリの存在を確認する（exists）
- パスがファイルかディレクトリかを判定する（type）

**主要な処理内容**：
1. `path basename`: パスの最後の要素を取得
2. `path dirname`: パスの親ディレクトリを取得
3. `path exists`: パスの存在を確認（true/false）
4. `path type`: パスの種別を取得（file/dir/symlink等）

**関連システム・外部連携**：
- std::path（Rust標準ライブラリ）
- std::fs（ファイルシステムアクセス）

**権限による制御**：
- path exists/path type: 対象パスへの読み取り権限が必要
- path basename/dirname: 権限不要（文字列操作のみ）

## 関連画面

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

## 機能種別

パス情報取得

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| input (basename) | String | Yes | 対象のパス文字列 | - |
| --replace (basename) | String | No | basenameを置換する文字列 | - |
| input (dirname) | String | Yes | 対象のパス文字列 | - |
| --replace (dirname) | String | No | dirnameを置換する文字列 | - |
| --num-levels (dirname) | Int | No | 上位に遡るレベル数 | 正の整数 |
| input (exists) | String | Yes | 確認対象のパス | - |
| --no-symlink (exists) | Switch | No | シンボリックリンクを解決しない | - |
| input (type) | String | Yes | 確認対象のパス | - |

### 入力データソース

パイプライン入力

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| basename 出力 | String | ファイル名（またはreplace適用後のパス） |
| dirname 出力 | String | 親ディレクトリ（またはreplace適用後のパス） |
| exists 出力 | Bool | 存在する場合true |
| type 出力 | String/Nothing | 種別（file/dir/symlink等）、存在しない場合null |

### path type 出力値

| 値 | 説明 |
|-----|------|
| file | 通常のファイル |
| dir | ディレクトリ |
| symlink | シンボリックリンク |
| block device | ブロックデバイス（Unix） |
| char device | キャラクタデバイス（Unix） |
| pipe | 名前付きパイプ（Unix） |
| socket | Unixソケット（Unix） |
| unknown | 不明 |

### 出力先

パイプライン出力

## 処理フロー

### 処理シーケンス

```
[path basename]
1. パス文字列の取得
   └─ パイプライン入力から取得
2. basenameの抽出
   └─ Path::file_name()を使用
3. --replace処理
   └─ 指定時はwith_file_name()で置換

[path dirname]
1. パス文字列の取得
   └─ パイプライン入力から取得
2. 上位レベルの計算
   └─ num-levels回parent()を呼び出し
3. --replace処理
   └─ 指定時は親部分を置換

[path exists]
1. パス文字列の取得
   └─ パイプライン入力から取得
2. パスの展開
   └─ 相対パスを絶対パスに変換
3. 存在確認
   └─ path.exists()またはsymlink_metadata()

[path type]
1. パス文字列の取得
   └─ パイプライン入力から取得
2. パスの展開
   └─ 相対パスを絶対パスに変換
3. 種別の判定
   └─ symlink_metadata().file_type()
```

### フローチャート

```mermaid
flowchart TD
    A[開始] --> B{コマンド種別}
    B -->|basename| C[パス取得]
    C --> D[file_name抽出]
    D --> E{--replace?}
    E -->|Yes| F[with_file_name]
    E -->|No| G[basename出力]
    F --> G
    B -->|dirname| H[パス取得]
    H --> I[num-levels回parent]
    I --> J{--replace?}
    J -->|Yes| K[親部分置換]
    J -->|No| L[dirname出力]
    K --> L
    B -->|exists| M[パス取得]
    M --> N[パス展開]
    N --> O{--no-symlink?}
    O -->|Yes| P[symlink_metadata]
    O -->|No| Q[path.exists]
    P --> R[Bool出力]
    Q --> R
    B -->|type| S[パス取得]
    S --> T[パス展開]
    T --> U[symlink_metadata]
    U --> V{存在?}
    V -->|No| W[null出力]
    V -->|Yes| X[file_type判定]
    X --> Y[種別文字列出力]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-40-1 | 空basename | basenameが空の場合は空文字列を返す | ルートパス等 |
| BR-40-2 | 上位遡り | num-levelsで指定した回数parent()を呼び出す | --num-levels指定時 |
| BR-40-3 | 上位到達 | ルートに到達したらそれ以上遡らない | 上限到達時 |
| BR-40-4 | 空パス | 空パスの場合existsはfalseを返す | 空文字列入力時 |
| BR-40-5 | 存在しない | 存在しないパスのtypeはnullを返す | NotFound時 |
| BR-40-6 | symlink | typeはシンボリックリンク自体の種別を返す | シンボリックリンク時 |

### 計算ロジック

- basename: `Path::file_name()`
- dirname: `Path::parent()`を指定回数呼び出し
- exists: `path.exists()`または`std::fs::symlink_metadata()`
- type: `std::fs::Metadata::file_type()`

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

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

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

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| PipelineEmpty | 入力エラー | パイプライン入力が空 | 入力を提供 |
| OnlySupportsThisInputType | 型エラー | String以外の入力 | 文字列を入力 |
| IOError | ファイルエラー | アクセス失敗（exists/type） | 権限を確認 |

### リトライ仕様

該当なし

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

該当なし

## パフォーマンス要件

- const実行をサポート（basename, dirname, exists, type）
- リスト入力に対してはmap処理で効率的に実行

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

- exists/typeは対象パスへの読み取り権限が必要
- 権限がない場合はIOErrorを返す

## 備考

- Unix固有の種別（block device, pipe, socket等）はUnixでのみ返される
- path existsは権限がない場合もfalseを返す可能性がある
- path typeのnull出力は存在しないパスを示す

---

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

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

### 推奨読解順序

#### Step 1: path basenameを理解する

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

**主要処理フロー**:
1. **46-66行目**: run関数でパイプライン処理
2. **138-149行目**: get_basename関数で抽出/置換
3. **140行目**: --replace時のwith_file_name処理

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

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

**主要処理フロー**:
1. **53-74行目**: run関数でパイプライン処理
2. **157-185行目**: get_dirname関数で親取得
3. **162-170行目**: num-levels回のparent()呼び出し

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

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

**主要処理フロー**:
1. **49-69行目**: run関数でパイプライン処理
2. **131-157行目**: exists関数で存在確認
3. **136-146行目**: --no-symlink時のsymlink_metadata使用

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

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

**主要処理フロー**:
1. **47-67行目**: run関数でパイプライン処理
2. **106-113行目**: path_type関数で種別判定
3. **115-140行目**: get_file_type関数（Unix固有種別含む）

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

```
PathBasename::run()
    │
    └─ input.map(operate(&get_basename))
           │
           └─ get_basename()
                  │
                  ├─ path.file_name()
                  │
                  └─ [--replace] → path.with_file_name()

PathDirname::run()
    │
    └─ input.map(operate(&get_dirname))
           │
           └─ get_dirname()
                  │
                  ├─ path.parent() × num_levels
                  │
                  └─ [--replace] → path.strip_prefix() + join

PathExists::run()
    │
    └─ input.map(operate(&exists))
           │
           └─ exists()
                  │
                  ├─ expand_path_with()
                  │
                  ├─ [--no-symlink] → symlink_metadata()
                  │
                  └─ [default] → path.exists()

PathType::run()
    │
    └─ input.map(operate(&path_type))
           │
           └─ path_type()
                  │
                  ├─ expand_path_with()
                  │
                  ├─ symlink_metadata()
                  │
                  └─ get_file_type()
```

### データフロー図

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

String ────────▶ PathBasename::run() ───▶ String
    │                  │
--replace ─────────────┘

String ────────▶ PathDirname::run() ────▶ String
    │                  │
--replace ─────────────┤
--num-levels ──────────┘

String ────────▶ PathExists::run() ─────▶ Bool
    │                  │
--no-symlink ──────────┘

String ────────▶ PathType::run() ───────▶ String/Null
                       │
                       └─ get_file_type()
                              │
                              ├─ file
                              ├─ dir
                              ├─ symlink
                              └─ [Unix] block/char/pipe/socket
```

### 関連ファイル一覧

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