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}