dot_writer/
attribute.rs

1use super::writer::Statement;
2
3/// Structs that implement [`Attributes`] are used for writing the attributes
4/// of a diagraph, graph, subgraph, subgraph cluster, node or edge.
5///
6/// The raw [`Attributes::set`] function is provided for setting arbitary atrributes,
7/// but it is recommended to use the more typesafe functions where available.
8/// If a typesafe function is missing and you have to use the `set` function, please do file an issue in the github and we can add it.
9pub trait Attributes: Sized {
10    /// Set the name of the font family for label text
11    fn set_font(&mut self, label: &str) -> &mut Self {
12        self.set("fontname", label, true)
13    }
14
15    /// Set arbitary html, useful for constructing more complex nodes
16    fn set_html(&mut self, label: &str) -> &mut Self {
17        self.set("label", label, false)
18    }
19
20    /// Set the display label for a graph, node or edge
21    fn set_label(&mut self, label: &str) -> &mut Self {
22        self.set("label", label, true)
23    }
24
25    /// Set the label to appear at the head of an edge
26    fn set_head_label(&mut self, label: &str) -> &mut Self {
27        self.set("headlabel", label, true)
28    }
29
30    /// Set the label to appear at the tail of an edge
31    fn set_tail_label(&mut self, label: &str) -> &mut Self {
32        self.set("taillabel", label, true)
33    }
34
35    /// Set the edge or line color
36    fn set_color(&mut self, color: Color) -> &mut Self {
37        self.set("color", color.as_str(), false)
38    }
39
40    /// Set the color to fill the area with
41    fn set_fill_color(&mut self, color: Color) -> &mut Self {
42        self.set("fillcolor", color.as_str(), false)
43    }
44
45    /// Set the color of the font used for text
46    fn set_font_color(&mut self, color: Color) -> &mut Self {
47        self.set("fontcolor", color.as_str(), false)
48    }
49
50    /// Set the background color
51    fn set_background_color(&mut self, color: Color) -> &mut Self {
52        self.set("bgcolor", color.as_str(), false)
53    }
54
55    /// Set the shape of a graph, subgraph, cluster or node
56    fn set_shape(&mut self, shape: Shape) -> &mut Self {
57        self.set("shape", shape.as_str(), false)
58    }
59
60    /// Set the style
61    fn set_style(&mut self, style: Style) -> &mut Self {
62        self.set("style", style.as_str(), true)
63    }
64
65    /// Set type of arrow head for edge lines (the arrow at the destination)
66    fn set_arrow_head(&mut self, arrow_type: ArrowType) -> &mut Self {
67        self.set("arrowhead", arrow_type.as_str(), false)
68    }
69
70    /// Set type of arrow tail for edge lines (the arrow at the source)
71    fn set_arrow_tail(&mut self, arrow_type: ArrowType) -> &mut Self {
72        self.set("arrowtail", arrow_type.as_str(), false)
73    }
74
75    /// Set the relative rank, which affects layout
76    fn set_rank(&mut self, rank: Rank) -> &mut Self {
77        self.set("rank", rank.as_str(), false)
78    }
79
80    /// Set the pen width for drawing lines
81    fn set_pen_width(&mut self, width: f32) -> &mut Self {
82        self.set("penwidth", &width.to_string(), false)
83    }
84
85    /// Set the arrow size
86    fn set_arrow_size(&mut self, size: f32) -> &mut Self {
87        self.set("arrowsize", &size.to_string(), false)
88    }
89
90    /// Set the font size
91    fn set_font_size(&mut self, size: f32) -> &mut Self {
92        self.set("fontsize", &size.to_string(), false)
93    }
94
95    // Set direction of graph layout
96    fn set_rank_direction(&mut self, rank_direction: RankDirection) -> &mut Self {
97        self.set("rankdir", rank_direction.as_str(), false)
98    }
99
100    /// Sets an attribute. See the Graphviz [documentation](https://graphviz.org/doc/info/attrs.html)
101    /// for a full list of available names and values.
102    /// Set the arguement `quote` to true if the `value` should be written in quotes `"`, to escape
103    /// any special characters. Note that any quote in the string need to be escaped before calling.
104    /// This function does NOT check that `name` or `value` are valid DOT strings.
105    fn set(&mut self, name: &str, value: &str, quote: bool) -> &mut Self;
106}
107
108/// An [`AttributesList`] sets the attributes of an edge or node.
109/// See the [`Attributes`] trait for more information on what fields can be set.
110pub struct AttributesList<'d, 'w> {
111    statement: Statement<'d, 'w>,
112    at_least_one_set: bool,
113}
114
115impl<'d, 'w> AttributesList<'d, 'w> {
116    pub(crate) fn new(statement: Statement<'d, 'w>) -> Self {
117        Self {
118            statement,
119            at_least_one_set: false,
120        }
121    }
122}
123
124impl<'d, 'w> Attributes for AttributesList<'d, 'w> {
125    fn set(&mut self, name: &str, value: &str, quote: bool) -> &mut Self {
126        if !self.at_least_one_set {
127            self.at_least_one_set = true;
128            self.statement.write(b" [");
129        } else {
130            self.statement.write(b", ");
131        }
132        self.statement.write(name.as_bytes());
133        self.statement.write(b"=");
134        if quote {
135            self.statement.write_quoted(value.as_bytes());
136        } else {
137            self.statement.write(value.as_bytes());
138        }
139        self
140    }
141}
142
143impl<'d, 'w> Drop for AttributesList<'d, 'w> {
144    fn drop(&mut self) {
145        if self.at_least_one_set {
146            self.statement.write(b"]");
147        }
148    }
149}
150
151/// Shape of a component. This list is not comprehensive, the full list
152/// is visible [here](https://graphviz.org/doc/info/shapes.html#polygon)
153/// and can instead be set using [`Attributes::set`].
154#[derive(Debug, Copy, Clone, Eq, PartialEq)]
155pub enum Shape {
156    Circle,
157    Record,
158    Rectangle,
159    None,
160    Mdiamond,
161    Mrecord,
162    Msquare,
163}
164
165impl Shape {
166    fn as_str(&self) -> &str {
167        match self {
168            Self::Circle => "circle",
169            Self::Record => "record",
170            Self::Mrecord => "Mrecord",
171            Self::Mdiamond => "Mdiamond",
172            Self::Rectangle => "rectangle",
173            Self::Msquare => "Msquare",
174            Self::None => "none",
175        }
176    }
177}
178
179/// Color of a line or fill. This list is far from comprehensive, the
180/// full list is visible [here](https://graphviz.org/doc/info/colors.html),
181/// and can instead be set using [`Attributes::set`]
182#[derive(Debug, Copy, Clone, Eq, PartialEq)]
183pub enum Color {
184    Black,
185    Grey,
186    LightGrey,
187    PaleGreen,
188    PaleTurquoise,
189    Red,
190    White,
191    Blue,
192    Gray20,
193}
194
195impl Color {
196    pub fn as_str(&self) -> &str {
197        match self {
198            Self::Black => "black",
199            Self::Grey => "gray",
200            Self::LightGrey => "lightgray",
201            Self::PaleGreen => "palegreen",
202            Self::PaleTurquoise => "paleturquoise",
203            Self::Red => "red",
204            Self::White => "white",
205            Self::Blue => "blue",
206            Self::Gray20 => "gray20",
207        }
208    }
209}
210
211/// Style of design. Note that some of these or only valid for
212/// certain combinations of node, edge and cluster. See documentation
213/// [here](https://graphviz.org/docs/attr-types/style/) for more information.
214#[derive(Debug, Copy, Clone, Eq, PartialEq)]
215pub enum Style {
216    /// Nodes and edges
217    Dashed,
218    /// Nodes and edges
219    Dotted,
220    /// Nodes and edges
221    Solid,
222    /// Nodes and edges
223    Invisible,
224    /// Nodes and edges
225    Bold,
226
227    /// Edges only
228    Tapered,
229
230    /// Nodes only
231    Wedged,
232    /// Only for elliptically-shaped nodes
233    Diagonals,
234
235    /// Node and clusters
236    Filled,
237    /// Node and clusters
238    Striped,
239    /// Node and clusters
240    Rounded,
241
242    /// Any
243    Radial,
244    /// Any
245    Unfilled,
246}
247
248impl Style {
249    fn as_str(&self) -> &str {
250        match self {
251            Self::Dashed => "dashed",
252            Self::Dotted => "dotted",
253            Self::Solid => "solid",
254            Self::Invisible => "invis",
255            Self::Bold => "bold",
256
257            Self::Tapered => "tapered",
258
259            Self::Wedged => "wedged",
260            Self::Diagonals => "diagonals",
261
262            Self::Filled => "filled",
263            Self::Striped => "striped",
264            Self::Rounded => "rounded",
265
266            Self::Radial => "radial",
267            Self::Unfilled => "",
268        }
269    }
270}
271
272/// Node rank type, for more information see
273/// [Graphviz documentation](https://graphviz.org/docs/attr-types/rankType/).
274#[derive(Debug, Copy, Clone, Eq, PartialEq)]
275pub enum Rank {
276    Min,
277    Same,
278    Max,
279    Source,
280    Sink,
281}
282
283impl Rank {
284    fn as_str(&self) -> &str {
285        match self {
286            Self::Source => "source",
287            Self::Min => "min",
288            Self::Same => "same",
289            Self::Max => "max",
290            Self::Sink => "sink",
291        }
292    }
293}
294
295/// Arrow types, used to set either head or tail, for more information see
296/// [Graphviz documentation](https://graphviz.org/docs/attr-types/arrowType/).
297#[derive(Debug, Copy, Clone, Eq, PartialEq)]
298pub enum ArrowType {
299    Normal,
300    Dot,
301    Inv,
302    InvDot,
303    ODit,
304    InvODot,
305    None,
306    Tee,
307    Empty,
308    InvEmpty,
309    Diamond,
310    ODiamond,
311    EDiamond,
312    Crow,
313    Box,
314    OBox,
315    Open,
316    HalfOpen,
317    Vee,
318}
319
320impl ArrowType {
321    fn as_str(&self) -> &str {
322        match self {
323            Self::Normal => "normal",
324            Self::Dot => "dot",
325            Self::Inv => "inv",
326            Self::InvDot => "invdot",
327            Self::ODit => "odot",
328            Self::InvODot => "invdot",
329            Self::None => "none",
330            Self::Tee => "tee",
331            Self::Empty => "empty",
332            Self::InvEmpty => "invempty",
333            Self::Diamond => "diamond",
334            Self::ODiamond => "odiamond",
335            Self::EDiamond => "ediamond",
336            Self::Crow => "crow",
337            Self::Box => "box",
338            Self::OBox => "obox",
339            Self::Open => "open",
340            Self::HalfOpen => "halfopen",
341            Self::Vee => "vee",
342        }
343    }
344}
345
346/// Direction to layout graph, for more information see
347/// [Graphviz documentation](https://graphviz.org/docs/attr-types/rankdir/).
348#[derive(Debug, Copy, Clone, Eq, PartialEq)]
349pub enum RankDirection {
350    TopBottom,
351    BottomTop,
352    LeftRight,
353    RightLeft,
354}
355
356impl RankDirection {
357    fn as_str(&self) -> &str {
358        match self {
359            Self::TopBottom => "TB",
360            Self::BottomTop => "BT",
361            Self::LeftRight => "LR",
362            Self::RightLeft => "RL",
363        }
364    }
365}