use weaver_lang::Registry;
pub trait Plugin: Send + Sync {
fn name(&self) -> &str;
fn register(&self, registry: &mut Registry);
fn init(&self) -> Result<(), PluginError> {
Ok(())
}
fn manifest(&self) -> PluginManifest {
PluginManifest {
name: self.name().to_string(),
version: "0.1.0".to_string(),
description: String::new(),
provides_processors: Vec::new(),
provides_commands: Vec::new(),
}
}
}
#[derive(Debug, Clone)]
pub struct PluginManifest {
pub name: String,
pub version: String,
pub description: String,
pub provides_processors: Vec<CallableDoc>,
pub provides_commands: Vec<CallableDoc>,
}
#[derive(Debug, Clone)]
pub struct CallableDoc {
pub name: String,
pub description: String,
pub example: String,
}
#[derive(Debug)]
pub struct PluginError {
pub plugin_name: String,
pub message: String,
}
impl std::fmt::Display for PluginError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "plugin '{}' error: {}", self.plugin_name, self.message)
}
}
impl std::error::Error for PluginError {}
#[cfg(not(feature = "stdlib"))]
pub struct CorePlugin;
#[cfg(not(feature = "stdlib"))]
impl Plugin for CorePlugin {
fn name(&self) -> &str {
"core"
}
fn register(&self, registry: &mut Registry) {
use weaver_lang::{ClosureCommand, ClosureProcessor, Value};
registry.register_command(ClosureCommand::new("set_var", |args| {
let _key = args.first().and_then(|v| v.as_string()).unwrap_or("");
let _val = args.get(1).cloned().unwrap_or(Value::None);
Ok(None)
}));
registry.register_command(ClosureCommand::new("get_var", |args| {
let _key = args.first().and_then(|v| v.as_string()).unwrap_or("");
Ok(Some(Value::None))
}));
registry.register_processor(ClosureProcessor::new("text", "join", |props| {
let items = props.get("items").and_then(|v| v.as_array()).unwrap_or(&[]);
let sep = props
.get("separator")
.and_then(|v| v.as_string())
.unwrap_or(", ");
let joined: String = items
.iter()
.map(|v| v.to_output_string())
.collect::<Vec<_>>()
.join(sep);
Ok(Value::String(joined))
}));
registry.register_processor(ClosureProcessor::new("text", "upper", |props| {
let text = props.get("text").and_then(|v| v.as_string()).unwrap_or("");
Ok(Value::String(text.to_uppercase()))
}));
registry.register_processor(ClosureProcessor::new("text", "lower", |props| {
let text = props.get("text").and_then(|v| v.as_string()).unwrap_or("");
Ok(Value::String(text.to_lowercase()))
}));
registry.register_processor(ClosureProcessor::new("text", "contains", |props| {
let text = props.get("text").and_then(|v| v.as_string()).unwrap_or("");
let sub = props
.get("substring")
.and_then(|v| v.as_string())
.unwrap_or("");
Ok(Value::Bool(text.contains(sub)))
}));
registry.register_processor(ClosureProcessor::new("math", "add", |props| {
let a = props.get("a").and_then(|v| v.as_number()).unwrap_or(0.0);
let b = props.get("b").and_then(|v| v.as_number()).unwrap_or(0.0);
Ok(Value::Number(a + b))
}));
registry.register_processor(ClosureProcessor::new("math", "mul", |props| {
let a = props.get("a").and_then(|v| v.as_number()).unwrap_or(0.0);
let b = props.get("b").and_then(|v| v.as_number()).unwrap_or(0.0);
Ok(Value::Number(a * b))
}));
registry.register_processor(ClosureProcessor::new("array", "length", |props| {
let items = props.get("items").and_then(|v| v.as_array()).unwrap_or(&[]);
Ok(Value::Number(items.len() as f64))
}));
}
fn manifest(&self) -> PluginManifest {
PluginManifest {
name: "core".to_string(),
version: env!("CARGO_PKG_VERSION").to_string(),
description: "Minimal built-in commands and processors (stdlib disabled)".to_string(),
provides_processors: vec![
CallableDoc {
name: "text.join".into(),
description: "Join array elements into a string".into(),
example: r#"@[text.join(items: ["a", "b", "c"], separator: ", ")]"#.into(),
},
CallableDoc {
name: "math.add".into(),
description: "Add two numbers".into(),
example: "@[math.add(a: 1, b: 2)]".into(),
},
],
provides_commands: vec![CallableDoc {
name: "set_var".into(),
description: "Set a variable in the given scope".into(),
example: r#"$[set_var("state:visited", true)]"#.into(),
}],
}
}
}