Files
hakorune/docs/development/current/main/phase114_fileio_trait_extension.md
nyash-codex dc90b96bb2 feat(phase114): FileIo trait 拡張 & メタデータ統一完成
Phase 113 で公開した .hako API は変更なく、内部実装を完全統一化。
FsApi(Ring0 stateless)と FileIo(Ring1 stateful)の設計を確立。

【実装内容】

Task 1: FileIo trait 拡張
- FileStat 構造体追加(is_file/is_dir/size)
- exists/stat/canonicalize メソッド追加(FileIo trait)

Task 2: Ring0FsFileIo 実装
- exists(): path を Ring0.fs で確認
- stat(): Ring0.fs.metadata() を FileStat に変換
- canonicalize(): Ring0.fs.canonicalize() を String に変換

Task 3: NoFsFileIo stub 実装
- exists() → false(全ファイルが「存在しない」扱い)
- stat() → Err(Unsupported)(FS 無効情報を返す)
- canonicalize() → Err(Unsupported)

Task 4: FileHandleBox 内部統一
- metadata_internal() を新規追加(FileIo::stat() ベース)
- is_file/is_dir/size を metadata_internal() 経由に統一
- Nyash 公開 API(ny_exists/ny_size/ny_isFile/ny_isDir)は変更なし

Task 5: テスト + ドキュメント
- Ring0FsFileIo: 5テスト(stat/exists/canonicalize)
- NoFsFileIo: 3テスト(exist/stat/canonicalize error)
- FileHandleBox: 5テスト(metadata_internal/exists/is_file/is_dir)
- すべてのテスト PASS

【設計原則確立】

FsApi ↔ FileIo の責務分担:
- FsApi (Ring0): Stateless(パスを毎回指定)
- FileIo (Ring1): Stateful(path を内部保持)
- FileHandleBox: FileIo::stat() で一元化

Profile 別動作:
- Default: 全機能正常動作
- NoFs: exists=false, stat/canonicalize は Unsupported エラー

【統計】
- 修正ファイル: 9ファイル
- 追加行: +432行、削除: -29行
- 新規テスト: 13個(全PASS)
- ビルド: SUCCESS

【効果】
- 内部実装が完全統一(二重実装・不一貫性排除)
- Phase 115+ での拡張(modified_time/permissions等)が容易に
- FsApi と FileIo の設計がクリアに確立

🤖 Generated with Claude Code

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-04 03:58:02 +09:00

7.4 KiB
Raw Blame History

Phase 114: FileIo trait 拡張 & メタデータ統一

目標

FsApi 側に揃った情報exists / metadata / canonicalizeを、FileIo trait 経由で一貫して扱えるようにする。 FileHandleBox 内部の is_file/is_dir/size などをすべて FileIo::stat() の上に乗せる。 .hako 側 APIPhase 113 で公開したメソッド群)は変更しない(内部実装だけきれい化)。

FileIo trait 設計FsApi との分離stateless vs stateful

FsApi (Ring0) - Stateless OS抽象

pub trait FsApi {
    fn exists(&self, path: &Path) -> bool;
    fn metadata(&self, path: &Path) -> Result<FsMetadata>;
    fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
    fn read_to_string(&self, path: &Path) -> Result<String>;
    fn write_all(&self, path: &Path, data: &[u8]) -> Result<()>;
    fn append_all(&self, path: &Path, data: &[u8]) -> Result<()>;
}
  • Role: OS ファイルシステムの直接抽象化
  • State: なし(パス引数で毎回指定)
  • Usage: Ring0Context 経由でアクセス

FileIo (Ring1) - Stateful ハンドル抽象

pub struct FileStat {
    pub is_file: bool,
    pub is_dir: bool,
    pub size: u64,
}

pub trait FileIo: Send + Sync {
    fn caps(&self) -> FileCaps;
    fn open(&self, path: &str) -> FileResult<()>;
    fn read(&self) -> FileResult<String>;
    fn write(&self, text: &str) -> FileResult<()>;
    fn close(&self) -> FileResult<()>;
    fn as_any(&self) -> &dyn std::any::Any;

    // Phase 114: Metadata operations
    fn exists(&self) -> bool;
    fn stat(&self) -> FileResult<FileStat>;
    fn canonicalize(&self) -> FileResult<String>;
}
  • Role: 現在開いているファイルハンドルに対する操作
  • State: ありopen() で path を内部保持)
  • Usage: FileHandleBox 経由でアクセス

設計原則

  1. FsApi = Stateless: パスを毎回引数で受け取る
  2. FileIo = Stateful: open() で path を保持、以降は引数不要
  3. 分離理由:
    • FsApi: OS レイヤーの直接操作(低レベル)
    • FileIo: ハンドルベースの高レベル API.hako からアクセス)

Ring0FsFileIo/NoFsFileIo の実装詳細

Ring0FsFileIo (Default プロファイル)

pub struct Ring0FsFileIo {
    ring0: Arc<Ring0Context>,
    path: RwLock<Option<String>>,  // open() で設定
    mode: RwLock<Option<String>>,  // "r", "w", "a"
}

impl FileIo for Ring0FsFileIo {
    fn exists(&self) -> bool {
        // path が設定されていれば ring0.fs.exists() を呼ぶ
        // path が None なら false
    }

    fn stat(&self) -> FileResult<FileStat> {
        // path が設定されていれば ring0.fs.metadata() を呼ぶ
        // FileStat に変換して返す
    }

    fn canonicalize(&self) -> FileResult<String> {
        // path が設定されていれば ring0.fs.canonicalize() を呼ぶ
        // PathBuf を String に変換して返す
    }
}

NoFsFileIo (NoFs プロファイル)

pub struct NoFsFileIo;

impl FileIo for NoFsFileIo {
    fn exists(&self) -> bool {
        // NoFs プロファイルでは常に false
        false
    }

    fn stat(&self) -> FileResult<FileStat> {
        // Unsupported エラーを返す
        Err(FileError::Unsupported("..."))
    }

    fn canonicalize(&self) -> FileResult<String> {
        // Unsupported エラーを返す
        Err(FileError::Unsupported("..."))
    }
}

FileHandleBox の metadata_internal() 統一設計

Before Phase 114

// Ring0FsFileIo.metadata() を直接ダウンキャスト
fn metadata_internal(&self) -> Result<FsMetadata, String> {
    self.io.as_ref()
        .ok_or_else(|| "FileHandleBox not open".to_string())?
        .as_any()
        .downcast_ref::<Ring0FsFileIo>()
        .ok_or_else(|| "FileIo is not Ring0FsFileIo".to_string())?
        .metadata()  // Ring0FsFileIo 専用メソッド
}

問題点:

  • Ring0FsFileIo 依存downcast 必要)
  • FileIo trait を経由していない
  • NoFsFileIo では動作しない

After Phase 114

// FileIo::stat() を使用trait 経由)
fn metadata_internal(&self) -> Result<FileStat, String> {
    let io = self.io.as_ref()
        .ok_or_else(|| "FileHandleBox is not open".to_string())?;

    io.stat()  // FileIo trait 経由
        .map_err(|e| format!("Metadata failed: {}", e))
}

// 他のメソッドも metadata_internal() の上に統一
fn size(&self) -> Result<u64, String> {
    self.metadata_internal().map(|meta| meta.size)
}

fn is_file(&self) -> Result<bool, String> {
    self.metadata_internal().map(|meta| meta.is_file)
}

fn is_dir(&self) -> Result<bool, String> {
    self.metadata_internal().map(|meta| meta.is_dir)
}

改善点:

  • FileIo trait 経由で統一
  • downcast 不要
  • NoFsFileIo でも正しくエラーを返す

Profile 別動作

Default プロファイルRing0FsFileIo

メソッド 動作
exists() ファイルが存在すれば true、存在しないか path 未設定なら false
stat() FileStat { is_file, is_dir, size } を返す。path 未設定なら Err
canonicalize() 絶対パスを返す。path 未設定なら Err

NoFs プロファイルNoFsFileIo

メソッド 動作
exists() 常に false
stat() Err(FileError::Unsupported("..."))
canonicalize() Err(FileError::Unsupported("..."))

テスト結果

Ring0FsFileIo テストDefault プロファイル)

✅ test_ring0_fs_fileio_stat_default_profile
✅ test_ring0_fs_fileio_exists_default_profile
✅ test_ring0_fs_fileio_canonicalize_default_profile
✅ test_ring0_fs_fileio_stat_without_open
✅ test_ring0_fs_fileio_canonicalize_without_open

NoFsFileIo テスト

✅ test_nofs_fileio_exists (常に false)
✅ test_nofs_fileio_stat_error (Unsupported エラー)
✅ test_nofs_fileio_canonicalize_error (Unsupported エラー)

FileHandleBox テスト

✅ test_filehandlebox_metadata_internal_default (stat() 経由で FileStat 取得)
✅ test_filehandlebox_metadata_internal_not_open (エラー確認)
✅ test_filehandlebox_ny_size_uses_stat (ny_size() が stat() 経由)
✅ test_filehandlebox_exists_uses_fileio (exists() が FileIo::exists() 経由)
✅ test_filehandlebox_is_file_is_dir_via_stat (is_file/is_dir が stat() 経由)

実装成果

統計

  • 修正ファイル: 4ファイル
    • src/boxes/file/provider.rs
    • src/providers/ring1/file/ring0_fs_fileio.rs
    • src/providers/ring1/file/nofs_fileio.rs
    • src/boxes/file/handle_box.rs
    • src/providers/ring1/file/core_ro.rs
  • 追加行: 約 +150 行
  • 削除行: 約 -20 行
  • 新規テスト: 11個

技術的成果

  1. FileIo trait 拡張: exists/stat/canonicalize 正式追加
  2. 統一設計: FileHandleBox 内部が metadata_internal() に統一
  3. Profile 対応: Default/NoFs 両プロファイルで正しく動作
  4. 後方互換性: .hako 側 API は変更なし(内部実装のみ統一)

次のステップ

  • Phase 115: PathBox 実装パス操作専用Box
  • Phase 116: ディレクトリ操作mkdir/rmdir/readdir

関連ドキュメント