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}