feat(phase96): ArrayService/MapService実装完了 - downcastパターン確立

Phase 96完全達成 - Ring1-Core層の主要Service実装完成

### 実装成果
-  ArrayService trait定義(len/get/set/push)
-  MapService trait定義(size/has/get/set)
-  ArrayBoxAdapter/MapBoxAdapter unit struct化
-  downcastパターン実装(複数インスタンス対応)
-  #[allow(dead_code)] 4→2箇所(2削除)
-  テスト53/53 PASS(100%)

### 変更ファイル
- src/runtime/core_services.rs: ArrayService/MapService実装(+134行)
- src/runtime/plugin_host.rs: 初期化ロジック更新(+8/-12行)
- docs/development/current/main/core_boxes_design.md: Section 13追加(+228行)

### 3つのAdapterパターン確立
1. **Ring0直結型**(ConsoleService): OS API thin wrapper
2. **純粋関数型**(StringService): Box状態不要
3. **downcast型**(ArrayService/MapService) NEW
   - unit struct + downcast_ref パターン
   - 複数インスタンス対応
   - Rust idiomatic API(Option/Result)

### API設計
- ArrayService: Rust型(i64)引数、内部でBox変換
- MapService: Rust型(&str)引数、内部でBox変換
- 戻り値: Option/Result で型安全

### 技術的成果
- 型安全性向上(downcast_ref によるコンパイル時検証)
- コード簡略化(#[allow(dead_code)] 2削除)
- 設計明確化(3パターンの使い分け確立)

### 削減統計
- #[allow(dead_code)]: 2箇所削除
- innerフィールド: 2個削除
- Box依存: 2箇所削除

### 次のステップ
Phase 96.5: use文整理とコメント更新
Phase 97: IntegerService/BoolService実装(#[allow(dead_code)] 完全削除)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
nyash-codex
2025-12-03 10:27:39 +09:00
parent 1108a61533
commit 81a5a04eb7
3 changed files with 450 additions and 34 deletions

View File

@ -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<Box<dyn NyashBox>>;
/// 指定インデックスに要素を設定
fn set(&self, arr: &dyn NyashBox, index: i64, value: Box<dyn NyashBox>) -> Result<(), String>;
/// 配列の末尾に要素を追加
fn push(&self, arr: &dyn NyashBox, value: Box<dyn NyashBox>) -> 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<Box<dyn NyashBox>>;
/// 値を設定
fn set(&self, map: &dyn NyashBox, key: &str, value: Box<dyn NyashBox>) -> 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<dyn NyashBox>,
}
/// Phase 96: downcast パターンで複数インスタンス対応
pub struct ArrayBoxAdapter;
impl ArrayBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> 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::<ArrayBox>()
.map(|a| a.len() as i64)
.unwrap_or(0)
}
fn get(&self, arr: &dyn NyashBox, index: i64) -> Option<Box<dyn NyashBox>> {
use crate::boxes::array::ArrayBox;
use crate::box_trait::IntegerBox;
let arr_box = arr.as_any().downcast_ref::<ArrayBox>()?;
let index_box = Box::new(IntegerBox::new(index));
Some(arr_box.get(index_box))
}
fn set(&self, arr: &dyn NyashBox, index: i64, value: Box<dyn NyashBox>) -> Result<(), String> {
use crate::boxes::array::ArrayBox;
use crate::box_trait::IntegerBox;
let arr_box = arr.as_any()
.downcast_ref::<ArrayBox>()
.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<dyn NyashBox>) -> Result<(), String> {
use crate::boxes::array::ArrayBox;
let arr_box = arr.as_any()
.downcast_ref::<ArrayBox>()
.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<dyn NyashBox>,
}
/// Phase 96: downcast パターンで複数インスタンス対応
pub struct MapBoxAdapter;
impl MapBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> 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::<MapBox>()
.map(|m| {
// MapBox::size() は Box<dyn NyashBox> を返すため、IntegerBox に変換
let size_box = m.size();
size_box.as_any()
.downcast_ref::<crate::box_trait::IntegerBox>()
.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::<MapBox>() {
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::<BoolBox>()
.map(|b| b.value)
.unwrap_or(false)
}
fn get(&self, map: &dyn NyashBox, key: &str) -> Option<Box<dyn NyashBox>> {
use crate::boxes::map_box::MapBox;
use crate::box_trait::StringBox;
let map_box = map.as_any().downcast_ref::<MapBox>()?;
let key_box = Box::new(StringBox::new(key));
Some(map_box.get(key_box))
}
fn set(&self, map: &dyn NyashBox, key: &str, value: Box<dyn NyashBox>) -> Result<(), String> {
use crate::boxes::map_box::MapBox;
use crate::box_trait::StringBox;
let map_box = map.as_any()
.downcast_ref::<MapBox>()
.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::<IntegerBox>().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::<IntegerBox>().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::<StringBox>().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::<StringBox>().unwrap();
assert_eq!(name_str.value, "Alice");
let age = adapter.get(&map, "age").unwrap();
let age_int = age.as_any().downcast_ref::<IntegerBox>().unwrap();
assert_eq!(age_int.value, 25);
}
}

View File

@ -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") {