Files
hakorune/docs/development/current/main/phase107_fsapi_fileio_bridge.md

315 lines
12 KiB
Markdown
Raw Normal View History

# Phase 107: Ring0.FsApi ↔ FileIo 統合FileBox の足場固め)
## 0. ゴール
- Ring0.FsApiOS ファイル APIと FileIoFileBox プラグイン用 I/O 抽象)の関係を明確に整理
- 自前 FileBox 実装の「OS への道」を一本のパイプにする:
```
FileBox → FileIo implementation → Ring0.FsApi → std::fs
```
- これにより以下を実現:
- FileBox まわりのハードコード削減
- 将来の no-fs プロファイル / mock Fs の差し替え容易化
- Ring0 と Ring1(FileBox) の依存関係の明確化
---
## 1. スコープと非スコープ
### スコープ(今回やること)
1. **設計&ドキュメント**
- FsApi / FileIo / FileBox / provider_lock の関係を図と文章で整理
- FileIo を「Ring0.FsApi のラッパprovider」として位置づけ
2. **実装(段階的)**
- Ring0.FsApi を read/write の SSOT として確認・微調整
- Ring0 ベースの FileIo 実装追加Ring0FsFileIo
- selfhost/通常ランタイムでこれをデフォルト provider として登録
3. **Fail-Fast との接続**
- Phase 106 の「FileBox provider 必須チェック」と矛盾しない仕様確認
- 標準パスで必ず Ring0FsFileIo が入ることを保証
### 非スコープ(今回はやらない)
- FileBox の write/delete/copy 全実装(別 Phase
- FileBox API 大幅変更(メソッド名変更等)
- minimal/no-fs プロファイル実装Phase 108 候補)
---
## 2. Task 1: FsApi を SSOT として整理docs + 確認)
### 2.1 実装内容
**ファイル**:
- `src/runtime/ring0/traits.rs`
- `docs/development/current/main/core_boxes_design.md`
- **新規**: `docs/development/current/main/phase107_fsapi_fileio_bridge.md` (このファイル)
### 2.2 やること
1. **traits.rs で FsApi の公開インターフェースを確認**
```rust
pub trait FsApi: Send + Sync {
fn read_file(&self, path: &Path) -> Result<Vec<u8>, IoError>;
fn write_file(&self, path: &Path, content: &[u8]) -> Result<(), IoError>;
// stat / exists 相当?
}
```
2. **このドキュメントphase107_fsapi_fileio_bridge.mdに記載**
- 「FsApi = OS ファイル I/O の SSOTRust ローカル)」
- 「FileIo = FileBox 用 provider interface。実装の 1 つとして FsApi を内部で使う」という関係
- Ring0 → Ring1 の一方向依存の図
3. **core_boxes_design.md に一文追加**
- FileBox セクションに「実体 I/O は FileIo → FsApi → OS」を記載
---
## 3. Task 2: FileIo を「FsApi ラッパ」として設計
### 3.1 実装内容
**ファイル**:
- `src/boxes/file/provider.rs`
- `phase107_fsapi_fileio_bridge.md`
### 3.2 やること
1. **FileIo trait の役割を明確化**
```rust
pub trait FileIo: Send + Sync {
fn caps(&self) -> FileCaps;
fn open(&self, path: &str) -> FileResult<()>;
fn read(&self) -> FileResult<String>;
fn close(&self) -> FileResult<()>;
}
```
- **設計**: FileIo は「現在開いているファイルハンドル」に対する操作
- **FsApi** は statelessPath → 読込/書込)
- **FileIo** は statefulopen → read/close
2. **実装設計を docs に記載**(擬似コード):
```rust
pub struct Ring0FsFileIo {
ring0: Arc<Ring0Context>,
path: String,
caps: FileCaps,
}
impl FileIo for Ring0FsFileIo {
fn caps(&self) -> FileCaps { self.caps }
fn open(&self, path: &str) -> FileResult<()> {
// FsApi 経由で存在確認など
Ok(())
}
fn read(&self) -> FileResult<String> {
self.ring0.fs.read_file(Path::new(&self.path))
.map(|bytes| String::from_utf8_lossy(&bytes).to_string())
.map_err(FileError::Io)
}
fn close(&self) -> FileResult<()> { Ok(()) }
}
```
3. **実装時の検討事項を明示**(以下のセクション参照)
---
## 3.3 実装時の検討事項(重要)
### ① Byte → String 変換のポリシー
**問題**: Ring0FsFileIo の read() で `String::from_utf8_lossy()` を使うと、バイナリファイルの無効な UTF-8 を置換してしまう。
**検討事項**:
- **Option A**: 現状通り `from_utf8_lossy()` で置換
- 利点: Nyash が文字列中心だから OK
- 欠点: バイナリ情報が失われる
- **Option B**: `String::from_utf8()` で Err を返す
- 利点: エラーが明示的
- 欠点: FileIo trait を `Result<String, Utf8Error>` に変更する必要あり(破壊的)
**推奨**: **Option Afrom_utf8_lossyを採用**
- 理由: Nyash は言語実装として「テキストファイル」が主用途
- バイナリ対応は「将来の拡張」として Phase 108+ で検討
- docs に「FileBox は UTF-8 テキストファイル向け」と明記
### ② FileBox の open 状態での二重 open
**問題**: FileBox.open() が呼ばれるたびに provider.open() が呼ばれる。
- 既存の FileBox.open() → provider.open() の流れで、close() なしに再度 open() が呼ばれるケース
**検討事項**:
- **Option A**: close() を呼び出し側に強制する
- FileBox.open() 前に明示的に close() 呼び出し
- **Option B**: Ring0FsFileIo 内で自動管理
- 新しい open() が来たら前回の close を自動実行
- **Option C**: セマンティクスを明記
- 「一度 open したら close() まで新しい open は受け付けない」
**推奨**: **Option Cセマンティクス明記** + docs 追記
- Ring0FsFileIo.open() は「既に path が set されていたら Err を返す」
- FileBox.open() の docs に「FileBox は同時に 1 ファイルのみ開く」と明記
- 複数ファイル同時アクセスは「将来の FileHandleBox」で対応
### ③ プラグイン優先ロジックでの Err ハンドリング
**問題**: `provider_lock::set_filebox_provider()` は OnceLock なので 1 回のみ可能。
- プラグインが先に登録 → init_default_filebox_provider() が Err を返す
**検討事項**:
- **Option A**: Err を単に無視する
- 呼び出し側で Err を無視silent
- **Option B**: Warning をログに出す
- ring0.log.info() / warn() で「プラグイン provider が既に設定されています」と出力
- **Option C**: init_default_filebox_provider() を Option を返すように変更
- 呼び出し側で「設定済み」を区別可能
**推奨**: **Option BWarning ログ出力)**
- 実装: `init_default_filebox_provider()` の戻り値を `Result<(), String>` にして、
- Ok(()) → デフォルト provider を登録した
- Err(msg) → 既にプラグイン側で登録済みmsg = "Plugin provider already registered"
- 呼び出し側で `ring0.log.debug()` で記録verbose 時に可視)
- Fail-Fast は保たれるMissingService は出ない)
---
## 4. Task 3: Ring0 ベースの FileIo 実装を追加
### 4.1 実装内容
**ファイル候補**:
- `src/boxes/file/core_ro.rs` または `src/boxes/file/builtin_factory.rs`
- `src/runtime/provider_lock.rs`(ヘルパー関数)
- `src/runtime/plugin_host.rs`(起動時初期化)
### 4.2 やること
1. **Ring0FsFileIo 実装を追加**
- フィールド: `Arc<Ring0Context>`, `String path`
- open(path): path を保持、FsApi 経由で存在確認(読み取り向け)
- read(): FsApi.read_file → String 変換from_utf8_lossy ポリシー採用)
- close(): 単に Ok(())(実質 noop、ハンドル管理なし
- caps: `FileCaps { read: true, write: false }` (Phase 107 では read-only)
2. **provider_lock 側にヘルパー追加**
```rust
pub fn init_default_filebox_provider(
ring0: &Arc<Ring0Context>
) -> Result<(), String> {
// Ring0 ベースの FileIo を登録
let provider = Arc::new(Ring0FsFileIo::new(ring0.clone()));
set_filebox_provider(provider)
.map_err(|_| "Plugin FileBox provider already registered".to_string())
}
```
3. **PluginHost/initialize_runtime に統合**
- CoreServices 初期化後に `init_default_filebox_provider(&ring0)` を呼ぶ
- 戻り値が Err の場合は debug ログを出力(プラグイン優先)
- Fail-Fast は影響なし(既にプラグイン provider が set されているだけ)
---
## 5. Task 4: Fail-Fast & プロファイルとの整合
### 5.1 実装内容
**ファイル**:
- `src/runtime/plugin_host.rs`
- `docs/development/current/main/phase106_filebox_design_revised.md`
### 5.2 やること
1. **Phase 106 との整合確認**
- Phase 106: 「FileBox provider 未登録なら CoreInitError::MissingService」
- Phase 107: 「標準パスで Ring0FsFileIo が自動登録されるので MissingService は基本的に起きない」
- phase106 のドキュメントに追記: 「Phase 107 で自動登録機構が追加された」
2. **将来用フックdocs に記載)**
- minimal/no-fs プロファイル導入時:
- `CoreBoxId::File.is_core_required(profile)` に拡張
- その profile では `init_default_filebox_provider()` を呼ばない
- これで「FileBox 無し環境」も可能に
---
## 6. Task 5: ドキュメント統合
### 6.1 実装内容
**ファイル**:
- `docs/development/current/main/core_boxes_design.md`
- `docs/development/current/main/phase106_filebox_design_revised.md`
- `phase107_fsapi_fileio_bridge.md` (このドキュメント)
- `ring0-inventory.md` (任意)
### 6.2 やること
1. **core_boxes_design.md に図と説明を追加**
```
[FileBox]
↓ (provider経由)
[Ring0FsFileIo] (FileIo実装)
↓ (read_file/write_file呼び出し)
[Ring0.FsApi] (OS I/O抽象)
[std::fs]
```
2. **phase106_filebox_design_revised.md の "Phase 107" セクション更新**
- 「Phase 107 で FsApi 統合を行う予定」が「実装済み」に変更
3. **phase107_fsapi_fileio_bridge.md にまとめる**
- FsApi / FileIo / FileBox / provider_lock / PluginHost の関係を 1 ドキュメントで整理
---
## 7. 実装チェックリストPhase 107
- [ ] FsApi / FileIo / FileBox / provider_lock / PluginHost の関係が図付きで整理されている
- [ ] Ring0 ベースの FileIo 実装Ring0FsFileIoが追加されている
- [ ] selfhost/通常ランタイム起動で、デフォルトとして Ring0FsFileIo が provider_lock に登録される
- [ ] UTF-8 ハンドリング ポリシーfrom_utf8_lossy採用が docs に明記されている
- [ ] FileBox の一度に1ファイルのみ open セマンティクスが docs に明記されている
- [ ] プラグイン優先時の Err ハンドリングdebug ログ出力)が実装されている
- [ ] Phase 106 との整合確認完了MissingService は基本レア)
- [ ] 将来用フックminimal/no-fs プロファイル)が docs に記載されている
- [ ] ビルド・テスト完全成功FileBox/provider_lock/plugin_host 関連テスト)
---
## 8. 設計原則Phase 107 で確立)
### 層の棲み分けが完全化
```
層 役割 知識範囲
──────────────────────────────────────────────────────────────
Ring0.FsApi OS I/O 抽象化 Rust std::fs のみ
Ring0FsFileIo FileIo 実装 (1つの実装例) Ring0.FsApi 使用
FileIo trait FileBox 向け I/O 抽象 FsApi を知らない
provider_lock 登録・参照 OnceLock管理
FileBox ユーザーAPI provider 経由のみ
```
### 拡張ポイント
**将来の実装**:
- MockFileIo: FsApi の代わりに in-memory mock を使う
- NetworkFileIo: FsApi の代わりに remote FS を使う
- minimal/no-fs: provider 登録をスキップFileBox optional化
---
**Phase 107 指示書作成日**: 2025-12-03検討事項追加版