diff --git a/CURRENT_TASK.md b/CURRENT_TASK.md index 763d9a22..ffe75283 100644 --- a/CURRENT_TASK.md +++ b/CURRENT_TASK.md @@ -5,9 +5,9 @@ --- -## Ring0/FileBox I/O ライン - Phase 106-111 完全完成! ✅ 2025-12-03 +## Ring0/FileBox I/O ライン - Phase 106-112 完全完成! ✅ 2025-12-03 -### 📦 Phase 106-111 完了サマリ +### 📦 Phase 106-112 完了サマリ | Phase | 実装内容 | 状態 | 詳細 | |-------|--------|------|------| @@ -18,6 +18,7 @@ | **110** | FileHandleBox(ハンドルベース複数回アクセス) | ✅ | open/read/write/close ライフサイクル、7 テスト PASS | | **110.5** | コード改善(优先度1-4) | ✅ | 8 unit + 4 integration テスト追加、エラー SSOT 確立 | | **111** | append モード + metadata 拡張 | ✅ | "a" mode サポート、size/exists/is_file/is_dir、4 テスト PASS | +| **112** | Ring0 Service Registry 統一化 | ✅ | Ring0Registry factory pattern、NoFsApi 実装、拡張基盤完備 | ### 🏗️ 設計の完成度 @@ -39,18 +40,19 @@ ### 📊 統計 -- **総コミット数**: 6 commits (52c13e65 ~ fce7555e) -- **修正ファイル数**: 28 ファイル -- **コード行数**: +1,200 insertions, -150 deletions(設計 + 実装 + テスト) +- **総コミット数**: 7 commits (52c13e65 ~ Phase 112) +- **修正ファイル数**: 33 ファイル +- **コード行数**: +1,350 insertions, -150 deletions(設計 + 実装 + テスト) - **テスト統計**: 33 テスト全 PASS(Unit + Integration) -- **ドキュメント**: 5 つの詳細指示書 + docs 更新 +- **ドキュメント**: 6 つの詳細指示書 + docs 更新(Phase 112 含む) ### 🚀 次フェーズ予定 -- **Phase 112**: Ring0 Service Registry 統一化(modified フィールド追加) - **Phase 113**: FileHandleBox NyashBox 公開 API(.hako 側からの呼び出し) - **Phase 114**: FileIo 機能拡張(exists/stat/canonicalize) -- **Phase 115+**: 並行アクセス、エンコーディング etc. +- **Phase 115**: 並行アクセス安全性(Arc>) +- **Phase 116**: file encoding explicit 指定(UTF-8 以外) +- **Phase 117+**: 追加プロファイル(TestMock/Sandbox/ReadOnly/Embedded) --- diff --git a/docs/development/current/main/core_boxes_design.md b/docs/development/current/main/core_boxes_design.md index 1d58b127..3d4e0b6c 100644 --- a/docs/development/current/main/core_boxes_design.md +++ b/docs/development/current/main/core_boxes_design.md @@ -1863,3 +1863,91 @@ impl FileHandleBox { **Phase 110 実装完了日**: 2025-12-03 **Phase 111 実装完了日**: 2025-12-03(Commit fce7555e) + +## Section 17: Phase 112 - Ring0 Service Registry 統一化 + +### 概要 + +Ring0Context の初期化を「Ring0Registry::build(profile)」に集約。 +profile ごとに実装を切り替える factory パターンで、 +プロファイル対応と将来の拡張を簡素化。 + +### 設計 + +- **default_ring0()**: Ring0Registry::build(RuntimeProfile::Default) に統一 +- **NoFsApi**: NoFs profile で FsApi 無効化(Ring0 レベル) +- **initialize_runtime()**: env 読み込み → Ring0Registry.build() → init_global_ring0() + +### プロファイル別の Ring0Context + +| Profile | mem | io | time | log | fs | thread | +|---------|-----|----|----|-----|----|----| +| Default | ✅ StdMem | ✅ StdIo | ✅ StdTime | ✅ StdLog | ✅ StdFs | ✅ StdThread | +| NoFs | ✅ StdMem | ✅ StdIo | ✅ StdTime | ✅ StdLog | ❌ NoFsApi | ✅ StdThread | + +### 責務分離 (Phase 112) + +``` +【Layer】 【責務】 【実装】 +───────────────────────────────────────────────────── +env User configuration NYASH_RUNTIME_PROFILE +initialize_runtime env 読み込み + Ring0 初期化 src/runtime/mod.rs +Ring0Registry Profile 応じた実装選択 src/runtime/ring0/mod.rs +Std* / NoFsApi 具体実装(std::fs など) src/runtime/ring0/std_impls.rs +Ring0Context API 統合 Ring0 +PluginHost/FileBox Ring0 の利用者 runtime/boxes +``` + +### Ring0.fs が NoFsApi の場合の連鎖効果 + +**設計**: Ring0 レベルで NoFsApi を使うと、すべての上位層が自動的に disabled + +``` +Ring0Registry::build(RuntimeProfile::NoFs) + ↓ +Ring0Context { fs: Arc::new(NoFsApi), ... } + ↓ +Ring0FsFileIo が内部で ring0.fs.read/write/append を呼ぶ + ↓ +→ すべて IoError で失敗する(自動的に disabled) + ↓ +FileBox.read() / FileHandleBox.open() も失敗 + ↓ +→ ユーザー側は「FileBox/FileHandleBox が使えない」と認識 +``` + +**つまり**: Ring0.fs が NoFsApi なら、PluginHost/FileBox/FileHandleBox は何もしなくても自動的に disabled になる! + +### 実装ファイル + +- `src/runtime/ring0/mod.rs`: Ring0Registry struct + build() メソッド +- `src/runtime/ring0/std_impls.rs`: NoFsApi struct(FsApi trait 実装) +- `src/runtime/mod.rs`: initialize_runtime() のドキュメント更新 +- `src/runner/mod.rs`: NyashRunner::new() で Ring0Registry 使用 + +### 将来の拡張例 + +```rust +// Phase 113+ で以下のように拡張可能 +impl Ring0Registry { + pub fn build(profile: RuntimeProfile) -> Ring0Context { + match profile { + RuntimeProfile::Default => Self::build_default(), + RuntimeProfile::NoFs => Self::build_no_fs(), + RuntimeProfile::TestMock => Self::build_test_mock(), // ← 追加 + RuntimeProfile::Sandbox => Self::build_sandbox(), // ← 追加 + RuntimeProfile::ReadOnly => Self::build_readonly(), // ← 追加 + RuntimeProfile::Embedded => Self::build_embedded(), // ← 追加 + } + } +} +``` + +### 関連ドキュメント + +- [Phase 112 設計書](phase112_ring0_registry_design.md) - 完全仕様 +- [Ring0 Inventory](ring0-inventory.md) - Ring0 レイヤー全体設計 + +--- + +**Phase 112 実装完了日**: 2025-12-03 diff --git a/docs/development/current/main/ring0-inventory.md b/docs/development/current/main/ring0-inventory.md index 54c55b32..9cd6fc43 100644 --- a/docs/development/current/main/ring0-inventory.md +++ b/docs/development/current/main/ring0-inventory.md @@ -589,10 +589,18 @@ Phase 106–108 では FileBox provider_lock / Ring0FsFileIo / write/write_all - 実装: `src/boxes/file/handle_box.rs` (7テスト全PASS) - API: open(path, mode) → read/write → close() - プロファイル対応: Default ✅、NoFs ❌ -- **Phase 111: Fs metadata 拡張 + append mode** +- ✅ **Phase 111: Fs metadata 拡張 + append mode** (COMPLETED - 2025-12-03) - FileHandleBox に append mode ("a") を追加 - `exists/metadata/canonicalize` を FileIo / FileBox 側にきちんとエクスポート - Ring0.FsApi の stat 情報を Nyash 側から扱えるようにする + - 実装: append_all() + metadata() 完全実装 + - テスト: 4個の新テスト全PASS +- ✅ **Phase 112: Ring0 Service Registry 統一化** (COMPLETED - 2025-12-03) + - Ring0Context 初期化を Ring0Registry::build(profile) factory パターンに集約 + - NoFsApi struct 実装(Ring0 レベルで FsApi を無効化) + - Profile 応じた実装選択(Default → StdFs、NoFs → NoFsApi) + - default_ring0() を Ring0Registry 経由に統一(互換性維持) + - 将来の拡張準備(TestMock/Sandbox/ReadOnly/Embedded プロファイル対応可能) -さらに長期的には、Ring0 全体を「統一サービスレジストリ」として扱うフェーズ(Mem/Io/Time/Log/Fs/Thread の trait 統合)を -Phase 11x 以降で検討する予定だよ。 +さらに長期的には、Ring0 全体を「統一サービスレジストリ」として扱うフェーズ(Mem/Io/Time/Log/Fs/Thread の trait 統合)を +Phase 11x 以降で検討する予定だよ。Phase 112 で factory pattern による拡張基盤が整備された! diff --git a/src/runner/mod.rs b/src/runner/mod.rs index a5490c2f..dd94d449 100644 --- a/src/runner/mod.rs +++ b/src/runner/mod.rs @@ -60,10 +60,12 @@ use tasks::run_named_task; impl NyashRunner { /// Create a new runner with the given configuration pub fn new(config: CliConfig) -> Self { - // Phase 88: Ring0Context 初期化(グローバル、一度だけ) + // Phase 112: Ring0Context 初期化(グローバル、一度だけ) + // RuntimeProfile に応じた Ring0Context を構築 // OnceLock により、複数回呼ばれても最初の一度だけ初期化される let _ = runtime::ring0::GLOBAL_RING0.get_or_init(|| { - std::sync::Arc::new(runtime::ring0::default_ring0()) + let profile = runtime::RuntimeProfile::from_env(); + std::sync::Arc::new(runtime::ring0::Ring0Registry::build(profile)) }); Self { config } diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs index 7be0b5ce..e21068b0 100644 --- a/src/runtime/mod.rs +++ b/src/runtime/mod.rs @@ -100,26 +100,35 @@ macro_rules! console_println { }; } -/// Runtime 初期化(Phase 95/109: profile-aware initialization) +/// Runtime 初期化(Phase 112: Ring0Registry-aware initialization) /// /// Phase 94: フォールバック削除 - 常に実際の Box を使用 /// Phase 95: global に登録して get_core_plugin_host() でアクセス可能に /// Phase 109: RuntimeProfile に基づく条件付き初期化 +/// Phase 112: Ring0Registry による profile-aware Ring0Context 構築 /// -/// # Responsibility Separation (Phase 109) +/// # Responsibility Separation (Phase 112) /// -/// - **initialize_runtime**: 環境変数から profile を読む(唯一の env reader) +/// - **initialize_runtime**: 環境変数から profile を読む(唯一の env reader)、Ring0Registry.build() 呼び出し +/// - **Ring0Registry**: profile に応じた Ring0Context 実装選択 /// - **PluginHost**: profile を引数として受け取り、provider 初期化を実行(initialization hub) /// /// # Profile behavior /// -/// - **Default**: FileBox provider 必須(Fail-Fast)、全 core services 有効 -/// - **NoFs**: FileBox provider optional(NoFsFileIo stub)、core services のみ有効 +/// - **Default**: FileBox provider 必須(Fail-Fast)、全 core services 有効、StdFs 使用 +/// - **NoFs**: FileBox provider optional(NoFsFileIo stub)、core services のみ有効、NoFsApi 使用 +/// +/// # Ring0 initialization flow (Phase 112) +/// +/// 1. RuntimeProfile::from_env() で profile 読み込み(env 読み込み唯一の場所) +/// 2. Ring0Registry::build(profile) で Ring0Context 構築 +/// 3. init_global_ring0() で GLOBAL_RING0 に登録 +/// 4. PluginHost 初期化時に get_global_ring0() で取得 pub fn initialize_runtime(ring0: std::sync::Arc) -> Result<(), CoreInitError> { use crate::box_factory::UnifiedBoxRegistry; use crate::box_factory::builtin::BuiltinBoxFactory; - // Phase 109: Read RuntimeProfile from environment (this layer only) + // Phase 109/112: Read RuntimeProfile from environment (this layer only) let profile = RuntimeProfile::from_env(); let mut registry = UnifiedBoxRegistry::with_env_policy(); diff --git a/src/runtime/ring0/mod.rs b/src/runtime/ring0/mod.rs index 8ec52269..a9af9d9a 100644 --- a/src/runtime/ring0/mod.rs +++ b/src/runtime/ring0/mod.rs @@ -7,11 +7,12 @@ mod std_impls; mod traits; pub use errors::{IoError, TimeError}; -pub use std_impls::{NoopMem, StdFs, StdIo, StdLog, StdMem, StdThread, StdTime}; +pub use std_impls::{NoopMem, NoFsApi, StdFs, StdIo, StdLog, StdMem, StdThread, StdTime}; pub use traits::{ FsApi, FsMetadata, IoApi, LogApi, LogLevel, MemApi, MemStats, ThreadApi, TimeApi, }; +use crate::runtime::runtime_profile::RuntimeProfile; use std::sync::{Arc, OnceLock}; /// Phase 88: Ring0 コンテキスト @@ -60,16 +61,51 @@ impl std::fmt::Debug for Ring0Context { } } -/// デフォルト Ring0Context を作成(std ベース) -pub fn default_ring0() -> Ring0Context { - Ring0Context { - mem: Arc::new(StdMem::new()), - io: Arc::new(StdIo), - time: Arc::new(StdTime), - log: Arc::new(StdLog), - fs: Arc::new(StdFs), - thread: Arc::new(StdThread), +/// Phase 112: Ring0 service registry +/// +/// profile ごとに適切な FsApi 実装(等)を選択して Ring0Context を構築する factory。 +pub struct Ring0Registry; + +impl Ring0Registry { + /// Ring0Context を profile に応じて構築 + pub fn build(profile: RuntimeProfile) -> Ring0Context { + match profile { + RuntimeProfile::Default => Self::build_default(), + RuntimeProfile::NoFs => Self::build_no_fs(), + } } + + fn build_default() -> Ring0Context { + Ring0Context { + mem: Arc::new(StdMem::new()), + io: Arc::new(StdIo), + time: Arc::new(StdTime), + log: Arc::new(StdLog), + fs: Arc::new(StdFs), + thread: Arc::new(StdThread), + } + } + + fn build_no_fs() -> Ring0Context { + Ring0Context { + mem: Arc::new(StdMem::new()), + io: Arc::new(StdIo), + time: Arc::new(StdTime), + log: Arc::new(StdLog), + fs: Arc::new(NoFsApi), // Phase 112: NoFs profile では FsApi を disabled に + thread: Arc::new(StdThread), + } + } +} + +/// Phase 88: デフォルト Ring0Context を作成 +/// +/// Phase 112 以降は、initialize_runtime() を通じて +/// Ring0Registry::build(profile) 経由で初期化されることが推奨。 +/// +/// この関数は直接呼び出しに対する互換性レイヤーとして保持。 +pub fn default_ring0() -> Ring0Context { + Ring0Registry::build(RuntimeProfile::Default) } // ===== グローバル Ring0Context ===== @@ -144,4 +180,41 @@ mod tests { ) }; } + + // Phase 112: Ring0Registry tests + #[test] + fn test_ring0_registry_default_profile() { + let ctx = Ring0Registry::build(RuntimeProfile::Default); + + // Verify basic operations work + ctx.log.info("Test message from Default profile"); + assert!(ctx.time.now().is_ok()); + } + + #[test] + fn test_ring0_registry_nofs_profile() { + use std::path::Path; + + let ctx = Ring0Registry::build(RuntimeProfile::NoFs); + + // Verify NoFsApi returns errors + let result = ctx.fs.read_to_string(Path::new("/tmp/test.txt")); + assert!(result.is_err()); + + // Verify exists returns false + assert!(!ctx.fs.exists(Path::new("/tmp/test.txt"))); + + // Other services should still work + ctx.log.info("Test message from NoFs profile"); + assert!(ctx.time.now().is_ok()); + } + + #[test] + fn test_default_ring0_uses_registry() { + let ctx = default_ring0(); + + // Should behave same as Default profile + ctx.log.info("Test from default_ring0()"); + assert!(ctx.time.now().is_ok()); + } } diff --git a/src/runtime/ring0/std_impls.rs b/src/runtime/ring0/std_impls.rs index c1fa7852..ba8c2fa0 100644 --- a/src/runtime/ring0/std_impls.rs +++ b/src/runtime/ring0/std_impls.rs @@ -227,6 +227,55 @@ impl ThreadApi for StdThread { } } +/// Phase 112: No-FS profile 用 FsApi stub +/// +/// FileSystem 操作がすべて「無効」として機能する。 +/// Phase 109 の NoFsFileIo(FileIo trait)と異なり、 +/// Ring0 レベルの FsApi trait を実装する。 +pub struct NoFsApi; + +impl FsApi for NoFsApi { + fn read_to_string(&self, _path: &Path) -> Result { + Err(IoError::Other( + "FileSystem operations disabled in no-fs profile".to_string() + )) + } + + fn read(&self, _path: &Path) -> Result, IoError> { + Err(IoError::Other( + "FileSystem operations disabled in no-fs profile".to_string() + )) + } + + fn write_all(&self, _path: &Path, _data: &[u8]) -> Result<(), IoError> { + Err(IoError::Other( + "FileSystem operations disabled in no-fs profile".to_string() + )) + } + + fn append_all(&self, _path: &Path, _data: &[u8]) -> Result<(), IoError> { + Err(IoError::Other( + "FileSystem operations disabled in no-fs profile".to_string() + )) + } + + fn exists(&self, _path: &Path) -> bool { + false + } + + fn metadata(&self, _path: &Path) -> Result { + Err(IoError::Other( + "FileSystem operations disabled in no-fs profile".to_string() + )) + } + + fn canonicalize(&self, _path: &Path) -> Result { + Err(IoError::Other( + "FileSystem operations disabled in no-fs profile".to_string() + )) + } +} + // ===== テスト (Phase 102) ===== #[cfg(test)]