diff --git a/docs/development/current/main/core_boxes_design.md b/docs/development/current/main/core_boxes_design.md index d9a28cf6..fedd1062 100644 --- a/docs/development/current/main/core_boxes_design.md +++ b/docs/development/current/main/core_boxes_design.md @@ -884,3 +884,231 @@ fn test_console_service_ring0_integration() { - IntegerService/BoolService 実装(純粋関数で実装) - 代表パス拡大(5-10箇所) - #[allow(dead_code)] 完全撲滅 + +--- + +## 13. Phase 96: ArrayService/MapService 実装 (2025-12-03) + +### 13.1 実装成果 + +**完了項目**: +- ✅ ArrayService trait 定義(len/get/set/push) +- ✅ MapService trait 定義(size/has/get/set) +- ✅ ArrayBoxAdapter/MapBoxAdapter unit struct 化 +- ✅ downcast パターンで複数インスタンス対応 +- ✅ #[allow(dead_code)] 完全削除(4箇所 → 2箇所) + +### 13.2 Adapter パターン完成 + +**3つのパターン確立**: + +1. **Ring0直結型**(ConsoleService) + - OS API thin wrapper + - Box 状態不要 + - Ring0Context 経由でシステムコール + +2. **純粋関数型**(StringService) + - 副作用なし + - Box 状態不要 + - 入力のみから出力を決定 + +3. **downcast型**(ArrayService/MapService) + - 複数インスタンス対応 + - Box 状態が必要 + - unit struct + downcast パターン + +### 13.3 ArrayService API + +**Trait 定義**: +```rust +pub trait ArrayService: Send + Sync { + /// 配列の要素数を取得 + fn len(&self, arr: &dyn NyashBox) -> i64; + + /// 指定インデックスの要素を取得 + fn get(&self, arr: &dyn NyashBox, index: i64) -> Option>; + + /// 指定インデックスに要素を設定 + fn set(&self, arr: &dyn NyashBox, index: i64, value: Box) -> Result<(), String>; + + /// 配列の末尾に要素を追加 + fn push(&self, arr: &dyn NyashBox, value: Box) -> Result<(), String>; +} +``` + +**実装例**: +```rust +impl ArrayService for ArrayBoxAdapter { + fn len(&self, arr: &dyn NyashBox) -> i64 { + arr.as_any() + .downcast_ref::() + .map(|a| a.len() as i64) + .unwrap_or(0) + } + + fn get(&self, arr: &dyn NyashBox, index: i64) -> Option> { + let arr_box = arr.as_any().downcast_ref::()?; + let index_box = Box::new(IntegerBox::new(index)); + Some(arr_box.get(index_box)) + } + + // set/push も同様の downcast パターン +} +``` + +### 13.4 MapService API + +**Trait 定義**: +```rust +pub trait MapService: Send + Sync { + /// マップのサイズを取得 + fn size(&self, map: &dyn NyashBox) -> i64; + + /// キーが存在するか確認 + fn has(&self, map: &dyn NyashBox, key: &str) -> bool; + + /// 値を取得 + fn get(&self, map: &dyn NyashBox, key: &str) -> Option>; + + /// 値を設定 + fn set(&self, map: &dyn NyashBox, key: &str, value: Box) -> Result<(), String>; +} +``` + +**実装例**: +```rust +impl MapService for MapBoxAdapter { + fn size(&self, map: &dyn NyashBox) -> i64 { + map.as_any() + .downcast_ref::() + .map(|m| { + let size_box = m.size(); + size_box.as_any() + .downcast_ref::() + .map(|i| i.value) + .unwrap_or(0) + }) + .unwrap_or(0) + } + + fn has(&self, map: &dyn NyashBox, key: &str) -> bool { + let map_box = match map.as_any().downcast_ref::() { + Some(m) => m, + None => return false, + }; + let key_box = Box::new(StringBox::new(key)); + let result = map_box.has(key_box); + result.as_any() + .downcast_ref::() + .map(|b| b.value) + .unwrap_or(false) + } + + // get/set も同様の downcast パターン +} +``` + +### 13.5 PluginHost 初期化更新 + +**Phase 95.5 パターンを踏襲**: +```rust +// ArrayBox (Phase 96: downcast パターン、存在チェックのみ) +if !registry.has_type("ArrayBox") { + return Err(CoreInitError::MissingService { + box_id: CoreBoxId::Array, + message: "ArrayBox not found in registry".to_string(), + }); +} +let array_service = Arc::new(ArrayBoxAdapter::new()); + +// MapBox (Phase 96: downcast パターン、存在チェックのみ) +if !registry.has_type("MapBox") { + return Err(CoreInitError::MissingService { + box_id: CoreBoxId::Map, + message: "MapBox not found in registry".to_string(), + }); +} +let map_service = Arc::new(MapBoxAdapter::new()); +``` + +### 13.6 テスト追加 + +**ArrayService テスト**: +```rust +#[test] +fn test_array_service_basic_operations() { + let arr = ArrayBox::new(); + let adapter = ArrayBoxAdapter::new(); + + // push + let value = Box::new(IntegerBox::new(42)); + adapter.push(&arr, value).unwrap(); + + // len + assert_eq!(adapter.len(&arr), 1); + + // get + let result = adapter.get(&arr, 0).unwrap(); + let int_box = result.as_any().downcast_ref::().unwrap(); + assert_eq!(int_box.value, 42); +} +``` + +**MapService テスト**: +```rust +#[test] +fn test_map_service_basic_operations() { + let map = MapBox::new(); + let adapter = MapBoxAdapter::new(); + + // set + let value = Box::new(StringBox::new("Hello")); + adapter.set(&map, "key1", value).unwrap(); + + // has + assert!(adapter.has(&map, "key1")); + assert!(!adapter.has(&map, "key2")); + + // get + let result = adapter.get(&map, "key1").unwrap(); + let str_box = result.as_any().downcast_ref::().unwrap(); + assert_eq!(str_box.value, "Hello"); + + // size + assert_eq!(adapter.size(&map), 1); +} +``` + +### 13.7 削減統計 + +| 項目 | Before | After | 削減 | +|------|--------|-------|------| +| #[allow(dead_code)] | 4箇所 | 2箇所 | 2削減 | +| inner フィールド | 4個 | 2個 | 2削減 | +| Box 依存 | 4箇所 | 2箇所 | 2削減 | + +**残存 Adapter**: +- IntegerBoxAdapter: Phase 97 で実装予定 +- BoolBoxAdapter: Phase 97 で実装予定 + +### 13.8 技術的成果 + +**型安全性向上**: +- downcast_ref によるコンパイル時検証 +- Option/Result による安全なエラーハンドリング +- Box ↔ Rust型 の明示的変換 + +**コード簡略化**: +- unit struct 化により inner フィールド削除 +- #[allow(dead_code)] 完全削除(2箇所削減) +- 存在チェックのみのシンプルな初期化 + +**設計明確化**: +- 3パターンの使い分け確立 +- Adapter の責務明確化(複数インスタンス対応) + +### 13.9 次のステップ(Phase 97) + +- IntegerService/BoolService 実装(純粋関数型で実装) +- 代表パス拡大(5-10箇所で実用化テスト) +- #[allow(dead_code)] 完全撲滅(0箇所達成) diff --git a/src/runtime/core_services.rs b/src/runtime/core_services.rs index a8cb2649..278154e9 100644 --- a/src/runtime/core_services.rs +++ b/src/runtime/core_services.rs @@ -25,13 +25,37 @@ pub trait BoolService: Send + Sync { } /// ArrayBox Service trait +/// +/// Phase 96: len/get/set/push 実装 pub trait ArrayService: Send + Sync { - // Phase 92 以降で実装 + /// 配列の要素数を取得 + fn len(&self, arr: &dyn NyashBox) -> i64; + + /// 指定インデックスの要素を取得 + fn get(&self, arr: &dyn NyashBox, index: i64) -> Option>; + + /// 指定インデックスに要素を設定 + fn set(&self, arr: &dyn NyashBox, index: i64, value: Box) -> Result<(), String>; + + /// 配列の末尾に要素を追加 + fn push(&self, arr: &dyn NyashBox, value: Box) -> Result<(), String>; } /// MapBox Service trait +/// +/// Phase 96: size/has/get/set 実装 pub trait MapService: Send + Sync { - // Phase 92 以降で実装 + /// マップのサイズを取得 + fn size(&self, map: &dyn NyashBox) -> i64; + + /// キーが存在するか確認 + fn has(&self, map: &dyn NyashBox, key: &str) -> bool; + + /// 値を取得 + fn get(&self, map: &dyn NyashBox, key: &str) -> Option>; + + /// 値を設定 + fn set(&self, map: &dyn NyashBox, key: &str, value: Box) -> Result<(), String>; } /// ConsoleBox Service trait @@ -166,40 +190,113 @@ impl BoolService for BoolBoxAdapter { /// ArrayBox → ArrayService Adapter /// -/// Phase 95.5: inner フィールドは #[allow(dead_code)] のまま保持 -/// Phase 96 以降で実装時に、Box 状態が必要か純粋関数で足りるか判断 -pub struct ArrayBoxAdapter { - #[allow(dead_code)] - inner: Box, -} +/// Phase 96: downcast パターンで複数インスタンス対応 +pub struct ArrayBoxAdapter; impl ArrayBoxAdapter { - pub fn new(box_instance: Box) -> Self { - Self { inner: box_instance } + pub fn new() -> Self { + Self } } impl ArrayService for ArrayBoxAdapter { - // Phase 96 以降で実装 + fn len(&self, arr: &dyn NyashBox) -> i64 { + use crate::boxes::array::ArrayBox; + arr.as_any() + .downcast_ref::() + .map(|a| a.len() as i64) + .unwrap_or(0) + } + + fn get(&self, arr: &dyn NyashBox, index: i64) -> Option> { + use crate::boxes::array::ArrayBox; + use crate::box_trait::IntegerBox; + let arr_box = arr.as_any().downcast_ref::()?; + let index_box = Box::new(IntegerBox::new(index)); + Some(arr_box.get(index_box)) + } + + fn set(&self, arr: &dyn NyashBox, index: i64, value: Box) -> Result<(), String> { + use crate::boxes::array::ArrayBox; + use crate::box_trait::IntegerBox; + let arr_box = arr.as_any() + .downcast_ref::() + .ok_or("Not an ArrayBox")?; + let index_box = Box::new(IntegerBox::new(index)); + arr_box.set(index_box, value); + Ok(()) + } + + fn push(&self, arr: &dyn NyashBox, value: Box) -> Result<(), String> { + use crate::boxes::array::ArrayBox; + let arr_box = arr.as_any() + .downcast_ref::() + .ok_or("Not an ArrayBox")?; + arr_box.push(value); + Ok(()) + } } /// MapBox → MapService Adapter /// -/// Phase 95.5: inner フィールドは #[allow(dead_code)] のまま保持 -/// Phase 96 以降で実装時に、Box 状態が必要か純粋関数で足りるか判断 -pub struct MapBoxAdapter { - #[allow(dead_code)] - inner: Box, -} +/// Phase 96: downcast パターンで複数インスタンス対応 +pub struct MapBoxAdapter; impl MapBoxAdapter { - pub fn new(box_instance: Box) -> Self { - Self { inner: box_instance } + pub fn new() -> Self { + Self } } impl MapService for MapBoxAdapter { - // Phase 96 以降で実装 + fn size(&self, map: &dyn NyashBox) -> i64 { + use crate::boxes::map_box::MapBox; + map.as_any() + .downcast_ref::() + .map(|m| { + // MapBox::size() は Box を返すため、IntegerBox に変換 + let size_box = m.size(); + size_box.as_any() + .downcast_ref::() + .map(|i| i.value) + .unwrap_or(0) + }) + .unwrap_or(0) + } + + fn has(&self, map: &dyn NyashBox, key: &str) -> bool { + use crate::boxes::map_box::MapBox; + use crate::box_trait::{BoolBox, StringBox}; + let map_box = match map.as_any().downcast_ref::() { + Some(m) => m, + None => return false, + }; + let key_box = Box::new(StringBox::new(key)); + let result = map_box.has(key_box); + result.as_any() + .downcast_ref::() + .map(|b| b.value) + .unwrap_or(false) + } + + fn get(&self, map: &dyn NyashBox, key: &str) -> Option> { + use crate::boxes::map_box::MapBox; + use crate::box_trait::StringBox; + let map_box = map.as_any().downcast_ref::()?; + let key_box = Box::new(StringBox::new(key)); + Some(map_box.get(key_box)) + } + + fn set(&self, map: &dyn NyashBox, key: &str, value: Box) -> Result<(), String> { + use crate::boxes::map_box::MapBox; + use crate::box_trait::StringBox; + let map_box = map.as_any() + .downcast_ref::() + .ok_or("Not a MapBox")?; + let key_box = Box::new(StringBox::new(key)); + map_box.set(key_box, value); + Ok(()) + } } /// ConsoleBox → ConsoleService Adapter @@ -317,4 +414,95 @@ mod tests { // panic しないことを確認 } + + #[test] + fn test_array_service_basic_operations() { + use crate::boxes::array::ArrayBox; + use crate::box_trait::IntegerBox; + + let arr = ArrayBox::new(); + let adapter = ArrayBoxAdapter::new(); + + // push + let value = Box::new(IntegerBox::new(42)); + adapter.push(&arr, value).unwrap(); + + // len + assert_eq!(adapter.len(&arr), 1); + + // get + let result = adapter.get(&arr, 0).unwrap(); + let int_box = result.as_any().downcast_ref::().unwrap(); + assert_eq!(int_box.value, 42); + } + + #[test] + fn test_array_service_set() { + use crate::boxes::array::ArrayBox; + use crate::box_trait::IntegerBox; + + let arr = ArrayBox::new(); + let adapter = ArrayBoxAdapter::new(); + + // push initial value + adapter.push(&arr, Box::new(IntegerBox::new(10))).unwrap(); + + // set + adapter.set(&arr, 0, Box::new(IntegerBox::new(20))).unwrap(); + + // verify + let result = adapter.get(&arr, 0).unwrap(); + let int_box = result.as_any().downcast_ref::().unwrap(); + assert_eq!(int_box.value, 20); + } + + #[test] + fn test_map_service_basic_operations() { + use crate::boxes::map_box::MapBox; + use crate::box_trait::StringBox; + + let map = MapBox::new(); + let adapter = MapBoxAdapter::new(); + + // set + let value = Box::new(StringBox::new("Hello")); + adapter.set(&map, "key1", value).unwrap(); + + // has + assert!(adapter.has(&map, "key1")); + assert!(!adapter.has(&map, "key2")); + + // get + let result = adapter.get(&map, "key1").unwrap(); + let str_box = result.as_any().downcast_ref::().unwrap(); + assert_eq!(str_box.value, "Hello"); + + // size + assert_eq!(adapter.size(&map), 1); + } + + #[test] + fn test_map_service_multiple_keys() { + use crate::boxes::map_box::MapBox; + use crate::box_trait::{IntegerBox, StringBox}; + + let map = MapBox::new(); + let adapter = MapBoxAdapter::new(); + + // set multiple keys + adapter.set(&map, "name", Box::new(StringBox::new("Alice"))).unwrap(); + adapter.set(&map, "age", Box::new(IntegerBox::new(25))).unwrap(); + + // verify size + assert_eq!(adapter.size(&map), 2); + + // verify values + let name = adapter.get(&map, "name").unwrap(); + let name_str = name.as_any().downcast_ref::().unwrap(); + assert_eq!(name_str.value, "Alice"); + + let age = adapter.get(&map, "age").unwrap(); + let age_int = age.as_any().downcast_ref::().unwrap(); + assert_eq!(age_int.value, 25); + } } diff --git a/src/runtime/plugin_host.rs b/src/runtime/plugin_host.rs index 51593de5..424bd69a 100644 --- a/src/runtime/plugin_host.rs +++ b/src/runtime/plugin_host.rs @@ -127,23 +127,23 @@ impl PluginHost { })?; let bool_service = Arc::new(BoolBoxAdapter::new(bool_box)); - // ArrayBox - let array_box = registry - .create_box("ArrayBox", &[]) - .map_err(|e| CoreInitError::MissingService { + // ArrayBox (Phase 96: downcast パターン、存在チェックのみ) + if !registry.has_type("ArrayBox") { + return Err(CoreInitError::MissingService { box_id: CoreBoxId::Array, - message: format!("ArrayBox creation failed: {}", e), - })?; - let array_service = Arc::new(ArrayBoxAdapter::new(array_box)); + message: "ArrayBox not found in registry".to_string(), + }); + } + let array_service = Arc::new(ArrayBoxAdapter::new()); - // MapBox - let map_box = registry - .create_box("MapBox", &[]) - .map_err(|e| CoreInitError::MissingService { + // MapBox (Phase 96: downcast パターン、存在チェックのみ) + if !registry.has_type("MapBox") { + return Err(CoreInitError::MissingService { box_id: CoreBoxId::Map, - message: format!("MapBox creation failed: {}", e), - })?; - let map_service = Arc::new(MapBoxAdapter::new(map_box)); + message: "MapBox not found in registry".to_string(), + }); + } + let map_service = Arc::new(MapBoxAdapter::new()); // ConsoleBox (Phase 95.5: Ring0 直結、存在チェックのみ) if !registry.has_type("ConsoleBox") {