1 // Copyright (c) 2018 The predicates-rs Project Developers.
2 //
3 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4 // http://www.apache.org/license/LICENSE-2.0> or the MIT license
5 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
6 // option. This file may not be copied, modified, or distributed
7 // except according to those terms.
8
9 //! Render `Case` as a tree.
10
11 use std::fmt;
12
13 use predicates_core::reflection;
14
15 /// Render `Self` as a displayable tree.
16 pub trait CaseTreeExt {
17 /// Render `Self` as a displayable tree.
tree(&self) -> CaseTree18 fn tree(&self) -> CaseTree;
19 }
20
21 impl<'a> CaseTreeExt for reflection::Case<'a> {
tree(&self) -> CaseTree22 fn tree(&self) -> CaseTree {
23 CaseTree(convert(self))
24 }
25 }
26
27 type CaseTreeInner = termtree::Tree<Displayable>;
28
convert(case: &reflection::Case<'_>) -> CaseTreeInner29 fn convert(case: &reflection::Case<'_>) -> CaseTreeInner {
30 let mut leaves: Vec<CaseTreeInner> = vec![];
31
32 leaves.extend(case.predicate().iter().flat_map(|pred| {
33 pred.parameters().map(|item| {
34 let root = Displayable::new(&item);
35 termtree::Tree::new(root).with_multiline(true)
36 })
37 }));
38
39 leaves.extend(case.products().map(|item| {
40 let root = Displayable::new(item);
41 termtree::Tree::new(root).with_multiline(true)
42 }));
43
44 leaves.extend(case.children().map(convert));
45
46 let root = case
47 .predicate()
48 .map(|p| Displayable::new(&p))
49 .unwrap_or_default();
50 CaseTreeInner::new(root).with_leaves(leaves)
51 }
52
53 /// A `Case` rendered as a tree for display.
54 #[allow(missing_debug_implementations)]
55 pub struct CaseTree(CaseTreeInner);
56
57 impl fmt::Display for CaseTree {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result58 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59 self.0.fmt(f)
60 }
61 }
62
63 #[derive(Default)]
64 struct Displayable {
65 primary: String,
66 alternate: String,
67 }
68
69 impl Displayable {
new(display: &dyn std::fmt::Display) -> Self70 fn new(display: &dyn std::fmt::Display) -> Self {
71 let primary = format!("{}", display);
72 let alternate = format!("{:#}", display);
73 Self { primary, alternate }
74 }
75 }
76
77 impl fmt::Display for Displayable {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 if f.alternate() {
80 self.alternate.fmt(f)
81 } else {
82 self.primary.fmt(f)
83 }
84 }
85 }
86