use crate::ast::value::Value;
use crate::error::EvalError;
use crate::eval::EvalContext;
use std::collections::HashMap;
pub trait WeaverCommand: Send + Sync {
fn call(
&self,
args: Vec<Value>,
ctx: &mut dyn EvalContext,
registry: &Registry,
) -> Result<Option<Value>, EvalError>;
fn signature(&self) -> CommandSignature;
}
pub trait WeaverProcessor: Send + Sync {
fn call(&self, properties: HashMap<String, Value>) -> Result<Value, EvalError>;
fn signature(&self) -> ProcessorSignature;
}
#[derive(Debug, Clone)]
pub struct CommandSignature {
pub name: String,
pub params: Vec<ParamDef>,
}
#[derive(Debug, Clone)]
pub struct ProcessorSignature {
pub namespace: String,
pub name: String,
pub properties: Vec<PropertyDef>,
}
#[derive(Debug, Clone)]
pub struct ParamDef {
pub name: String,
pub expected_type: Option<ValueType>,
pub required: bool,
}
#[derive(Debug, Clone)]
pub struct PropertyDef {
pub key: String,
pub expected_type: Option<ValueType>,
pub required: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ValueType {
String,
Number,
Bool,
Array,
Any,
}
impl ValueType {
pub fn matches(&self, value: &Value) -> bool {
match self {
ValueType::Any => true,
ValueType::String => matches!(value, Value::String(_)),
ValueType::Number => matches!(value, Value::Number(_)),
ValueType::Bool => matches!(value, Value::Bool(_)),
ValueType::Array => matches!(value, Value::Array(_)),
}
}
}
pub struct Registry {
commands: HashMap<String, Box<dyn WeaverCommand>>,
processors: HashMap<String, Box<dyn WeaverProcessor>>,
}
impl Registry {
pub fn new() -> Self {
Self {
commands: HashMap::new(),
processors: HashMap::new(),
}
}
pub fn register_command(&mut self, cmd: impl WeaverCommand + 'static) {
let sig = cmd.signature();
self.commands.insert(sig.name.clone(), Box::new(cmd));
}
pub fn register_processor(&mut self, proc: impl WeaverProcessor + 'static) {
let sig = proc.signature();
let key = format!("{}.{}", sig.namespace, sig.name);
self.processors.insert(key, Box::new(proc));
}
pub fn call_command(
&self,
name: &str,
args: Vec<Value>,
ctx: &mut dyn EvalContext,
) -> Result<Option<Value>, EvalError> {
match self.commands.get(name) {
Some(cmd) => cmd.call(args, ctx, self),
None => Err(EvalError::undefined_command(name)),
}
}
pub fn call_processor(
&self,
namespace: &str,
name: &str,
properties: HashMap<String, Value>,
) -> Result<Value, EvalError> {
let key = format!("{namespace}.{name}");
match self.processors.get(&key) {
Some(proc) => proc.call(properties),
None => Err(EvalError::undefined_processor(namespace, name)),
}
}
}
impl Default for Registry {
fn default() -> Self {
Self::new()
}
}
pub struct ClosureCommand<F>
where
F: Fn(Vec<Value>) -> Result<Option<Value>, EvalError> + Send + Sync,
{
sig: CommandSignature,
func: F,
}
impl<F> ClosureCommand<F>
where
F: Fn(Vec<Value>) -> Result<Option<Value>, EvalError> + Send + Sync,
{
pub fn new(name: impl Into<String>, func: F) -> Self {
Self {
sig: CommandSignature {
name: name.into(),
params: Vec::new(),
},
func,
}
}
}
impl<F> WeaverCommand for ClosureCommand<F>
where
F: Fn(Vec<Value>) -> Result<Option<Value>, EvalError> + Send + Sync,
{
fn call(
&self,
args: Vec<Value>,
_ctx: &mut dyn EvalContext,
_registry: &Registry,
) -> Result<Option<Value>, EvalError> {
(self.func)(args)
}
fn signature(&self) -> CommandSignature {
self.sig.clone()
}
}
pub struct ClosureProcessor<F>
where
F: Fn(HashMap<String, Value>) -> Result<Value, EvalError> + Send + Sync,
{
sig: ProcessorSignature,
func: F,
}
impl<F> ClosureProcessor<F>
where
F: Fn(HashMap<String, Value>) -> Result<Value, EvalError> + Send + Sync,
{
pub fn new(
namespace: impl Into<String>,
name: impl Into<String>,
func: F,
) -> Self {
Self {
sig: ProcessorSignature {
namespace: namespace.into(),
name: name.into(),
properties: Vec::new(),
},
func,
}
}
}
impl<F> WeaverProcessor for ClosureProcessor<F>
where
F: Fn(HashMap<String, Value>) -> Result<Value, EvalError> + Send + Sync,
{
fn call(&self, properties: HashMap<String, Value>) -> Result<Value, EvalError> {
(self.func)(properties)
}
fn signature(&self) -> ProcessorSignature {
self.sig.clone()
}
}