diff --git a/src/function/mod.rs b/src/function/mod.rs index b2c3c6e..07cc966 100644 --- a/src/function/mod.rs +++ b/src/function/mod.rs @@ -1,3 +1,4 @@ +use std::fmt; use std::path::PathBuf; use regex::Regex; @@ -16,6 +17,20 @@ pub enum ComparisonOperator { GreaterThanOrEqual, } +impl fmt::Display for ComparisonOperator { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use self::ComparisonOperator::*; + match self { + Equal => write!(f, "=="), + NotEqual => write!(f, "=="), + LessThan => write!(f, "<"), + GreaterThan => write!(f, ">"), + LessThanOrEqual => write!(f, "<="), + GreaterThanOrEqual => write!(f, ">="), + } + } +} + #[derive(Debug)] pub enum Function { FilePath(PathBuf), @@ -28,6 +43,22 @@ pub enum Function { Version(PathBuf, String, ComparisonOperator), } +impl fmt::Display for Function { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Function::*; + match self { + FilePath(p) => write!(f, "file(\"{}\")", p.display()), + FileRegex(p, r) => write!(f, "file(\"{}/{}\")", p.display(), r), + ActivePath(p) => write!(f, "active(\"{}\")", p.display()), + ActiveRegex(r) => write!(f, "active(\"{}\")", r), + Many(p, r) => write!(f, "many(\"{}/{}\")", p.display(), r), + ManyActive(r) => write!(f, "many_active(\"{}\")", r), + Checksum(p, c) => write!(f, "checksum(\"{}\", {:02X?})", p.display(), c), + Version(p, v, c) => write!(f, "version(\"{}\", \"{}\", {})", p.display(), v, c), + } + } +} + impl PartialEq for Function { fn eq(&self, other: &Function) -> bool { use Function::*; @@ -63,6 +94,72 @@ mod tests { Regex::new(string).unwrap() } + #[test] + fn function_fmt_for_file_path_should_format_correctly() { + let function = Function::FilePath("subdir/Blank.esm".into()); + + assert_eq!("file(\"subdir/Blank.esm\")", &format!("{}", function)); + } + + #[test] + fn function_fmt_for_file_regex_should_format_correctly() { + let function = Function::FileRegex("subdir".into(), regex("Blank.*")); + + assert_eq!("file(\"subdir/Blank.*\")", &format!("{}", function)); + } + + #[test] + fn function_fmt_for_active_path_should_format_correctly() { + let function = Function::ActivePath("Blank.esm".into()); + + assert_eq!("active(\"Blank.esm\")", &format!("{}", function)); + } + + #[test] + fn function_fmt_for_active_regex_should_format_correctly() { + let function = Function::ActiveRegex(regex("Blank.*")); + + assert_eq!("active(\"Blank.*\")", &format!("{}", function)); + } + + #[test] + fn function_fmt_for_many_should_format_correctly() { + let function = Function::Many("subdir".into(), regex("Blank.*")); + + assert_eq!("many(\"subdir/Blank.*\")", &format!("{}", function)); + } + + #[test] + fn function_fmt_for_many_active_should_format_correctly() { + let function = Function::ManyActive(regex("Blank.*")); + + assert_eq!("many_active(\"Blank.*\")", &format!("{}", function)); + } + + #[test] + fn function_fmt_for_checksum_should_format_correctly() { + let function = Function::Checksum("subdir/Blank.esm".into(), 0xDEADBEEF); + + assert_eq!( + "checksum(\"subdir/Blank.esm\", DEADBEEF)", + &format!("{}", function) + ); + } + + #[test] + fn function_fmt_for_version_should_format_correctly() { + let function = Function::Version( + "subdir/Blank.esm".into(), + "1.2a".into(), + ComparisonOperator::Equal, + ); + + assert_eq!( + "version(\"subdir/Blank.esm\", \"1.2a\", ==)", + &format!("{}", function) + ); + } + #[test] fn function_eq_for_file_path_should_check_pathbuf() { assert_eq!( diff --git a/src/lib.rs b/src/lib.rs index c2b9ee0..eae423f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -13,6 +13,7 @@ mod version; use std::collections::{HashMap, HashSet}; use std::ffi::OsStr; +use std::fmt; use std::io; use std::path::{Path, PathBuf}; use std::str; @@ -116,6 +117,13 @@ impl Expression { } } +impl fmt::Display for Expression { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let strings: Vec = self.0.iter().map(CompoundCondition::to_string).collect(); + write!(f, "{}", strings.join(" or ")) + } +} + // Conditions joined by 'and' #[derive(Debug, PartialEq, Eq)] struct CompoundCondition(Vec); @@ -139,6 +147,13 @@ impl CompoundCondition { } } +impl fmt::Display for CompoundCondition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let strings: Vec = self.0.iter().map(Condition::to_string).collect(); + write!(f, "{}", strings.join(" and ")) + } +} + #[derive(Debug, PartialEq, Eq)] enum Condition { Function(Function), @@ -174,6 +189,17 @@ impl Condition { } } +impl fmt::Display for Condition { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + use Condition::*; + match self { + Function(function) => write!(f, "{}", function), + InvertedFunction(function) => write!(f, "not {}", function), + Expression(e) => write!(f, "({})", e), + } + } +} + #[cfg(test)] mod tests { use super::*; @@ -478,6 +504,30 @@ mod tests { assert!(condition.eval(&state).unwrap()); } + #[test] + fn condition_fmt_should_format_function_correctly() { + let condition = Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))); + + assert_eq!("file(\"Cargo.toml\")", &format!("{}", condition)); + } + + #[test] + fn condition_fmt_should_format_inverted_function_correctly() { + let condition = + Condition::InvertedFunction(Function::FilePath(PathBuf::from("Cargo.toml"))); + + assert_eq!("not file(\"Cargo.toml\")", &format!("{}", condition)); + } + + #[test] + fn condition_fmt_should_format_expression_correctly() { + let condition = Condition::Expression(Expression(vec![CompoundCondition(vec![ + Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))), + ])])); + + assert_eq!("(file(\"Cargo.toml\"))", &format!("{}", condition)); + } + #[test] fn compound_condition_eval_should_be_true_if_all_conditions_are_true() { let state = state("."); @@ -502,6 +552,25 @@ mod tests { assert!(!compound_condition.eval(&state).unwrap()); } + #[test] + fn compound_condition_fmt_should_format_correctly() { + let compound_condition = CompoundCondition(vec![ + Condition::Function(Function::FilePath(PathBuf::from("Cargo.toml"))), + Condition::Function(Function::FilePath(PathBuf::from("missing"))), + ]); + + assert_eq!( + "file(\"Cargo.toml\") and file(\"missing\")", + &format!("{}", compound_condition) + ); + + let compound_condition = CompoundCondition(vec![Condition::Function(Function::FilePath( + PathBuf::from("Cargo.toml"), + ))]); + + assert_eq!("file(\"Cargo.toml\")", &format!("{}", compound_condition)); + } + #[test] fn expression_eval_should_be_true_if_any_compound_condition_is_true() { let state = state("."); @@ -531,4 +600,27 @@ mod tests { ]); assert!(!expression.eval(&state).unwrap()); } + + #[test] + fn expression_fmt_should_format_correctly() { + let expression = Expression(vec![ + CompoundCondition(vec![Condition::Function(Function::FilePath( + PathBuf::from("Cargo.toml"), + ))]), + CompoundCondition(vec![Condition::Function(Function::FilePath( + PathBuf::from("missing"), + ))]), + ]); + + assert_eq!( + "file(\"Cargo.toml\") or file(\"missing\")", + &format!("{}", expression) + ); + + let expression = Expression(vec![CompoundCondition(vec![Condition::Function( + Function::FilePath(PathBuf::from("Cargo.toml")), + )])]); + + assert_eq!("file(\"Cargo.toml\")", &format!("{}", expression)); + } }