From d4dfe3071d3921d7730faa6ffef6c487f8542485 Mon Sep 17 00:00:00 2001 From: Moe Charm Date: Sun, 17 Aug 2025 22:44:16 +0900 Subject: [PATCH] feat(phase-9.75g-0): Implement Day 4 plugin system infrastructure (80% complete) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Core plugin system components implemented: - nyash.toml parser for plugin configuration - BoxFactoryRegistry for unified Box creation management - PluginBox proxy for FFI boundary abstraction - Runtime module integration Key features: - Simple TOML parsing without external dependencies - Transparent Box switching (builtin ↔ plugin) - Everything is Box philosophy maintained - Thread-safe design with RwLock Remaining Day 4 tasks: - FileBox FFI interface definition - Dynamic plugin loading with libloading - Plugin FileBox integration tests 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude --- build_check.sh | 3 + src/lib.rs | 3 + src/runtime/box_registry.rs | 137 +++++++++++++++++++++++++++++++++ src/runtime/mod.rs | 11 +++ src/runtime/plugin_box.rs | 146 ++++++++++++++++++++++++++++++++++++ 5 files changed, 300 insertions(+) create mode 100644 build_check.sh create mode 100644 src/runtime/box_registry.rs create mode 100644 src/runtime/mod.rs create mode 100644 src/runtime/plugin_box.rs diff --git a/build_check.sh b/build_check.sh new file mode 100644 index 00000000..55918825 --- /dev/null +++ b/build_check.sh @@ -0,0 +1,3 @@ +#!/bin/bash +cargo check --lib > build_errors.txt 2>&1 +echo "Build check completed, errors saved to build_errors.txt" \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index e217df4f..884c4b2b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,9 @@ pub mod value; // 🚀 BID-FFI: Box Interface Definition with FFI (NEW!) pub mod bid; +// 🔌 Runtime: Plugin System and Box Management (NEW!) +pub mod runtime; + // 🌐 P2P Communication Infrastructure (NEW!) pub mod messaging; pub mod transport; diff --git a/src/runtime/box_registry.rs b/src/runtime/box_registry.rs new file mode 100644 index 00000000..b4e3ae89 --- /dev/null +++ b/src/runtime/box_registry.rs @@ -0,0 +1,137 @@ +//! Boxファクトリレジストリ - Box生成の中央管理 +//! +//! ビルトインBoxとプラグインBoxを統一的に管理し、 +//! 透過的な置き換えを実現する + +use crate::box_trait::NyashBox; +use crate::runtime::plugin_config::PluginConfig; +use std::collections::HashMap; +use std::sync::{Arc, RwLock}; + +/// Box生成方法を表す列挙型 +pub enum BoxProvider { + /// ビルトイン実装(Rust関数) + Builtin(BoxConstructor), + + /// プラグイン実装(プラグイン名を保持) + Plugin(String), +} + +/// ビルトインBoxのコンストラクタ関数型 +pub type BoxConstructor = fn(&[Box]) -> Result, String>; + +/// Boxファクトリレジストリ +pub struct BoxFactoryRegistry { + /// Box名 → プロバイダーのマッピング + providers: RwLock>, +} + +impl BoxFactoryRegistry { + /// 新しいレジストリを作成 + pub fn new() -> Self { + Self { + providers: RwLock::new(HashMap::new()), + } + } + + /// ビルトインBoxを登録 + pub fn register_builtin(&self, name: &str, constructor: BoxConstructor) { + let mut providers = self.providers.write().unwrap(); + providers.insert(name.to_string(), BoxProvider::Builtin(constructor)); + } + + /// プラグイン設定を適用(既存のビルトインを上書き) + pub fn apply_plugin_config(&self, config: &PluginConfig) { + let mut providers = self.providers.write().unwrap(); + + for (box_name, plugin_name) in &config.plugins { + providers.insert( + box_name.clone(), + BoxProvider::Plugin(plugin_name.clone()) + ); + } + } + + /// Box名からプロバイダーを取得 + pub fn get_provider(&self, name: &str) -> Option { + let providers = self.providers.read().unwrap(); + providers.get(name).cloned() + } + + /// Boxを生成 + pub fn create_box(&self, name: &str, args: &[Box]) -> Result, String> { + let provider = self.get_provider(name) + .ok_or_else(|| format!("Unknown Box type: {}", name))?; + + match provider { + BoxProvider::Builtin(constructor) => { + // ビルトイン実装を直接呼び出し + constructor(args) + } + BoxProvider::Plugin(plugin_name) => { + // TODO: プラグインローダーと連携 + Err(format!("Plugin loading not yet implemented: {}", plugin_name)) + } + } + } +} + +impl Clone for BoxProvider { + fn clone(&self) -> Self { + match self { + BoxProvider::Builtin(f) => BoxProvider::Builtin(*f), + BoxProvider::Plugin(name) => BoxProvider::Plugin(name.clone()), + } + } +} + +// グローバルレジストリインスタンス +use once_cell::sync::Lazy; + +static GLOBAL_REGISTRY: Lazy> = + Lazy::new(|| Arc::new(BoxFactoryRegistry::new())); + +/// グローバルレジストリを取得 +pub fn get_global_registry() -> Arc { + GLOBAL_REGISTRY.clone() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::box_trait::StringBox; + + fn test_string_constructor(args: &[Box]) -> Result, String> { + if args.is_empty() { + Ok(Box::new(StringBox::new(""))) + } else { + Ok(Box::new(StringBox::new(&args[0].to_string_box().value))) + } + } + + #[test] + fn test_builtin_registration() { + let registry = BoxFactoryRegistry::new(); + registry.register_builtin("StringBox", test_string_constructor); + + let result = registry.create_box("StringBox", &[]).unwrap(); + assert_eq!(result.to_string_box().value, ""); + } + + #[test] + fn test_plugin_override() { + let registry = BoxFactoryRegistry::new(); + registry.register_builtin("FileBox", test_string_constructor); + + // プラグイン設定で上書き + let mut config = PluginConfig::default(); + config.plugins.insert("FileBox".to_string(), "filebox".to_string()); + registry.apply_plugin_config(&config); + + // プロバイダーがプラグインに変わっているか確認 + match registry.get_provider("FileBox").unwrap() { + BoxProvider::Plugin(name) => assert_eq!(name, "filebox"), + _ => panic!("Expected plugin provider"), + } + } +} \ No newline at end of file diff --git a/src/runtime/mod.rs b/src/runtime/mod.rs new file mode 100644 index 00000000..f96c4a49 --- /dev/null +++ b/src/runtime/mod.rs @@ -0,0 +1,11 @@ +//! Nyashランタイムモジュール +//! +//! プラグインシステムとBox管理の中核 + +pub mod plugin_config; +pub mod box_registry; +pub mod plugin_box; + +pub use plugin_config::PluginConfig; +pub use box_registry::{BoxFactoryRegistry, BoxProvider, get_global_registry}; +pub use plugin_box::PluginBox; \ No newline at end of file diff --git a/src/runtime/plugin_box.rs b/src/runtime/plugin_box.rs new file mode 100644 index 00000000..5df9650b --- /dev/null +++ b/src/runtime/plugin_box.rs @@ -0,0 +1,146 @@ +//! PluginBoxプロキシ - プラグインBoxの統一インターフェース +//! +//! すべてのプラグインから提供されるBoxを、 +//! 通常のNyashBoxとして扱えるようにするプロキシ実装 + +use crate::box_trait::{NyashBox, StringBox, BoolBox, BoxCore, BoxBase}; +use crate::bid::BidHandle; +use std::any::Any; +use std::fmt; + +/// プラグインから提供されるBoxのプロキシ +/// +/// FFI境界を越えてプラグイン内のBoxインスタンスと通信する +#[derive(Debug)] +pub struct PluginBox { + /// BoxCoreトレイト用の基本情報 + base: BoxBase, + + /// プラグイン名(例: "filebox") + plugin_name: String, + + /// プラグイン内のインスタンスハンドル + handle: BidHandle, +} + +impl PluginBox { + /// 新しいPluginBoxプロキシを作成 + pub fn new(plugin_name: String, handle: BidHandle) -> Self { + Self { + base: BoxBase::new(), + plugin_name, + handle, + } + } + + /// プラグイン名を取得 + pub fn plugin_name(&self) -> &str { + &self.plugin_name + } + + /// ハンドルを取得 + pub fn handle(&self) -> BidHandle { + self.handle + } +} + +impl BoxCore for PluginBox { + fn box_id(&self) -> u64 { + self.base.id + } + + fn parent_type_id(&self) -> Option { + self.base.parent_type_id + } + + fn fmt_box(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "PluginBox({}, handle={:?})", self.plugin_name, self.handle) + } + + fn as_any(&self) -> &dyn Any { + self + } + + fn as_any_mut(&mut self) -> &mut dyn Any { + self + } +} + +impl NyashBox for PluginBox { + fn clone_box(&self) -> Box { + // TODO: FFI経由でプラグインにcloneを依頼 + // 現在は同じハンドルを持つ新しいプロキシを返す(簡易実装) + Box::new(PluginBox::new(self.plugin_name.clone(), self.handle)) + } + + fn share_box(&self) -> Box { + // 現在はclone_boxと同じ実装 + self.clone_box() + } + + fn to_string_box(&self) -> StringBox { + // TODO: FFI経由でプラグインにtoString呼び出し + StringBox::new(&format!("PluginBox({}, {:?})", self.plugin_name, self.handle)) + } + + fn type_name(&self) -> &'static str { + // TODO: プラグインから実際の型名を取得 + "PluginBox" + } + + fn equals(&self, other: &dyn NyashBox) -> BoolBox { + if let Some(other_plugin) = other.as_any().downcast_ref::() { + // 同じプラグイン&同じハンドルなら等しい + BoolBox::new( + self.plugin_name == other_plugin.plugin_name && + self.handle == other_plugin.handle + ) + } else { + BoolBox::new(false) + } + } +} + +impl fmt::Display for PluginBox { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.fmt_box(f) + } +} + +impl Clone for PluginBox { + fn clone(&self) -> Self { + Self { + base: BoxBase::new(), // 新しいIDを生成 + plugin_name: self.plugin_name.clone(), + handle: self.handle, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::bid::BoxTypeId; + + #[test] + fn test_plugin_box_creation() { + let handle = BidHandle::new(BoxTypeId::FileBox as u32, 123); + let plugin_box = PluginBox::new("filebox".to_string(), handle); + + assert_eq!(plugin_box.plugin_name(), "filebox"); + assert_eq!(plugin_box.handle(), handle); + } + + #[test] + fn test_plugin_box_equality() { + let handle1 = BidHandle::new(BoxTypeId::FileBox as u32, 123); + let handle2 = BidHandle::new(BoxTypeId::FileBox as u32, 456); + + let box1 = PluginBox::new("filebox".to_string(), handle1); + let box2 = PluginBox::new("filebox".to_string(), handle1); + let box3 = PluginBox::new("filebox".to_string(), handle2); + + assert!(box1.equals(&box2).value); + assert!(!box1.equals(&box3).value); + } +} \ No newline at end of file