Phase 21.5 (AOT/LLVM Optimization Prep) - FileBox ring-1 (core-ro) provider: priority=-100, always available, no panic path - src/runner/modes/common_util/provider_registry.rs: CoreRoFileProviderFactory - Auto-registers at startup, eliminates fallback panic structurally - StringBox fast path prototypes (length/size optimization) - Performance benchmarks (C/Python/Hako comparison baseline) Phase 22.1 (JsonFrag Unification) - JsonFrag.last_index_of_from() for backward search (VM fallback) - Replace hand-written lastIndexOf in lower_loop_sum_bc_box.hako - SentinelExtractorBox for Break/Continue pattern extraction MirBuilder Refactor (Box → JsonFrag Migration) - 20+ lower_*_box.hako: Box-heavy → JsonFrag text assembly - MirBuilderMinBox: lightweight using set for dev env - Registry-only fast path with [registry:*] tag observation - pattern_util_box.hako: enhanced pattern matching Dev Environment & Testing - Dev toggles: SMOKES_DEV_PREINCLUDE=1 (point-enable), HAKO_MIR_BUILDER_SKIP_LOOPS=1 - phase2160: registry opt-in tests (array/map get/set/push/len) - content verification - phase2034: rc-dependent → token grep (grep -F based validation) - run_quick.sh: fast smoke testing harness - ENV documentation: docs/ENV_VARS.md Test Results ✅ quick phase2034: ALL GREEN (MirBuilder internal patterns) ✅ registry phase2160: ALL GREEN (array/map get/set/push/len) ✅ rc-dependent tests → content token verification complete ✅ PREINCLUDE policy: default OFF, point-enable only where needed Technical Notes - No INCLUDE by default (maintain minimalism) - FAIL_FAST=0 in Bring-up contexts only (explicit dev toggles) - Tag-based route observation ([mirbuilder/min:*], [registry:*]) - MIR structure validation (not just rc parity) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
147 lines
6.0 KiB
Rust
147 lines
6.0 KiB
Rust
//! Provider registry: selects concrete providers for core resources (e.g. FileBox).
|
||
//! SSOT (Single Source of Truth) for provider selection via ProviderFactory registration.
|
||
|
||
use std::sync::{Arc, Mutex, OnceLock};
|
||
|
||
use crate::boxes::file::provider::FileIo;
|
||
use crate::boxes::file::core_ro::CoreRoFileIo;
|
||
|
||
#[allow(dead_code)]
|
||
pub enum FileBoxMode { Auto, CoreRo, PluginOnly }
|
||
|
||
/// Factory for creating FileIo providers
|
||
pub trait ProviderFactory: Send + Sync {
|
||
fn box_name(&self) -> &str;
|
||
fn create_provider(&self) -> Arc<dyn FileIo>;
|
||
fn is_available(&self) -> bool;
|
||
fn priority(&self) -> i32 {
|
||
0 // Default priority (higher = preferred)
|
||
}
|
||
}
|
||
|
||
/// Global registry of provider factories
|
||
static PROVIDER_FACTORIES: OnceLock<Mutex<Vec<Arc<dyn ProviderFactory>>>> = OnceLock::new();
|
||
|
||
/// Register a provider factory (called by builtin/dynamic loaders)
|
||
pub fn register_provider_factory(factory: Arc<dyn ProviderFactory>) {
|
||
let registry = PROVIDER_FACTORIES.get_or_init(|| Mutex::new(Vec::new()));
|
||
registry.lock().unwrap().push(factory);
|
||
}
|
||
|
||
/// Built‑in ring‑1 FileBox provider (core‑ro) — always available, lowest priority
|
||
struct CoreRoFileProviderFactory;
|
||
|
||
impl ProviderFactory for CoreRoFileProviderFactory {
|
||
fn box_name(&self) -> &str { "FileBox" }
|
||
fn create_provider(&self) -> Arc<dyn FileIo> { Arc::new(CoreRoFileIo::new()) }
|
||
fn is_available(&self) -> bool { true }
|
||
fn priority(&self) -> i32 { -100 } // ring‑1: lower than any plugin/provider
|
||
}
|
||
|
||
/// Ensure ring‑1 (core‑ro) provider is present in the registry
|
||
fn ensure_builtin_file_provider_registered() {
|
||
let reg = PROVIDER_FACTORIES.get_or_init(|| Mutex::new(Vec::new()));
|
||
let mut guard = reg.lock().unwrap();
|
||
// If at least one FileBox provider exists, we still keep ring‑1 present for safety; avoid duplicates by checking any core‑ro present by priority
|
||
let has_core_ro = guard.iter().any(|f| f.box_name() == "FileBox" && f.priority() <= -100);
|
||
if !has_core_ro {
|
||
guard.push(Arc::new(CoreRoFileProviderFactory));
|
||
}
|
||
}
|
||
|
||
/// Read FileBox mode from environment variables
|
||
#[allow(dead_code)]
|
||
pub fn read_filebox_mode_from_env() -> FileBoxMode {
|
||
match std::env::var("NYASH_FILEBOX_MODE").unwrap_or_else(|_| "auto".to_string()).as_str() {
|
||
"core-ro" => FileBoxMode::CoreRo,
|
||
"plugin-only" => FileBoxMode::PluginOnly,
|
||
_ => {
|
||
if std::env::var("NYASH_DISABLE_PLUGINS").as_deref() == Ok("1") {
|
||
FileBoxMode::CoreRo
|
||
} else { FileBoxMode::Auto }
|
||
}
|
||
}
|
||
}
|
||
|
||
/// Select provider based on mode and registered factories (SSOT)
|
||
#[allow(dead_code)]
|
||
pub fn select_file_provider(mode: FileBoxMode) -> Arc<dyn FileIo> {
|
||
let quiet_pipe = crate::config::env::env_bool("NYASH_JSON_ONLY");
|
||
// Always ensure ring‑1 (core‑ro) exists before inspecting registry
|
||
ensure_builtin_file_provider_registered();
|
||
let registry = PROVIDER_FACTORIES.get();
|
||
|
||
match mode {
|
||
FileBoxMode::Auto => {
|
||
// Try: dynamic/builtin (by priority) → core-ro fallback
|
||
if let Some(reg) = registry {
|
||
let mut factories: Vec<_> = reg.lock().unwrap()
|
||
.iter()
|
||
.filter(|f| f.box_name() == "FileBox" && f.is_available())
|
||
.cloned()
|
||
.collect();
|
||
|
||
// Sort by priority (descending)
|
||
factories.sort_by(|a, b| b.priority().cmp(&a.priority()));
|
||
|
||
if let Some(factory) = factories.first() {
|
||
if !quiet_pipe {
|
||
eprintln!("[provider-registry] FileBox: using registered provider (priority={})", factory.priority());
|
||
}
|
||
return factory.create_provider();
|
||
}
|
||
}
|
||
|
||
// Fallback policy
|
||
// Allow a narrow, explicit carve‑out:
|
||
// - When JSON‑only pipeline is active (quiet structured I/O), or
|
||
// - When NYASH_FILEBOX_ALLOW_FALLBACK=1 is set,
|
||
// always use core‑ro provider even if Fail‑Fast is ON.
|
||
let allow_fb_override =
|
||
crate::config::env::env_bool("NYASH_JSON_ONLY") ||
|
||
crate::config::env::env_bool("NYASH_FILEBOX_ALLOW_FALLBACK");
|
||
|
||
if crate::config::env::fail_fast() && !allow_fb_override {
|
||
eprintln!("[failfast/provider/filebox:auto-fallback-blocked]");
|
||
panic!("Fail-Fast: FileBox provider fallback is disabled (NYASH_FAIL_FAST=0 or NYASH_FILEBOX_ALLOW_FALLBACK=1 to override)");
|
||
} else {
|
||
if !quiet_pipe {
|
||
eprintln!(
|
||
"[provider-registry] FileBox: using core-ro fallback{}",
|
||
if allow_fb_override { " (override)" } else { "" }
|
||
);
|
||
}
|
||
Arc::new(CoreRoFileIo::new())
|
||
}
|
||
}
|
||
FileBoxMode::PluginOnly => {
|
||
// Try only registered providers, Fail-Fast if none available
|
||
if let Some(reg) = registry {
|
||
let mut factories: Vec<_> = reg.lock().unwrap()
|
||
.iter()
|
||
.filter(|f| f.box_name() == "FileBox" && f.is_available())
|
||
.cloned()
|
||
.collect();
|
||
|
||
factories.sort_by(|a, b| b.priority().cmp(&a.priority()));
|
||
|
||
if let Some(factory) = factories.first() {
|
||
if !quiet_pipe {
|
||
eprintln!("[provider-registry] FileBox: using plugin-only provider (priority={})", factory.priority());
|
||
}
|
||
return factory.create_provider();
|
||
}
|
||
}
|
||
|
||
panic!("FileBox plugin-only mode: no provider registered. Set NYASH_FILEBOX_MODE=auto or NYASH_FILEBOX_MODE=core-ro to use fallback.");
|
||
}
|
||
FileBoxMode::CoreRo => {
|
||
// Always use core-ro, ignore registry
|
||
if !quiet_pipe {
|
||
eprintln!("[provider-registry] FileBox: using core-ro (forced)");
|
||
}
|
||
Arc::new(CoreRoFileIo::new())
|
||
}
|
||
}
|
||
}
|