rush_core 0.1.2

The rules engine is based on the rete algorithm
Documentation
use crate::std_tool::{ArrayContain, ArraySub, Env};
use crate::{
    AsyncRuleFlow, CalcNode, Exec, Function, FunctionImpl, FunctionSet, HostFunction, RuleFlow,
};
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use std::collections::HashMap;
use std::fmt::{Debug, Display, Formatter};
use std::sync::Arc;
use wd_tools::sync::Acl;

pub struct Rush {
    pub(crate) functions: Acl<HashMap<String, Arc<dyn Function>>>,
    pub(crate) nodes: HashMap<String, Vec<Box<dyn CalcNode>>>,
    pub(crate) nodes_seq: Vec<String>,
    pub(crate) exec: HashMap<String, Box<dyn Exec>>,
}

impl Debug for Rush {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        let mut fs = vec![];
        for (i, _) in self.functions.share().iter() {
            fs.push(i.to_string());
        }
        let mut nodes = vec![];
        for (i, _) in self.nodes.iter() {
            nodes.push(i.to_string());
        }
        let mut rules = vec![];
        for (i, _) in self.exec.iter() {
            rules.push(i.to_string());
        }
        write!(
            f,
            "{{ functions:{:?},nodes:{:?},rules:{:?} }}",
            fs, nodes, rules
        )
    }
}
impl Display for Rush {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        Debug::fmt(self, f)
    }
}

impl Rush {
    pub fn new() -> Self {
        let functions = Acl::new(HashMap::new());
        let nodes = HashMap::new();
        let nodes_seq = Vec::new();
        let rules = HashMap::new();
        let rh = Self {
            functions,
            nodes,
            nodes_seq,
            exec: rules,
        };
        rh.raw_register_function("contain", ArrayContain {})
            .raw_register_function("sub", ArraySub {})
            .raw_register_function("env", Env::default())
    }
    pub fn register_rule<
        C: CalcNode + Send + Sync + 'static,
        E: Exec + Send + Sync + 'static,
        T: Into<String>,
    >(
        mut self,
        name: T,
        nodes: Vec<C>,
        exec: E,
    ) -> Self {
        let name = name.into();
        let mut ns: Vec<Box<dyn CalcNode>> = vec![];
        for i in nodes {
            ns.push(Box::new(i));
        }
        let mut index = usize::MAX;
        if self.nodes.contains_key(&name) {
            for (i,n) in self.nodes_seq.iter().enumerate(){
                if n == &name{
                    index = i;
                    break
                }
            }
        }
        if index != usize::MAX{
            self.nodes_seq.remove(index);
        }
        self.nodes_seq.push(name.clone());
        self.nodes.insert(name.clone(), ns);
        self.exec.insert(name, Box::new(exec));
        self
    }
    pub fn delete_rule<T: AsRef<str>>(&mut self, name: T) {
        let mut index = usize::MAX;
        if self.exec.contains_key(name.as_ref()) {
            for (i,n) in self.nodes_seq.iter().enumerate(){
                if n == name.as_ref() {
                    index = i;
                    break
                }
            }
        }
        if index != usize::MAX{
            self.nodes_seq.remove(index);
        }
        self.nodes.remove(name.as_ref());
        self.exec.remove(name.as_ref());
    }
    pub fn raw_register_function<S: Into<String>, F: Function>(self, name: S, function: F) -> Self {
        self.functions.update(|x| {
            let mut map = (*x).clone();
            map.insert(name.into(), Arc::new(function));
            map
        });
        self
    }
    pub fn register_function<S: Into<String>, Args, Out, F>(self, name: S, function: F) -> Self
    where
        F: HostFunction<Args, Out> + 'static,
        Out: Serialize,
    {
        self.raw_register_function(name, FunctionImpl::new(function))
    }

    pub fn delete_function<S: AsRef<str>>(self, name: S) -> Self {
        self.functions.update(|x| {
            let mut map = (*x).clone();
            map.remove(name.as_ref());
            map
        });
        self
    }

    pub fn execute(&self, obj: &Value, list: Vec<String>) -> anyhow::Result<Value> {
        let mut output = Value::Object(Map::new());
        for name in list.iter() {
            if let Some(r) = self.exec.get(name) {
                r.execute(self.functions.share(), obj, &mut output)?;
            }
        }
        Ok(output)
    }
    /// input_value
    /// 1. 计算匹配到的规则
    /// 2. 找出规则进行结果生成
    fn flow_value(&self, obj: Value) -> anyhow::Result<Value> {
        let mut rules = vec![];
        'lp: for k in self.nodes_seq.iter() {
            let v = self.nodes.get(k).unwrap();
            for i in v.iter() {
                if !i.when(self.functions.share(), &obj)? {
                    continue 'lp;
                }
            }
            rules.push(k.to_string());
        }
        self.execute(&obj, rules)
    }
}

impl<C, E, I: IntoIterator<Item = (String, Vec<C>, E)>> From<I> for Rush
where
    C: CalcNode + 'static,
    E: Exec + 'static,
{
    fn from(value: I) -> Self {
        let mut rush = Rush::new();
        for (name, calc, exec) in value {
            rush = rush.register_rule(name, calc, exec);
        }
        rush
    }
}

impl FunctionSet for HashMap<String, Arc<dyn Function>> {
    fn get(&self, name: &str) -> Option<Arc<dyn Function>> {
        self.get(name).map(|a| a.clone())
    }
}

impl RuleFlow for Rush {
    fn flow<Obj: Serialize, Out: Deserialize<'static>>(&self, obj: Obj) -> anyhow::Result<Out> {
        let value = serde_json::to_value(obj)?;
        let result = self.flow_value(value)?;
        let out = Out::deserialize(result)?;
        Ok(out)
    }
}
impl AsyncRuleFlow for Rush {}

#[cfg(test)]
mod test {
    use crate::rush::Rush;
    use crate::{CalcNode, Exec, FunctionSet, RuleFlow};
    use serde::{Deserialize, Serialize};
    use serde_json::Value;
    use std::sync::Arc;

    struct CalcNodeImpl;
    impl CalcNode for CalcNodeImpl {
        fn when(&self, _fs: Arc<dyn FunctionSet>, _input: &Value) -> anyhow::Result<bool> {
            return Ok(true);
        }
    }
    struct RuleImpl;
    impl Exec for RuleImpl {
        fn execute(
            &self,
            _fs: Arc<dyn FunctionSet>,
            _input: &Value,
            _output: &mut Value,
        ) -> anyhow::Result<()> {
            Ok(())
        }
    }
    #[derive(Debug, Default, Serialize, Deserialize)]
    struct ObjTest {
        #[serde(default = "String::default")]
        pub name: String,
    }

    //cargo test --color=always --lib rush::test::test_simple --no-fail-fast -- --exact unstable-options --show-output
    #[test]
    fn test_simple() {
        let mr = Rush::new();
        let result: ObjTest = mr
            .flow(ObjTest {
                name: "hello world".into(),
            })
            .expect("input failed");
        println!("result ---> {result:?}");
    }
}