feat(phase94): Box→Service conversion with actual registry integration

Phase 94完全達成 - UnifiedBoxRegistryから実際のBoxを取得してServiceに変換

### 実装成果
-  6個のAdapter実装(StringBox/Integer/Bool/Array/Map/Console)
-  Dummy実装完全削除(38行削減)
-  Fail-Fast原則徹底(フォールバック削除)
-  テスト7/7 PASS(100%)

### 変更ファイル
- src/runtime/core_services.rs: 6個のAdapter実装(+93行)
- src/runtime/plugin_host.rs: 実際のBox取得ロジック(+54行)、Dummy削除(-17行)
- src/runtime/mod.rs: フォールバック削除(-8行)

### 技術的成果
- Box<dyn NyashBox>を直接保持(型安全性確保)
- registry.create_box()で実際のBoxインスタンス取得
- BuiltinBoxFactory登録でcore_required Boxes提供

### 次のステップ
Phase 95: Service traitメソッド実装(Console/String/Array/Map)

🤖 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 09:14:49 +09:00
parent b3de4cac4b
commit f4144a22a6
4 changed files with 217 additions and 103 deletions

View File

@ -1,6 +1,6 @@
# Core Boxes 設計ドキュメントPhase 87 完了版) # Core Boxes 設計ドキュメントPhase 8794 完了版)
Phase 87 で実装された CoreBoxId/CoreMethodId の完全仕様。 Phase 87 で実装された CoreBoxId/CoreMethodId と、Phase 9194 で統合された CoreServices/PluginHost/Adapter の仕様。
**目的**: Box名・メソッド名のハードコードを型安全な enum に箱化することで、以下を実現: **目的**: Box名・メソッド名のハードコードを型安全な enum に箱化することで、以下を実現:
- ✅ コンパイル時検証(タイポ撲滅) - ✅ コンパイル時検証(タイポ撲滅)
@ -8,7 +8,10 @@ Phase 87 で実装された CoreBoxId/CoreMethodId の完全仕様。
- ✅ SSOTSingle Source of Truth確立 - ✅ SSOTSingle Source of Truth確立
- ✅ 保守性向上 - ✅ 保守性向上
**実装状況**: ✅ Phase 87 完了2025-12-02 **実装状況**:
- ✅ Phase 87: CoreBoxId/CoreMethodId 実装
- ✅ Phase 91: CoreServices/PluginHost skeleton
- ✅ Phase 94: Box → Service Adapter 実装と Dummy 削除
--- ---
@ -546,4 +549,3 @@ NYASH_USE_PLUGIN_HOST=1 ./target/release/nyash apps/tests/selfhost_min.hako
2. `src/runtime/core_services.rs`: CoreServices::dummy() ヘルパー 2. `src/runtime/core_services.rs`: CoreServices::dummy() ヘルパー
3. `src/runtime/mod.rs`: initialize_runtime() 実装(環境変数制御) 3. `src/runtime/mod.rs`: initialize_runtime() 実装(環境変数制御)
4. `src/runner/selfhost.rs`: PluginHost 初期化追加 4. `src/runner/selfhost.rs`: PluginHost 初期化追加

View File

@ -5,6 +5,7 @@
use std::sync::Arc; use std::sync::Arc;
use crate::runtime::CoreBoxId; use crate::runtime::CoreBoxId;
use crate::box_trait::NyashBox;
/// StringBox Service trait /// StringBox Service trait
pub trait StringService: Send + Sync { pub trait StringService: Send + Sync {
@ -80,20 +81,108 @@ impl CoreServices {
// Phase 92 以降で各 Service の初期化を検証 // Phase 92 以降で各 Service の初期化を検証
} }
/// Phase 93: ダミー実装Phase 94 で削除予定) }
pub fn dummy() -> Self {
use crate::runtime::plugin_host::*; // ============================================================================
Self { // Phase 94: Adapter Pattern - Box → Service 変換
string: Arc::new(DummyStringService), // ============================================================================
integer: Arc::new(DummyIntegerService),
bool: Arc::new(DummyBoolService), /// StringBox → StringService Adapter
array: Arc::new(DummyArrayService), pub struct StringBoxAdapter {
map: Arc::new(DummyMapService), #[allow(dead_code)]
console: Arc::new(DummyConsoleService), inner: Box<dyn NyashBox>,
}
impl StringBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> Self {
Self { inner: box_instance }
} }
}
impl StringService for StringBoxAdapter {
// Phase 95 以降で実装
}
/// IntegerBox → IntegerService Adapter
pub struct IntegerBoxAdapter {
#[allow(dead_code)]
inner: Box<dyn NyashBox>,
}
impl IntegerBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> Self {
Self { inner: box_instance }
} }
} }
impl IntegerService for IntegerBoxAdapter {
// Phase 95 以降で実装
}
/// BoolBox → BoolService Adapter
pub struct BoolBoxAdapter {
#[allow(dead_code)]
inner: Box<dyn NyashBox>,
}
impl BoolBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> Self {
Self { inner: box_instance }
}
}
impl BoolService for BoolBoxAdapter {
// Phase 95 以降で実装
}
/// ArrayBox → ArrayService Adapter
pub struct ArrayBoxAdapter {
#[allow(dead_code)]
inner: Box<dyn NyashBox>,
}
impl ArrayBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> Self {
Self { inner: box_instance }
}
}
impl ArrayService for ArrayBoxAdapter {
// Phase 95 以降で実装
}
/// MapBox → MapService Adapter
pub struct MapBoxAdapter {
#[allow(dead_code)]
inner: Box<dyn NyashBox>,
}
impl MapBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> Self {
Self { inner: box_instance }
}
}
impl MapService for MapBoxAdapter {
// Phase 95 以降で実装
}
/// ConsoleBox → ConsoleService Adapter
pub struct ConsoleBoxAdapter {
#[allow(dead_code)]
inner: Box<dyn NyashBox>,
}
impl ConsoleBoxAdapter {
pub fn new(box_instance: Box<dyn NyashBox>) -> Self {
Self { inner: box_instance }
}
}
impl ConsoleService for ConsoleBoxAdapter {
// Phase 95 以降で実装
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;

View File

@ -59,31 +59,20 @@ pub use unified_registry::{
// Use unified plugin loader (formerly v2) // Use unified plugin loader (formerly v2)
// pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy // pub use plugin_loader::{PluginLoaderV2 as PluginLoader, get_global_loader_v2 as get_global_loader}; // legacy
/// Runtime 初期化Phase 93: 実装完了) /// Runtime 初期化Phase 94: Fail-Fast 実装完了)
/// ///
/// 環境変数 NYASH_USE_PLUGIN_HOST=1 で PluginHost を有効化。 /// Phase 94: フォールバック削除 - 常に実際の Box を使用
pub fn initialize_runtime(ring0: std::sync::Arc<Ring0Context>) -> Result<plugin_host::PluginHost, CoreInitError> { pub fn initialize_runtime(ring0: std::sync::Arc<Ring0Context>) -> Result<plugin_host::PluginHost, CoreInitError> {
use crate::box_factory::UnifiedBoxRegistry; use crate::box_factory::UnifiedBoxRegistry;
use crate::box_factory::builtin::BuiltinBoxFactory;
let registry = UnifiedBoxRegistry::with_env_policy(); let mut registry = UnifiedBoxRegistry::with_env_policy();
// Phase 93: 環境変数で有効化(段階的展開) // Phase 94: BuiltinBoxFactory を登録して core_required Boxes を提供
let use_plugin_host = std::env::var("NYASH_USE_PLUGIN_HOST") registry.register(std::sync::Arc::new(BuiltinBoxFactory::new()));
.ok()
.and_then(|v| if v == "1" { Some(()) } else { None })
.is_some();
if use_plugin_host { // Phase 94: 常に実際の Box → Service 変換を使用Fail-Fast原則
let plugin_host = plugin_host::PluginHost::with_core_from_registry(ring0, &registry)?; let plugin_host = plugin_host::PluginHost::with_core_from_registry(ring0, &registry)?;
plugin_host.ensure_core_initialized(); plugin_host.ensure_core_initialized();
Ok(plugin_host) Ok(plugin_host)
} else {
// フォールバックPhase 94 で削除予定)
let core = core_services::CoreServices::dummy();
Ok(plugin_host::PluginHost {
ring0,
core,
optional: std::collections::HashMap::new(),
})
}
} }

View File

@ -90,33 +90,77 @@ impl PluginHost {
/// UnifiedBoxRegistry から core_required Box を取得して CoreServices を初期化 /// UnifiedBoxRegistry から core_required Box を取得して CoreServices を初期化
/// ///
/// Phase 93: ダミー Service 実装(存在確認のみ) /// Phase 94: 実際の Box → Service 変換実装
/// Phase 94: 実際の Box → Service 変換実装予定
pub fn with_core_from_registry( pub fn with_core_from_registry(
ring0: Arc<Ring0Context>, ring0: Arc<Ring0Context>,
registry: &UnifiedBoxRegistry, registry: &UnifiedBoxRegistry,
) -> Result<Self, CoreInitError> { ) -> Result<Self, CoreInitError> {
// Phase 93: 各 core_required Box が registry に存在するか確認 use crate::runtime::core_services::*;
for id in CoreServices::required_ids() {
let box_name = id.name();
// Phase 93: has_type() で存在確認 // Phase 94: 各 core_required Box を取得して Adapter に変換
if !registry.has_type(box_name) {
return Err(CoreInitError::MissingService {
box_id: *id,
message: format!("{} not found in registry", box_name),
});
}
}
// Phase 93: ダミー Service 実装で CoreServices を構築 // StringBox
let string_box = registry
.create_box("StringBox", &[])
.map_err(|e| CoreInitError::MissingService {
box_id: CoreBoxId::String,
message: format!("StringBox creation failed: {}", e),
})?;
let string_service = Arc::new(StringBoxAdapter::new(string_box));
// IntegerBox
let integer_box = registry
.create_box("IntegerBox", &[])
.map_err(|e| CoreInitError::MissingService {
box_id: CoreBoxId::Integer,
message: format!("IntegerBox creation failed: {}", e),
})?;
let integer_service = Arc::new(IntegerBoxAdapter::new(integer_box));
// BoolBox
let bool_box = registry
.create_box("BoolBox", &[])
.map_err(|e| CoreInitError::MissingService {
box_id: CoreBoxId::Bool,
message: format!("BoolBox creation failed: {}", e),
})?;
let bool_service = Arc::new(BoolBoxAdapter::new(bool_box));
// ArrayBox
let array_box = registry
.create_box("ArrayBox", &[])
.map_err(|e| CoreInitError::MissingService {
box_id: CoreBoxId::Array,
message: format!("ArrayBox creation failed: {}", e),
})?;
let array_service = Arc::new(ArrayBoxAdapter::new(array_box));
// MapBox
let map_box = registry
.create_box("MapBox", &[])
.map_err(|e| CoreInitError::MissingService {
box_id: CoreBoxId::Map,
message: format!("MapBox creation failed: {}", e),
})?;
let map_service = Arc::new(MapBoxAdapter::new(map_box));
// ConsoleBox
let console_box = registry
.create_box("ConsoleBox", &[])
.map_err(|e| CoreInitError::MissingService {
box_id: CoreBoxId::Console,
message: format!("ConsoleBox creation failed: {}", e),
})?;
let console_service = Arc::new(ConsoleBoxAdapter::new(console_box));
// Phase 94: 実際の Adapter を使用して CoreServices を構築
let core = CoreServices { let core = CoreServices {
string: Arc::new(DummyStringService), string: string_service,
integer: Arc::new(DummyIntegerService), integer: integer_service,
bool: Arc::new(DummyBoolService), bool: bool_service,
array: Arc::new(DummyArrayService), array: array_service,
map: Arc::new(DummyMapService), map: map_service,
console: Arc::new(DummyConsoleService), console: console_service,
}; };
Ok(PluginHost { Ok(PluginHost {
@ -132,25 +176,6 @@ impl PluginHost {
} }
} }
// Phase 93: ダミー Service 実装Phase 94 で削除予定)
pub struct DummyStringService;
impl super::core_services::StringService for DummyStringService {}
pub struct DummyIntegerService;
impl super::core_services::IntegerService for DummyIntegerService {}
pub struct DummyBoolService;
impl super::core_services::BoolService for DummyBoolService {}
pub struct DummyArrayService;
impl super::core_services::ArrayService for DummyArrayService {}
pub struct DummyMapService;
impl super::core_services::MapService for DummyMapService {}
pub struct DummyConsoleService;
impl super::core_services::ConsoleService for DummyConsoleService {}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@ -176,38 +201,42 @@ mod tests {
#[test] #[test]
fn test_core_services_all_fields() { fn test_core_services_all_fields() {
let services = CoreServices { // Phase 94: 実際の registry を使用してテスト
string: Arc::new(DummyStringService), use crate::runtime::ring0::default_ring0;
integer: Arc::new(DummyIntegerService), use crate::box_factory::builtin::BuiltinBoxFactory;
bool: Arc::new(DummyBoolService),
array: Arc::new(DummyArrayService), let ring0 = Arc::new(default_ring0());
map: Arc::new(DummyMapService), let mut registry = UnifiedBoxRegistry::new();
console: Arc::new(DummyConsoleService), registry.register(Arc::new(BuiltinBoxFactory::new()));
};
services.ensure_initialized(); let plugin_host = PluginHost::with_core_from_registry(ring0, &registry)
// panic しないことを確認Phase 92 で検証ロジック追加予定) .expect("CoreServices should be initialized with builtin boxes");
plugin_host.ensure_core_initialized();
// panic しないことを確認
} }
#[test] #[test]
fn test_core_services_coverage() { fn test_core_services_coverage() {
// Phase 87 CoreBoxId の core_required (6個) 全てをカバーすることを検証 // Phase 94: 実際の registry を使用して全フィールドが存在することを確認
let services = CoreServices { use crate::runtime::ring0::default_ring0;
string: Arc::new(DummyStringService), use crate::box_factory::builtin::BuiltinBoxFactory;
integer: Arc::new(DummyIntegerService),
bool: Arc::new(DummyBoolService), let ring0 = Arc::new(default_ring0());
array: Arc::new(DummyArrayService), let mut registry = UnifiedBoxRegistry::new();
map: Arc::new(DummyMapService), registry.register(Arc::new(BuiltinBoxFactory::new()));
console: Arc::new(DummyConsoleService),
}; let plugin_host = PluginHost::with_core_from_registry(ring0, &registry)
.expect("CoreServices should be initialized");
// Phase 87 core_required (6個) と一致することを確認 // Phase 87 core_required (6個) と一致することを確認
// String, Integer, Bool, Array, Map, Console // String, Integer, Bool, Array, Map, Console
let _string = &services.string; let _string = &plugin_host.core.string;
let _integer = &services.integer; let _integer = &plugin_host.core.integer;
let _bool = &services.bool; let _bool = &plugin_host.core.bool;
let _array = &services.array; let _array = &plugin_host.core.array;
let _map = &services.map; let _map = &plugin_host.core.map;
let _console = &services.console; let _console = &plugin_host.core.console;
// 全フィールドが存在することを確認 // 全フィールドが存在することを確認
} }
@ -247,7 +276,7 @@ mod tests {
#[test] #[test]
fn test_with_core_from_registry_missing_box() { fn test_with_core_from_registry_missing_box() {
// Phase 93: registry が空の場合はエラーを返すことを確認 // Phase 94: registry が空の場合はエラーを返すことを確認
use crate::runtime::ring0::default_ring0; use crate::runtime::ring0::default_ring0;
let ring0 = Arc::new(default_ring0()); let ring0 = Arc::new(default_ring0());
let registry = UnifiedBoxRegistry::new(); let registry = UnifiedBoxRegistry::new();
@ -255,10 +284,15 @@ mod tests {
let result = PluginHost::with_core_from_registry(ring0, &registry); let result = PluginHost::with_core_from_registry(ring0, &registry);
assert!(result.is_err()); assert!(result.is_err());
// エラーメッセージに "not found" が含まれることを確認 // Phase 94: エラーメッセージに "creation failed" または "Unknown Box type" が含まれることを確認
if let Err(e) = result { if let Err(e) = result {
let msg = format!("{}", e); let msg = format!("{}", e);
assert!(msg.contains("not found")); eprintln!("Error message: {}", msg); // デバッグ出力
assert!(
msg.contains("creation failed") || msg.contains("Unknown Box type"),
"Error message should contain 'creation failed' or 'Unknown Box type', got: {}",
msg
);
} }
} }
} }