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
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
use super::attribute::{Attributes, AttributesList};
use super::writer::{DotWriter, Line, Quote, Statement};

/// A [`Scope`] struct represents either a graph, digraph, subgraph or cluster.
/// Its the workhorse of the DOT writing, and can be used to create new sub-scopes,
/// add nodes, add edges, or adjust default attributes for any of the above.
///
/// The only way to construct a top level graph or digraph scope is to call the
/// [`DotWriter::graph`] or [`DotWriter::digraph`] functions on a new [`DotWriter`].
pub struct Scope<'d, 'w> {
    writer: &'d mut DotWriter<'w>,
}

impl<'d, 'w> Scope<'d, 'w> {
    pub(crate) fn new(writer: &'d mut DotWriter<'w>, prefix: &[u8]) -> Scope<'d, 'w> {
        {
            let mut line = Line::new(writer);
            line.write(prefix);
            line.write(b" {");
        }
        writer.indent();
        Self { writer }
    }

    /// Starts a new nested subgraph, returning another Scope for writing to that subgraph.
    pub fn subgraph(&mut self) -> Scope<'_, 'w> {
        Scope::new(self.writer, b"subgraph")
    }

    /// Starts a new nested cluster subgraph, returning another Scope for writing to it.
    /// A cluster is a special case of a subgraph which groups its child nodes together.
    /// See the "Subgraphs and Clusters" section of the
    /// [Graphviz documentation](https://graphviz.org/doc/info/lang.html)
    /// for more information.
    pub fn cluster(&mut self) -> Scope<'_, 'w> {
        let label = format!("subgraph cluster_{}", self.writer.next_id());
        Scope::new(self.writer, label.as_bytes())
    }

    // Returns a struct for writing the default attributes for all subgraphs from now on.
    pub fn graph_attributes(&mut self) -> AttributesList<'_, 'w> {
        let mut statement = Statement::new(self.writer);
        statement.write(b"graph");
        AttributesList::new(statement)
    }

    // Returns a struct for writing the default attributes for all edges from now on.
    pub fn edge_attributes(&mut self) -> AttributesList<'_, 'w> {
        let mut statement = Statement::new(self.writer);
        statement.write(b"edge");
        AttributesList::new(statement)
    }

    // Returns a struct for writing the default attributes for all nodes from now on.
    pub fn node_attributes(&mut self) -> AttributesList<'_, 'w> {
        let mut statement = Statement::new(self.writer);
        statement.write(b"node");
        AttributesList::new(statement)
    }

    /// Creates a new node, with an automatic default id of the format `node_x`
    /// where x is an incerementing integer. You don't have to declare nodes before
    /// using them in a call to [`Scope::edge`], but you do have to declare them using this
    /// function if you want to set specifc attributes for this node (font etc).
    ///
    /// The returned value can be used to get the automatically generated id,
    /// and also to set the attributes.
    pub fn node_auto(&mut self) -> Node<'_, 'w> {
        let id = self.writer.next_id();
        self.node_named(format!("node_{}", id))
    }

    /// Creates a new node, with the specified id. You don't have to declare nodes before
    /// using them in a call to [`Scope::edge`], but you do have to declare them using this
    /// function if you want to set specific attributes for this node (font etc).
    ///
    /// The returned value can be used to get the assigned name,
    /// and also to set the attributes.
    pub fn node_named<S: Into<String>>(&mut self, id: S) -> Node<'_, 'w> {
        Node::new(Statement::new(self.writer), id.into())
    }

    /// Add a new edge joining `start_node_id` and `end_node_id` nodes.
    /// Note that nodes do not need to be already defined by [`Scope::node_auto`]
    /// or by [`Scope::node_named`] (unless you want to set node-specific attributes).
    /// Arguments can be just strings, or you can use the [`Node::id`] of an already
    /// defined node:
    ///
    /// ```
    /// use dot_writer::DotWriter;
    ///
    /// let mut output_bytes = Vec::new();
    /// {
    ///   let mut writer = DotWriter::from(&mut output_bytes);
    ///   writer.set_pretty_print(false);
    ///   let mut digraph = writer.digraph();
    ///   let a = digraph.node_auto().id();
    ///   digraph.edge(a, "b");
    /// }
    /// assert_eq!(
    ///     std::str::from_utf8(&output_bytes).unwrap(),
    ///     "digraph {node_0;node_0->b;}"
    /// );
    ///
    /// ```
    pub fn edge<S, E>(&mut self, start_node_id: S, end_node_id: E) -> EdgeList<'_, 'w>
    where
        S: AsRef<[u8]>,
        E: AsRef<[u8]>,
    {
        EdgeList::new(Statement::new(self.writer), start_node_id, end_node_id)
    }
}

impl<'d, 'w> Attributes for Scope<'d, 'w> {
    fn set(&mut self, name: &str, value: &str, quote: bool) -> &mut Self {
        {
            let mut statement = Statement::new(self.writer);
            statement.write(name.as_bytes());
            statement.write(b"=");
            if quote {
                Quote::new(&mut statement).write(value.as_bytes());
            } else {
                statement.write(value.as_bytes());
            }
        }
        self
    }
}

impl<'d, 'w> Drop for Scope<'d, 'w> {
    fn drop(&mut self) {
        self.writer.unindent();
        Line::new(self.writer).write(b"}");
    }
}

/// An [`EdgeList`] is returned from [`Scope::edge`] and can be used to set the attributes
/// of an edge, or to chain additional nodes onto the edge statement.
pub struct EdgeList<'d, 'w> {
    statement: Statement<'d, 'w>,
}

impl<'d, 'w> EdgeList<'d, 'w> {
    fn new<S: AsRef<[u8]>, E: AsRef<[u8]>>(
        mut statement: Statement<'d, 'w>,
        start_node_id: S,
        end_node_id: E,
    ) -> EdgeList<'d, 'w> {
        statement.write(start_node_id.as_ref());
        statement.write_edge_operator();
        statement.write(end_node_id.as_ref());
        EdgeList { statement }
    }

    /// Adds another edge from the last node added to `node_id`.
    /// Note that nodes do not need to be already defined by [`Scope::node_auto`]
    /// or by [`Scope::node_named`] (unless you want to set node-specifc attributes).
    ///
    /// ```
    /// use dot_writer::DotWriter;
    ///
    /// let mut output_bytes = Vec::new();
    /// {
    ///   let mut writer = DotWriter::from(&mut output_bytes);
    ///   writer.set_pretty_print(false);
    ///   writer.digraph().edge("a", "b").edge("c").edge("d");
    /// }
    /// assert_eq!(
    ///     std::str::from_utf8(&output_bytes).unwrap(),
    ///     "digraph {a->b->c->d;}"
    /// );
    ///
    /// ```
    pub fn edge<N: AsRef<[u8]>>(&mut self, node_id: N) -> &mut Self {
        self.statement.write_edge_operator();
        self.statement.write(node_id.as_ref());
        self
    }

    /// Start writing the attributes section of an edge definition.
    /// See the returned structure for what attributes can be set.
    /// Note that no more nodes can be added after this function is called.
    pub fn attributes(self) -> AttributesList<'d, 'w> {
        AttributesList::new(self.statement)
    }
}

/// A [`Node`] is returned from [`Scope::node_named`] or [`Scope::node_auto`],
/// and allows for getting the id of the node for future reference via [`Node::id`],
///
/// Importantly it also implements [`Attributes`] for setting attributes
/// to override existing defaults for this specific node.
pub struct Node<'d, 'w> {
    attributes: AttributesList<'d, 'w>,
    id: String,
}

impl<'d, 'w> Node<'d, 'w> {
    fn new(mut statement: Statement<'d, 'w>, id: String) -> Self {
        statement.write(id.as_bytes());
        Node {
            attributes: AttributesList::new(statement),
            id,
        }
    }

    /// Returns a copy of the [`Node`] id, for later use with [`Scope::edge`].
    /// Note that as the node definition will not finish writing until after
    /// this [`Node`] goes out of scope, you'll need to save the id if you
    /// want to draw an edge to it later. This function is most usefull when
    /// the edge was automatically generated with [`Scope::node_auto`]:
    /// ```
    /// use dot_writer::DotWriter;
    ///
    /// let mut output_bytes = Vec::new();
    /// {
    ///     let mut writer = DotWriter::from(&mut output_bytes);
    ///     writer.set_pretty_print(false);
    ///     let mut digraph = writer.digraph();
    ///     let a_id = {
    ///         let mut sub_graph = digraph.subgraph();
    ///         let a_id = sub_graph.node_auto().id();
    ///         // sub_graph goes out of scope here to close bracket
    ///         // but id is kept for later call
    ///         a_id
    ///     };
    ///     digraph.edge(a_id, "b");
    /// }
    /// assert_eq!(
    ///     std::str::from_utf8(&output_bytes).unwrap(),
    ///     "digraph {subgraph {node_0;}node_0->b;}"
    /// );
    ///
    /// ```
    pub fn id(&self) -> NodeId {
        NodeId {
            id: self.id.clone(),
        }
    }
}

impl<'d, 'w> std::ops::DerefMut for Node<'d, 'w> {
    fn deref_mut(&mut self) -> &mut AttributesList<'d, 'w> {
        &mut self.attributes
    }
}

impl<'d, 'w> std::ops::Deref for Node<'d, 'w> {
    type Target = AttributesList<'d, 'w>;

    fn deref(&self) -> &AttributesList<'d, 'w> {
        &self.attributes
    }
}

/// A [`NodeId`] wraps a string storing the Id of a Node
/// It's designed for use with the [`Scope::edge`] function,
/// and for creating [`PortId`] using [`NodeId::port`].
#[derive(Clone, Debug)]
pub struct NodeId {
    id: String,
}

impl NodeId {
    /// Creates a [`PortId`] for refering to a port.These are specific sub parts of
    /// a [`Shape::Record`](`crate::attribute::Shape::Record`)
    /// or [`Shape::Mrecord`](`crate::attribute::Shape::Mrecord`)
    /// (for more information see "Record-based Nodes"
    /// on the [Graphviz documentation](https://graphviz.org/doc/info/shapes.html)).
    pub fn port(&self, port_id: &str) -> PortId {
        PortId {
            id: format!("{}:{}", self.id, port_id),
        }
    }
}

impl AsRef<[u8]> for NodeId {
    fn as_ref(&self) -> &[u8] {
        self.id.as_bytes()
    }
}

/// A [`PortId`] wraps a string referning to the nnode id and port of a specifc record
/// or Mrecord node (for more information see "Record-based Nodes"
/// in the [Graphviz documentation](https://graphviz.org/doc/info/shapes.html)).
#[derive(Clone, Debug)]
pub struct PortId {
    id: String,
}

impl PortId {
    /// Creates a [`PortPosId`] for refering to a specific position on a record or Mrecord
    /// port for an edge to attach to.
    pub fn position(&self, position: PortPosition) -> PortPosId {
        PortPosId {
            id: format!("{}:{}", self.id, position.as_ref()),
        }
    }
}

impl AsRef<[u8]> for PortId {
    fn as_ref(&self) -> &[u8] {
        self.id.as_bytes()
    }
}

/// A [`PortPosId`] wraps a string referning to the [`NodeId`], [`PortId`] and [`PortPosition`]
/// of a specifc record or Mrecord node (for more information see "portPos"
/// in the [Graphviz documentation](https://graphviz.org/docs/attr-types/portPos/)).
#[derive(Clone, Debug)]
pub struct PortPosId {
    id: String,
}

impl AsRef<[u8]> for PortPosId {
    fn as_ref(&self) -> &[u8] {
        self.id.as_bytes()
    }
}

/// Refers the position to add an edge for of a specifc record or Mrecord node
/// (for more information see "portPos"
/// in the [Graphviz documentation](https://graphviz.org/docs/attr-types/portPos/)).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PortPosition {
    North,
    NorthEast,
    East,
    SouthEast,
    South,
    SouthWest,
    West,
    NorthWest,
    Centre,
    Auto,
}

impl AsRef<str> for PortPosition {
    fn as_ref(&self) -> &str {
        match self {
            Self::North => "n",
            Self::NorthEast => "ne",
            Self::East => "e",
            Self::SouthEast => "se",
            Self::South => "s",
            Self::SouthWest => "sw",
            Self::West => "w",
            Self::NorthWest => "nw",
            Self::Centre => "c",
            Self::Auto => "_",
        }
    }
}