dot_writer/
scope.rs

1use super::attribute::{Attributes, AttributesList};
2use super::writer::{DotWriter, Line, Statement};
3
4/// A [`Scope`] struct represents either a graph, digraph, subgraph or cluster.
5/// Its the workhorse of the DOT writing, and can be used to create new sub-scopes,
6/// add nodes, add edges, or adjust default attributes for any of the above.
7///
8/// The only way to construct a top level graph or digraph scope is to call the
9/// [`DotWriter::graph`] or [`DotWriter::digraph`] functions on a new [`DotWriter`].
10pub struct Scope<'d, 'w> {
11    writer: &'d mut DotWriter<'w>,
12}
13
14impl<'d, 'w> Scope<'d, 'w> {
15    pub(crate) fn new(writer: &'d mut DotWriter<'w>, prefix: &[u8]) -> Scope<'d, 'w> {
16        {
17            let mut line = Line::new(writer);
18            line.write_with_whitespace(prefix);
19            line.write(b" {");
20        }
21        writer.indent();
22        Self { writer }
23    }
24
25    /// Starts a new nested subgraph, returning another Scope for writing to that subgraph.
26    pub fn subgraph(&mut self) -> Scope<'_, 'w> {
27        Scope::new(self.writer, b"subgraph")
28    }
29
30    /// Starts a new nested cluster subgraph, returning another Scope for writing to it.
31    /// A cluster is a special case of a subgraph which groups its child nodes together.
32    /// See the "Subgraphs and Clusters" section of the
33    /// [Graphviz documentation](https://graphviz.org/doc/info/lang.html)
34    /// for more information.
35    pub fn cluster(&mut self) -> Scope<'_, 'w> {
36        let label = format!("subgraph cluster_{}", self.writer.next_id());
37        Scope::new(self.writer, label.as_bytes())
38    }
39
40    /// Returns a struct for writing the default attributes for all subgraphs from now on.
41    pub fn graph_attributes(&mut self) -> AttributesList<'_, 'w> {
42        let mut statement = Statement::new(self.writer);
43        statement.write(b"graph");
44        AttributesList::new(statement)
45    }
46
47    /// Returns a struct for writing the default attributes for all edges from now on.
48    pub fn edge_attributes(&mut self) -> AttributesList<'_, 'w> {
49        let mut statement = Statement::new(self.writer);
50        statement.write(b"edge");
51        AttributesList::new(statement)
52    }
53
54    /// Returns a struct for writing the default attributes for all nodes from now on.
55    pub fn node_attributes(&mut self) -> AttributesList<'_, 'w> {
56        let mut statement = Statement::new(self.writer);
57        statement.write(b"node");
58        AttributesList::new(statement)
59    }
60
61    /// Creates a new node, with an automatic default id of the format `node_x`
62    /// where x is an incerementing integer. You don't have to declare nodes before
63    /// using them in a call to [`Scope::edge`], but you do have to declare them using this
64    /// function if you want to set specifc attributes for this node (font etc).
65    ///
66    /// The returned value can be used to get the automatically generated id,
67    /// and also to set the attributes.
68    ///
69    /// ```
70    /// use dot_writer::{DotWriter, Color, Attributes};
71    ///
72    /// let dot_string = DotWriter::write_string(|writer| {
73    ///   writer.set_pretty_print(false);
74    ///   let mut digraph = writer.digraph();
75    ///   let a = digraph.node_auto().id();
76    ///   let b = {
77    ///     let mut node = digraph.node_auto();
78    ///     node.set_color(Color::Red);
79    ///     node.id()
80    ///   };
81    ///   digraph.edge(a, b);
82    /// });
83    /// assert_eq!(
84    ///     dot_string,
85    ///     "digraph{node_0;node_1[color=red];node_0->node_1;}"
86    /// );
87    /// ```
88    pub fn node_auto(&mut self) -> Node<'_, 'w> {
89        let id = self.writer.next_id();
90        self.node_named(format!("node_{}", id))
91    }
92
93    /// Creates a new node, with the specified id. You don't have to declare nodes before
94    /// using them in a call to [`Scope::edge`], but you do have to declare them using this
95    /// function if you want to set specific attributes for this node (font etc).
96    ///
97    /// The returned value can be used to get the assigned name,
98    /// and also to set the attributes.
99    ///
100    /// ```
101    /// use dot_writer::{DotWriter, Color, Attributes};
102    ///
103    /// let dot_string = DotWriter::write_string(|writer| {
104    ///   writer.set_pretty_print(false);
105    ///   let mut digraph = writer.digraph();
106    ///   let a = digraph.node_named("alpha").id();
107    ///   let b = {
108    ///     let mut node = digraph.node_named("beta");
109    ///     node.set_color(Color::Red);
110    ///     node.id()
111    ///   };
112    ///   digraph.edge(a, b);
113    /// });
114    /// assert_eq!(
115    ///     dot_string,
116    ///     "digraph{alpha;beta[color=red];alpha->beta;}"
117    /// );
118    /// ```
119    pub fn node_named<S: Into<String>>(&mut self, id: S) -> Node<'_, 'w> {
120        Node::new(Statement::new(self.writer), id.into())
121    }
122
123    /// Add a new edge joining `start_node_id` and `end_node_id` nodes.
124    /// Note that nodes do not need to be already defined by [`Scope::node_auto`]
125    /// or by [`Scope::node_named`] (unless you want to set node-specific attributes).
126    /// Arguments can be just strings, or you can use the [`Node::id`] of an already
127    /// defined node:
128    ///
129    /// ```
130    /// use dot_writer::DotWriter;
131    ///
132    /// let mut output_bytes = Vec::new();
133    /// {
134    ///   let mut writer = DotWriter::from(&mut output_bytes);
135    ///   writer.set_pretty_print(false);
136    ///   let mut digraph = writer.digraph();
137    ///   let a = digraph.node_auto().id();
138    ///   digraph.edge(a, "b");
139    /// }
140    /// assert_eq!(
141    ///     std::str::from_utf8(&output_bytes).unwrap(),
142    ///     "digraph{node_0;node_0->b;}"
143    /// );
144    /// ```
145    pub fn edge<F, T>(&mut self, from_node_id: F, to_node_id: T) -> EdgeList<'_, 'w>
146    where
147        F: AsRef<[u8]>,
148        T: AsRef<[u8]>,
149    {
150        EdgeList::new(Statement::new(self.writer), from_node_id, to_node_id)
151    }
152
153    /// Add N-1 edges joining all node ids or subgraphs in the iterator,
154    /// in the same manner as [`Scope::edge`].
155    /// The return value will be None if less than 2 items are passed in.
156    ///
157    /// ```
158    /// use dot_writer::DotWriter;
159    ///
160    /// let mut output_bytes = Vec::new();
161    /// {
162    ///   let mut writer = DotWriter::from(&mut output_bytes);
163    ///   writer.set_pretty_print(false);
164    ///   let mut digraph = writer.digraph();
165    ///   digraph.edges(["a", "b", "c"]);
166    /// }
167    /// assert_eq!(
168    ///     std::str::from_utf8(&output_bytes).unwrap(),
169    ///     "digraph{a->b->c;}"
170    /// );
171    /// ```
172    pub fn edges<I, E>(&mut self, items: I) -> Option<EdgeList<'_, 'w>>
173    where
174        I: IntoIterator<Item = E>,
175        E: AsRef<[u8]>,
176    {
177        let mut iter = items.into_iter();
178        let mut edge_list = self.edge(iter.next()?, iter.next()?);
179        for item in iter {
180            edge_list.edge(item);
181        }
182        Some(edge_list)
183    }
184}
185
186impl<'d, 'w> Attributes for Scope<'d, 'w> {
187    fn set(&mut self, name: &str, value: &str, quote: bool) -> &mut Self {
188        {
189            let mut statement = Statement::new(self.writer);
190            statement.write(name.as_bytes());
191            statement.write(b"=");
192            if quote {
193                statement.write_quoted(value.as_bytes());
194            } else {
195                statement.write(value.as_bytes());
196            }
197        }
198        self
199    }
200}
201
202impl<'d, 'w> Drop for Scope<'d, 'w> {
203    fn drop(&mut self) {
204        self.writer.unindent();
205        Line::new(self.writer).write(b"}");
206    }
207}
208
209/// An [`EdgeList`] is returned from [`Scope::edge`] and can be used to set the attributes
210/// of an edge, or to chain additional nodes onto the edge statement.
211pub struct EdgeList<'d, 'w> {
212    statement: Statement<'d, 'w>,
213}
214
215impl<'d, 'w> EdgeList<'d, 'w> {
216    fn new<S: AsRef<[u8]>, E: AsRef<[u8]>>(
217        mut statement: Statement<'d, 'w>,
218        start_node_id: S,
219        end_node_id: E,
220    ) -> EdgeList<'d, 'w> {
221        statement.write(start_node_id.as_ref());
222        statement.write_edge_operator();
223        statement.write(end_node_id.as_ref());
224        EdgeList { statement }
225    }
226
227    /// Adds another edge from the last node added to `node_id`.
228    /// Note that nodes do not need to be already defined by [`Scope::node_auto`]
229    /// or by [`Scope::node_named`] (unless you want to set node-specifc attributes).
230    ///
231    /// ```
232    /// use dot_writer::DotWriter;
233    ///
234    /// let mut output_bytes = Vec::new();
235    /// {
236    ///   let mut writer = DotWriter::from(&mut output_bytes);
237    ///   writer.set_pretty_print(false);
238    ///   writer.digraph().edge("a", "b").edge("c").edge("d");
239    /// }
240    /// assert_eq!(
241    ///     std::str::from_utf8(&output_bytes).unwrap(),
242    ///     "digraph{a->b->c->d;}"
243    /// );
244    ///
245    /// ```
246    pub fn edge<N: AsRef<[u8]>>(&mut self, node_id: N) -> &mut Self {
247        self.statement.write_edge_operator();
248        self.statement.write(node_id.as_ref());
249        self
250    }
251
252    /// Start writing the attributes section of an edge definition.
253    /// See the returned structure for what attributes can be set.
254    /// Note that no more nodes can be added after this function is called.
255    pub fn attributes(self) -> AttributesList<'d, 'w> {
256        AttributesList::new(self.statement)
257    }
258}
259
260/// A [`Node`] is returned from [`Scope::node_named`] or [`Scope::node_auto`],
261/// and allows for getting the id of the node for future reference via [`Node::id`],
262///
263/// Importantly it also implements [`Attributes`] for setting attributes
264/// to override existing defaults for this specific node.
265pub struct Node<'d, 'w> {
266    attributes: AttributesList<'d, 'w>,
267    id: String,
268}
269
270impl<'d, 'w> Node<'d, 'w> {
271    fn new(mut statement: Statement<'d, 'w>, id: String) -> Self {
272        statement.write(id.as_bytes());
273        Node {
274            attributes: AttributesList::new(statement),
275            id,
276        }
277    }
278
279    /// Returns a copy of the [`Node`] id, for later use with [`Scope::edge`].
280    /// Note that as the node definition will not finish writing until after
281    /// this [`Node`] goes out of scope, you'll need to save the id if you
282    /// want to draw an edge to it later. This function is most usefull when
283    /// the edge was automatically generated with [`Scope::node_auto`]:
284    /// ```
285    /// use dot_writer::DotWriter;
286    ///
287    /// let mut output_bytes = Vec::new();
288    /// {
289    ///     let mut writer = DotWriter::from(&mut output_bytes);
290    ///     writer.set_pretty_print(false);
291    ///     let mut digraph = writer.digraph();
292    ///     let a_id = {
293    ///         let mut sub_graph = digraph.subgraph();
294    ///         let a_id = sub_graph.node_auto().id();
295    ///         // sub_graph goes out of scope here to close bracket
296    ///         // but id is kept for later call
297    ///         a_id
298    ///     };
299    ///     digraph.edge(a_id, "b");
300    /// }
301    /// assert_eq!(
302    ///     std::str::from_utf8(&output_bytes).unwrap(),
303    ///     "digraph{subgraph{node_0;}node_0->b;}"
304    /// );
305    ///
306    /// ```
307    pub fn id(&self) -> NodeId {
308        NodeId {
309            id: self.id.clone(),
310        }
311    }
312}
313
314impl<'d, 'w> std::ops::DerefMut for Node<'d, 'w> {
315    fn deref_mut(&mut self) -> &mut AttributesList<'d, 'w> {
316        &mut self.attributes
317    }
318}
319
320impl<'d, 'w> std::ops::Deref for Node<'d, 'w> {
321    type Target = AttributesList<'d, 'w>;
322
323    fn deref(&self) -> &AttributesList<'d, 'w> {
324        &self.attributes
325    }
326}
327
328/// A [`NodeId`] wraps a string storing the Id of a Node
329/// It's designed for use with the [`Scope::edge`] function,
330/// and for creating [`PortId`] using [`NodeId::port`].
331#[derive(Clone, Debug)]
332pub struct NodeId {
333    id: String,
334}
335
336impl NodeId {
337    /// Creates a [`PortId`] for refering to a port. These are specific sub parts of
338    /// a [`Shape::Record`](`crate::attribute::Shape::Record`)
339    /// or [`Shape::Mrecord`](`crate::attribute::Shape::Mrecord`)
340    /// (for more information see "Record-based Nodes"
341    /// on the [Graphviz documentation](https://graphviz.org/doc/info/shapes.html)).
342    pub fn port(&self, port_id: &str) -> PortId {
343        PortId {
344            id: format!("{}:{}", self.id, port_id),
345        }
346    }
347}
348
349impl From<String> for NodeId {
350    fn from(id: String) -> Self {
351        Self { id }
352    }
353}
354
355impl From<NodeId> for String {
356    fn from(node_id: NodeId) -> Self {
357        node_id.id
358    }
359}
360
361impl AsRef<[u8]> for NodeId {
362    fn as_ref(&self) -> &[u8] {
363        self.id.as_bytes()
364    }
365}
366
367/// A [`PortId`] wraps a string referning to the nnode id and port of a specifc record
368/// or Mrecord node (for more information see "Record-based Nodes"
369/// in the [Graphviz documentation](https://graphviz.org/doc/info/shapes.html)).
370#[derive(Clone, Debug)]
371pub struct PortId {
372    id: String,
373}
374
375impl PortId {
376    /// Creates a [`PortPosId`] for refering to a specific position on a record or Mrecord
377    /// port for an edge to attach to.
378    pub fn position(&self, position: PortPosition) -> PortPosId {
379        PortPosId {
380            id: format!("{}:{}", self.id, position.as_ref()),
381        }
382    }
383}
384
385impl From<String> for PortId {
386    fn from(id: String) -> Self {
387        Self { id }
388    }
389}
390
391impl From<PortId> for String {
392    fn from(port_id: PortId) -> Self {
393        port_id.id
394    }
395}
396
397impl AsRef<[u8]> for PortId {
398    fn as_ref(&self) -> &[u8] {
399        self.id.as_bytes()
400    }
401}
402
403/// A [`PortPosId`] wraps a string referning to the [`NodeId`], [`PortId`] and [`PortPosition`]
404/// of a specifc record or Mrecord node (for more information see "portPos"
405/// in the [Graphviz documentation](https://graphviz.org/docs/attr-types/portPos/)).
406#[derive(Clone, Debug)]
407pub struct PortPosId {
408    id: String,
409}
410
411impl From<String> for PortPosId {
412    fn from(id: String) -> Self {
413        Self { id }
414    }
415}
416
417impl From<PortPosId> for String {
418    fn from(port_pos_id: PortPosId) -> Self {
419        port_pos_id.id
420    }
421}
422
423impl AsRef<[u8]> for PortPosId {
424    fn as_ref(&self) -> &[u8] {
425        self.id.as_bytes()
426    }
427}
428
429/// Refers the position to add an edge for of a specifc record or Mrecord node
430/// (for more information see "portPos"
431/// in the [Graphviz documentation](https://graphviz.org/docs/attr-types/portPos/)).
432#[derive(Debug, Copy, Clone, Eq, PartialEq)]
433pub enum PortPosition {
434    North,
435    NorthEast,
436    East,
437    SouthEast,
438    South,
439    SouthWest,
440    West,
441    NorthWest,
442    Centre,
443    Auto,
444}
445
446impl AsRef<str> for PortPosition {
447    fn as_ref(&self) -> &str {
448        match self {
449            Self::North => "n",
450            Self::NorthEast => "ne",
451            Self::East => "e",
452            Self::SouthEast => "se",
453            Self::South => "s",
454            Self::SouthWest => "sw",
455            Self::West => "w",
456            Self::NorthWest => "nw",
457            Self::Centre => "c",
458            Self::Auto => "_",
459        }
460    }
461}
462
463#[cfg(test)]
464mod test {
465    use super::*;
466
467    #[test]
468    fn test_manual_and_auto_nodes() {
469        let output = DotWriter::write_string(|writer| {
470            let mut digraph = writer.digraph();
471            digraph.node_auto();
472            let a = digraph.node_named("a").id();
473            digraph.node_named(NodeId::from(String::from("b")));
474            digraph.edge(a, "b").edge(String::from("c"));
475        });
476        assert_eq!(output, "digraph{node_0;a;b;a->b->c;}")
477    }
478}