rustlogic_march1917/
lib.rs

1//! Hello fellow Rustacians! RustLogic is a crate for parsing and handling simple logical expressings.
2//!
3//! This crate contains the basic foundation for handling stringed logical formulas.
4//! If you want to want to submit an issue or a pull request,
5//! please visit the [GitHub](https://github.com/coastalwhite/rustlogic) page.
6//!
7//! Thanks for your time and enjoy this crate!
8//!
9//! # Examples
10//! ## 4-1 Multiplexer
11//! ```
12//! # use std::collections::HashMap;
13//! let multiplexer_4_to_1 =
14//!     rustlogic::parse(
15//!         "([a]&~[x]&~[y])|([b]&~[x]&[y])|([c]&[x]&~[y])|([d]&[x]&[y])"
16//!     )
17//!     .expect("Failed to parse 4-1 multiplexer");
18//!
19//! let mut variable_map = HashMap::new();
20//!
21//! // Input: 1001
22//! variable_map.insert("a", true);
23//! variable_map.insert("b", false);
24//! variable_map.insert("c", false);
25//! variable_map.insert("d", true);
26//!
27//! // Selector: 11
28//! variable_map.insert("x", true);
29//! variable_map.insert("y", true);
30//!
31//! // Should select fourth item from bus so true
32//! let value = multiplexer_4_to_1.get_value_from_variables(&variable_map).unwrap();
33//! println!("{}", value); // Will print true!
34//! # assert!(value);
35//! # variable_map.insert("d", false);
36//! # let value = multiplexer_4_to_1.get_value_from_variables(&variable_map).unwrap();
37//! # assert!(!value);
38//! ```
39//!
40//! ## Evaluating a logical string with variables and custom logical operators
41//! ```
42//! use rustlogic::operators;
43//! use std::collections::HashMap;
44//!
45//! // Define the set
46//! let operator_set = operators::common_sets::worded();
47//!
48//! let parsed = rustlogic::custom_parse("(NOT $[A] AND TRUE) OR $[B]", &operator_set);
49//! # assert!(parsed.is_ok());
50//!
51//! // We assign the variables to their values
52//! let mut hm = HashMap::new();
53//! hm.insert("A", false);
54//! hm.insert("B", false);
55//! # assert!(parsed.unwrap().get_value_from_variables(&hm).is_ok());
56//!
57//! // Now contains the value of the logical expression
58//! # let parsed = rustlogic::custom_parse("(NOT $[A] AND TRUE) OR $[B]", &operator_set);
59//! let value = parsed.unwrap().get_value_from_variables(&hm).unwrap();
60//! # assert!(value);
61//! ```
62
63mod multidimensional_logicnode;
64pub mod operators;
65mod util;
66
67use operators::OperatorSet;
68use std::collections::HashMap;
69
70/// An Enum of all the possible states of a head of a logical formula
71#[derive(Debug)]
72pub enum LogicNode {
73    /// AND operation of left and right
74    And(Box<LogicNode>, Box<LogicNode>),
75
76    /// OR operation of left and right
77    Or(Box<LogicNode>, Box<LogicNode>),
78
79    /// NOT operation of child
80    Not(Box<LogicNode>),
81
82    /// True, always returns true
83    True,
84
85    /// False, always returns false
86    False,
87
88    /// Variable, always returns value of variable passed in
89    Variable(String),
90}
91
92impl LogicNode {
93    /// Will retrieve the value of a [LogicNode](enum.LogicNode.html)
94    /// given a [HashMap](https://doc.rust-lang.org/std/collections/struct.HashMap.html) of variables
95    ///
96    /// # Output
97    /// Will output a result type with either the value of the [LogicNode](enum.LogicNode.html)
98    /// given the [Hashmap](https://doc.rust-lang.org/std/collections/struct.HashMap.html)
99    /// or will give a error with the variable missing from the 'variables' argument given.
100    ///
101    /// # Examples
102    /// ## X-OR
103    /// ```
104    /// # use std::collections::HashMap;
105    /// # use rustlogic::parse;
106    /// let xor = parse("([a]|[b])&~([a]&[b])")
107    ///     .expect("Failed to parse xor gate");
108    ///
109    /// let mut variable_map = HashMap::new();
110    /// variable_map.insert("a", true);
111    /// variable_map.insert("b", true);
112    /// let value_1 = xor.get_value_from_variables(&variable_map).unwrap();
113    /// println!("First value: {}!", value_1); // Will print false!
114    /// # assert!(!value_1);
115    ///
116    /// variable_map.insert("a", false);
117    /// let value_2 = xor.get_value_from_variables(&variable_map).unwrap();
118    /// println!("Second value: {}!", value_1); // Will print true!
119    /// # assert!(value_2);
120    ///
121    /// variable_map.insert("b", false);
122    /// let value_3 = xor.get_value_from_variables(&variable_map).unwrap();
123    /// println!("Third value: {}!", value_1); // Will print false!
124    /// # assert!(!value_3);
125    /// ```
126    pub fn get_value_from_variables(
127        &self,
128        variables: &HashMap<&str, bool>,
129    ) -> Result<bool, String> {
130        use LogicNode::*;
131
132        match self {
133            And(left, right) => Ok(left.get_value_from_variables(variables)?
134                && right.get_value_from_variables(variables)?),
135
136            Or(left, right) => Ok(left.get_value_from_variables(variables)?
137                || right.get_value_from_variables(variables)?),
138
139            Not(child) => Ok(!child.get_value_from_variables(variables)?),
140
141            True => Ok(true),
142
143            False => Ok(false),
144
145            // Fetch variable, if it cannot be found through error with name of that variable
146            Variable(variable) => Ok(variables
147                .get(&variable[..])
148                .ok_or(variable.clone())?
149                .clone()),
150        }
151    }
152
153    /// Will retrieve the value of a [LogicNode](enum.LogicNode.html) given purely the formula
154    ///
155    /// # Output
156    /// Will return a result with either the value of the [LogicNode](enum.LogicNode.html) or an error value containing
157    /// the name of the (or one of the) variable contained within the [LogicNode](enum.LogicNode.html).
158    ///
159    /// # Examples
160    /// ## Basic usage
161    /// ```
162    /// # use std::collections::HashMap;
163    /// # use rustlogic::parse;
164    /// let and_of_true_and_false = parse("0&1")
165    ///     .expect("Unable to parse AND of true and false");
166    /// let or_of_true_and_false = parse("0|1")
167    ///     .expect("Unable to parse OR of true and false");
168    ///
169    /// let value_1 = and_of_true_and_false.get_value().unwrap();
170    /// println!("0&1: {}", value_1); // Will return false!
171    /// # assert!(!value_1);
172    ///
173    /// let value_2 = or_of_true_and_false.get_value().unwrap();
174    /// println!("0|1: {}", value_2); // Will return true!
175    /// # assert!(value_2);
176    /// ```
177    pub fn get_value(&self) -> Result<bool, String> {
178        use LogicNode::*;
179
180        match self {
181            And(left, right) => Ok(left.get_value()? && right.get_value()?),
182
183            Or(left, right) => Ok(left.get_value()? || right.get_value()?),
184
185            Not(child) => Ok(!child.get_value()?),
186
187            True => Ok(true),
188
189            False => Ok(false),
190
191            // Tree contains variable so return error
192            Variable(variable) => Err(variable.clone()),
193        }
194    }
195
196    /// Will return whether a given [LogicNode](enum.LogicNode.html) contains a variable
197    ///
198    /// # Examples
199    /// ## Basic usage
200    /// ```
201    /// # use std::collections::HashMap;
202    /// # use rustlogic::parse;
203    /// let with_variable = parse("([a]&0)")
204    ///     .expect("Unable to parse with variable");
205    /// let without_variable = parse("(1&0)")
206    ///     .expect("Unable to parse without variable");
207    ///
208    /// // Will return true!
209    /// println!("First contains variable: {}", with_variable.contains_variable());
210    /// # assert!(with_variable.contains_variable());
211    ///
212    /// // Will return false!
213    /// println!("Second contains variable: {}", without_variable.contains_variable());
214    /// # assert!(!without_variable.contains_variable());
215    /// ```
216    pub fn contains_variable(&self) -> bool {
217        use LogicNode::*;
218
219        match self {
220            And(left, right) | Or(left, right) => {
221                left.contains_variable() || right.contains_variable()
222            }
223
224            Not(child) => child.contains_variable(),
225
226            True | False => false,
227
228            Variable(_) => true,
229        }
230    }
231
232    /// Returns a sorted vector of all Variables used in a LogicNode
233    ///
234    /// # Examples
235    /// ## NOR
236    /// ```
237    /// # use std::collections::HashMap;
238    /// # use rustlogic::parse;
239    /// let nor = parse("~([a]|[b])")
240    ///     .expect("Unable to parse with variable");
241    ///
242    /// // Will return ["a", "b"]
243    /// println!("Variables in nor: {:?}", nor.get_variables());
244    /// # assert_eq!(nor.get_variables(), vec!["a", "b"]);
245    /// ```
246    pub fn get_variables(&self) -> Vec<String> {
247        let mut variables = Vec::new();
248
249        use LogicNode::*;
250
251        // Go through elements recurvely
252        match self {
253            And(left, right) | Or(left, right) => {
254                variables.extend(left.get_variables().iter().cloned());
255                variables.extend(right.get_variables().iter().cloned());
256            }
257
258            Not(child) => variables.extend(child.get_variables().iter().cloned()),
259
260            True | False => (),
261
262            Variable(var) => variables = vec![var.clone()],
263        }
264
265        // Sort the variables in preparation for the deduplication
266        variables.sort();
267
268        // Remove duplicates
269        variables.dedup();
270
271        variables
272    }
273
274    /// Inserts formula in place of variable
275    ///
276    /// # Examples
277    /// ## Insert AND in OR
278    /// ```rust
279    /// # use std::collections::HashMap;
280    /// # use rustlogic::parse;
281    /// let or = parse("[AND]|[b]").expect("Error parsing or");
282    ///
283    /// # assert_eq!(or.to_string(), "( [AND] | [b] )");
284    /// let and_in_or = or.insert_formula(
285    ///     "AND",
286    ///     &parse("[x]&[y]").expect("Error parsing AND")
287    /// );
288    ///
289    /// println!("{}", and_in_or); // Will print ( ( [x] & [y] ) | [b] )
290    /// # assert_eq!(and_in_or.to_string(), "( ( [x] & [y] ) | [b] )");
291    pub fn insert_formula(&self, variable: &str, formula: &LogicNode) -> LogicNode {
292        use LogicNode::*;
293
294        match self {
295            And(left, right) => And(
296                Box::new(left.insert_formula(variable, formula)),
297                Box::new(right.insert_formula(variable, formula)),
298            ),
299
300            Or(left, right) => Or(
301                Box::new(left.insert_formula(variable, formula)),
302                Box::new(right.insert_formula(variable, formula)),
303            ),
304
305            Not(child) => Not(Box::new(child.insert_formula(variable, formula))),
306
307            Variable(var) => {
308                if var == variable {
309                    return formula.clone();
310                }
311
312                Variable(var.clone())
313            }
314
315            True => True,
316            False => False,
317        }
318    }
319
320    /// Convert a Logical node to a String using a certain Operator Set
321    ///
322    /// # Example
323    /// ```
324    /// use rustlogic::operators;
325    ///
326    /// // Create our logical node
327    /// let parsed = rustlogic::parse("(~[A]&[B])|[C]").unwrap();
328    ///
329    /// // Create a string using the worded operator set
330    /// let worded = parsed.to_string_using_set(&operators::common_sets::worded());
331    ///
332    /// // Will print "((NOT $[A] AND $[B]) OR $[C])"!
333    /// println!("{}", worded);
334    /// assert_eq!(worded, String::from("((NOT $[A] AND $[B]) OR $[C])"));
335    /// ```
336    pub fn to_string_using_set(&self, operator_set: &OperatorSet) -> String {
337        multidimensional_logicnode::MultiDimensionalLogicNode::new(self)
338            .to_string_using_set(operator_set)
339    }
340}
341
342impl std::fmt::Display for LogicNode {
343    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
344        let multi_dim = multidimensional_logicnode::MultiDimensionalLogicNode::new(self);
345        write!(f, "{}", multi_dim)
346    }
347}
348
349impl std::clone::Clone for LogicNode {
350    fn clone(&self) -> Self {
351        use LogicNode::*;
352
353        match self {
354            And(left, right) => And(left.clone(), right.clone()),
355            Or(left, right) => Or(left.clone(), right.clone()),
356            Not(child) => Not(child.clone()),
357            True => True,
358            False => False,
359            Variable(var) => Variable(var.clone()),
360        }
361    }
362}
363
364/// Function used to find the matching parenthesis given a string
365/// and a position open a opening group symbol (taking into account depth)
366/// Will return None if position given is not a bracket or
367/// if no matching bracket was found
368fn get_group_content(
369    input_string: &str,
370    position: usize,
371    operator_set: &OperatorSet,
372) -> Option<String> {
373    use util::{multi_search, search};
374
375    let opener = operator_set.group_open();
376    let closer = operator_set.group_close();
377    let var_opener = operator_set.variable_open();
378    let var_closer = operator_set.variable_close();
379
380    // Check whether input_string starts with opening symbol
381    if search(&input_string[position..], opener) != Some(0) {
382        return None;
383    }
384
385    let mut depth: u16 = 0;
386    let mut search_location = position + opener.len();
387
388    let multi_search_query = vec![opener, closer, var_opener];
389
390    // Search through string for the first occurance of a opening or closing symbol
391    let mut search_result = multi_search(&input_string[search_location..], &multi_search_query);
392    while search_result != None {
393        let unwrapped_search_result = search_result.unwrap();
394
395        let query_index = unwrapped_search_result.0;
396        let pool_index = unwrapped_search_result.1;
397
398        match query_index {
399            0 => {
400                // Opening symbol found at pool_index
401
402                depth += 1;
403                search_location += pool_index + opener.len();
404            }
405            1 => {
406                // Closing symbol found at pool_index
407
408                if depth == 0 {
409                    return Some(String::from(
410                        &input_string[position + opener.len()..pool_index + search_location],
411                    ));
412                }
413
414                depth -= 1;
415                search_location += pool_index + closer.len();
416            }
417            _ => {
418                // Variable open symbol found at pool_index
419
420                let var_closer_loc = search(
421                    &input_string[search_location + pool_index + var_opener.len()..],
422                    var_closer,
423                );
424
425                match var_closer_loc {
426                    None => {
427                        return None;
428                    }
429                    Some(var_closer_loc) => {
430                        search_location +=
431                            pool_index + var_opener.len() + var_closer_loc + var_closer.len();
432                    }
433                }
434            }
435        }
436
437        search_result = multi_search(&input_string[search_location..], &multi_search_query);
438    }
439
440    // No matching closing symbol found so return nothing
441    None
442}
443
444fn get_variable_content(
445    input_string: &str,
446    position: usize,
447    operator_set: &OperatorSet,
448) -> Option<String> {
449    use util::search;
450
451    let opener = operator_set.variable_open();
452    let closer = operator_set.variable_close();
453
454    if search(&input_string[position..], opener) != Some(0) {
455        return None;
456    }
457
458    let search_string = &input_string[position + opener.len()..];
459    let closer_location = search(search_string, closer);
460
461    match closer_location {
462        None => None,
463        Some(closer_location) => Some(String::from(&search_string[..closer_location])),
464    }
465}
466
467fn easy_parse(input_string: &str, operator_set: &OperatorSet) -> Option<Result<LogicNode, usize>> {
468    use LogicNode::*;
469
470    if input_string.is_empty() {
471        return Some(Err(0));
472    }
473
474    if input_string == operator_set.true_symbol() {
475        return Some(Ok(True));
476    }
477
478    if input_string == operator_set.false_symbol() {
479        return Some(Ok(False));
480    }
481
482    let variable_content_option = get_variable_content(input_string, 0, operator_set);
483    if variable_content_option.is_some() {
484        let variable_content = variable_content_option.unwrap();
485
486        if variable_content.len()
487            == input_string.len()
488                - operator_set.variable_open().len()
489                - operator_set.variable_close().len()
490        {
491            return Some(Ok(Variable(variable_content)));
492        }
493    }
494
495    let group_content_option = get_group_content(input_string, 0, operator_set);
496    if group_content_option.is_some() {
497        let group_content = group_content_option.unwrap();
498
499        if group_content.len()
500            == input_string.len()
501                - operator_set.group_open().len()
502                - operator_set.group_close().len()
503        {
504            return Some(
505                custom_parse(&group_content[..], operator_set)
506                    .map_err(|err_loc| err_loc + operator_set.group_open().len()),
507            );
508        }
509    }
510
511    None
512}
513
514fn infix_parse(
515    input_string: &str,
516    position: usize,
517    operator_set: &OperatorSet,
518) -> Result<LogicNode, usize> {
519    let symbol = if input_string[position..].starts_with(operator_set.and()) {
520        operator_set.and()
521    } else if input_string[position..].starts_with(operator_set.or()) {
522        operator_set.or()
523    } else {
524        return Err(position);
525    };
526
527    let left = custom_parse(&input_string[..position], operator_set)?;
528    let right = custom_parse(&input_string[position + symbol.len()..], operator_set)
529        .map_err(|err_loc| position + symbol.len() + err_loc)?;
530
531    Ok(
532        match input_string[position..].starts_with(operator_set.and()) {
533            true => LogicNode::And(Box::new(left), Box::new(right)),
534            false => LogicNode::Or(Box::new(left), Box::new(right)),
535        },
536    )
537}
538
539/// Parsing of logic formulas with non-default operator symbols
540/// Use the [OperatorSet](struct.OperatorSet.html) Struct
541///
542/// # Examples
543/// ## Using a worded set
544/// ```
545/// use rustlogic::operators;
546///
547/// // Define the set
548/// let operator_set = operators::common_sets::worded();
549///
550/// let parsed = rustlogic::custom_parse("(NOT $[A] AND $[B]) OR $[C]", &operator_set);
551/// # assert!(parsed.is_ok());
552///
553/// // -- snipp
554/// ```
555///
556/// ## Having a customized operator set
557/// ```
558/// use rustlogic::operators;
559///
560/// // This will be our new symbol for the AND logical function
561/// let new_and_operator = "<AND>";
562///
563/// // Then we take the default set and adjust the AND symbol
564/// let custom_operator_set = operators::common_sets::default()
565///     .adjust_and(new_and_operator);
566///
567/// // Now we can use this set to parse logical strings
568/// let parsed = rustlogic::custom_parse("([A]<AND>[B])|[C]", &custom_operator_set);
569/// # assert!(parsed.is_ok());
570///
571/// // -- snipp
572/// ```
573///
574/// ## Evaluating a custom logical string
575/// ```
576/// use rustlogic::operators;
577///
578/// // Define the set
579/// let operator_set = operators::common_sets::worded();
580///
581/// let parsed = rustlogic::custom_parse("(NOT FALSE AND TRUE) OR FALSE", &operator_set);
582/// # assert!(parsed.is_ok());
583/// # assert!(parsed.unwrap().get_value().is_ok());
584///
585/// /// Now contains the value of the logical expression
586/// # let parsed = rustlogic::custom_parse("(NOT FALSE AND TRUE) OR FALSE", &operator_set);
587/// let value = parsed.unwrap().get_value().unwrap();
588/// # assert!(value);
589/// ```
590///
591/// ## Evaluating a custom logical string with variables
592/// ```
593/// use rustlogic::operators;
594/// use std::collections::HashMap;
595///
596/// // Define the set
597/// let operator_set = operators::common_sets::worded();
598///
599/// let parsed = rustlogic::custom_parse("(NOT $[A] AND $[B]) OR $[C]", &operator_set);
600/// # assert!(parsed.is_ok());
601///
602/// // We assign the variables to their values
603/// let mut hm = HashMap::new();
604/// hm.insert("A", false);
605/// hm.insert("B", true);
606/// hm.insert("C", false);
607/// # assert!(parsed.unwrap().get_value_from_variables(&hm).is_ok());
608///
609/// // Now contains the value of the logical expression
610/// # let parsed = rustlogic::custom_parse("(NOT $[A] AND $[B]) OR $[C]", &operator_set);
611/// let value = parsed.unwrap().get_value_from_variables(&hm).unwrap();
612/// # assert!(value);
613/// ```
614pub fn custom_parse(input_string: &str, operator_set: &OperatorSet) -> Result<LogicNode, usize> {
615    let easy_parse_option = easy_parse(input_string, operator_set);
616    if easy_parse_option.is_some() {
617        return easy_parse_option.unwrap();
618    }
619
620    if input_string.starts_with(operator_set.not()) {
621        let easy_parse_option = easy_parse(&input_string[operator_set.not().len()..], operator_set);
622        if easy_parse_option.is_some() {
623            return easy_parse_option
624                .unwrap()
625                .map_err(|err_loc| err_loc + operator_set.not().len())
626                .map(|node| LogicNode::Not(Box::new(node)));
627        }
628    }
629
630    use util::multi_search;
631
632    let multi_search_query = vec![
633        operator_set.group_open(),
634        operator_set.variable_open(),
635        operator_set.and(),
636        operator_set.or(),
637    ];
638    let mut ms_start = 0;
639    let mut ms_result = multi_search(input_string, &multi_search_query);
640
641    while ms_result.is_some() {
642        match ms_result {
643            None => return Err(input_string.len()), // Unreachable branch
644            Some((0, pool_index)) => {
645                let group_content =
646                    get_group_content(input_string, ms_start + pool_index, operator_set);
647
648                if group_content.is_none() {
649                    return Err(ms_start + pool_index + operator_set.group_open().len());
650                }
651
652                ms_start += pool_index
653                    + group_content.unwrap().len()
654                    + operator_set.group_open().len()
655                    + operator_set.group_close().len();
656                ms_result = multi_search(&input_string[ms_start..], &multi_search_query);
657            }
658            Some((1, pool_index)) => {
659                let variable_content =
660                    get_variable_content(input_string, ms_start + pool_index, operator_set);
661
662                if variable_content.is_none() {
663                    return Err(ms_start + pool_index + operator_set.variable_open().len());
664                }
665
666                ms_start += pool_index
667                    + variable_content.unwrap().len()
668                    + operator_set.variable_open().len()
669                    + operator_set.variable_close().len();
670                ms_result = multi_search(&input_string[ms_start..], &multi_search_query);
671            }
672            Some((_, pool_index)) => {
673                return infix_parse(input_string, ms_start + pool_index, operator_set)
674            }
675        };
676    }
677
678    Err(input_string.len())
679}
680
681/// Parse a formula string into in [LogicNode](enum.LogicNode.html) object
682///
683/// Outputs a results with an ok of the [LogicNode](enum.LogicNode.html) or the location of the error
684///
685/// # Examples
686///
687/// ## Basic usage (A pure variable)
688/// ```
689/// # use std::collections::HashMap;
690/// # use rustlogic::parse;
691/// let a_variable = parse("[a]").expect("Failed to parse variable");
692///
693/// let mut variable_map = HashMap::new();
694/// variable_map.insert("a", true);
695///
696/// let value = a_variable.get_value_from_variables(&variable_map).unwrap();
697/// println!("{}", value); // Will print true!
698/// # assert!(value);
699/// ```
700///
701/// ## 4-1 Multiplexer
702/// ```
703/// # use std::collections::HashMap;
704/// # use rustlogic::parse;
705/// let multiplexer_4_to_1 = parse("([a]&~[x]&~[y])|([b]&~[x]&[y])|([c]&[x]&~[y])|([d]&[x]&[y])")
706///     .expect("Failed to parse 4-1 multiplexer");
707///
708/// let mut variable_map = HashMap::new();
709///
710/// // Input: 1001
711/// variable_map.insert("a", true);
712/// variable_map.insert("b", false);
713/// variable_map.insert("c", false);
714/// variable_map.insert("d", true);
715///
716/// // Selector: 11
717/// variable_map.insert("x", true);
718/// variable_map.insert("y", true);
719///
720/// // Should select fourth item from bus so true
721/// let value = multiplexer_4_to_1.get_value_from_variables(&variable_map).unwrap();
722/// println!("{}", value); // Will print true!
723/// # assert!(value);
724/// # variable_map.insert("d", false);
725/// # let value = multiplexer_4_to_1.get_value_from_variables(&variable_map).unwrap();
726/// # assert!(!value);
727/// ```
728pub fn parse(input_string: &str) -> Result<LogicNode, usize> {
729    let operator_set = operators::common_sets::default();
730    custom_parse(input_string, &operator_set)
731}
732
733#[cfg(test)]
734mod tests {
735    use super::*;
736
737    #[test]
738    fn test_get_group_content() {
739        let default_op = operators::common_sets::default();
740
741        assert_eq!(
742            get_group_content("(Hi)", 0, &default_op),
743            Some(String::from("Hi"))
744        );
745
746        assert_eq!(
747            get_group_content("  (before(inbetween(in)[var)test])after)", 2, &default_op),
748            Some(String::from("before(inbetween(in)[var)test])after"))
749        );
750
751        let non_default_op = operators::common_sets::default()
752            .adjust_group_open(" { ")
753            .adjust_group_close(" } ");
754
755        assert_eq!(
756            get_group_content(
757                " { before { inbetween { in } [var } test] } after } ",
758                0,
759                &non_default_op
760            ),
761            Some(String::from(
762                "before { inbetween { in } [var } test] } after"
763            ))
764        );
765
766        assert_eq!(
767            get_group_content(
768                "before { inbetween { in } [var } test] } after } ",
769                0,
770                &non_default_op
771            ),
772            None
773        );
774
775        assert_eq!(
776            get_group_content(
777                " { before { inbetween { in } [var } test] } after",
778                0,
779                &non_default_op
780            ),
781            None
782        );
783    }
784
785    #[test]
786    fn test_get_variable_content() {
787        let default_op = operators::common_sets::default();
788
789        assert_eq!(
790            get_variable_content("[Hi]", 0, &default_op),
791            Some(String::from("Hi"))
792        );
793
794        assert_eq!(
795            get_variable_content("  [before(inbetween(in)[var)test])after]", 2, &default_op),
796            Some(String::from("before(inbetween(in)[var)test"))
797        );
798
799        let non_default_op = operators::common_sets::default()
800            .adjust_variable_open(" { ")
801            .adjust_variable_close(" } ");
802
803        assert_eq!(
804            get_variable_content(
805                " { before { inbetween { in } [var } test] } after } ",
806                0,
807                &non_default_op
808            ),
809            Some(String::from("before { inbetween { in"))
810        );
811
812        assert_eq!(
813            get_group_content(
814                " { before { inbetween { in [var test] after ",
815                0,
816                &non_default_op
817            ),
818            None
819        );
820    }
821
822    #[test]
823    fn test_operator_set() {
824        let operator_set = operators::common_sets::worded();
825
826        let mut hm = HashMap::new();
827
828        hm.insert("A", false);
829        hm.insert("B", true);
830        hm.insert("C", false);
831
832        assert!(custom_parse("(NOT $[A] AND $[B]) OR $[C]", &operator_set)
833            .unwrap()
834            .get_value_from_variables(&hm)
835            .unwrap());
836
837        assert!(custom_parse("(($[A]) AND $[B]) OR NOT $[C]", &operator_set)
838            .unwrap()
839            .get_value_from_variables(&hm)
840            .unwrap());
841
842        assert!(!custom_parse("($[A] AND NOT $[B]) OR $[C]", &operator_set)
843            .unwrap()
844            .get_value_from_variables(&hm)
845            .unwrap());
846    }
847
848    #[test]
849    fn test_parsing() {
850        assert!(parse("[a]").is_ok());
851        assert!(parse("~[a]").is_ok());
852        assert!(parse("([a])").is_ok());
853        assert!(parse("[a]|[b]").is_ok());
854        assert!(parse("[a]&[b]").is_ok());
855        assert!(parse("1").is_ok());
856        assert!(parse("0").is_ok());
857        assert!(parse("([a]&([b]|0))").is_ok());
858
859        assert!(parse("[a]&&[b]").is_err());
860        assert!(parse("[a]||[b]").is_err());
861        assert!(parse("~[a]&[b]").is_ok());
862    }
863
864    #[test]
865    fn test_values() {
866        let mut hm = HashMap::new();
867
868        hm.insert("a", false);
869        hm.insert("b", false);
870
871        assert!(!parse("[a]").unwrap().get_value_from_variables(&hm).unwrap());
872        assert!(parse("~[a]")
873            .unwrap()
874            .get_value_from_variables(&hm)
875            .unwrap());
876        assert!(!parse("([a])")
877            .unwrap()
878            .get_value_from_variables(&hm)
879            .unwrap());
880        assert!(!parse("[a]|[b]")
881            .unwrap()
882            .get_value_from_variables(&hm)
883            .unwrap());
884        assert!(!parse("[a]&[b]")
885            .unwrap()
886            .get_value_from_variables(&hm)
887            .unwrap());
888        assert!(parse("1").unwrap().get_value_from_variables(&hm).unwrap());
889        assert!(!parse("0").unwrap().get_value_from_variables(&hm).unwrap());
890        assert!(!parse("([a]&([b]|0))")
891            .unwrap()
892            .get_value_from_variables(&hm)
893            .unwrap());
894        assert!(!parse("~[a]&[b]")
895            .unwrap()
896            .get_value_from_variables(&hm)
897            .unwrap());
898
899        hm.insert("a", false);
900        hm.insert("b", true);
901        assert!(!parse("[a]").unwrap().get_value_from_variables(&hm).unwrap());
902        assert!(parse("~[a]")
903            .unwrap()
904            .get_value_from_variables(&hm)
905            .unwrap());
906        assert!(!parse("([a])")
907            .unwrap()
908            .get_value_from_variables(&hm)
909            .unwrap());
910        assert!(parse("[a]|[b]")
911            .unwrap()
912            .get_value_from_variables(&hm)
913            .unwrap());
914        assert!(!parse("[a]&[b]")
915            .unwrap()
916            .get_value_from_variables(&hm)
917            .unwrap());
918        assert!(parse("1").unwrap().get_value_from_variables(&hm).unwrap());
919        assert!(!parse("0").unwrap().get_value_from_variables(&hm).unwrap());
920        assert!(!parse("([a]&([b]|0))")
921            .unwrap()
922            .get_value_from_variables(&hm)
923            .unwrap());
924        assert!(parse("~[a]&[b]")
925            .unwrap()
926            .get_value_from_variables(&hm)
927            .unwrap());
928
929        hm.insert("a", true);
930        hm.insert("b", false);
931        assert!(parse("[a]").unwrap().get_value_from_variables(&hm).unwrap());
932        assert!(!parse("~[a]")
933            .unwrap()
934            .get_value_from_variables(&hm)
935            .unwrap());
936        assert!(parse("([a])")
937            .unwrap()
938            .get_value_from_variables(&hm)
939            .unwrap());
940        assert!(parse("[a]|[b]")
941            .unwrap()
942            .get_value_from_variables(&hm)
943            .unwrap());
944        assert!(!parse("[a]&[b]")
945            .unwrap()
946            .get_value_from_variables(&hm)
947            .unwrap());
948        assert!(parse("1").unwrap().get_value_from_variables(&hm).unwrap());
949        assert!(!parse("0").unwrap().get_value_from_variables(&hm).unwrap());
950        assert!(!parse("([a]&([b]|0))")
951            .unwrap()
952            .get_value_from_variables(&hm)
953            .unwrap());
954        assert!(!parse("~[a]&[b]")
955            .unwrap()
956            .get_value_from_variables(&hm)
957            .unwrap());
958
959        hm.insert("a", true);
960        hm.insert("b", true);
961        assert!(parse("[a]").unwrap().get_value_from_variables(&hm).unwrap());
962        assert!(!parse("~[a]")
963            .unwrap()
964            .get_value_from_variables(&hm)
965            .unwrap());
966        assert!(parse("([a])")
967            .unwrap()
968            .get_value_from_variables(&hm)
969            .unwrap());
970        assert!(parse("[a]|[b]")
971            .unwrap()
972            .get_value_from_variables(&hm)
973            .unwrap());
974        assert!(parse("[a]&[b]")
975            .unwrap()
976            .get_value_from_variables(&hm)
977            .unwrap());
978        assert!(parse("1").unwrap().get_value_from_variables(&hm).unwrap());
979        assert!(!parse("0").unwrap().get_value_from_variables(&hm).unwrap());
980        assert!(parse("([a]&([b]|0))")
981            .unwrap()
982            .get_value_from_variables(&hm)
983            .unwrap());
984        assert!(!parse("~[a]&[b]")
985            .unwrap()
986            .get_value_from_variables(&hm)
987            .unwrap());
988    }
989
990    #[test]
991    fn test_display() {
992        let three_way_and = parse("([a]&[b]&[c])").expect("Unable to parse three way and");
993        assert_eq!("( [a] & [b] & [c] )", format!("{}", three_way_and));
994
995        let three_way_and = parse("(([a]&[b])&[c])").expect("Unable to parse three way and");
996        assert_eq!("( [a] & [b] & [c] )", format!("{}", three_way_and));
997
998        let three_way_and = parse("([a]&([b]&[c]))").expect("Unable to parse three way and");
999        assert_eq!("( [a] & [b] & [c] )", format!("{}", three_way_and));
1000
1001        let three_way_or = parse("([a]|[b]|[c])").expect("Unable to parse three way or");
1002        assert_eq!("( [a] | [b] | [c] )", format!("{}", three_way_or));
1003
1004        let formula = parse("(~[a]&~[b]&~[c])").expect("Unable to parse three way and");
1005        assert_eq!("( ~[a] & ~[b] & ~[c] )", format!("{}", formula));
1006    }
1007}