diff --git a/Cargo.toml b/Cargo.toml index eab313a..599e5e9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,7 +21,9 @@ appveyor = { repository = "assert-rs/predicates-rs" } difference = { version = "2.0", optional = true } regex = { version="1.0", optional = true } float-cmp = { version="0.4", optional = true } +treeline = { version = "0.1", optional = true } [features] -default = ["difference", "regex", "float-cmp"] +default = ["difference", "regex", "float-cmp", "tree"] unstable = [] +tree = ["treeline",] diff --git a/examples/case_tree.rs b/examples/case_tree.rs new file mode 100644 index 0000000..bcc6a6f --- /dev/null +++ b/examples/case_tree.rs @@ -0,0 +1,15 @@ +extern crate predicates; + +use predicates::prelude::*; +use predicates::tree::CaseTreeExt; + +fn main() { + let pred = predicate::ne(5).not().and(predicate::ge(5)); + + let var = 5; + let case = pred.find_case(true, &var); + if let Some(case) = case { + println!("var is {}", var); + println!("{}", case.tree()); + } +} diff --git a/src/lib.rs b/src/lib.rs index 130f782..4e8627c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -98,6 +98,8 @@ extern crate difference; extern crate float_cmp; #[cfg(feature = "regex")] extern crate regex; +#[cfg(feature = "treeline")] +extern crate treeline; pub mod prelude; @@ -121,3 +123,5 @@ pub mod boolean; pub mod float; pub mod path; pub mod str; +#[cfg(feature = "tree")] +pub mod tree; diff --git a/src/tree.rs b/src/tree.rs new file mode 100644 index 0000000..5445a77 --- /dev/null +++ b/src/tree.rs @@ -0,0 +1,60 @@ +// Copyright (c) 2018 The predicates-rs Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Render `Case` as a tree. + +use std::fmt; + +use treeline; + +use reflection; + +/// Render `Self` as a displayable tree. +pub trait CaseTreeExt { + /// Render `Self` as a displayable tree. + fn tree(&self) -> CaseTree; +} + +impl<'a> CaseTreeExt for reflection::Case<'a> { + fn tree(&self) -> CaseTree { + CaseTree(convert(self)) + } +} + +type CaseTreeInner = treeline::Tree>; + +fn convert<'a>(case: &reflection::Case<'a>) -> CaseTreeInner { + let mut leaves: Vec = vec![]; + + leaves.extend(case.predicate().iter().flat_map(|pred| { + pred.parameters().map(|item| { + let root: Box = Box::new(item.to_string()); + treeline::Tree::new(root, vec![]) + }) + })); + + leaves.extend(case.products().map(|item| { + let root: Box = Box::new(item.to_string()); + treeline::Tree::new(root, vec![]) + })); + + leaves.extend(case.children().map(|item| convert(item))); + + let root = Box::new(case.predicate().map(|p| p.to_string()).unwrap_or_default()); + CaseTreeInner::new(root, leaves) +} + +/// A `Case` rendered as a tree for display. +#[allow(missing_debug_implementations)] +pub struct CaseTree(CaseTreeInner); + +impl fmt::Display for CaseTree { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.0.fmt(f) + } +}