# 機能設計書 72-Dirクラス

## 概要

本ドキュメントは、mrubyにおけるDirクラスの設計について記述する。ディレクトリの列挙・作成・削除・カレントディレクトリ操作等のファイルシステムディレクトリ操作を提供する。

### 本機能の処理概要

DirクラスはPOSIX標準のディレクトリ操作をRubyオブジェクトとしてラップし、ディレクトリエントリの読み取り、作成、削除、カレントディレクトリの変更等の機能を提供する。HAL（Hardware Abstraction Layer）を介してプラットフォーム非依存のインターフェースを実現している。

**業務上の目的・背景**：mrubyを組み込みシステムやスクリプティング環境で使用する際、ファイルシステムのディレクトリ操作は基本的な要件である。本クラスにより、ディレクトリ内のファイル列挙、ディレクトリ構造の作成・削除、作業ディレクトリの管理をRubyコードで記述できるようになる。

**機能の利用シーン**：ファイル管理スクリプトでのディレクトリ走査、ビルドシステムでの出力ディレクトリ作成、一時ディレクトリの作成と削除、プロジェクト内のファイル一覧取得など。

**主要な処理内容**：
1. ディレクトリオブジェクトのオープン・クローズ・エントリ読み取り
2. ディレクトリの作成（mkdir）・削除（rmdir/delete）
3. カレントディレクトリの取得（getwd/pwd）・変更（chdir）
4. ディレクトリの存在確認（exist?）・空判定（empty?）
5. ディレクトリエントリの一覧取得（entries/children）
6. ブロック付きchdir、ブロック付きopenによるリソース安全な操作

**関連システム・外部連携**：OSのファイルシステムAPI（opendir/readdir/closedir/mkdir/rmdir/getcwd/chdir等）にHALを介してアクセスする。

**権限による制御**：ディレクトリの作成・削除・chrootはOS側のファイルシステムパーミッションに依存する。chrootは特権プロセスでのみ実行可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | - | - | CLI/ライブラリとして利用。画面なし |

## 機能種別

ファイルシステム操作 / ディレクトリ管理

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| path | String | Yes | ディレクトリパス文字列 | NUL終端文字列（"z"フォーマット） |
| mode | Integer | No | mkdir時のパーミッション（デフォルト0777） | 整数型チェック |
| pos | Integer | Yes(seekのみ) | seekの位置 | 整数型チェック |

### 入力データソース

- Rubyコードからのパス文字列指定
- OSのファイルシステム

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| Dirオブジェクト | Dir | ディレクトリストリームオブジェクト |
| エントリ名 | String | ディレクトリエントリのファイル名 |
| エントリ配列 | Array | ディレクトリ内の全エントリ名配列 |
| パス文字列 | String | カレントディレクトリのパス |
| 位置 | Integer | ディレクトリストリーム内の現在位置 |
| 真偽値 | Boolean | exist?/empty?の結果 |

### 出力先

Rubyオブジェクトとして返却。

## 処理フロー

### 処理シーケンス

```
1. Dir.new(path)
   ├─ mrb_hal_dir_open でディレクトリをオープン
   └─ mrb_dir構造体にハンドルを保存

2. dir.read
   ├─ mrb_hal_dir_read で次のエントリを読み取り
   └─ 文字列またはnilを返す

3. dir.each { |name| ... }
   ├─ readを繰り返し呼び出し
   └─ nilが返るまでブロックを実行

4. Dir.entries(path)
   ├─ ディレクトリをオープン
   ├─ 全エントリを配列に追加
   └─ ディレクトリをクローズして配列を返す

5. Dir.chdir(path, &block)
   ├─ ブロックなし: _chdirで直接変更
   └─ ブロックあり: 現在パス保存→変更→ブロック実行→復元
```

### フローチャート

```mermaid
flowchart TD
    A[Dir.new path] --> B[mrb_hal_dir_open]
    B --> C{オープン成功?}
    C -->|Yes| D[ハンドル保存]
    C -->|No| E[SystemCallError]
    D --> F[dir.read]
    F --> G[mrb_hal_dir_read]
    G --> H{エントリあり?}
    H -->|Yes| I[文字列を返す]
    H -->|No| J[nilを返す]
    I --> F
    J --> K[dir.close]
    K --> L[mrb_hal_dir_close]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-72-1 | HAL抽象化 | 全ディレクトリ操作はHALインターフェースを介して行う | 常時 |
| BR-72-2 | リソース管理 | Dir.openにブロックを渡した場合、ブロック終了時に自動クローズ | ブロック付きopen呼び出し時 |
| BR-72-3 | chdir復元 | ブロック付きchdirは処理後に元のディレクトリに復元する | ブロック付きchdir呼び出し時 |
| BR-72-4 | ドットスキップ | children/each_childでは"."と".."をスキップする | children/each_child呼び出し時 |
| BR-72-5 | mkdirデフォルト | mkdirのデフォルトパーミッションは0777 | mode未指定時 |

### 計算ロジック

特になし。

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

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

本機能はデータベースを使用しない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| - | SystemCallError | ディレクトリオープン失敗 | mrb_sys_fail(mrb, path) |
| - | SystemCallError | ディレクトリ作成/削除失敗 | mrb_sys_fail(mrb, path) |
| - | IOError | クローズ済みディレクトリへの操作 | "closed directory"メッセージ |
| - | NotImplementedError | chrootが未サポートのプラットフォーム | ENOSYS時に発生 |
| - | NotImplementedError | seek/tellが未サポートのプラットフォーム | ENOSYS時に発生 |

### リトライ仕様

リトライは不要。

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

該当なし。

## パフォーマンス要件

- ディレクトリエントリの読み取りはストリーミング方式（全件メモリ保持不要）
- entries/childrenは全件を配列に格納するため、大量エントリ時はメモリ消費に注意

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

- chrootは特権プロセスでのみ実行可能
- mkdirのパーミッションはumaskにより制限される可能性がある
- パス文字列のバリデーションはOS側に委譲

## 備考

- DirクラスはEnumerableをincludeしており、each/each_childでイテレーション可能
- pos/pos=はそれぞれtell/seekのエイリアス
- pwd/exists?/rmdir/unlinkはそれぞれgetwd/exist?/delete/deleteのエイリアス

---

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

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

### 推奨読解順序

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

HALインターフェースとDirオブジェクトの内部構造を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | dir_hal.h | `mrbgems/mruby-dir/include/dir_hal.h` | HALの全関数プロトタイプ（open/close/read等） |
| 1-2 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_dir構造体定義（21-23行目） |

**読解のコツ**: dir_hal.hはプラットフォーム非依存の抽象インターフェースを定義する。mrb_dir_handle型はHAL実装側で定義される不透明型。mrb_dir構造体はこのハンドルをラップするだけのシンプルな構造。

#### Step 2: エントリーポイントを理解する

gem初期化で登録されるメソッドの全体像を把握する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_mruby_dir_gem_init関数（460-487行目） |

**主要処理フロー**:
1. **465行目**: HALの初期化
2. **467行目**: Dirクラス定義（Object配下）
3. **468行目**: MRB_TT_DATAとしてインスタンスタイプ設定
4. **469-477行目**: クラスメソッド登録（delete/exist?/getwd/mkdir/_chdir/chroot/empty?/entries/children）
5. **479-484行目**: インスタンスメソッド登録（close/initialize/read/rewind/seek/tell）
6. **486行目**: IOErrorクラス定義

#### Step 3: コアC実装を理解する

ディレクトリ操作のC実装を読む。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_dir_init（73-97行目）: Dir.new |
| 3-2 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_dir_read（289-307行目）: dir.read |
| 3-3 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_dir_getwd（152-168行目）: Dir.getwd |
| 3-4 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_dir_entries（406-426行目）: Dir.entries |
| 3-5 | dir.c | `mrbgems/mruby-dir/src/dir.c` | mrb_dir_children（436-458行目）: Dir.children |

**主要処理フロー**:
- **91行目**: mrb_get_argsで"z"フォーマット（NUL終端文字列）でパスを取得
- **92-93行目**: mrb_hal_dir_openでオープン、失敗時はmrb_sys_fail
- **300行目**: mrb_hal_dir_readで次のエントリ名を取得
- **155-167行目**: getwd内のバッファ自動拡張ロジック（ERANGE時にサイズ倍増）

#### Step 4: Ruby実装を理解する

Ruby側で定義されるイテレーション・ブロック付き操作を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | dir.rb | `mrbgems/mruby-dir/mrblib/dir.rb` | each/each_childメソッド（15-40行目） |
| 4-2 | dir.rb | `mrbgems/mruby-dir/mrblib/dir.rb` | Dir.open（76-90行目）: ブロック付きオープン |
| 4-3 | dir.rb | `mrbgems/mruby-dir/mrblib/dir.rb` | Dir.chdir（105-117行目）: ブロック付きchdir |
| 4-4 | dir.rb | `mrbgems/mruby-dir/mrblib/dir.rb` | エイリアス定義（42-43行目、119-122行目） |

**主要処理フロー**:
- **15-21行目**: eachメソッドはreadをnilまで繰り返しブロック呼び出し
- **76-90行目**: Dir.openはブロック付きの場合ensureでclose
- **105-117行目**: Dir.chdirはブロック付きの場合ensureで元ディレクトリに復帰

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

```
mrb_mruby_dir_gem_init
    ├─ mrb_hal_dir_init
    ├─ mrb_define_class_id (Dir)
    └─ 各メソッド登録

Dir.new(path)
    └─ mrb_dir_init
        └─ mrb_hal_dir_open

dir.read
    └─ mrb_dir_read
        └─ mrb_hal_dir_read

dir.each { |name| ... }  (Ruby)
    └─ dir.read (繰り返し)

Dir.entries(path)
    └─ mrb_dir_entries
        ├─ mrb_hal_dir_open
        ├─ mrb_hal_dir_read (繰り返し)
        └─ mrb_hal_dir_close

Dir.chdir(path, &block)  (Ruby)
    ├─ Dir.getwd (保存)
    ├─ Dir._chdir(path)
    │   └─ mrb_dir_chdir → mrb_hal_dir_chdir
    ├─ block.call(path)
    └─ Dir._chdir(wd) (復元, ensure)
```

### データフロー図

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

パス文字列 ────▶ mrb_hal_dir_open ──────▶ Dirオブジェクト
                     │
                     ▼
              mrb_hal_dir_read ──────▶ エントリ名(String/nil)
                     │
                     ▼ (繰り返し)
              全エントリ収集 ──────────▶ Array(entries/children)
                     │
                     ▼
              mrb_hal_dir_close ─────▶ nil
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| dir.c | `mrbgems/mruby-dir/src/dir.c` | ソース | Dirクラスのメイン実装（C） |
| dir.rb | `mrbgems/mruby-dir/mrblib/dir.rb` | ソース | Dirクラスの拡張実装（Ruby） |
| dir_hal.h | `mrbgems/mruby-dir/include/dir_hal.h` | ヘッダ | HALインターフェース定義 |
| mrbgem.rake | `mrbgems/mruby-dir/mrbgem.rake` | 設定 | gem定義ファイル |
| dir.rb (test) | `mrbgems/mruby-dir/test/dir.rb` | テスト | テストコード |
| dirtest.c | `mrbgems/mruby-dir/test/dirtest.c` | テスト | テスト用C補助コード |
