1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//! The module with the **Public API that is highly encouraged to be used**.
use crate::layouter_error;
use crate::{Drawer, Embedder, LayouterError, SvgDrawer, Visualize};
use id_tree::Tree;

///
/// The Result type that is uses within the public API `Layouter`.
///
pub type Result = layouter_error::Result<()>;

///
/// The Layouter type provides a simple builder mechanism with a fluent API.
///
pub struct Layouter<'a, 'b, 'c, T>
where
    T: Visualize,
{
    tree: &'a Tree<T>,
    drawer: Option<&'b dyn Drawer>,
    file_name: Option<&'c std::path::Path>,
}

impl<'a, 'b, 'c, T> Layouter<'a, 'b, 'c, T>
where
    T: Visualize,
{
    ///
    /// Creates a new Layouter with the required tree.
    ///
    /// ```
    /// use id_tree_layout::{Layouter, Visualize};
    /// use id_tree::{Tree, TreeBuilder};
    ///
    /// struct MyNodeData(i32);
    ///
    /// impl Visualize for MyNodeData {
    ///     fn visualize(&self) -> std::string::String { self.0.to_string() }
    ///     fn emphasize(&self) -> bool { false }
    /// }
    ///
    ///
    /// let tree: Tree<MyNodeData> = TreeBuilder::new().build();
    /// let layouter = Layouter::new(&tree);
    /// ```
    ///
    pub fn new(tree: &'a Tree<T>) -> Self {
        Self {
            tree,
            drawer: None,
            file_name: None,
        }
    }

    ///
    /// Sets the path of the output file on the layouter.
    ///
    /// ```
    /// use id_tree_layout::{Layouter, Visualize};
    /// use id_tree::{Tree, TreeBuilder};
    /// use std::path::Path;
    ///
    /// struct MyNodeData(i32);
    ///
    /// impl Visualize for MyNodeData {
    ///     fn visualize(&self) -> std::string::String { self.0.to_string() }
    ///     fn emphasize(&self) -> bool { false }
    /// }
    ///
    ///
    /// let tree: Tree<MyNodeData> = TreeBuilder::new().build();
    /// let layouter = Layouter::new(&tree)
    ///     .with_file_path(Path::new("test.svg"));
    /// ```
    ///
    pub fn with_file_path(self, path: &'c std::path::Path) -> Self {
        Self {
            tree: self.tree,
            file_name: Some(path),
            drawer: self.drawer,
        }
    }

    ///
    /// Sets a different drawer when you don't want to use the default svg-drawer.
    /// If this method is not called the crate's own svg-drawer is used.
    ///
    /// ```
    /// use id_tree_layout::{Drawer, Layouter, PlacedTreeItem, Visualize};
    /// use id_tree_layout::drawer::Result;
    /// use id_tree::{Tree, TreeBuilder};
    /// use std::path::Path;
    ///
    /// struct NilDrawer;
    /// impl Drawer for NilDrawer {
    ///     fn draw(&self, _file_name: &Path, _embedding: &[PlacedTreeItem]) -> Result {
    ///         Ok(())
    ///     }
    /// }
    ///
    /// struct MyNodeData(i32);
    ///
    /// impl Visualize for MyNodeData {
    ///     fn visualize(&self) -> std::string::String { self.0.to_string() }
    ///     fn emphasize(&self) -> bool { false }
    /// }
    ///
    ///
    /// let tree: Tree<MyNodeData> = TreeBuilder::new().build();
    /// let drawer = NilDrawer;
    /// let layouter = Layouter::new(&tree)
    ///     .with_drawer(&drawer)
    ///     .with_file_path(Path::new("test.svg"));
    /// ```
    ///
    pub fn with_drawer(self, drawer: &'b dyn Drawer) -> Self {
        Self {
            tree: self.tree,
            file_name: self.file_name,
            drawer: Some(drawer),
        }
    }

    ///
    /// When the layouter instance is fully configured this method invokes the necessary embedding
    /// functionality and uses the drawer which writes the result to the output file in its own
    /// output format.
    ///
    /// ```
    /// use id_tree_layout::{Layouter, Visualize};
    /// use id_tree::{Tree, TreeBuilder};
    /// use std::path::Path;
    ///
    /// struct MyNodeData(i32);
    ///
    /// impl Visualize for MyNodeData {
    ///     fn visualize(&self) -> std::string::String { self.0.to_string() }
    ///     fn emphasize(&self) -> bool { false }
    /// }
    ///
    ///
    /// let tree: Tree<MyNodeData> = TreeBuilder::new().build();
    /// Layouter::new(&tree)
    ///     .with_file_path(Path::new("test.svg"))
    ///     .write().expect("Failed writing layout")
    /// ```
    ///
    pub fn write(&self) -> Result {
        if self.file_name.is_none() {
            Err(LayouterError::from_description(
                "No output file name given - use Layouter::with_file_path.".to_string(),
            ))
        } else {
            let embedding = Embedder::embed(self.tree);
            let default_drawer = SvgDrawer::new();
            let drawer = self.drawer.unwrap_or(&default_drawer);
            drawer
                .draw(self.file_name.unwrap(), &embedding)
                .map_err(LayouterError::from_io_error)
        }
    }
}