/*! * Provider Verify (skeleton) * * Phase 15.5 受け口: 起動時に最小の必須メソッドを検証するための軽量フック。 * 既定はOFF。環境変数で warn/strict に切替える。 * * Env: * - NYASH_PROVIDER_VERIFY=warn|strict * - NYASH_VERIFY_REQUIRED_METHODS="StringBox:length,concat;ArrayBox:push,get" * (optional; merged with nyash.toml definitions when present) * * nyash.toml (optional; merged when present): * - [verify.required_methods] * StringBox = ["length","concat"] * ArrayBox = ["push","get"] * or * - [verify.required_methods.StringBox] * methods = ["length","concat"] * - [types.StringBox] * required_methods = ["length","concat"] */ use crate::runtime::get_global_ring0; use std::collections::HashMap; fn parse_required_methods(spec: &str) -> HashMap> { let mut map = HashMap::new(); for part in spec.split(';') { let p = part.trim(); if p.is_empty() { continue; } if let Some((ty, rest)) = p.split_once(':') { let methods: Vec = rest .split(',') .map(|s| s.trim()) .filter(|s| !s.is_empty()) .map(|s| s.to_string()) .collect(); if !methods.is_empty() { map.insert(ty.trim().to_string(), methods); } } } map } fn load_required_methods_from_toml() -> HashMap> { let mut map: HashMap> = HashMap::new(); let text = match std::fs::read_to_string("nyash.toml") { Ok(s) => s, Err(_) => return map, }; let doc: toml::Value = match toml::from_str(&text) { Ok(v) => v, Err(_) => return map, }; // Helper: add entry if array-of-strings let mut add_arr = |ty: &str, arr: &toml::Value| { if let Some(a) = arr.as_array() { let mut v: Vec = Vec::new(); for e in a { if let Some(s) = e.as_str() { let s = s.trim(); if !s.is_empty() { v.push(s.to_string()); } } } if !v.is_empty() { map.insert(ty.to_string(), v); } } }; // 1) [verify.required_methods] if let Some(vrfy) = doc.get("verify") { if let Some(req) = vrfy.get("required_methods") { if let Some(tbl) = req.as_table() { for (k, v) in tbl.iter() { if v.is_array() { add_arr(k, v); continue; } if let Some(t) = v.as_table() { if let Some(m) = t.get("methods") { add_arr(k, m); } } } } } } // 2) [types.].required_methods if let Some(types) = doc.get("types") { if let Some(tbl) = types.as_table() { for (k, v) in tbl.iter() { if let Some(t) = v.as_table() { if let Some(m) = t.get("required_methods") { add_arr(k, m); } } } } } map } pub fn verify_from_env() -> Result<(), String> { let mode = std::env::var("NYASH_PROVIDER_VERIFY").unwrap_or_default(); let mode = mode.to_ascii_lowercase(); if !(mode == "warn" || mode == "strict") { return Ok(()); } // Merge: nyash.toml + env override let mut req = load_required_methods_from_toml(); let spec = std::env::var("NYASH_VERIFY_REQUIRED_METHODS").unwrap_or_default(); if !spec.trim().is_empty() { let env_map = parse_required_methods(&spec); for (k, v) in env_map { req.entry(k).or_default().extend(v); } } if req.is_empty() { return Ok(()); } let host = crate::runtime::plugin_loader_unified::get_global_plugin_host(); let host = host.read().unwrap(); let mut failures: Vec = Vec::new(); for (ty, methods) in req.iter() { for m in methods { match host.resolve_method(ty, m) { Ok(_) => { /* ok */ } Err(_e) => failures.push(format!("{}.{}", ty, m)), } } } if failures.is_empty() { return Ok(()); } let msg = format!( "Provider verify failed ({}): missing methods: {}", mode, failures.join(", ") ); if mode == "warn" { get_global_ring0() .log .warn(&format!("[provider-verify][warn] {}", msg)); Ok(()) } else { Err(msg) } }