162 lines
4.7 KiB
Rust
162 lines
4.7 KiB
Rust
/*!
|
|
* 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<String, Vec<String>> {
|
|
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<String> = 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<String, Vec<String>> {
|
|
let mut map: HashMap<String, Vec<String>> = 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<String> = 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.<TypeName>].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<String> = 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)
|
|
}
|
|
}
|