2025-08-19 16:43:13 +09:00
|
|
|
|
/*!
|
|
|
|
|
|
* Unified Box Factory Architecture
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-08-19 16:43:13 +09:00
|
|
|
|
* Phase 9.78: 統合BoxFactoryアーキテクチャ
|
|
|
|
|
|
* すべてのBox生成(ビルトイン、ユーザー定義、プラグイン)を統一的に扱う
|
2025-09-17 07:43:07 +09:00
|
|
|
|
*
|
2025-08-19 16:43:13 +09:00
|
|
|
|
* Design principles:
|
|
|
|
|
|
* - "Everything is Box" 哲学の実装レベルでの体現
|
|
|
|
|
|
* - birth/finiライフサイクルの明確な責務分離
|
|
|
|
|
|
* - 保守性と拡張性の劇的向上
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
use crate::box_trait::NyashBox;
|
|
|
|
|
|
use std::collections::HashMap;
|
|
|
|
|
|
use std::sync::{Arc, RwLock};
|
|
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
/// Factory Priority Policy for Box creation (Phase 15.5 "Everything is Plugin")
|
|
|
|
|
|
///
|
|
|
|
|
|
/// Determines the order in which different Box factories are consulted
|
|
|
|
|
|
/// during Box creation to solve the StringBox/IntegerBox plugin priority issue.
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
pub enum FactoryPolicy {
|
|
|
|
|
|
/// Strict Plugin Priority: plugins > user > builtin
|
|
|
|
|
|
/// ⚡ SOLVES THE CORE PROBLEM: Plugins have highest priority
|
|
|
|
|
|
/// Use when plugins should completely replace builtins (Phase 15.5)
|
|
|
|
|
|
StrictPluginFirst,
|
|
|
|
|
|
|
|
|
|
|
|
/// Compatible Plugin Priority: plugins > builtin > user
|
|
|
|
|
|
/// 🔧 Compatibility mode: Plugins first, but builtins before user-defined
|
|
|
|
|
|
/// Use for gradual migration scenarios
|
|
|
|
|
|
CompatPluginFirst,
|
|
|
|
|
|
|
|
|
|
|
|
/// Legacy Builtin Priority: builtin > user > plugin (CURRENT DEFAULT)
|
|
|
|
|
|
/// ⚠️ PROBLEMATIC: Plugins can never override builtins
|
|
|
|
|
|
/// Only use for compatibility with existing setups
|
|
|
|
|
|
BuiltinFirst,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Factory type classification for policy-based ordering
|
|
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
|
|
|
|
pub enum FactoryType {
|
|
|
|
|
|
/// Built-in factory (StringBox, IntegerBox, etc.)
|
|
|
|
|
|
Builtin,
|
|
|
|
|
|
/// User-defined Box factory
|
|
|
|
|
|
User,
|
|
|
|
|
|
/// Plugin-provided Box factory
|
|
|
|
|
|
Plugin,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 09:30:42 +09:00
|
|
|
|
/// Runtime error types for Box operations
|
|
|
|
|
|
#[derive(Debug, thiserror::Error)]
|
|
|
|
|
|
pub enum RuntimeError {
|
|
|
|
|
|
#[error("invalid operation: {message}")]
|
|
|
|
|
|
InvalidOperation { message: String },
|
|
|
|
|
|
#[error("type error: {message}")]
|
|
|
|
|
|
TypeError { message: String },
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Shared state for interpreter context (legacy compatibility)
|
|
|
|
|
|
#[derive(Debug, Default, Clone)]
|
|
|
|
|
|
pub struct SharedState;
|
|
|
|
|
|
|
|
|
|
|
|
impl SharedState {
|
|
|
|
|
|
pub fn new() -> Self {
|
|
|
|
|
|
Self
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Unified interface for all Box creation
|
|
|
|
|
|
pub trait BoxFactory: Send + Sync {
|
|
|
|
|
|
/// Create a new Box instance with given arguments
|
|
|
|
|
|
fn create_box(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
name: &str,
|
|
|
|
|
|
args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
) -> Result<Box<dyn NyashBox>, RuntimeError>;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Check if this factory is currently available
|
|
|
|
|
|
fn is_available(&self) -> bool {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Get list of Box types this factory can create
|
|
|
|
|
|
fn box_types(&self) -> Vec<&str>;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Check if this factory supports birth/fini lifecycle
|
|
|
|
|
|
fn supports_birth(&self) -> bool {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
|
|
|
|
|
|
/// Identify builtin factory to enforce reserved-name protections
|
|
|
|
|
|
fn is_builtin_factory(&self) -> bool {
|
|
|
|
|
|
false
|
|
|
|
|
|
}
|
2025-09-24 11:43:11 +09:00
|
|
|
|
|
|
|
|
|
|
/// Identify factory type for policy-based priority ordering
|
|
|
|
|
|
fn factory_type(&self) -> FactoryType {
|
|
|
|
|
|
if self.is_builtin_factory() {
|
|
|
|
|
|
FactoryType::Builtin
|
|
|
|
|
|
} else {
|
|
|
|
|
|
FactoryType::Plugin // Default assumption for external factories
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Registry that manages all BoxFactory implementations
|
|
|
|
|
|
pub struct UnifiedBoxRegistry {
|
2025-09-24 11:43:11 +09:00
|
|
|
|
/// Ordered list of factories with policy-based priority
|
2025-08-19 16:43:13 +09:00
|
|
|
|
pub factories: Vec<Arc<dyn BoxFactory>>,
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Quick lookup cache for performance
|
|
|
|
|
|
type_cache: RwLock<HashMap<String, usize>>, // maps type name to factory index
|
2025-09-24 11:43:11 +09:00
|
|
|
|
|
|
|
|
|
|
/// Factory priority policy (Phase 15.5: Everything is Plugin)
|
|
|
|
|
|
policy: FactoryPolicy,
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
impl UnifiedBoxRegistry {
|
2025-09-24 11:43:11 +09:00
|
|
|
|
/// Create a new empty registry with default policy
|
2025-12-02 21:52:18 +09:00
|
|
|
|
/// Phase 86: Default changed to StrictPluginFirst via with_env_policy()
|
2025-08-19 16:43:13 +09:00
|
|
|
|
pub fn new() -> Self {
|
2025-12-02 21:52:18 +09:00
|
|
|
|
Self::with_env_policy()
|
2025-09-24 11:43:11 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Create a new empty registry with specified policy
|
|
|
|
|
|
pub fn with_policy(policy: FactoryPolicy) -> Self {
|
2025-08-19 16:43:13 +09:00
|
|
|
|
Self {
|
|
|
|
|
|
factories: Vec::new(),
|
|
|
|
|
|
type_cache: RwLock::new(HashMap::new()),
|
2025-09-24 11:43:11 +09:00
|
|
|
|
policy,
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
/// Create registry with policy from environment variable (Phase 15.5 setup)
|
|
|
|
|
|
pub fn with_env_policy() -> Self {
|
2025-12-24 08:43:48 +09:00
|
|
|
|
let policy = match crate::config::env::box_factory_policy().as_deref() {
|
2025-09-24 11:43:11 +09:00
|
|
|
|
Some("compat_plugin_first") => FactoryPolicy::CompatPluginFirst,
|
|
|
|
|
|
Some("builtin_first") => FactoryPolicy::BuiltinFirst,
|
|
|
|
|
|
Some("strict_plugin_first") | _ => FactoryPolicy::StrictPluginFirst, // Phase 15.5: Plugin First DEFAULT!
|
|
|
|
|
|
};
|
|
|
|
|
|
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[UnifiedBoxRegistry] 🎯 Factory Policy: {:?} (Phase 15.5: Everything is Plugin!)",
|
|
|
|
|
|
policy
|
|
|
|
|
|
);
|
2025-09-24 11:43:11 +09:00
|
|
|
|
Self::with_policy(policy)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Get current factory policy
|
|
|
|
|
|
pub fn get_policy(&self) -> FactoryPolicy {
|
|
|
|
|
|
self.policy
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
/// Set factory policy and rebuild cache to reflect new priorities
|
|
|
|
|
|
pub fn set_policy(&mut self, policy: FactoryPolicy) {
|
|
|
|
|
|
if self.policy != policy {
|
|
|
|
|
|
self.policy = policy;
|
|
|
|
|
|
self.rebuild_cache();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Rebuild type cache based on current policy
|
|
|
|
|
|
fn rebuild_cache(&mut self) {
|
|
|
|
|
|
// Clear existing cache
|
2025-08-19 16:43:13 +09:00
|
|
|
|
let mut cache = self.type_cache.write().unwrap();
|
2025-09-24 11:43:11 +09:00
|
|
|
|
cache.clear();
|
|
|
|
|
|
|
|
|
|
|
|
// Get factory priority order based on policy
|
|
|
|
|
|
let factory_order = self.get_factory_order_by_policy();
|
|
|
|
|
|
|
|
|
|
|
|
// Re-register types with policy-based priority
|
|
|
|
|
|
for &factory_index in factory_order.iter() {
|
|
|
|
|
|
if let Some(factory) = self.factories.get(factory_index) {
|
|
|
|
|
|
let types = factory.box_types();
|
|
|
|
|
|
|
feat(runtime): Phase 87 CoreBoxId/CoreMethodId 箱化完了
ハードコード文字列から型安全な enum への箱化により、
Box名・メソッド名管理を完全にコンパイル時検証可能に。
主な実装:
- CoreBoxId enum 定義(19個)
- core_required: 6個(String, Integer, Bool, Array, Map, Console)
- core_optional: 9個(Float, Null, File, Path, Regex, Math, Time, Json, Toml)
- 特殊型: 4個(Function, Result, Method, Missing)
- CoreMethodId enum 定義(30個)
- 各 Box のメソッドを型安全に管理
- 引数数、戻り値型情報を統合
- is_reserved_type() を CoreBoxId ベースにリファクタリング
- infer_boxcall_return_type() を CoreMethodId ベースに改良(75行 → 25行、67%削減)
検証結果:
- テスト: ✅ 11/11 passed(新規追加)
- ビルド: ✅ 成功(0エラー)
- 型安全性: ✅ タイポ不可能
効果:
- SSOT 確立(src/runtime/core_box_ids.rs に一元化)
- コンパイル時検証(実行時エラー → コンパイルエラー)
- 保守性向上(変更箇所の一元化)
- IDE 支援(enum 補完可能)
ドキュメント:
- core_boxes_design.md 作成(Phase 87 完全仕様)
- Phase 85 README 更新(Phase 87 セクション追加)
Phase 15.5「Everything is Plugin」アーキテクチャ基盤完成
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 22:22:32 +09:00
|
|
|
|
// Phase 87: Reserved core types using CoreBoxId (型安全化)
|
2025-09-24 11:43:11 +09:00
|
|
|
|
fn is_reserved_type(name: &str) -> bool {
|
feat(runtime): Phase 87 CoreBoxId/CoreMethodId 箱化完了
ハードコード文字列から型安全な enum への箱化により、
Box名・メソッド名管理を完全にコンパイル時検証可能に。
主な実装:
- CoreBoxId enum 定義(19個)
- core_required: 6個(String, Integer, Bool, Array, Map, Console)
- core_optional: 9個(Float, Null, File, Path, Regex, Math, Time, Json, Toml)
- 特殊型: 4個(Function, Result, Method, Missing)
- CoreMethodId enum 定義(30個)
- 各 Box のメソッドを型安全に管理
- 引数数、戻り値型情報を統合
- is_reserved_type() を CoreBoxId ベースにリファクタリング
- infer_boxcall_return_type() を CoreMethodId ベースに改良(75行 → 25行、67%削減)
検証結果:
- テスト: ✅ 11/11 passed(新規追加)
- ビルド: ✅ 成功(0エラー)
- 型安全性: ✅ タイポ不可能
効果:
- SSOT 確立(src/runtime/core_box_ids.rs に一元化)
- コンパイル時検証(実行時エラー → コンパイルエラー)
- 保守性向上(変更箇所の一元化)
- IDE 支援(enum 補完可能)
ドキュメント:
- core_boxes_design.md 作成(Phase 87 完全仕様)
- Phase 85 README 更新(Phase 87 セクション追加)
Phase 15.5「Everything is Plugin」アーキテクチャ基盤完成
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 22:22:32 +09:00
|
|
|
|
use crate::runtime::CoreBoxId;
|
|
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
// Phase 15.5: 環境変数でプラグイン優先モード時は保護解除
|
2025-12-24 08:43:48 +09:00
|
|
|
|
if crate::config::env::use_plugin_builtins() {
|
|
|
|
|
|
if let Some(types) = crate::config::env::plugin_override_types() {
|
|
|
|
|
|
if types.iter().any(|t| t == name) {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
return false; // 予約型として扱わない
|
2025-09-24 11:43:11 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-09-24 05:57:08 +09:00
|
|
|
|
}
|
feat(runtime): Phase 87 CoreBoxId/CoreMethodId 箱化完了
ハードコード文字列から型安全な enum への箱化により、
Box名・メソッド名管理を完全にコンパイル時検証可能に。
主な実装:
- CoreBoxId enum 定義(19個)
- core_required: 6個(String, Integer, Bool, Array, Map, Console)
- core_optional: 9個(Float, Null, File, Path, Regex, Math, Time, Json, Toml)
- 特殊型: 4個(Function, Result, Method, Missing)
- CoreMethodId enum 定義(30個)
- 各 Box のメソッドを型安全に管理
- 引数数、戻り値型情報を統合
- is_reserved_type() を CoreBoxId ベースにリファクタリング
- infer_boxcall_return_type() を CoreMethodId ベースに改良(75行 → 25行、67%削減)
検証結果:
- テスト: ✅ 11/11 passed(新規追加)
- ビルド: ✅ 成功(0エラー)
- 型安全性: ✅ タイポ不可能
効果:
- SSOT 確立(src/runtime/core_box_ids.rs に一元化)
- コンパイル時検証(実行時エラー → コンパイルエラー)
- 保守性向上(変更箇所の一元化)
- IDE 支援(enum 補完可能)
ドキュメント:
- core_boxes_design.md 作成(Phase 87 完全仕様)
- Phase 85 README 更新(Phase 87 セクション追加)
Phase 15.5「Everything is Plugin」アーキテクチャ基盤完成
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 22:22:32 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 87: CoreBoxId による型安全な判定
|
|
|
|
|
|
// core_required (6個) + 特殊型の一部を予約型として保護
|
|
|
|
|
|
CoreBoxId::from_name(name)
|
|
|
|
|
|
.map(|id| {
|
2025-12-11 20:54:33 +09:00
|
|
|
|
id.is_core_required()
|
|
|
|
|
|
|| matches!(id, CoreBoxId::Result | CoreBoxId::Method)
|
feat(runtime): Phase 87 CoreBoxId/CoreMethodId 箱化完了
ハードコード文字列から型安全な enum への箱化により、
Box名・メソッド名管理を完全にコンパイル時検証可能に。
主な実装:
- CoreBoxId enum 定義(19個)
- core_required: 6個(String, Integer, Bool, Array, Map, Console)
- core_optional: 9個(Float, Null, File, Path, Regex, Math, Time, Json, Toml)
- 特殊型: 4個(Function, Result, Method, Missing)
- CoreMethodId enum 定義(30個)
- 各 Box のメソッドを型安全に管理
- 引数数、戻り値型情報を統合
- is_reserved_type() を CoreBoxId ベースにリファクタリング
- infer_boxcall_return_type() を CoreMethodId ベースに改良(75行 → 25行、67%削減)
検証結果:
- テスト: ✅ 11/11 passed(新規追加)
- ビルド: ✅ 成功(0エラー)
- 型安全性: ✅ タイポ不可能
効果:
- SSOT 確立(src/runtime/core_box_ids.rs に一元化)
- コンパイル時検証(実行時エラー → コンパイルエラー)
- 保守性向上(変更箇所の一元化)
- IDE 支援(enum 補完可能)
ドキュメント:
- core_boxes_design.md 作成(Phase 87 完全仕様)
- Phase 85 README 更新(Phase 87 セクション追加)
Phase 15.5「Everything is Plugin」アーキテクチャ基盤完成
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-02 22:22:32 +09:00
|
|
|
|
})
|
|
|
|
|
|
.unwrap_or(false)
|
2025-09-24 05:57:08 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
for type_name in types {
|
|
|
|
|
|
// Enforce reserved names: only builtin factory may claim them
|
|
|
|
|
|
if is_reserved_type(type_name) && !factory.is_builtin_factory() {
|
|
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[UnifiedBoxRegistry] ❌ Rejecting registration of reserved type '{}' by non-builtin factory #{}",
|
|
|
|
|
|
type_name, factory_index
|
|
|
|
|
|
);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
// Policy-based priority: first in order wins
|
|
|
|
|
|
let entry = cache.entry(type_name.to_string());
|
|
|
|
|
|
use std::collections::hash_map::Entry;
|
|
|
|
|
|
match entry {
|
|
|
|
|
|
Entry::Occupied(existing) => {
|
|
|
|
|
|
// Collision: type already claimed by higher-priority factory
|
|
|
|
|
|
eprintln!("[UnifiedBoxRegistry] ⚠️ Policy '{}': type '{}' kept by higher priority factory #{}, ignoring factory #{}",
|
|
|
|
|
|
format!("{:?}", self.policy), existing.key(), existing.get(), factory_index);
|
|
|
|
|
|
}
|
|
|
|
|
|
Entry::Vacant(v) => {
|
|
|
|
|
|
v.insert(factory_index);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
2025-09-24 11:43:11 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
/// Get factory indices ordered by current policy priority
|
|
|
|
|
|
fn get_factory_order_by_policy(&self) -> Vec<usize> {
|
|
|
|
|
|
let mut factory_indices: Vec<usize> = (0..self.factories.len()).collect();
|
|
|
|
|
|
|
|
|
|
|
|
// Sort by factory type according to policy
|
|
|
|
|
|
factory_indices.sort_by_key(|&index| {
|
|
|
|
|
|
if let Some(factory) = self.factories.get(index) {
|
|
|
|
|
|
let factory_type = factory.factory_type();
|
|
|
|
|
|
|
|
|
|
|
|
match self.policy {
|
|
|
|
|
|
FactoryPolicy::StrictPluginFirst => match factory_type {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
FactoryType::Plugin => 0, // Highest priority
|
|
|
|
|
|
FactoryType::User => 1, // Medium priority
|
|
|
|
|
|
FactoryType::Builtin => 2, // Lowest priority
|
2025-09-24 11:43:11 +09:00
|
|
|
|
},
|
|
|
|
|
|
FactoryPolicy::CompatPluginFirst => match factory_type {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
FactoryType::Plugin => 0, // Highest priority
|
|
|
|
|
|
FactoryType::Builtin => 1, // Medium priority
|
|
|
|
|
|
FactoryType::User => 2, // Lowest priority
|
2025-09-24 11:43:11 +09:00
|
|
|
|
},
|
|
|
|
|
|
FactoryPolicy::BuiltinFirst => match factory_type {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
FactoryType::Builtin => 0, // Highest priority (current default)
|
|
|
|
|
|
FactoryType::User => 1, // Medium priority
|
|
|
|
|
|
FactoryType::Plugin => 2, // Lowest priority
|
2025-09-24 11:43:11 +09:00
|
|
|
|
},
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
999 // Invalid factory index, put at end
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
factory_indices
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Register a new factory (policy-aware)
|
|
|
|
|
|
pub fn register(&mut self, factory: Arc<dyn BoxFactory>) {
|
|
|
|
|
|
// Simply add to the factory list
|
2025-08-19 16:43:13 +09:00
|
|
|
|
self.factories.push(factory);
|
2025-09-24 11:43:11 +09:00
|
|
|
|
|
|
|
|
|
|
// Rebuild cache to apply policy-based priority ordering
|
|
|
|
|
|
// This ensures new factory is properly integrated with current policy
|
|
|
|
|
|
self.rebuild_cache();
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Create a Box using the unified interface
|
|
|
|
|
|
pub fn create_box(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
name: &str,
|
|
|
|
|
|
args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
// Prefer plugin-builtins when enabled and provider is available in v2 registry
|
2025-11-08 15:49:25 +09:00
|
|
|
|
// BUT: Skip if plugins are explicitly disabled
|
2025-12-24 08:43:48 +09:00
|
|
|
|
let plugins_disabled = crate::config::env::disable_plugins();
|
|
|
|
|
|
if !plugins_disabled && crate::config::env::use_plugin_builtins() {
|
2025-08-29 21:39:47 +09:00
|
|
|
|
use crate::runtime::{get_global_registry, BoxProvider};
|
2025-09-06 06:24:08 +09:00
|
|
|
|
// Allowlist types for override: env NYASH_PLUGIN_OVERRIDE_TYPES="ArrayBox,MapBox" (default: none)
|
2025-12-24 08:43:48 +09:00
|
|
|
|
let allow: Vec<String> = crate::config::env::plugin_override_types()
|
|
|
|
|
|
.unwrap_or_default();
|
2025-08-29 21:39:47 +09:00
|
|
|
|
if allow.iter().any(|t| t == name) {
|
|
|
|
|
|
let v2 = get_global_registry();
|
|
|
|
|
|
if let Some(provider) = v2.get_provider(name) {
|
|
|
|
|
|
if let BoxProvider::Plugin(_lib) = provider {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
return v2.create_box(name, args).map_err(|e| {
|
|
|
|
|
|
RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: format!("Plugin Box creation failed: {}", e),
|
|
|
|
|
|
}
|
|
|
|
|
|
});
|
2025-08-29 21:39:47 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-19 16:43:13 +09:00
|
|
|
|
// Check cache first
|
|
|
|
|
|
let cache = self.type_cache.read().unwrap();
|
|
|
|
|
|
if let Some(&factory_index) = cache.get(name) {
|
|
|
|
|
|
if let Some(factory) = self.factories.get(factory_index) {
|
|
|
|
|
|
if factory.is_available() {
|
|
|
|
|
|
return factory.create_box(name, args);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
drop(cache);
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-08 17:04:21 +09:00
|
|
|
|
// Linear search through all factories with fallback support
|
|
|
|
|
|
let mut last_error: Option<RuntimeError> = None;
|
|
|
|
|
|
let factory_order = self.get_factory_order_by_policy();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-08 17:04:21 +09:00
|
|
|
|
for &factory_index in factory_order.iter() {
|
|
|
|
|
|
if let Some(factory) = self.factories.get(factory_index) {
|
|
|
|
|
|
if !factory.is_available() {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-11-08 17:04:21 +09:00
|
|
|
|
// For factories that advertise types, check if they support this type
|
|
|
|
|
|
let box_types = factory.box_types();
|
|
|
|
|
|
if !box_types.is_empty() && !box_types.contains(&name) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Try to create the box
|
2025-12-24 08:43:48 +09:00
|
|
|
|
if crate::config::env::debug_plugin() {
|
2025-11-08 17:04:21 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[UnifiedBoxRegistry] try factory#{} {:?} for {}",
|
|
|
|
|
|
factory_index,
|
|
|
|
|
|
factory.factory_type(),
|
|
|
|
|
|
name
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
match factory.create_box(name, args) {
|
|
|
|
|
|
Ok(boxed) => return Ok(boxed),
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
// FileBox special case: handle fallback based on mode
|
|
|
|
|
|
if name == "FileBox" {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
if let Some(fallback_result) = self.try_filebox_fallback(name, args, &e)
|
|
|
|
|
|
{
|
2025-11-08 17:04:21 +09:00
|
|
|
|
return fallback_result;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// For other boxes or if FileBox fallback not applicable, continue
|
|
|
|
|
|
last_error = Some(e);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-11-08 17:04:21 +09:00
|
|
|
|
|
2025-09-05 13:29:17 +09:00
|
|
|
|
// Final fallback: if v2 plugin registry has a provider for this name, try it once
|
|
|
|
|
|
{
|
|
|
|
|
|
let v2 = crate::runtime::get_global_registry();
|
|
|
|
|
|
if let Some(_prov) = v2.get_provider(name) {
|
|
|
|
|
|
if let Ok(b) = v2.create_box(name, args) {
|
|
|
|
|
|
return Ok(b);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-11-08 17:04:21 +09:00
|
|
|
|
// Return the last error if we have one, otherwise generic error
|
|
|
|
|
|
if let Some(err) = last_error {
|
|
|
|
|
|
Err(err)
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: format!("Unknown Box type: {}", name),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Try FileBox fallback based on NYASH_FILEBOX_MODE
|
|
|
|
|
|
/// Returns Some(result) if fallback is applicable, None if should continue trying other factories
|
|
|
|
|
|
fn try_filebox_fallback(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
name: &str,
|
|
|
|
|
|
args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
original_error: &RuntimeError,
|
|
|
|
|
|
) -> Option<Result<Box<dyn NyashBox>, RuntimeError>> {
|
|
|
|
|
|
use crate::runner::modes::common_util::provider_registry;
|
|
|
|
|
|
|
|
|
|
|
|
let mode = provider_registry::read_filebox_mode_from_env();
|
|
|
|
|
|
|
|
|
|
|
|
match mode {
|
|
|
|
|
|
provider_registry::FileBoxMode::PluginOnly => {
|
|
|
|
|
|
// Fail-Fast: return the original error immediately
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[FileBox] Plugin creation failed in plugin-only mode: {}",
|
|
|
|
|
|
original_error
|
|
|
|
|
|
);
|
2025-11-08 17:04:21 +09:00
|
|
|
|
Some(Err(RuntimeError::InvalidOperation {
|
2025-11-21 06:25:17 +09:00
|
|
|
|
message: format!(
|
|
|
|
|
|
"FileBox plugin creation failed (plugin-only mode): {}",
|
|
|
|
|
|
original_error
|
|
|
|
|
|
),
|
2025-11-08 17:04:21 +09:00
|
|
|
|
}))
|
|
|
|
|
|
}
|
|
|
|
|
|
provider_registry::FileBoxMode::Auto => {
|
|
|
|
|
|
// Auto mode: try fallback to builtin/core-ro
|
2025-11-21 06:25:17 +09:00
|
|
|
|
eprintln!(
|
|
|
|
|
|
"[FileBox] Plugin creation failed, falling back to builtin/core-ro: {}",
|
|
|
|
|
|
original_error
|
|
|
|
|
|
);
|
2025-11-08 17:04:21 +09:00
|
|
|
|
|
|
|
|
|
|
// Try builtin factory if available
|
|
|
|
|
|
for factory in &self.factories {
|
|
|
|
|
|
if factory.is_builtin_factory() && factory.box_types().contains(&name) {
|
|
|
|
|
|
match factory.create_box(name, args) {
|
|
|
|
|
|
Ok(boxed) => {
|
|
|
|
|
|
eprintln!("[FileBox] Successfully created with builtin factory");
|
|
|
|
|
|
return Some(Ok(boxed));
|
|
|
|
|
|
}
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
eprintln!("[FileBox] Builtin factory also failed: {}", e);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// If builtin failed, return None to continue with other factories
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
provider_registry::FileBoxMode::CoreRo => {
|
|
|
|
|
|
// Core-ro mode: try builtin factory
|
|
|
|
|
|
eprintln!("[FileBox] Using core-ro mode, trying builtin factory");
|
|
|
|
|
|
|
|
|
|
|
|
for factory in &self.factories {
|
|
|
|
|
|
if factory.is_builtin_factory() && factory.box_types().contains(&name) {
|
|
|
|
|
|
match factory.create_box(name, args) {
|
|
|
|
|
|
Ok(boxed) => return Some(Ok(boxed)),
|
|
|
|
|
|
Err(e) => {
|
|
|
|
|
|
return Some(Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: format!("FileBox core-ro creation failed: {}", e),
|
|
|
|
|
|
}));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
None
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2025-08-19 16:43:13 +09:00
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
|
|
|
|
|
|
/// Check whether a type name is known to the registry
|
|
|
|
|
|
pub fn has_type(&self, name: &str) -> bool {
|
|
|
|
|
|
// Check cache first
|
|
|
|
|
|
{
|
|
|
|
|
|
let cache = self.type_cache.read().unwrap();
|
|
|
|
|
|
if let Some(&idx) = cache.get(name) {
|
|
|
|
|
|
if let Some(factory) = self.factories.get(idx) {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if factory.is_available() {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// Fallback: scan factories that can enumerate types
|
|
|
|
|
|
for factory in &self.factories {
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if !factory.is_available() {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
let types = factory.box_types();
|
2025-09-17 07:43:07 +09:00
|
|
|
|
if !types.is_empty() && types.contains(&name) {
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
}
|
|
|
|
|
|
false
|
|
|
|
|
|
}
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Get all available Box types
|
|
|
|
|
|
pub fn available_types(&self) -> Vec<String> {
|
|
|
|
|
|
let mut types = Vec::new();
|
|
|
|
|
|
for factory in &self.factories {
|
|
|
|
|
|
if factory.is_available() {
|
|
|
|
|
|
for type_name in factory.box_types() {
|
|
|
|
|
|
types.push(type_name.to_string());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
types.sort();
|
|
|
|
|
|
types.dedup();
|
|
|
|
|
|
types
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-09-17 07:43:07 +09:00
|
|
|
|
pub mod builtin;
|
|
|
|
|
|
pub mod plugin;
|
2025-08-19 16:43:13 +09:00
|
|
|
|
/// Re-export submodules
|
2025-09-17 16:11:01 +09:00
|
|
|
|
#[cfg(feature = "interpreter-legacy")]
|
2025-08-19 16:43:13 +09:00
|
|
|
|
pub mod user_defined;
|
|
|
|
|
|
|
2025-09-24 11:43:11 +09:00
|
|
|
|
// Phase 15.5: Separated builtin implementations for easy deletion
|
|
|
|
|
|
pub mod builtin_impls;
|
|
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
#[cfg(test)]
|
|
|
|
|
|
mod tests {
|
|
|
|
|
|
use super::*;
|
2025-09-17 07:43:07 +09:00
|
|
|
|
|
2025-08-19 16:43:13 +09:00
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_registry_creation() {
|
|
|
|
|
|
let registry = UnifiedBoxRegistry::new();
|
|
|
|
|
|
assert_eq!(registry.available_types().len(), 0);
|
|
|
|
|
|
}
|
2025-12-02 21:52:18 +09:00
|
|
|
|
|
|
|
|
|
|
// Phase 86: BoxFactory Priority Tests
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_default_policy_is_strict_plugin_first() {
|
2025-12-24 08:43:48 +09:00
|
|
|
|
let prev = crate::config::env::box_factory_policy();
|
2025-12-02 21:52:18 +09:00
|
|
|
|
// Ensure NYASH_BOX_FACTORY_POLICY is not set
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::reset_box_factory_policy();
|
2025-12-02 21:52:18 +09:00
|
|
|
|
|
|
|
|
|
|
let registry = UnifiedBoxRegistry::new();
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
|
registry.get_policy(),
|
|
|
|
|
|
FactoryPolicy::StrictPluginFirst,
|
|
|
|
|
|
"Default policy should be StrictPluginFirst"
|
|
|
|
|
|
);
|
2025-12-17 01:06:46 +09:00
|
|
|
|
|
|
|
|
|
|
if let Some(v) = prev {
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::set_box_factory_policy(&v);
|
2025-12-17 01:06:46 +09:00
|
|
|
|
}
|
2025-12-02 21:52:18 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_env_policy_override() {
|
2025-12-24 08:43:48 +09:00
|
|
|
|
let prev = crate::config::env::box_factory_policy();
|
2025-12-17 01:06:46 +09:00
|
|
|
|
|
2025-12-02 21:52:18 +09:00
|
|
|
|
// Test builtin_first override
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::set_box_factory_policy("builtin_first");
|
2025-12-02 21:52:18 +09:00
|
|
|
|
let registry = UnifiedBoxRegistry::with_env_policy();
|
|
|
|
|
|
assert_eq!(registry.get_policy(), FactoryPolicy::BuiltinFirst);
|
|
|
|
|
|
|
|
|
|
|
|
// Test compat_plugin_first override
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::set_box_factory_policy("compat_plugin_first");
|
2025-12-02 21:52:18 +09:00
|
|
|
|
let registry = UnifiedBoxRegistry::with_env_policy();
|
|
|
|
|
|
assert_eq!(registry.get_policy(), FactoryPolicy::CompatPluginFirst);
|
|
|
|
|
|
|
|
|
|
|
|
// Test strict_plugin_first explicit
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::set_box_factory_policy("strict_plugin_first");
|
2025-12-02 21:52:18 +09:00
|
|
|
|
let registry = UnifiedBoxRegistry::with_env_policy();
|
|
|
|
|
|
assert_eq!(registry.get_policy(), FactoryPolicy::StrictPluginFirst);
|
|
|
|
|
|
|
|
|
|
|
|
// Cleanup
|
2025-12-17 01:06:46 +09:00
|
|
|
|
if let Some(v) = prev {
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::set_box_factory_policy(&v);
|
2025-12-17 01:06:46 +09:00
|
|
|
|
} else {
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::reset_box_factory_policy();
|
2025-12-17 01:06:46 +09:00
|
|
|
|
}
|
2025-12-02 21:52:18 +09:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_reserved_type_protection() {
|
|
|
|
|
|
// Ensure env vars are cleared
|
2025-12-24 08:43:48 +09:00
|
|
|
|
// Note: can't clear env vars directly; tests rely on default behavior
|
2025-12-02 21:52:18 +09:00
|
|
|
|
|
|
|
|
|
|
// Create a mock non-builtin factory that claims a reserved type
|
|
|
|
|
|
struct MockPluginFactory;
|
|
|
|
|
|
|
|
|
|
|
|
impl BoxFactory for MockPluginFactory {
|
|
|
|
|
|
fn create_box(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
name: &str,
|
|
|
|
|
|
_args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
|
|
|
|
// This should never be called for StringBox since it's rejected
|
|
|
|
|
|
Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: format!("Mock factory attempted to create: {}", name),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn box_types(&self) -> Vec<&str> {
|
|
|
|
|
|
vec!["StringBox", "CustomBox"] // Claims a reserved type
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn is_builtin_factory(&self) -> bool {
|
|
|
|
|
|
false // Non-builtin
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn factory_type(&self) -> FactoryType {
|
|
|
|
|
|
FactoryType::Plugin
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut registry = UnifiedBoxRegistry::new();
|
|
|
|
|
|
registry.register(Arc::new(MockPluginFactory));
|
|
|
|
|
|
|
|
|
|
|
|
// Test that create_box fails for StringBox (not registered in cache)
|
|
|
|
|
|
let result = registry.create_box("StringBox", &[]);
|
|
|
|
|
|
assert!(
|
|
|
|
|
|
result.is_err(),
|
|
|
|
|
|
"StringBox creation should fail when only non-builtin factory provides it"
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
// Verify the error message indicates it's unknown (not in cache)
|
|
|
|
|
|
if let Err(e) = result {
|
|
|
|
|
|
let err_msg = format!("{}", e);
|
|
|
|
|
|
assert!(
|
|
|
|
|
|
err_msg.contains("Unknown Box type") || err_msg.contains("Mock factory"),
|
|
|
|
|
|
"Error message should indicate StringBox is not properly registered: {}",
|
|
|
|
|
|
err_msg
|
|
|
|
|
|
);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_plugin_override_with_env() {
|
|
|
|
|
|
// This test verifies that NYASH_USE_PLUGIN_BUILTINS or
|
|
|
|
|
|
// NYASH_PLUGIN_OVERRIDE_TYPES allows plugins to override reserved types
|
|
|
|
|
|
|
|
|
|
|
|
// Create a mock plugin factory
|
|
|
|
|
|
struct MockPluginFactory;
|
|
|
|
|
|
|
|
|
|
|
|
impl BoxFactory for MockPluginFactory {
|
|
|
|
|
|
fn create_box(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
name: &str,
|
|
|
|
|
|
_args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
|
|
|
|
if name == "StringBox" {
|
|
|
|
|
|
// Return a mock box for testing
|
|
|
|
|
|
Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: "Mock plugin StringBox".to_string(),
|
|
|
|
|
|
})
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: "Unknown".to_string(),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn box_types(&self) -> Vec<&str> {
|
|
|
|
|
|
vec!["StringBox"]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn is_builtin_factory(&self) -> bool {
|
|
|
|
|
|
false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn factory_type(&self) -> FactoryType {
|
|
|
|
|
|
FactoryType::Plugin
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Test with NYASH_PLUGIN_OVERRIDE_TYPES
|
2025-12-24 08:43:48 +09:00
|
|
|
|
crate::config::env::set_box_factory_policy("strict_plugin_first"); // ensure plugin first
|
2025-12-02 21:52:18 +09:00
|
|
|
|
std::env::set_var("NYASH_PLUGIN_OVERRIDE_TYPES", "StringBox");
|
|
|
|
|
|
let mut registry = UnifiedBoxRegistry::new();
|
|
|
|
|
|
registry.register(Arc::new(MockPluginFactory));
|
|
|
|
|
|
|
|
|
|
|
|
// With override enabled, StringBox should not be rejected
|
|
|
|
|
|
// (Note: has_type will be false because create_box fails, but registration shouldn't be rejected)
|
|
|
|
|
|
std::env::remove_var("NYASH_PLUGIN_OVERRIDE_TYPES");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
|
fn test_non_reserved_plugin_priority() {
|
|
|
|
|
|
// Test that non-reserved types (like FileBox) can be overridden by plugins
|
|
|
|
|
|
|
|
|
|
|
|
struct MockBuiltinFactory;
|
|
|
|
|
|
impl BoxFactory for MockBuiltinFactory {
|
|
|
|
|
|
fn create_box(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
_name: &str,
|
|
|
|
|
|
_args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
|
|
|
|
Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: "Builtin FileBox".to_string(),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn box_types(&self) -> Vec<&str> {
|
|
|
|
|
|
vec!["FileBox"]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn is_builtin_factory(&self) -> bool {
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn factory_type(&self) -> FactoryType {
|
|
|
|
|
|
FactoryType::Builtin
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
struct MockPluginFactory;
|
|
|
|
|
|
impl BoxFactory for MockPluginFactory {
|
|
|
|
|
|
fn create_box(
|
|
|
|
|
|
&self,
|
|
|
|
|
|
_name: &str,
|
|
|
|
|
|
_args: &[Box<dyn NyashBox>],
|
|
|
|
|
|
) -> Result<Box<dyn NyashBox>, RuntimeError> {
|
|
|
|
|
|
Err(RuntimeError::InvalidOperation {
|
|
|
|
|
|
message: "Plugin FileBox".to_string(),
|
|
|
|
|
|
})
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn box_types(&self) -> Vec<&str> {
|
|
|
|
|
|
vec!["FileBox"]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn is_builtin_factory(&self) -> bool {
|
|
|
|
|
|
false
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn factory_type(&self) -> FactoryType {
|
|
|
|
|
|
FactoryType::Plugin
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
let mut registry = UnifiedBoxRegistry::new();
|
|
|
|
|
|
|
|
|
|
|
|
// Register builtin first, then plugin
|
|
|
|
|
|
registry.register(Arc::new(MockBuiltinFactory));
|
|
|
|
|
|
registry.register(Arc::new(MockPluginFactory));
|
|
|
|
|
|
|
|
|
|
|
|
// With StrictPluginFirst policy, plugin should have priority
|
|
|
|
|
|
// Both fail, but the error message tells us which was tried first
|
|
|
|
|
|
let result = registry.create_box("FileBox", &[]);
|
|
|
|
|
|
assert!(result.is_err());
|
|
|
|
|
|
|
|
|
|
|
|
// The error should be from plugin (tried first) or builtin (fallback)
|
|
|
|
|
|
// This test just verifies the mechanism works
|
|
|
|
|
|
}
|
2025-08-21 15:30:57 +09:00
|
|
|
|
}
|