feat(phase-9.75g-0): Implement Day 4 plugin system infrastructure (80% complete)

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 <noreply@anthropic.com>
This commit is contained in:
Moe Charm
2025-08-17 22:44:16 +09:00
parent 72b63546b0
commit d4dfe3071d
5 changed files with 300 additions and 0 deletions

3
build_check.sh Normal file
View File

@ -0,0 +1,3 @@
#!/bin/bash
cargo check --lib > build_errors.txt 2>&1
echo "Build check completed, errors saved to build_errors.txt"

View File

@ -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;

137
src/runtime/box_registry.rs Normal file
View File

@ -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<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, String>;
/// Boxファクトリレジストリ
pub struct BoxFactoryRegistry {
/// Box名 → プロバイダーのマッピング
providers: RwLock<HashMap<String, BoxProvider>>,
}
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<BoxProvider> {
let providers = self.providers.read().unwrap();
providers.get(name).cloned()
}
/// Boxを生成
pub fn create_box(&self, name: &str, args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, 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<Arc<BoxFactoryRegistry>> =
Lazy::new(|| Arc::new(BoxFactoryRegistry::new()));
/// グローバルレジストリを取得
pub fn get_global_registry() -> Arc<BoxFactoryRegistry> {
GLOBAL_REGISTRY.clone()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::box_trait::StringBox;
fn test_string_constructor(args: &[Box<dyn NyashBox>]) -> Result<Box<dyn NyashBox>, 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"),
}
}
}

11
src/runtime/mod.rs Normal file
View File

@ -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;

146
src/runtime/plugin_box.rs Normal file
View File

@ -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<std::any::TypeId> {
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<dyn NyashBox> {
// TODO: FFI経由でプラグインにcloneを依頼
// 現在は同じハンドルを持つ新しいプロキシを返す(簡易実装)
Box::new(PluginBox::new(self.plugin_name.clone(), self.handle))
}
fn share_box(&self) -> Box<dyn NyashBox> {
// 現在は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::<PluginBox>() {
// 同じプラグイン&同じハンドルなら等しい
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);
}
}