resc 0.3.1

A Redis based task orchestrator
use {
    crate::*,
    log::*,
    regex::Regex,
    serde::Deserialize,
    std::collections::HashMap,
};


/// a rule, defined by a condition (the "on" pattern)
/// and what to do with the matching tasks
#[derive(Debug, Clone, Deserialize)]
pub struct Rule {

    /// the name, unused for now, but having it in the JSON
    /// file helps making it clearer and it could be used in
    /// logging in the future, so it's mandatory
    #[serde(default = "Rule::default_name")]
    pub name: String,

    /// the input task parser. It checks the rule applies to
    /// the task and it extracts the token which will be used
    /// to generate the output task
    #[serde(with = "serde_regex", alias = "on")]
    pub on_regex: Regex,

    /// The optional fetchers which may query some additional
    /// token for generation of the output task
    #[serde(default, alias = "fetch")]
    pub fetchers: Vec<Fetcher>,

    /// The recipe for building the output tasks when the rule
    /// is verified and the fetchers did their job
    #[serde(alias = "make")]
    pub makers: Makers,

}

impl Rule {
    pub fn default_name() -> String {
        "<anonymous rule>".into()
    }
    pub fn is_match(&self, task: &str) -> bool {
        self.on_regex.is_match(task)
    }
    /// Assuming the rule matches, computes the rule results
    /// (there's only one RuleResult when no fetcher is involved)
    pub fn results(&self, task: &str) -> Result<Vec<RuleResult>, RescError> {
        // props will contain the token usable for generating
        // the task name, output queue and output set
        let mut props: HashMap<String, String> = HashMap::new();
        props.insert("input_task".to_owned(), task.to_owned());
        let caps = self.on_regex.captures(task).unwrap();
        let mut results = Vec::new();
        for groupname in self.on_regex.capture_names() {
            if let Some(name) = groupname {
                if let Some(value) = caps.name(name) {
                    props.insert(name.to_string(), value.as_str().to_string());
                }
            }
        }
        if !self.fetchers.is_empty() {
            // if there are fetchers, we'll fetch all the possible results
            // and generate a ruleresult per fetchresult
            for fetcher in &self.fetchers {
                let fetch_results = fetcher.results(&props)?;
                debug!("    -> fetch results {:#?}", &fetch_results);
                for mut fetch_result in fetch_results {
                    // we inject the parent properties
                    // This is heavy but makes the whole simpler
                    for (key, value) in &props {
                        fetch_result.props.insert(key.clone(), value.clone());
                    }
                    trace!(" merged: {:#?}", &fetch_result.props);
                    self.makers.make(&fetch_result.props, &mut results);
                }
            }
        } else {
            self.makers.make(&props, &mut results);
        }
        Ok(results)
    }
}