use std::collections::HashMap; use lazy_static::lazy_static; use serde::{Deserialize, Serialize}; mod regex_wrapper; use regex_wrapper::Regex; lazy_static! { static ref RE: Regex = Regex::new("%([A-Z])"); } /// Representation of a rule in LangEvolveRs #[derive(Debug, Serialize, Deserialize, Clone)] pub struct Rule { /// Regex that should match the input text from: Regex, /// Text to replace matched text to: String, } impl Rule { /// Create new rule /// /// # Arguments /// /// * `from` - literal string that represents the regex that should match /// the input text /// * `to` - literal string that represents the regex text that should /// replaced the text matched by `from` /// /// # Example /// ``` /// # use lazy_static::lazy_static; /// # #[path = "mod.rs"] /// # mod rule; /// # use rule::Rule; /// let rule = Rule::new("ab+c*", "ab"); /// ``` pub fn new(from: &str, to: &str) -> Self { Rule { from: Regex::new(from), to: String::from(to), } } /// Detect the number of categories in a rule /// /// For a rule, this function detects the number of categories set in the /// `from` member of a `Rule` and in its `to` member. The result is returned /// as a tuple of `u8`s. /// /// # Example /// /// ``` /// # #[path = "mod.rs"] /// # mod rule; /// # use rule::Rule; /// let rule = Rule::new("%Bea*i+", "a%A%C"); /// let nb_rules = rule.detect_number_categories(); /// assert_eq!((1 as u8, 2 as u8), nb_rules); /// ``` pub fn detect_number_categories(&self) -> (u8, u8) { let captures_from = self.from.to_string().matches('%').count() as u8; let captures_to = self.to.matches('%').count() as u8; (captures_from, captures_to) } fn simple_rewrite(&self, categories: &HashMap) -> Self { let mut rule = self.clone(); for (category, content) in categories { rule.from = Regex::new( rule.from .to_string() .replace( format!("%{}", category).as_str(), format!("[{}]", content).as_str(), ) .as_str(), ); } rule } // TODO break categories in different rules pub fn update( &self, categories: &HashMap, ) -> std::result::Result { let mut rule = self.clone(); let (from_match, to_match) = self.detect_number_categories(); // If there are only simple rewrites to make in the from String if from_match > 0 && to_match == 0 { rule = self.simple_rewrite(&categories); } // If there are equivalences between from and to if from_match > 0 && to_match <= from_match && to_match > 0 {} Ok(rule) } pub fn get_from(&self) -> &Regex { &self.from } pub fn get_to(&self) -> String { self.to.clone() } } impl From for Rule { /// Allow to create a rule from a single `String` /// /// It is possible to create a rule from a string, delimited by a `>`. This /// means a rule like `%C>%D` will be interpreted as going from `%C` to /// `%D`. /// /// # Example /// /// ``` /// # #[path = "mod.rs"] /// # mod rule; /// # use rule::Rule; /// let rule1 = Rule::new("%C", "%D"); /// let rule2 = Rule::from(String::from("%C>%D")); /// assert_eq!(rule1, rule2); /// ``` fn from(source: String) -> Self { let components: Vec<&str> = source.split_terminator('>').collect(); Rule::new(components[0], components[1]) } } impl PartialEq for Rule { /// Equality between `Rule` structs /// /// This allows for equality comparison between two `Rule` structs. /// /// # Example /// /// ``` /// # #[path = "mod.rs"] /// # mod rule; /// use rule::Rule; /// let rule1 = Rule::new("%C", "%D"); /// let rule2 = Rule::from("%C>%D"); /// assert!(rule1 == rule2); /// assert!(rule2 == rule1); /// ``` fn eq(&self, other: &Self) -> bool { self.from == other.from && self.to == other.to } } impl Eq for Rule {} #[test] fn rule_new() { let rule1 = Rule::new("([ae]+)i", "${1}i"); let rule2 = Rule { from: Regex::new("([ae]+)i"), to: String::from("${1}i"), }; assert_eq!(rule1, rule2); }