summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorseancarroll <seanc28@gmail.com>2020-12-22 22:08:58 -0600
committerseancarroll <seanc28@gmail.com>2020-12-22 22:08:58 -0600
commitab7b8cd1e9c76258a3f5e565bcb83df00835fc1c (patch)
treee473152dec7d2250523d7bbcad0e402e433d1ca4
parentbb4d9a4ba6e698c5b882b9d9ef863d05cdb835c4 (diff)
downloaddotavious-ab7b8cd1e9c76258a3f5e565bcb83df00835fc1c.zip
trying some things to reduce some duplication when building node/edge statements and nodes/edges
-rw-r--r--src/lib.rs1463
1 files changed, 1218 insertions, 245 deletions
diff --git a/src/lib.rs b/src/lib.rs
index 273b577..47a05d0 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -246,7 +246,7 @@ impl<'a> Dot<'a>
where
W: Write,
{
- for comment in &self.graph.comments {
+ if let Some(comment) = &self.graph.comment {
// TODO: split comment into lines of 80 or so characters
writeln!(w, "// {}", comment)?;
}
@@ -257,7 +257,23 @@ impl<'a> Dot<'a>
let edge_op = self.graph.edgeop();
//writeln!(w, "{}{} {} {{", strict, self.graph.as_slice(), id)?;
- writeln!(w, "{}{} {} {{", strict, self.graph.graph_type(), id)?;
+ writeln!(w, "{}{} {} {{", strict, &self.graph.graph_type(), id)?;
+
+ // if !&self.graph.edge_attributes.is_empty() {
+ // write!(w, "{} edge [", INDENT);
+ // let mut iter = self.graph.edge_attributes.iter();
+ // let first = iter.next().unwrap();
+ // write!(w, "{}={}", first.0, first.1.to_dot_string());
+ // for (key, value) in iter {
+ // write!(w, ", ");
+ // write!(w, "{}={}", key, value.to_dot_string());
+ // }
+ // writeln!(w, "];");
+ // }
+
+ if let Some(edge_attributes) = self.graph.edge_attributes {
+ write!(w, "{}", edge_attributes.to_dot_string());
+ }
// TODO: clean this up
@@ -401,7 +417,10 @@ pub struct Graph<'a> {
pub strict: bool,
// Comment added to the first line of the source.
- pub comments: Vec<String>,
+ pub comment: Option<String>,
+
+ // pub edge_attributes: IndexMap<String, AttributeText<'a>>,
+ pub edge_attributes: Option<EdgeAttributeStatement<'a>>,
pub attributes: IndexMap<AttributeType, IndexMap<String, AttributeText<'a>>>,
@@ -416,7 +435,9 @@ impl<'a> Graph<'a> {
id: Option<String>,
is_directed: bool,
strict: bool,
- comments: Vec<String>,
+ comment: Option<String>,
+ // edge_attributes: IndexMap<String, AttributeText<'a>>,
+ edge_attributes: Option<EdgeAttributeStatement<'a>>,
attributes: IndexMap<AttributeType, IndexMap<String, AttributeText<'a>>>,
nodes: Vec<Node<'a>>,
edges: Vec<Edge<'a>>,
@@ -425,7 +446,8 @@ impl<'a> Graph<'a> {
id,
is_directed,
strict,
- comments,
+ comment,
+ edge_attributes,
attributes,
nodes,
edges,
@@ -552,14 +574,19 @@ pub struct GraphBuilder<'a> {
strict: bool,
+ // graph_attributes: IndexMap<String, AttributeText<'a>>,
+ // node_attributes: IndexMap<String, AttributeText<'a>>,
+
+ // edge_attributes: IndexMap<String, AttributeText<'a>>,
+ edge_attributes: Option<EdgeAttributeStatement<'a>>,
+
attributes: IndexMap<AttributeType, IndexMap<String, AttributeText<'a>>>,
nodes: Vec<Node<'a>>,
edges: Vec<Edge<'a>>,
- // Graphviz says it can only be comment
- comments: Vec<String>,
+ comment: Option<String>,
}
// TODO: id should be an escString
@@ -569,10 +596,12 @@ impl<'a> GraphBuilder<'a> {
id: id,
is_directed: true,
strict: false,
+ edge_attributes: None,
+ // edge_attributes: IndexMap::new(),
attributes: IndexMap::new(),
nodes: Vec::new(),
edges: Vec::new(),
- comments: Vec::new(),
+ comment: None,
}
}
@@ -581,10 +610,12 @@ impl<'a> GraphBuilder<'a> {
id: id,
is_directed: false,
strict: false,
+ // edge_attributes: IndexMap::new(),
+ edge_attributes: None,
attributes: IndexMap::new(),
nodes: Vec::new(),
edges: Vec::new(),
- comments: Vec::new(),
+ comment: None,
}
}
@@ -598,17 +629,38 @@ impl<'a> GraphBuilder<'a> {
self
}
- pub fn add_attribute(&mut self, attribute_type: AttributeType, key: String, value: AttributeText<'a>) -> &mut Self {
- self.attributes.entry(attribute_type).or_insert(IndexMap::new())
- .insert(key, value);
+
+ // pub fn add_edge_attributes(&mut self, edge_attributes: IndexMap<String, AttributeText<'a>>) -> &mut Self {
+ // self.edge_attributes = edge_attributes;
+ // self
+ // }
+ pub fn add_edge_attributes(&mut self, edge_attributes: EdgeAttributeStatement<'a>) -> &mut Self {
+ self.edge_attributes = Some(edge_attributes);
+ self
+ }
+
+ pub fn add_attribute(
+ &mut self,
+ attribute_type: AttributeType,
+ key: String, value: AttributeText<'a>
+ ) -> &mut Self {
+ self.get_attributes(attribute_type).insert(key, value);
self
}
- pub fn add_attributes(&mut self, attribute_type: AttributeType, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
- self.attributes.entry(attribute_type).or_insert(IndexMap::new()).extend(attributes);
+ pub fn add_attributes(
+ &mut self,
+ attribute_type: AttributeType,
+ attributes: HashMap<String, AttributeText<'a>>
+ ) -> &mut Self {
+ self.get_attributes(attribute_type).extend(attributes);
self
}
+ fn get_attributes(&mut self, attribute_type: AttributeType) -> &mut IndexMap<String, AttributeText<'a>> {
+ self.attributes.entry(attribute_type).or_insert(IndexMap::new())
+ }
+
pub fn strict(&mut self) -> &mut Self {
self.strict = true;
self
@@ -639,10 +691,12 @@ impl<'a> GraphBuilder<'a> {
self.add_attribute(AttributeType::Graph, String::from("bb"), AttributeText::quotted(bounding_box))
}
+ /// If true, the drawing is centered in the output canvas.
pub fn center(&mut self, center: bool) -> &mut Self {
self.add_attribute(AttributeType::Graph, String::from("center"), AttributeText::attr(center.to_string()))
}
+ /// Specifies the character encoding used when interpreting string input as a text label.
pub fn charset(&mut self, charset: String) -> &mut Self {
self.add_attribute(AttributeType::Graph, String::from("charset"), AttributeText::quotted(charset))
}
@@ -650,10 +704,18 @@ impl<'a> GraphBuilder<'a> {
/// Classnames to attach to the node, edge, graph, or cluster’s SVG element.
/// Combine with stylesheet for styling SVG output using CSS classnames.
/// Multiple space-separated classes are supported.
- pub fn class(&mut self, class: String) -> &mut Self {
- self.add_attribute(AttributeType::Graph, String::from("class"), AttributeText::quotted(class))
+ pub fn class(&mut self, attribute_type: AttributeType, class: String) -> &mut Self {
+ set_class(self.get_attributes(attribute_type), class);
+ self
}
+ /// Mode used for handling clusters.
+ /// If clusterrank=local, a subgraph whose name begins with cluster is given special treatment.
+ /// The subgraph is laid out separately, and then integrated as a unit into its parent graph,
+ /// with a bounding rectangle drawn about it.
+ /// If the cluster has a label parameter, this label is displayed within the rectangle.
+ /// Note also that there can be clusters within clusters.
+ /// The modes clusterrank=global and clusterrank=none appear to be identical, both turning off the special cluster processing.
pub fn cluster_rank(&mut self, cluster_rank: ClusterMode) -> &mut Self {
self.add_attribute(AttributeType::Graph, String::from("clusterrank"), AttributeText::quotted(cluster_rank.as_slice()))
}
@@ -662,13 +724,14 @@ impl<'a> GraphBuilder<'a> {
/// In particular, if a color value has form "xxx" or "//xxx", then the color xxx will be evaluated
/// according to the current color scheme. If no color scheme is set, the standard X11 naming is used.
/// For example, if colorscheme=bugn9, then color=7 is interpreted as color="/bugn9/7".
- pub fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
- self.add_attribute(AttributeType::Graph, String::from("colorscheme"), AttributeText::quotted(color_scheme))
+ pub fn color_scheme(&mut self, attribute_type: AttributeType, color_scheme: String) -> &mut Self {
+ set_color_scheme(self.get_attributes(attribute_type), color_scheme);
+ self
}
/// Comments are inserted into output. Device-dependent
pub fn comment(&mut self, comment: String) -> &mut Self {
- self.comments.push(comment);
+ self.comment = Some(comment);
self
}
@@ -1088,7 +1151,9 @@ impl<'a> GraphBuilder<'a> {
id: self.id.to_owned(),
is_directed: self.is_directed,
strict: self.strict,
- comments: self.comments.clone(), // TODO: is clone the only option here?
+ comment: self.comment.clone(), // TODO: is clone the only option here?
+ // edge_attributes: self.edge_attributes.clone(),
+ edge_attributes: self.edge_attributes.clone(),
attributes: self.attributes.clone(), // TODO: is clone the only option here?
nodes: self.nodes.clone(), // TODO: is clone the only option here?
edges: self.edges.clone(), // TODO: is clone the only option here?
@@ -1322,20 +1387,9 @@ impl Splines {
pub struct Node<'a> {
pub id: String,
-
pub port: Option<String>,
-
// pub compass: Option<Compass>,
-
- // // TODO: enum?
- // shape: Option<String>,
-
- // label: Option<String>,
-
pub attributes: IndexMap<String, AttributeText<'a>>,
-
- // style
-
}
impl<'a> Node<'a> {
@@ -1404,18 +1458,32 @@ pub struct NodeBuilder<'a> {
id: String,
port: Option<String>,
-
- comment: Option<String>,
attributes: IndexMap<String, AttributeText<'a>>,
}
+impl<'a> NodeAttributes<'a> for NodeBuilder<'a> {
+ fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ self.attributes.insert(key.into(), value);
+ self
+ }
+
+ fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>> {
+ &mut self.attributes
+ }
+
+ /// Add multiple attribures to the edge.
+ fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
+ self.attributes.extend(attributes);
+ self
+ }
+}
+
impl<'a> NodeBuilder<'a> {
pub fn new(id: String) -> Self {
Self {
id: id,
port: None,
- comment: None,
attributes: IndexMap::new(),
}
}
@@ -1431,51 +1499,898 @@ impl<'a> NodeBuilder<'a> {
// self
// }
+ // // TODO: constrain
+ // /// Indicates the preferred area for a node or empty cluster when laid out by patchwork.
+ // /// default: 1.0, minimum: >0
+ // pub fn area(&mut self, area: f32) -> &mut Self {
+ // self.attributes.insert(String::from("area"), AttributeText::attr(area.to_string()));
+ // self
+ // }
+
+ // /// Classnames to attach to the node’s SVG element.
+ // /// Combine with stylesheet for styling SVG output using CSS classnames.
+ // /// Multiple space-separated classes are supported.
+ // pub fn class(&mut self, class: String) -> &mut Self {
+ // set_class(&mut self.attributes, class);
+ // self
+ // }
+
+ // // color / color list
+ // /// Basic drawing color for graphics, not text. For the latter, use the fontcolor attribute.
+ // pub fn color(&mut self, color: String) -> &mut Self {
+ // set_color(&mut self.attributes, color);
+ // self
+ // }
+
+ // /// This attribute specifies a color scheme namespace: the context for interpreting color names.
+ // /// In particular, if a color value has form "xxx" or "//xxx", then the color xxx will be evaluated
+ // /// according to the current color scheme. If no color scheme is set, the standard X11 naming is used.
+ // /// For example, if colorscheme=bugn9, then color=7 is interpreted as color="/bugn9/7".
+ // pub fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
+ // set_color_scheme(&mut self.attributes, color_scheme);
+ // self
+ // }
+
+ // /// Comments are inserted into output. Device-dependent
+ // pub fn comment(&mut self, comment: String) -> &mut Self {
+ // self.comment = Some(comment);
+ // self
+ // }
+
+ // /// Distortion factor for shape=polygon.
+ // /// Positive values cause top part to be larger than bottom; negative values do the opposite.
+ // pub fn distortion(&mut self, distortion: f32) -> &mut Self {
+ // self.add_attribute(String::from("distortion"), AttributeText::attr(distortion.to_string()))
+ // }
+
+ // // color
+ // // color list
+ // /// Color used to fill the background of a node or cluster assuming style=filled, or a filled arrowhead.
+ // pub fn fill_color(&mut self, fill_color: String) -> &mut Self {
+ // self.add_attribute(String::from("fillcolor"), AttributeText::quotted(fill_color))
+ // }
+
+ // /// If true, the node size is specified by the values of the width and height attributes only and
+ // /// is not expanded to contain the text label.
+ // /// There will be a warning if the label (with margin) cannot fit within these limits.
+ // /// If false, the size of a node is determined by smallest width and height needed to contain its label
+ // /// and image, if any, with a margin specified by the margin attribute.
+ // pub fn fixed_size(&mut self, fixed_size: bool) -> &mut Self {
+ // self.add_attribute(String::from("fixedsize"), AttributeText::quotted(fixed_size.to_string()))
+ // }
+
+ // // color
+ // // color list
+ // /// Color used for text.
+ // pub fn font_color(&mut self, font_color: String) -> &mut Self {
+ // self.add_attribute(String::from("fontcolor"), AttributeText::quotted(font_color))
+ // }
+
+ // /// Font used for text.
+ // pub fn font_name(&mut self, font_name: String) -> &mut Self {
+ // self.add_attribute(String::from("fontname"), AttributeText::quotted(font_name))
+ // }
+
+ // /// Font size, in points, used for text.
+ // /// default: 14.0, minimum: 1.0
+ // pub fn font_size(&mut self, font_size: f32) -> &mut Self {
+ // self.add_attribute(String::from("fontsize"), AttributeText::quotted(font_size.to_string()))
+ // }
+
+ // /// If a gradient fill is being used, this determines the angle of the fill.
+ // pub fn gradient_angle(&mut self, gradient_angle: u32) -> &mut Self {
+ // self.add_attribute(String::from("gradientangle"), AttributeText::attr(gradient_angle.to_string()))
+ // }
+
+ // /// If the end points of an edge belong to the same group, i.e., have the same group attribute,
+ // /// parameters are set to avoid crossings and keep the edges straight.
+ // pub fn group(&mut self, group: String) -> &mut Self {
+ // self.add_attribute(String::from("group"), AttributeText::attr(group))
+ // }
+
+ // // TODO: constrain
+ // /// Height of node, in inches.
+ // /// default: 0.5, minimum: 0.02
+ // pub fn height(&mut self, height: f32) -> &mut Self {
+ // self.add_attribute(String::from("height"), AttributeText::attr(height.to_string()))
+ // }
+
+ // // TODO: delete and just use url?
+ // /// Synonym for URL.
+ // pub fn href(&mut self, href: String) -> &mut Self {
+ // self.add_attribute(String::from("href"), AttributeText::escaped(href))
+ // }
+
+ // /// Gives the name of a file containing an image to be displayed inside a node.
+ // /// The image file must be in one of the recognized formats,
+ // /// typically JPEG, PNG, GIF, BMP, SVG, or Postscript, and be able to be converted
+ // /// into the desired output format.
+ // pub fn image(&mut self, image: String) -> &mut Self {
+ // self.add_attribute(String::from("image"), AttributeText::quotted(image))
+ // }
+
+ // /// Controls how an image is positioned within its containing node.
+ // /// Only has an effect when the image is smaller than the containing node.
+ // pub fn image_pos(&mut self, image_pos: ImagePosition) -> &mut Self {
+ // self.add_attribute(String::from("imagepos"), AttributeText::quotted(image_pos.as_slice()))
+ // }
+
+ // /// Controls how an image fills its containing node.
+ // pub fn image_scale_bool(&mut self, image_scale: bool) -> &mut Self {
+ // self.add_attribute(String::from("imagescale"), AttributeText::quotted(image_scale.to_string()))
+ // }
+
+ // /// Controls how an image fills its containing node.
+ // pub fn image_scale(&mut self, image_scale: ImageScale) -> &mut Self {
+ // self.add_attribute(String::from("imagescale"), AttributeText::quotted(image_scale.as_slice()))
+ // }
+
+ // /// Text label attached to objects.
+ // pub fn label<S: Into<Cow<'a, str>>>(&mut self, text: S) -> &mut Self {
+ // self.attributes.insert(String::from("label"), QuottedStr(text.into()));
+ // self
+ // }
+
+ // // Vertical placement of labels for nodes, root graphs and clusters.
+ // // For graphs and clusters, only labelloc=t and labelloc=b are allowed, corresponding to placement at the top and bottom, respectively.
+ // // By default, root graph labels go on the bottom and cluster labels go on the top.
+ // // Note that a subgraph inherits attributes from its parent. Thus, if the root graph sets labelloc=b, the subgraph inherits this value.
+ // // For nodes, this attribute is used only when the height of the node is larger than the height of its label.
+ // // If labelloc=t, labelloc=c, labelloc=b, the label is aligned with the top, centered, or aligned with the bottom of the node, respectively.
+ // // By default, the label is vertically centered.
+ // pub fn label_location(&mut self, label_location: LabelLocation) -> &mut Self {
+ // self.add_attribute(String::from("labelloc"), AttributeText::attr(label_location.as_slice()))
+ // }
+
+ // /// Specifies layers in which the node, edge or cluster is present.
+ // pub fn layer(&mut self, layer: String) -> &mut Self {
+ // self.add_attribute(String::from("layer"), AttributeText::attr(layer))
+ // }
+
+ // // TODO: point
+ // /// For nodes, this attribute specifies space left around the node’s label.
+ // /// If the margin is a single double, both margins are set equal to the given value.
+ // /// Note that the margin is not part of the drawing but just empty space left around the drawing.
+ // /// The margin basically corresponds to a translation of drawing, as would be necessary to center a drawing on a page.
+ // /// Nothing is actually drawn in the margin. To actually extend the background of a drawing, see the pad attribute.
+ // /// By default, the value is 0.11,0.055.
+ // pub fn margin(&mut self, margin: f32) -> &mut Self {
+ // self.add_attribute(String::from("margin"), AttributeText::attr(margin.to_string()))
+ // }
+
+ // /// By default, the justification of multi-line labels is done within the largest context that makes sense.
+ // /// Thus, in the label of a polygonal node, a left-justified line will align with the left side of the node (shifted by the prescribed margin).
+ // /// In record nodes, left-justified line will line up with the left side of the enclosing column of fields.
+ // /// If nojustify=true, multi-line labels will be justified in the context of itself.
+ // /// For example, if nojustify is set, the first label line is long, and the second is shorter and left-justified,
+ // /// the second will align with the left-most character in the first line, regardless of how large the node might be.
+ // pub fn no_justify(&mut self, no_justify: bool) -> &mut Self {
+ // self.add_attribute(String::from("nojustify"), AttributeText::attr(no_justify.to_string()))
+ // }
+
+ // /// If ordering="out", then the outedges of a node, that is, edges with the node as its tail node, must appear left-to-right in the same order in which they are defined in the input.
+ // /// If ordering="in", then the inedges of a node must appear left-to-right in the same order in which they are defined in the input.
+ // /// If defined as a graph or subgraph attribute, the value is applied to all nodes in the graph or subgraph.
+ // /// Note that the graph attribute takes precedence over the node attribute.
+ // pub fn ordering(&mut self, ordering: Ordering) -> &mut Self {
+ // self.add_attribute(String::from("ordering"), AttributeText::attr(ordering.as_slice()))
+ // }
+
+ // // TODO: constrain to 0 - 360. Docs say min is 360 which should be max right?
+ // /// When used on nodes: Angle, in degrees, to rotate polygon node shapes.
+ // /// For any number of polygon sides, 0 degrees rotation results in a flat base.
+ // /// When used on graphs: If "[lL]*", sets graph orientation to landscape.
+ // /// Used only if rotate is not defined.
+ // /// Default: 0.0 and minimum: 360.0
+ // pub fn orientation(&mut self, orientation: f32) -> &mut Self {
+ // self.add_attribute(String::from("orientation"), AttributeText::attr(orientation.to_string()))
+ // }
+
+ // /// Specifies the width of the pen, in points, used to draw lines and curves,
+ // /// including the boundaries of edges and clusters.
+ // /// default: 1.0, minimum: 0.0
+ // pub fn pen_width(&mut self, pen_width: f32) -> &mut Self {
+ // self.add_attribute(String::from("penwidth"), AttributeText::attr(pen_width.to_string()))
+ // }
+
+ // /// Set number of peripheries used in polygonal shapes and cluster boundaries.
+ // pub fn peripheries(&mut self, pen_width: u32) -> &mut Self {
+ // self.add_attribute(String::from("penwidth"), AttributeText::attr(pen_width.to_string()))
+ // }
+
+ // /// Position of node, or spline control points.
+ // /// the position indicates the center of the node. On output, the coordinates are in points.
+ // pub fn pos(&mut self, pos: Point) -> &mut Self {
+ // self.add_attribute(String::from("pos"), AttributeText::attr(pos.to_formatted_string()))
+ // }
+
+ // // TODO: add post_spline
+
+ // // TODO: add rect type?
+ // // "%f,%f,%f,%f"
+ // /// Rectangles for fields of records, in points.
+ // pub fn rects(&mut self, rects: String) -> &mut Self {
+ // self.add_attribute(String::from("rects"), AttributeText::attr(rects))
+ // }
+
+ // /// If true, force polygon to be regular, i.e., the vertices of the polygon will
+ // /// lie on a circle whose center is the center of the node.
+ // pub fn regular(&mut self, regular: bool) -> &mut Self {
+ // self.add_attribute(String::from("regular"), AttributeText::attr(regular.to_string()))
+ // }
+
+ // /// Gives the number of points used for a circle/ellipse node.
+ // pub fn sample_points(&mut self, sample_points: u32) -> &mut Self {
+ // self.add_attribute(String::from("samplepoints"), AttributeText::attr(sample_points.to_string()))
+ // }
+
+ // /// Sets the shape of a node.
+ // pub fn shape(&mut self, shape: Shape) -> &mut Self {
+ // self.add_attribute(String::from("shape"), AttributeText::attr(shape.as_slice()))
+ // }
+
+ // // TODO: constrain
+ // /// Print guide boxes in PostScript at the beginning of routesplines if showboxes=1, or at the end if showboxes=2.
+ // /// (Debugging, TB mode only!)
+ // /// default: 0, minimum: 0
+ // pub fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
+ // self.add_attribute(String::from("showboxes"), AttributeText::attr(show_boxes.to_string()))
+ // }
+
+ // /// Number of sides when shape=polygon.
+ // pub fn sides(&mut self, sides: u32) -> &mut Self {
+ // self.add_attribute(String::from("sides"), AttributeText::attr(sides.to_string()))
+ // }
+
+ // // TODO: constrain
+ // /// Skew factor for shape=polygon.
+ // /// Positive values skew top of polygon to right; negative to left.
+ // /// default: 0.0, minimum: -100.0
+ // pub fn skew(&mut self, skew: f32) -> &mut Self {
+ // self.add_attribute(String::from("skew"), AttributeText::attr(skew.to_string()))
+ // }
+
+ // /// If packmode indicates an array packing, sortv specifies an insertion order
+ // /// among the components, with smaller values inserted first.
+ // /// default: 0, minimum: 0
+ // pub fn sortv(&mut self, sortv: u32) -> &mut Self {
+ // self.add_attribute(String::from("sortv"), AttributeText::attr(sortv.to_string()))
+ // }
+
+ // /// Set style information for components of the graph.
+ // pub fn style(&mut self, style: String) -> &mut Self {
+ // self.add_attribute(String::from("style"), AttributeText::attr(style))
+ // }
+
+ // /// If the object has a URL, this attribute determines which window of the browser is used for the URL.
+ // pub fn target(&mut self, target: String) -> &mut Self {
+ // self.add_attribute(String::from("target"), AttributeText::escaped(target))
+ // }
+
+ // /// Tooltip annotation attached to the node or edge.
+ // /// If unset, Graphviz will use the object’s label if defined.
+ // /// Note that if the label is a record specification or an HTML-like label,
+ // /// the resulting tooltip may be unhelpful.
+ // /// In this case, if tooltips will be generated, the user should set a tooltip attribute explicitly.
+ // pub fn tooltip(&mut self, tooltip: String) -> &mut Self {
+ // self.add_attribute(String::from("tooltip"), AttributeText::escaped(tooltip))
+ // }
+
+ // /// Hyperlinks incorporated into device-dependent output.
+ // pub fn url(&mut self, url: String) -> &mut Self {
+ // self.add_attribute(String::from("url"), AttributeText::escaped(url))
+ // }
+
+ // /// Sets the coordinates of the vertices of the node’s polygon, in inches.
+ // /// A list of points, separated by spaces.
+ // pub fn vertices(&mut self, vertices: String) -> &mut Self {
+ // self.add_attribute(String::from("vertices"), AttributeText::quotted(vertices))
+ // }
+
+ // /// Width of node, in inches.
+ // /// This is taken as the initial, minimum width of the node.
+ // /// If fixedsize is true, this will be the final width of the node.
+ // /// Otherwise, if the node label requires more width to fit, the node’s
+ // /// width will be increased to contain the label.
+ // pub fn width(&mut self, width: f32) -> &mut Self {
+ // self.add_attribute(String::from("width"), AttributeText::attr(width.to_string()))
+ // }
+
+ // /// External label for a node or edge.
+ // /// The label will be placed outside of the node but near it.
+ // /// These labels are added after all nodes and edges have been placed.
+ // /// The labels will be placed so that they do not overlap any node or label.
+ // /// This means it may not be possible to place all of them.
+ // /// To force placing all of them, set forcelabels=true.
+ // pub fn xlabel(&mut self, width: String) -> &mut Self {
+ // self.add_attribute(String::from("xlabel"), AttributeText::escaped(width))
+ // }
+
+ // /// Position of an exterior label, in points.
+ // /// The position indicates the center of the label.
+ // pub fn xlp(&mut self, xlp: Point) -> &mut Self {
+ // self.add_attribute(String::from("xlp"), AttributeText::escaped(xlp.to_formatted_string()))
+ // }
+
+ // /// Add an attribute to the node.
+ // pub fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ // self.attributes.insert(key.into(), value);
+ // self
+ // }
+
+ // /// Add multiple attribures to the node.
+ // pub fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
+ // self.attributes.extend(attributes);
+ // self
+ // }
+
+ pub fn build(&self) -> Node<'a> {
+ Node {
+ // TODO: are these to_owned and clones necessary?
+ id: self.id.to_owned(),
+ port: self.port.to_owned(),
+ attributes: self.attributes.clone()
+ }
+ }
+}
+
+pub enum ImagePosition {
+ TopLeft,
+ TopCentered,
+ TopRight,
+ MiddleLeft,
+ MiddleCentered,
+ MiddleRight,
+ BottomLeft,
+ BottomCentered,
+ BottomRight,
+}
+
+impl ImagePosition {
+ pub fn as_slice(self) -> &'static str {
+ match self {
+ ImagePosition::TopLeft => "tl",
+ ImagePosition::TopCentered => "tc",
+ ImagePosition::TopRight => "tr",
+ ImagePosition::MiddleLeft => "ml",
+ ImagePosition::MiddleCentered => "mc",
+ ImagePosition::MiddleRight => "mr",
+ ImagePosition::BottomLeft => "bl",
+ ImagePosition::BottomCentered => "bc",
+ ImagePosition::BottomRight => "br",
+ }
+ }
+}
+
+pub enum ImageScale {
+ Width,
+ Height,
+ Both,
+}
+
+impl ImageScale {
+ pub fn as_slice(self) -> &'static str {
+ match self {
+ ImageScale::Width => "width",
+ ImageScale::Height => "height",
+ ImageScale::Both => "both",
+ }
+ }
+}
+
+
+#[derive(Clone, Debug)]
+pub struct Edge<'a> {
+
+ pub source: String,
+
+ pub target: String,
+
+ pub attributes: IndexMap<String, AttributeText<'a>>,
+}
+
+impl<'a> Edge<'a> {
+
+ pub fn new(source: String, target: String) -> Edge<'a> {
+ Edge {
+ source,
+ target,
+ attributes: IndexMap::new(),
+ }
+ }
+}
+
+pub struct EdgeBuilder<'a> {
+ pub source: String,
+
+ pub target: String,
+
+ attributes: IndexMap<String, AttributeText<'a>>,
+}
+
+impl<'a> EdgeAttributes<'a> for EdgeBuilder<'a> {
+ fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ self.attributes.insert(key.into(), value);
+ self
+ }
+
+ fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>> {
+ &mut self.attributes
+ }
+
+ // /// Add multiple attribures to the edge.
+ // fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
+ // self.attributes.extend(attributes);
+ // self
+ // }
+}
+
+impl<'a> EdgeBuilder<'a> {
+ pub fn new(source: String, target: String) -> EdgeBuilder<'a> {
+ EdgeBuilder {
+ source,
+ target,
+ attributes: IndexMap::new(),
+ }
+ }
+
+ // /// Style of arrowhead on the head node of an edge.
+ // /// This will only appear if the dir attribute is forward or both.
+ // pub fn arrow_head(&mut self, arrowhead: ArrowType) -> &mut Self {
+ // self.add_attribute(String::from("arrowhead"), AttributeText::attr(arrowhead.as_slice()))
+ // }
+
+ // // TODO: constrain
+ // /// Multiplicative scale factor for arrowheads.
+ // /// default: 1.0, minimum: 0.0
+ // pub fn arrow_size(&mut self, arrow_size: f32) -> &mut Self {
+ // self.add_attribute(String::from("arrowsize"), AttributeText::attr(arrow_size.to_string()))
+ // }
+
+ // /// Style of arrowhead on the tail node of an edge.
+ // /// This will only appear if the dir attribute is back or both.
+ // pub fn arrowtail(&mut self, arrowtail: ArrowType) -> &mut Self {
+ // self.add_attribute(String::from("arrowtail"), AttributeText::attr(arrowtail.as_slice()))
+ // }
+
+ // /// Classnames to attach to the edge’s SVG element.
+ // /// Combine with stylesheet for styling SVG output using CSS classnames.
+ // /// Multiple space-separated classes are supported.
+ // pub fn class(&mut self, class: String) -> &mut Self {
+ // set_class(&mut self.attributes, class);
+ // self
+ // }
+
+ // // color / color list
+ // /// Basic drawing color for graphics, not text. For the latter, use the fontcolor attribute.
+ // pub fn color(&mut self, color: String) -> &mut Self {
+ // set_color(&mut self.attributes, color);
+ // self
+ // }
+
+ // /// This attribute specifies a color scheme namespace: the context for interpreting color names.
+ // /// In particular, if a color value has form "xxx" or "//xxx", then the color xxx will be evaluated
+ // /// according to the current color scheme. If no color scheme is set, the standard X11 naming is used.
+ // /// For example, if colorscheme=bugn9, then color=7 is interpreted as color="/bugn9/7".
+ // pub fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
+ // set_color_scheme(&mut self.attributes, color_scheme);
+ // self
+ // }
+
+ // /// Comments are inserted into output. Device-dependent
+ // pub fn comment(&mut self, comment: String) -> &mut Self {
+ // self.comment = Some(comment);
+ // self
+ // }
+
+ // /// If false, the edge is not used in ranking the nodes.
+ // pub fn constriant(&mut self, constriant: bool) -> &mut Self {
+ // self.add_attribute(String::from("constriant"), AttributeText::attr(constriant.to_string()))
+ // }
+
+ // /// If true, attach edge label to edge by a 2-segment polyline, underlining the label,
+ // /// then going to the closest point of spline.
+ // pub fn decorate(&mut self, decorate: bool) -> &mut Self {
+ // self.add_attribute(String::from("decorate"), AttributeText::attr(decorate.to_string()))
+ // }
+
+ // /// Edge type for drawing arrowheads.
+ // /// Indicates which ends of the edge should be decorated with an arrowhead.
+ // /// The actual style of the arrowhead can be specified using the arrowhead and arrowtail attributes.
+ // pub fn dir(&mut self, dir: Direction) -> &mut Self {
+ // self.add_attribute(String::from("dir"), AttributeText::attr(dir.as_slice()))
+ // }
+
+ // /// If the edge has a URL or edgeURL attribute, edgetarget determines which window
+ // /// of the browser is used for the URL attached to the non-label part of the edge.
+ // /// Setting edgetarget=_graphviz will open a new window if it doesn’t already exist, or reuse it if it does.
+ // pub fn edge_target(&mut self, edge_target: String) -> &mut Self {
+ // self.add_attribute(String::from("edgetarget"), AttributeText::escaped(edge_target))
+ // }
+
+ // /// Tooltip annotation attached to the non-label part of an edge.
+ // /// Used only if the edge has a URL or edgeURL attribute.
+ // pub fn edge_tooltip(&mut self, edge_tooltip: String) -> &mut Self {
+ // self.add_attribute(String::from("edgetooltip"), AttributeText::escaped(edge_tooltip))
+ // }
+
+ // /// The link for the non-label parts of an edge.
+ // /// edgeURL overrides any URL defined for the edge.
+ // /// Also, edgeURL is used near the head or tail node unless overridden by headURL or tailURL, respectively.
+ // pub fn edge_url(&mut self, edge_url: String) -> &mut Self {
+ // self.add_attribute(String::from("edgeurl"), AttributeText::escaped(edge_url))
+ // }
+
+ // // color
+ // // color list
+ // /// Color used to fill the background of a node or cluster assuming style=filled, or a filled arrowhead.
+ // pub fn fill_color(&mut self, fill_color: String) -> &mut Self {
+ // self.add_attribute(String::from("fillcolor"), AttributeText::quotted(fill_color))
+ // }
+
+ // // color
+ // // color list
+ // /// Color used for text.
+ // pub fn font_color(&mut self, font_color: String) -> &mut Self {
+ // self.add_attribute(String::from("fontcolor"), AttributeText::quotted(font_color))
+ // }
+
+ // /// Font used for text.
+ // pub fn font_name(&mut self, font_name: String) -> &mut Self {
+ // self.add_attribute(String::from("fontname"), AttributeText::quotted(font_name))
+ // }
+
+ // /// Font size, in points, used for text.
+ // /// default: 14.0, minimum: 1.0
+ // pub fn font_size(&mut self, font_size: f32) -> &mut Self {
+ // self.add_attribute(String::from("fontsize"), AttributeText::quotted(font_size.to_string()))
+ // }
+
+ // /// Position of an edge’s head label, in points. The position indicates the center of the label.
+ // pub fn head_lp(&mut self, head_lp: Point) -> &mut Self {
+ // self.add_attribute(String::from("head_lp"), AttributeText::quotted(head_lp.to_formatted_string()))
+ // }
+
+ // /// If true, the head of an edge is clipped to the boundary of the head node;
+ // /// otherwise, the end of the edge goes to the center of the node, or the center
+ // /// of a port, if applicable.
+ // pub fn head_clip(&mut self, head_clip: bool) -> &mut Self {
+ // self.add_attribute(String::from("headclip"), AttributeText::quotted(head_clip.to_string()))
+ // }
+
+ // /// Text label to be placed near head of edge.
+ // pub fn head_label(&mut self, head_label: String) -> &mut Self {
+ // self.add_attribute(String::from("headlabel"), AttributeText::quotted(head_label))
+ // }
+
+ // // TODO: portPos struct?
+ // /// Indicates where on the head node to attach the head of the edge.
+ // /// In the default case, the edge is aimed towards the center of the node,
+ // /// and then clipped at the node boundary.
+ // pub fn head_port(&mut self, head_port: String) -> &mut Self {
+ // self.add_attribute(String::from("headport"), AttributeText::quotted(head_port))
+ // }
+
+ // /// If the edge has a headURL, headtarget determines which window of the browser is used for the URL.
+ // /// Setting headURL=_graphviz will open a new window if the window doesn’t already exist,
+ // /// or reuse the window if it does.
+ // /// If undefined, the value of the target is used.
+ // pub fn head_target(&mut self, head_target: String) -> &mut Self {
+ // self.add_attribute(String::from("headtarget"), AttributeText::escaped(head_target))
+ // }
+
+ // /// Tooltip annotation attached to the head of an edge.
+ // /// Used only if the edge has a headURL attribute.
+ // pub fn head_tooltip(&mut self, head_tooltip: String) -> &mut Self {
+ // self.add_attribute(String::from("headtooltip"), AttributeText::escaped(head_tooltip))
+ // }
+
+ // /// If defined, headURL is output as part of the head label of the edge.
+ // /// Also, this value is used near the head node, overriding any URL value.
+ // pub fn head_url(&mut self, head_url: String) -> &mut Self {
+ // self.add_attribute(String::from("headURL"), AttributeText::escaped(head_url))
+ // }
+
+ // /// An escString or an HTML label.
+ // pub fn label(&mut self, label: String) -> &mut Self {
+ // self.add_attribute(String::from("label"), AttributeText::quotted(label))
+ // }
+
+ // // TODO: constrain
+ // /// Determines, along with labeldistance, where the headlabel / taillabel are
+ // /// placed with respect to the head / tail in polar coordinates.
+ // /// The origin in the coordinate system is the point where the edge touches the node.
+ // /// The ray of 0 degrees goes from the origin back along the edge, parallel to the edge at the origin.
+ // /// The angle, in degrees, specifies the rotation from the 0 degree ray,
+ // /// with positive angles moving counterclockwise and negative angles moving clockwise.
+ // /// default: -25.0, minimum: -180.0
+ // pub fn label_angle(&mut self, label_angle: f32) -> &mut Self {
+ // self.add_attribute(String::from("labelangle"), AttributeText::attr(label_angle.to_string()))
+ // }
+
+ // /// Multiplicative scaling factor adjusting the distance that the headlabel / taillabel is from the head / tail node.
+ // /// default: 1.0, minimum: 0.0
+ // pub fn label_distance(&mut self, label_distance: f32) -> &mut Self {
+ // self.add_attribute(String::from("labeldistance"), AttributeText::attr(label_distance.to_string()))
+ // }
+
+ // /// If true, allows edge labels to be less constrained in position.
+ // /// In particular, it may appear on top of other edges.
+ // pub fn label_float(&mut self, label_float: bool) -> &mut Self {
+ // self.add_attribute(String::from("labelfloat"), AttributeText::attr(label_float.to_string()))
+ // }
+
+ // // TODO: color
+ // /// Color used for headlabel and taillabel.
+ // pub fn label_font_color(&mut self, label_font_color: String) -> &mut Self {
+ // self.add_attribute(String::from("labelfontcolor"), AttributeText::quotted(label_font_color))
+ // }
+
+ // /// Font used for headlabel and taillabel.
+ // /// If not set, defaults to edge’s fontname.
+ // pub fn label_font_name(&mut self, label_font_name: String) -> &mut Self {
+ // self.add_attribute(String::from("labelfontname"), AttributeText::attr(label_font_name))
+ // }
+
+ // // TODO: constrains
+ // /// Font size, in points, used for headlabel and taillabel.
+ // /// If not set, defaults to edge’s fontsize.
+ // /// default: 14.0, minimum: 1.0
+ // pub fn label_font_size(&mut self, label_font_size: f32) -> &mut Self {
+ // self.add_attribute(String::from("labelfontsize"), AttributeText::attr(label_font_size.to_string()))
+ // }
+
+ // /// If the edge has a URL or labelURL attribute, this attribute determines
+ // /// which window of the browser is used for the URL attached to the label.
+ // pub fn label_target(&mut self, label_target: String) -> &mut Self {
+ // self.add_attribute(String::from("labeltarget"), AttributeText::escaped(label_target))
+ // }
+
+ // /// Tooltip annotation attached to label of an edge.
+ // /// Used only if the edge has a URL or labelURL attribute.
+ // pub fn label_tooltip(&mut self, label_tooltip: String) -> &mut Self {
+ // self.add_attribute(String::from("labeltooltip"), AttributeText::escaped(label_tooltip))
+ // }
+
+ // /// If defined, labelURL is the link used for the label of an edge.
+ // /// labelURL overrides any URL defined for the edge.
+ // pub fn label_url(&mut self, label_url: String) -> &mut Self {
+ // self.add_attribute(String::from("labelurl"), AttributeText::escaped(label_url))
+ // }
+
+ // // TODO: layer
+ // pub fn layer(&mut self, layer: String) -> &mut Self {
+ // self.add_attribute(String::from("layer"), AttributeText::quotted(layer))
+ // }
+
+ // pub fn lhead(&mut self, lhead: String) -> &mut Self {
+ // self.add_attribute(String::from("lhead"), AttributeText::quotted(lhead))
+ // }
+
+ // /// Logical tail of an edge.
+ // /// When compound=true, if ltail is defined and is the name of a cluster
+ // /// containing the real tail, the edge is clipped to the boundary of the cluster.
+ // pub fn ltail(&mut self, ltail: String) -> &mut Self {
+ // self.add_attribute(String::from("ltail"), AttributeText::quotted(ltail))
+ // }
+
+ // /// Minimum edge length (rank difference between head and tail).
+ // pub fn min_len(&mut self, min_len: u32) -> &mut Self {
+ // self.add_attribute(String::from("minlen"), AttributeText::attr(min_len.to_string()))
+ // }
+
+ // pub fn no_justify(&mut self, no_justify: bool) -> &mut Self {
+ // self.add_attribute(String::from("nojustify"), AttributeText::attr(no_justify.to_string()))
+ // }
+
+ // pub fn pen_width(&mut self, pen_width: f32) -> &mut Self {
+ // self.add_attribute(String::from("penwidth"), AttributeText::attr(pen_width.to_string()))
+ // }
+
+ // /// Position of node, or spline control points.
+ // /// the position indicates the center of the node. On output, the coordinates are in points.
+ // pub fn pos(&mut self, pos: Point) -> &mut Self {
+ // self.add_attribute(String::from("pos"), AttributeText::attr(pos.to_formatted_string()))
+ // }
+
+ // /// Edges with the same head and the same samehead value are aimed at the same point on the head.
+ // pub fn same_head(&mut self, same_head: String) -> &mut Self {
+ // self.add_attribute(String::from("samehead"), AttributeText::quotted(same_head))
+ // }
+
+ // /// Edges with the same tail and the same sametail value are aimed at the same point on the tail.
+ // pub fn same_tail(&mut self, same_tail: String) -> &mut Self {
+ // self.add_attribute(String::from("sametail"), AttributeText::quotted(same_tail))
+ // }
+
+ // // TODO: constrain
+ // /// Print guide boxes in PostScript at the beginning of routesplines if showboxes=1, or at the end if showboxes=2.
+ // /// (Debugging, TB mode only!)
+ // /// default: 0, minimum: 0
+ // pub fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
+ // self.add_attribute(String::from("showboxes"), AttributeText::attr(show_boxes.to_string()))
+ // }
+
+ // /// Set style information for components of the graph.
+ // pub fn style(&mut self, style: String) -> &mut Self {
+ // self.add_attribute(String::from("style"), AttributeText::attr(style))
+ // }
+
+ // /// Position of an edge’s tail label, in points.
+ // /// The position indicates the center of the label.
+ // pub fn tail_lp(&mut self, tail_lp: Point) -> &mut Self {
+ // self.add_attribute(String::from("tail_lp"), AttributeText::quotted(tail_lp.to_formatted_string()))
+ // }
+
+ // /// If true, the tail of an edge is clipped to the boundary of the tail node; otherwise,
+ // /// the end of the edge goes to the center of the node, or the center of a port, if applicable.
+ // pub fn tail_clip(&mut self, tail_clip: bool) -> &mut Self {
+ // self.add_attribute(String::from("tailclip"), AttributeText::quotted(tail_clip.to_string()))
+ // }
+
+ // /// Text label to be placed near tail of edge.
+ // pub fn tail_label(&mut self, tail_label: String) -> &mut Self {
+ // self.add_attribute(String::from("taillabel"), AttributeText::quotted(tail_label))
+ // }
+
+ // // TODO: portPos struct?
+ // /// Indicates where on the tail node to attach the tail of the edge.
+ // pub fn tail_port(&mut self, tail_port: String) -> &mut Self {
+ // self.add_attribute(String::from("tailport"), AttributeText::quotted(tail_port))
+ // }
+
+ // /// If the edge has a tailURL, tailtarget determines which window of the browser is used for the URL.
+ // pub fn tail_target(&mut self, tail_target: String) -> &mut Self {
+ // self.add_attribute(String::from("tailtarget"), AttributeText::escaped(tail_target))
+ // }
+
+ // /// Tooltip annotation attached to the tail of an edge.
+ // pub fn tail_tooltip(&mut self, tail_tooltip: String) -> &mut Self {
+ // self.add_attribute(String::from("tailtooltip"), AttributeText::escaped(tail_tooltip))
+ // }
+
+ // /// If defined, tailURL is output as part of the tail label of the edge.
+ // /// Also, this value is used near the tail node, overriding any URL value.
+ // pub fn tail_url(&mut self, tail_url: String) -> &mut Self {
+ // self.add_attribute(String::from("tailURL"), AttributeText::escaped(tail_url))
+ // }
+
+ // /// If the object has a URL, this attribute determines which window of the browser is used for the URL.
+ // pub fn target(&mut self, target: String) -> &mut Self {
+ // self.add_attribute(String::from("target"), AttributeText::escaped(target))
+ // }
+
+ // /// Tooltip annotation attached to the node or edge.
+ // /// If unset, Graphviz will use the object’s label if defined.
+ // /// Note that if the label is a record specification or an HTML-like label,
+ // /// the resulting tooltip may be unhelpful.
+ // /// In this case, if tooltips will be generated, the user should set a tooltip attribute explicitly.
+ // pub fn tooltip(&mut self, tooltip: String) -> &mut Self {
+ // self.add_attribute(String::from("tooltip"), AttributeText::escaped(tooltip))
+ // }
+
+ // /// Hyperlinks incorporated into device-dependent output.
+ // pub fn url(&mut self, url: String) -> &mut Self {
+ // self.add_attribute(String::from("url"), AttributeText::escaped(url))
+ // }
+
+ // // TODO: contrain
+ // /// Weight of edge.
+ // /// The heavier the weight, the shorter, straighter and more vertical the edge is.
+ // /// default: 1, minimum: 0
+ // pub fn weight(&mut self, weight: u32) -> &mut Self {
+ // self.add_attribute(String::from("weight"), AttributeText::attr(weight.to_string()))
+ // }
+
+ // /// External label for a node or edge.
+ // /// The label will be placed outside of the node but near it.
+ // /// These labels are added after all nodes and edges have been placed.
+ // /// The labels will be placed so that they do not overlap any node or label.
+ // /// This means it may not be possible to place all of them.
+ // /// To force placing all of them, set forcelabels=true.
+ // pub fn xlabel(&mut self, width: String) -> &mut Self {
+ // self.add_attribute(String::from("xlabel"), AttributeText::escaped(width))
+ // }
+
+ // /// Position of an exterior label, in points.
+ // /// The position indicates the center of the label.
+ // pub fn xlp(&mut self, xlp: Point) -> &mut Self {
+ // self.add_attribute(String::from("xlp"), AttributeText::escaped(xlp.to_formatted_string()))
+ // }
+
+ /// Add an attribute to the edge.
+ pub fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ self.attributes.insert(key.into(), value);
+ self
+ }
+
+ /// Add multiple attribures to the edge.
+ pub fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
+ self.attributes.extend(attributes);
+ self
+ }
+
+ pub fn build(&self) -> Edge<'a> {
+ Edge {
+ // TODO: are these to_owned and clones necessary?
+ source: self.source.to_owned(),
+ target: self.target.to_owned(),
+ attributes: self.attributes.clone()
+ }
+ }
+}
+
+trait AttributeStatement<'a> {
+ fn get_attribute_statement_type(&self) -> &'static str;
+
+ fn get_attributes(&self) -> &IndexMap<String, AttributeText<'a>>;
+
+ fn to_dot_string(&self) -> String {
+ if self.get_attributes().is_empty() {
+ return String::from("");
+ }
+ let mut dot_string = format!("{}{} [", INDENT, self.get_attribute_statement_type());
+ let attributes = &self.get_attributes();
+ let mut iter = attributes.iter();
+ let first = iter.next().unwrap();
+ dot_string.push_str(format!("{}={}", first.0, first.1.to_dot_string()).as_str());
+ for (key, value) in iter {
+ dot_string.push_str(", ");
+ dot_string.push_str(format!("{}={}", key, value.to_dot_string()).as_str());
+ }
+ dot_string.push_str("];\n");
+ dot_string.to_string()
+ }
+}
+
+trait NodeAttributes<'a> {
+
// TODO: constrain
/// Indicates the preferred area for a node or empty cluster when laid out by patchwork.
/// default: 1.0, minimum: >0
- pub fn area(&mut self, area: f32) -> &mut Self {
- self.attributes.insert(String::from("area"), AttributeText::attr(area.to_string()));
- self
+ fn area(&mut self, area: f32) -> &mut Self {
+ self.add_attribute(String::from("area"), AttributeText::attr(area.to_string()))
}
- /// Classnames to attach to the node, edge, graph, or cluster’s SVG element.
+ /// Classnames to attach to the node’s SVG element.
/// Combine with stylesheet for styling SVG output using CSS classnames.
/// Multiple space-separated classes are supported.
- pub fn class(&mut self, class: String) -> &mut Self {
- self.add_attribute(String::from("class"), AttributeText::quotted(class))
+ fn class(&mut self, class: String) -> &mut Self {
+ set_class(self.get_attributes_mut(), class);
+ self
}
// color / color list
/// Basic drawing color for graphics, not text. For the latter, use the fontcolor attribute.
- pub fn color(&mut self, color: String) -> &mut Self {
- self.add_attribute(String::from("color"), AttributeText::quotted(color))
+ fn color(&mut self, color: String) -> &mut Self {
+ set_color(self.get_attributes_mut(), color);
+ self
}
/// This attribute specifies a color scheme namespace: the context for interpreting color names.
/// In particular, if a color value has form "xxx" or "//xxx", then the color xxx will be evaluated
/// according to the current color scheme. If no color scheme is set, the standard X11 naming is used.
/// For example, if colorscheme=bugn9, then color=7 is interpreted as color="/bugn9/7".
- pub fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
- self.add_attribute(String::from("colorscheme"), AttributeText::quotted(color_scheme))
+ fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
+ set_color_scheme(self.get_attributes_mut(), color_scheme);
+ self
}
/// Comments are inserted into output. Device-dependent
- pub fn comment(&mut self, comment: String) -> &mut Self {
- self.comment = Some(comment);
- self
+ fn comment(&mut self, comment: String) -> &mut Self {
+ self.add_attribute(String::from("comment"), AttributeText::attr(comment))
}
/// Distortion factor for shape=polygon.
/// Positive values cause top part to be larger than bottom; negative values do the opposite.
- pub fn distortion(&mut self, distortion: f32) -> &mut Self {
+ fn distortion(&mut self, distortion: f32) -> &mut Self {
self.add_attribute(String::from("distortion"), AttributeText::attr(distortion.to_string()))
}
// color
// color list
/// Color used to fill the background of a node or cluster assuming style=filled, or a filled arrowhead.
- pub fn fill_color(&mut self, fill_color: String) -> &mut Self {
+ fn fill_color(&mut self, fill_color: String) -> &mut Self {
self.add_attribute(String::from("fillcolor"), AttributeText::quotted(fill_color))
}
@@ -1484,49 +2399,49 @@ impl<'a> NodeBuilder<'a> {
/// There will be a warning if the label (with margin) cannot fit within these limits.
/// If false, the size of a node is determined by smallest width and height needed to contain its label
/// and image, if any, with a margin specified by the margin attribute.
- pub fn fixed_size(&mut self, fixed_size: bool) -> &mut Self {
+ fn fixed_size(&mut self, fixed_size: bool) -> &mut Self {
self.add_attribute(String::from("fixedsize"), AttributeText::quotted(fixed_size.to_string()))
}
// color
// color list
/// Color used for text.
- pub fn font_color(&mut self, font_color: String) -> &mut Self {
+ fn font_color(&mut self, font_color: String) -> &mut Self {
self.add_attribute(String::from("fontcolor"), AttributeText::quotted(font_color))
}
/// Font used for text.
- pub fn font_name(&mut self, font_name: String) -> &mut Self {
+ fn font_name(&mut self, font_name: String) -> &mut Self {
self.add_attribute(String::from("fontname"), AttributeText::quotted(font_name))
}
/// Font size, in points, used for text.
/// default: 14.0, minimum: 1.0
- pub fn font_size(&mut self, font_size: f32) -> &mut Self {
+ fn font_size(&mut self, font_size: f32) -> &mut Self {
self.add_attribute(String::from("fontsize"), AttributeText::quotted(font_size.to_string()))
}
/// If a gradient fill is being used, this determines the angle of the fill.
- pub fn gradient_angle(&mut self, gradient_angle: u32) -> &mut Self {
+ fn gradient_angle(&mut self, gradient_angle: u32) -> &mut Self {
self.add_attribute(String::from("gradientangle"), AttributeText::attr(gradient_angle.to_string()))
}
/// If the end points of an edge belong to the same group, i.e., have the same group attribute,
/// parameters are set to avoid crossings and keep the edges straight.
- pub fn group(&mut self, group: String) -> &mut Self {
+ fn group(&mut self, group: String) -> &mut Self {
self.add_attribute(String::from("group"), AttributeText::attr(group))
}
// TODO: constrain
/// Height of node, in inches.
/// default: 0.5, minimum: 0.02
- pub fn height(&mut self, height: f32) -> &mut Self {
+ fn height(&mut self, height: f32) -> &mut Self {
self.add_attribute(String::from("height"), AttributeText::attr(height.to_string()))
}
// TODO: delete and just use url?
/// Synonym for URL.
- pub fn href(&mut self, href: String) -> &mut Self {
+ fn href(&mut self, href: String) -> &mut Self {
self.add_attribute(String::from("href"), AttributeText::escaped(href))
}
@@ -1534,30 +2449,29 @@ impl<'a> NodeBuilder<'a> {
/// The image file must be in one of the recognized formats,
/// typically JPEG, PNG, GIF, BMP, SVG, or Postscript, and be able to be converted
/// into the desired output format.
- pub fn image(&mut self, image: String) -> &mut Self {
+ fn image(&mut self, image: String) -> &mut Self {
self.add_attribute(String::from("image"), AttributeText::quotted(image))
}
/// Controls how an image is positioned within its containing node.
/// Only has an effect when the image is smaller than the containing node.
- pub fn image_pos(&mut self, image_pos: ImagePosition) -> &mut Self {
+ fn image_pos(&mut self, image_pos: ImagePosition) -> &mut Self {
self.add_attribute(String::from("imagepos"), AttributeText::quotted(image_pos.as_slice()))
}
/// Controls how an image fills its containing node.
- pub fn image_scale_bool(&mut self, image_scale: bool) -> &mut Self {
+ fn image_scale_bool(&mut self, image_scale: bool) -> &mut Self {
self.add_attribute(String::from("imagescale"), AttributeText::quotted(image_scale.to_string()))
}
/// Controls how an image fills its containing node.
- pub fn image_scale(&mut self, image_scale: ImageScale) -> &mut Self {
+ fn image_scale(&mut self, image_scale: ImageScale) -> &mut Self {
self.add_attribute(String::from("imagescale"), AttributeText::quotted(image_scale.as_slice()))
}
/// Text label attached to objects.
- pub fn label<S: Into<Cow<'a, str>>>(&mut self, text: S) -> &mut Self {
- self.attributes.insert(String::from("label"), QuottedStr(text.into()));
- self
+ fn label<S: Into<Cow<'a, str>>>(&mut self, text: S) -> &mut Self {
+ self.add_attribute(String::from("label"), AttributeText::quotted(text))
}
// Vertical placement of labels for nodes, root graphs and clusters.
@@ -1567,12 +2481,12 @@ impl<'a> NodeBuilder<'a> {
// For nodes, this attribute is used only when the height of the node is larger than the height of its label.
// If labelloc=t, labelloc=c, labelloc=b, the label is aligned with the top, centered, or aligned with the bottom of the node, respectively.
// By default, the label is vertically centered.
- pub fn label_location(&mut self, label_location: LabelLocation) -> &mut Self {
+ fn label_location(&mut self, label_location: LabelLocation) -> &mut Self {
self.add_attribute(String::from("labelloc"), AttributeText::attr(label_location.as_slice()))
}
/// Specifies layers in which the node, edge or cluster is present.
- pub fn layer(&mut self, layer: String) -> &mut Self {
+ fn layer(&mut self, layer: String) -> &mut Self {
self.add_attribute(String::from("layer"), AttributeText::attr(layer))
}
@@ -1583,7 +2497,7 @@ impl<'a> NodeBuilder<'a> {
/// The margin basically corresponds to a translation of drawing, as would be necessary to center a drawing on a page.
/// Nothing is actually drawn in the margin. To actually extend the background of a drawing, see the pad attribute.
/// By default, the value is 0.11,0.055.
- pub fn margin(&mut self, margin: f32) -> &mut Self {
+ fn margin(&mut self, margin: f32) -> &mut Self {
self.add_attribute(String::from("margin"), AttributeText::attr(margin.to_string()))
}
@@ -1593,7 +2507,7 @@ impl<'a> NodeBuilder<'a> {
/// If nojustify=true, multi-line labels will be justified in the context of itself.
/// For example, if nojustify is set, the first label line is long, and the second is shorter and left-justified,
/// the second will align with the left-most character in the first line, regardless of how large the node might be.
- pub fn no_justify(&mut self, no_justify: bool) -> &mut Self {
+ fn no_justify(&mut self, no_justify: bool) -> &mut Self {
self.add_attribute(String::from("nojustify"), AttributeText::attr(no_justify.to_string()))
}
@@ -1601,7 +2515,7 @@ impl<'a> NodeBuilder<'a> {
/// If ordering="in", then the inedges of a node must appear left-to-right in the same order in which they are defined in the input.
/// If defined as a graph or subgraph attribute, the value is applied to all nodes in the graph or subgraph.
/// Note that the graph attribute takes precedence over the node attribute.
- pub fn ordering(&mut self, ordering: Ordering) -> &mut Self {
+ fn ordering(&mut self, ordering: Ordering) -> &mut Self {
self.add_attribute(String::from("ordering"), AttributeText::attr(ordering.as_slice()))
}
@@ -1611,25 +2525,25 @@ impl<'a> NodeBuilder<'a> {
/// When used on graphs: If "[lL]*", sets graph orientation to landscape.
/// Used only if rotate is not defined.
/// Default: 0.0 and minimum: 360.0
- pub fn orientation(&mut self, orientation: f32) -> &mut Self {
+ fn orientation(&mut self, orientation: f32) -> &mut Self {
self.add_attribute(String::from("orientation"), AttributeText::attr(orientation.to_string()))
}
/// Specifies the width of the pen, in points, used to draw lines and curves,
/// including the boundaries of edges and clusters.
/// default: 1.0, minimum: 0.0
- pub fn pen_width(&mut self, pen_width: f32) -> &mut Self {
+ fn pen_width(&mut self, pen_width: f32) -> &mut Self {
self.add_attribute(String::from("penwidth"), AttributeText::attr(pen_width.to_string()))
}
/// Set number of peripheries used in polygonal shapes and cluster boundaries.
- pub fn peripheries(&mut self, pen_width: u32) -> &mut Self {
+ fn peripheries(&mut self, pen_width: u32) -> &mut Self {
self.add_attribute(String::from("penwidth"), AttributeText::attr(pen_width.to_string()))
}
/// Position of node, or spline control points.
/// the position indicates the center of the node. On output, the coordinates are in points.
- pub fn pos(&mut self, pos: Point) -> &mut Self {
+ fn pos(&mut self, pos: Point) -> &mut Self {
self.add_attribute(String::from("pos"), AttributeText::attr(pos.to_formatted_string()))
}
@@ -1638,23 +2552,23 @@ impl<'a> NodeBuilder<'a> {
// TODO: add rect type?
// "%f,%f,%f,%f"
/// Rectangles for fields of records, in points.
- pub fn rects(&mut self, rects: String) -> &mut Self {
+ fn rects(&mut self, rects: String) -> &mut Self {
self.add_attribute(String::from("rects"), AttributeText::attr(rects))
}
/// If true, force polygon to be regular, i.e., the vertices of the polygon will
/// lie on a circle whose center is the center of the node.
- pub fn regular(&mut self, regular: bool) -> &mut Self {
+ fn regular(&mut self, regular: bool) -> &mut Self {
self.add_attribute(String::from("regular"), AttributeText::attr(regular.to_string()))
}
/// Gives the number of points used for a circle/ellipse node.
- pub fn sample_points(&mut self, sample_points: u32) -> &mut Self {
+ fn sample_points(&mut self, sample_points: u32) -> &mut Self {
self.add_attribute(String::from("samplepoints"), AttributeText::attr(sample_points.to_string()))
}
/// Sets the shape of a node.
- pub fn shape(&mut self, shape: Shape) -> &mut Self {
+ fn shape(&mut self, shape: Shape) -> &mut Self {
self.add_attribute(String::from("shape"), AttributeText::attr(shape.as_slice()))
}
@@ -1662,12 +2576,12 @@ impl<'a> NodeBuilder<'a> {
/// Print guide boxes in PostScript at the beginning of routesplines if showboxes=1, or at the end if showboxes=2.
/// (Debugging, TB mode only!)
/// default: 0, minimum: 0
- pub fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
+ fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
self.add_attribute(String::from("showboxes"), AttributeText::attr(show_boxes.to_string()))
}
/// Number of sides when shape=polygon.
- pub fn sides(&mut self, sides: u32) -> &mut Self {
+ fn sides(&mut self, sides: u32) -> &mut Self {
self.add_attribute(String::from("sides"), AttributeText::attr(sides.to_string()))
}
@@ -1675,24 +2589,24 @@ impl<'a> NodeBuilder<'a> {
/// Skew factor for shape=polygon.
/// Positive values skew top of polygon to right; negative to left.
/// default: 0.0, minimum: -100.0
- pub fn skew(&mut self, skew: f32) -> &mut Self {
+ fn skew(&mut self, skew: f32) -> &mut Self {
self.add_attribute(String::from("skew"), AttributeText::attr(skew.to_string()))
}
/// If packmode indicates an array packing, sortv specifies an insertion order
/// among the components, with smaller values inserted first.
/// default: 0, minimum: 0
- pub fn sortv(&mut self, sortv: u32) -> &mut Self {
+ fn sortv(&mut self, sortv: u32) -> &mut Self {
self.add_attribute(String::from("sortv"), AttributeText::attr(sortv.to_string()))
}
/// Set style information for components of the graph.
- pub fn style(&mut self, style: String) -> &mut Self {
+ fn style(&mut self, style: String) -> &mut Self {
self.add_attribute(String::from("style"), AttributeText::attr(style))
}
/// If the object has a URL, this attribute determines which window of the browser is used for the URL.
- pub fn target(&mut self, target: String) -> &mut Self {
+ fn target(&mut self, target: String) -> &mut Self {
self.add_attribute(String::from("target"), AttributeText::escaped(target))
}
@@ -1701,18 +2615,18 @@ impl<'a> NodeBuilder<'a> {
/// Note that if the label is a record specification or an HTML-like label,
/// the resulting tooltip may be unhelpful.
/// In this case, if tooltips will be generated, the user should set a tooltip attribute explicitly.
- pub fn tooltip(&mut self, tooltip: String) -> &mut Self {
+ fn tooltip(&mut self, tooltip: String) -> &mut Self {
self.add_attribute(String::from("tooltip"), AttributeText::escaped(tooltip))
}
/// Hyperlinks incorporated into device-dependent output.
- pub fn url(&mut self, url: String) -> &mut Self {
+ fn url(&mut self, url: String) -> &mut Self {
self.add_attribute(String::from("url"), AttributeText::escaped(url))
}
/// Sets the coordinates of the vertices of the node’s polygon, in inches.
/// A list of points, separated by spaces.
- pub fn vertices(&mut self, vertices: String) -> &mut Self {
+ fn vertices(&mut self, vertices: String) -> &mut Self {
self.add_attribute(String::from("vertices"), AttributeText::quotted(vertices))
}
@@ -1721,7 +2635,7 @@ impl<'a> NodeBuilder<'a> {
/// If fixedsize is true, this will be the final width of the node.
/// Otherwise, if the node label requires more width to fit, the node’s
/// width will be increased to contain the label.
- pub fn width(&mut self, width: f32) -> &mut Self {
+ fn width(&mut self, width: f32) -> &mut Self {
self.add_attribute(String::from("width"), AttributeText::attr(width.to_string()))
}
@@ -1731,243 +2645,215 @@ impl<'a> NodeBuilder<'a> {
/// The labels will be placed so that they do not overlap any node or label.
/// This means it may not be possible to place all of them.
/// To force placing all of them, set forcelabels=true.
- pub fn xlabel(&mut self, width: String) -> &mut Self {
+ fn xlabel(&mut self, width: String) -> &mut Self {
self.add_attribute(String::from("xlabel"), AttributeText::escaped(width))
}
/// Position of an exterior label, in points.
/// The position indicates the center of the label.
- pub fn xlp(&mut self, xlp: Point) -> &mut Self {
+ fn xlp(&mut self, xlp: Point) -> &mut Self {
self.add_attribute(String::from("xlp"), AttributeText::escaped(xlp.to_formatted_string()))
}
/// Add an attribute to the node.
- pub fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self;
+
+ /// Add multiple attribures to the node.
+ fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self;
+
+ fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>>;
+
+}
+
+impl<'a> NodeAttributes<'a> for NodeAttributeStatementBuilder<'a> {
+
+ fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>> {
+ &mut self.attributes
+ }
+
+ fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
self.attributes.insert(key.into(), value);
self
}
/// Add multiple attribures to the node.
- pub fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
+ fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
self.attributes.extend(attributes);
self
}
-
- pub fn build(&self) -> Node<'a> {
- Node {
- // TODO: are these to_owned and clones necessary?
- id: self.id.to_owned(),
- port: self.port.to_owned(),
- attributes: self.attributes.clone()
- }
- }
}
-pub enum ImagePosition {
- TopLeft,
- TopCentered,
- TopRight,
- MiddleLeft,
- MiddleCentered,
- MiddleRight,
- BottomLeft,
- BottomCentered,
- BottomRight,
+// I'm not a huge fan of needing this builder but having a hard time getting around &mut without it
+pub struct NodeAttributeStatementBuilder<'a> {
+ pub attributes: IndexMap<String, AttributeText<'a>>,
}
-impl ImagePosition {
- pub fn as_slice(self) -> &'static str {
- match self {
- ImagePosition::TopLeft => "tl",
- ImagePosition::TopCentered => "tc",
- ImagePosition::TopRight => "tr",
- ImagePosition::MiddleLeft => "ml",
- ImagePosition::MiddleCentered => "mc",
- ImagePosition::MiddleRight => "mr",
- ImagePosition::BottomLeft => "bl",
- ImagePosition::BottomCentered => "bc",
- ImagePosition::BottomRight => "br",
+impl<'a> NodeAttributeStatementBuilder<'a> {
+
+ pub fn new() -> Self {
+ Self {
+ attributes: IndexMap::new(),
}
}
-}
-pub enum ImageScale {
- Width,
- Height,
- Both,
-}
-
-impl ImageScale {
- pub fn as_slice(self) -> &'static str {
- match self {
- ImageScale::Width => "width",
- ImageScale::Height => "height",
- ImageScale::Both => "both",
+ pub fn build(&self) -> NodeAttributeStatement<'a> {
+ NodeAttributeStatement {
+ attributes: self.attributes.clone(),
}
}
-}
+}
#[derive(Clone, Debug)]
-pub struct Edge<'a> {
-
- pub source: String,
-
- pub target: String,
-
- pub comment: Option<String>,
-
- pub attributes: HashMap<String, AttributeText<'a>>,
+pub struct NodeAttributeStatement<'a> {
+ pub attributes: IndexMap<String, AttributeText<'a>>,
}
-impl<'a> Edge<'a> {
+impl<'a> NodeAttributeStatement<'a> {
- pub fn new(source: String, target: String) -> Edge<'a> {
- Edge {
- source,
- target,
- comment: None,
- attributes: HashMap::new(),
+ pub fn new() -> Self {
+ Self {
+ attributes: IndexMap::new(),
}
}
-}
-pub struct EdgeBuilder<'a> {
- pub source: String,
-
- pub target: String,
-
- pub comment: Option<String>,
-
- attributes: HashMap<String, AttributeText<'a>>,
+ pub fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ self.attributes.insert(key.into(), value);
+ self
+ }
}
-impl<'a> EdgeBuilder<'a> {
- pub fn new(source: String, target: String) -> EdgeBuilder<'a> {
- EdgeBuilder {
- source,
- target,
- comment: None,
- attributes: HashMap::new(),
- }
- }
+
+trait EdgeAttributes<'a> {
+ fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>>;
/// Style of arrowhead on the head node of an edge.
/// This will only appear if the dir attribute is forward or both.
- pub fn arrow_head(&mut self, arrowhead: ArrowType) -> &mut Self {
+ fn arrow_head(&mut self, arrowhead: ArrowType) -> &mut Self {
self.add_attribute(String::from("arrowhead"), AttributeText::attr(arrowhead.as_slice()))
}
// TODO: constrain
/// Multiplicative scale factor for arrowheads.
/// default: 1.0, minimum: 0.0
- pub fn arrow_size(&mut self, arrow_size: f32) -> &mut Self {
+ fn arrow_size(&mut self, arrow_size: f32) -> &mut Self {
self.add_attribute(String::from("arrowsize"), AttributeText::attr(arrow_size.to_string()))
}
/// Style of arrowhead on the tail node of an edge.
/// This will only appear if the dir attribute is back or both.
- pub fn arrowtail(&mut self, arrowtail: ArrowType) -> &mut Self {
+ fn arrowtail(&mut self, arrowtail: ArrowType) -> &mut Self {
self.add_attribute(String::from("arrowtail"), AttributeText::attr(arrowtail.as_slice()))
}
+ /// Classnames to attach to the edge’s SVG element.
+ /// Combine with stylesheet for styling SVG output using CSS classnames.
+ /// Multiple space-separated classes are supported.
+ fn class(&mut self, class: String) -> &mut Self {
+ // self.add_attribute(String::from("class"), AttributeText::quotted(class));
+ set_class(self.get_attributes_mut(), class);
+ self
+ }
+
// color / color list
/// Basic drawing color for graphics, not text. For the latter, use the fontcolor attribute.
- pub fn color(&mut self, color: String) -> &mut Self {
- self.add_attribute(String::from("color"), AttributeText::quotted(color))
+ fn color(&mut self, color: String) -> &mut Self {
+ set_color(self.get_attributes_mut(), color);
+ self
}
/// This attribute specifies a color scheme namespace: the context for interpreting color names.
/// In particular, if a color value has form "xxx" or "//xxx", then the color xxx will be evaluated
/// according to the current color scheme. If no color scheme is set, the standard X11 naming is used.
/// For example, if colorscheme=bugn9, then color=7 is interpreted as color="/bugn9/7".
- pub fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
- self.add_attribute(String::from("colorscheme"), AttributeText::quotted(color_scheme))
+ fn color_scheme(&mut self, color_scheme: String) -> &mut Self {
+ set_color_scheme(self.get_attributes_mut(), color_scheme);
+ self
}
/// Comments are inserted into output. Device-dependent
- pub fn comment(&mut self, comment: String) -> &mut Self {
- self.comment = Some(comment);
+ fn comment(&mut self, comment: String) -> &mut Self {
+ self.add_attribute(String::from("comment"), AttributeText::attr(comment.to_string()));
self
}
/// If false, the edge is not used in ranking the nodes.
- pub fn constriant(&mut self, constriant: bool) -> &mut Self {
+ fn constriant(&mut self, constriant: bool) -> &mut Self {
self.add_attribute(String::from("constriant"), AttributeText::attr(constriant.to_string()))
}
/// If true, attach edge label to edge by a 2-segment polyline, underlining the label,
/// then going to the closest point of spline.
- pub fn decorate(&mut self, decorate: bool) -> &mut Self {
+ fn decorate(&mut self, decorate: bool) -> &mut Self {
self.add_attribute(String::from("decorate"), AttributeText::attr(decorate.to_string()))
}
/// Edge type for drawing arrowheads.
/// Indicates which ends of the edge should be decorated with an arrowhead.
/// The actual style of the arrowhead can be specified using the arrowhead and arrowtail attributes.
- pub fn dir(&mut self, dir: Direction) -> &mut Self {
+ fn dir(&mut self, dir: Direction) -> &mut Self {
self.add_attribute(String::from("dir"), AttributeText::attr(dir.as_slice()))
}
/// If the edge has a URL or edgeURL attribute, edgetarget determines which window
/// of the browser is used for the URL attached to the non-label part of the edge.
/// Setting edgetarget=_graphviz will open a new window if it doesn’t already exist, or reuse it if it does.
- pub fn edge_target(&mut self, edge_target: String) -> &mut Self {
+ fn edge_target(&mut self, edge_target: String) -> &mut Self {
self.add_attribute(String::from("edgetarget"), AttributeText::escaped(edge_target))
}
/// Tooltip annotation attached to the non-label part of an edge.
/// Used only if the edge has a URL or edgeURL attribute.
- pub fn edge_tooltip(&mut self, edge_tooltip: String) -> &mut Self {
+ fn edge_tooltip(&mut self, edge_tooltip: String) -> &mut Self {
self.add_attribute(String::from("edgetooltip"), AttributeText::escaped(edge_tooltip))
}
/// The link for the non-label parts of an edge.
/// edgeURL overrides any URL defined for the edge.
/// Also, edgeURL is used near the head or tail node unless overridden by headURL or tailURL, respectively.
- pub fn edge_url(&mut self, edge_url: String) -> &mut Self {
+ fn edge_url(&mut self, edge_url: String) -> &mut Self {
self.add_attribute(String::from("edgeurl"), AttributeText::escaped(edge_url))
}
// color
// color list
/// Color used to fill the background of a node or cluster assuming style=filled, or a filled arrowhead.
- pub fn fill_color(&mut self, fill_color: String) -> &mut Self {
+ fn fill_color(&mut self, fill_color: String) -> &mut Self {
self.add_attribute(String::from("fillcolor"), AttributeText::quotted(fill_color))
}
// color
// color list
/// Color used for text.
- pub fn font_color(&mut self, font_color: String) -> &mut Self {
+ fn font_color(&mut self, font_color: String) -> &mut Self {
self.add_attribute(String::from("fontcolor"), AttributeText::quotted(font_color))
}
/// Font used for text.
- pub fn font_name(&mut self, font_name: String) -> &mut Self {
+ fn font_name(&mut self, font_name: String) -> &mut Self {
self.add_attribute(String::from("fontname"), AttributeText::quotted(font_name))
}
/// Font size, in points, used for text.
/// default: 14.0, minimum: 1.0
- pub fn font_size(&mut self, font_size: f32) -> &mut Self {
+ fn font_size(&mut self, font_size: f32) -> &mut Self {
self.add_attribute(String::from("fontsize"), AttributeText::quotted(font_size.to_string()))
}
/// Position of an edge’s head label, in points. The position indicates the center of the label.
- pub fn head_lp(&mut self, head_lp: Point) -> &mut Self {
+ fn head_lp(&mut self, head_lp: Point) -> &mut Self {
self.add_attribute(String::from("head_lp"), AttributeText::quotted(head_lp.to_formatted_string()))
}
/// If true, the head of an edge is clipped to the boundary of the head node;
/// otherwise, the end of the edge goes to the center of the node, or the center
/// of a port, if applicable.
- pub fn head_clip(&mut self, head_clip: bool) -> &mut Self {
+ fn head_clip(&mut self, head_clip: bool) -> &mut Self {
self.add_attribute(String::from("headclip"), AttributeText::quotted(head_clip.to_string()))
}
/// Text label to be placed near head of edge.
- pub fn head_label(&mut self, head_label: String) -> &mut Self {
+ fn head_label(&mut self, head_label: String) -> &mut Self {
self.add_attribute(String::from("headlabel"), AttributeText::quotted(head_label))
}
@@ -1975,7 +2861,7 @@ impl<'a> EdgeBuilder<'a> {
/// Indicates where on the head node to attach the head of the edge.
/// In the default case, the edge is aimed towards the center of the node,
/// and then clipped at the node boundary.
- pub fn head_port(&mut self, head_port: String) -> &mut Self {
+ fn head_port(&mut self, head_port: String) -> &mut Self {
self.add_attribute(String::from("headport"), AttributeText::quotted(head_port))
}
@@ -1983,24 +2869,24 @@ impl<'a> EdgeBuilder<'a> {
/// Setting headURL=_graphviz will open a new window if the window doesn’t already exist,
/// or reuse the window if it does.
/// If undefined, the value of the target is used.
- pub fn head_target(&mut self, head_target: String) -> &mut Self {
+ fn head_target(&mut self, head_target: String) -> &mut Self {
self.add_attribute(String::from("headtarget"), AttributeText::escaped(head_target))
}
/// Tooltip annotation attached to the head of an edge.
/// Used only if the edge has a headURL attribute.
- pub fn head_tooltip(&mut self, head_tooltip: String) -> &mut Self {
+ fn head_tooltip(&mut self, head_tooltip: String) -> &mut Self {
self.add_attribute(String::from("headtooltip"), AttributeText::escaped(head_tooltip))
}
/// If defined, headURL is output as part of the head label of the edge.
/// Also, this value is used near the head node, overriding any URL value.
- pub fn head_url(&mut self, head_url: String) -> &mut Self {
+ fn head_url(&mut self, head_url: String) -> &mut Self {
self.add_attribute(String::from("headURL"), AttributeText::escaped(head_url))
}
/// An escString or an HTML label.
- pub fn label(&mut self, label: String) -> &mut Self {
+ fn label(&mut self, label: String) -> &mut Self {
self.add_attribute(String::from("label"), AttributeText::quotted(label))
}
@@ -2012,31 +2898,31 @@ impl<'a> EdgeBuilder<'a> {
/// The angle, in degrees, specifies the rotation from the 0 degree ray,
/// with positive angles moving counterclockwise and negative angles moving clockwise.
/// default: -25.0, minimum: -180.0
- pub fn label_angle(&mut self, label_angle: f32) -> &mut Self {
+ fn label_angle(&mut self, label_angle: f32) -> &mut Self {
self.add_attribute(String::from("labelangle"), AttributeText::attr(label_angle.to_string()))
}
/// Multiplicative scaling factor adjusting the distance that the headlabel / taillabel is from the head / tail node.
/// default: 1.0, minimum: 0.0
- pub fn label_distance(&mut self, label_distance: f32) -> &mut Self {
+ fn label_distance(&mut self, label_distance: f32) -> &mut Self {
self.add_attribute(String::from("labeldistance"), AttributeText::attr(label_distance.to_string()))
}
/// If true, allows edge labels to be less constrained in position.
/// In particular, it may appear on top of other edges.
- pub fn label_float(&mut self, label_float: bool) -> &mut Self {
+ fn label_float(&mut self, label_float: bool) -> &mut Self {
self.add_attribute(String::from("labelfloat"), AttributeText::attr(label_float.to_string()))
}
// TODO: color
/// Color used for headlabel and taillabel.
- pub fn label_font_color(&mut self, label_font_color: String) -> &mut Self {
+ fn label_font_color(&mut self, label_font_color: String) -> &mut Self {
self.add_attribute(String::from("labelfontcolor"), AttributeText::quotted(label_font_color))
}
/// Font used for headlabel and taillabel.
/// If not set, defaults to edge’s fontname.
- pub fn label_font_name(&mut self, label_font_name: String) -> &mut Self {
+ fn label_font_name(&mut self, label_font_name: String) -> &mut Self {
self.add_attribute(String::from("labelfontname"), AttributeText::attr(label_font_name))
}
@@ -2044,70 +2930,70 @@ impl<'a> EdgeBuilder<'a> {
/// Font size, in points, used for headlabel and taillabel.
/// If not set, defaults to edge’s fontsize.
/// default: 14.0, minimum: 1.0
- pub fn label_font_size(&mut self, label_font_size: f32) -> &mut Self {
+ fn label_font_size(&mut self, label_font_size: f32) -> &mut Self {
self.add_attribute(String::from("labelfontsize"), AttributeText::attr(label_font_size.to_string()))
}
/// If the edge has a URL or labelURL attribute, this attribute determines
/// which window of the browser is used for the URL attached to the label.
- pub fn label_target(&mut self, label_target: String) -> &mut Self {
+ fn label_target(&mut self, label_target: String) -> &mut Self {
self.add_attribute(String::from("labeltarget"), AttributeText::escaped(label_target))
}
/// Tooltip annotation attached to label of an edge.
/// Used only if the edge has a URL or labelURL attribute.
- pub fn label_tooltip(&mut self, label_tooltip: String) -> &mut Self {
+ fn label_tooltip(&mut self, label_tooltip: String) -> &mut Self {
self.add_attribute(String::from("labeltooltip"), AttributeText::escaped(label_tooltip))
}
/// If defined, labelURL is the link used for the label of an edge.
/// labelURL overrides any URL defined for the edge.
- pub fn label_url(&mut self, label_url: String) -> &mut Self {
+ fn label_url(&mut self, label_url: String) -> &mut Self {
self.add_attribute(String::from("labelurl"), AttributeText::escaped(label_url))
}
// TODO: layer
- pub fn layer(&mut self, layer: String) -> &mut Self {
+ fn layer(&mut self, layer: String) -> &mut Self {
self.add_attribute(String::from("layer"), AttributeText::quotted(layer))
}
- pub fn lhead(&mut self, lhead: String) -> &mut Self {
+ fn lhead(&mut self, lhead: String) -> &mut Self {
self.add_attribute(String::from("lhead"), AttributeText::quotted(lhead))
}
/// Logical tail of an edge.
/// When compound=true, if ltail is defined and is the name of a cluster
/// containing the real tail, the edge is clipped to the boundary of the cluster.
- pub fn ltail(&mut self, ltail: String) -> &mut Self {
+ fn ltail(&mut self, ltail: String) -> &mut Self {
self.add_attribute(String::from("ltail"), AttributeText::quotted(ltail))
}
/// Minimum edge length (rank difference between head and tail).
- pub fn min_len(&mut self, min_len: u32) -> &mut Self {
+ fn min_len(&mut self, min_len: u32) -> &mut Self {
self.add_attribute(String::from("minlen"), AttributeText::attr(min_len.to_string()))
}
- pub fn no_justify(&mut self, no_justify: bool) -> &mut Self {
+ fn no_justify(&mut self, no_justify: bool) -> &mut Self {
self.add_attribute(String::from("nojustify"), AttributeText::attr(no_justify.to_string()))
}
- pub fn pen_width(&mut self, pen_width: f32) -> &mut Self {
+ fn pen_width(&mut self, pen_width: f32) -> &mut Self {
self.add_attribute(String::from("penwidth"), AttributeText::attr(pen_width.to_string()))
}
/// Position of node, or spline control points.
/// the position indicates the center of the node. On output, the coordinates are in points.
- pub fn pos(&mut self, pos: Point) -> &mut Self {
+ fn pos(&mut self, pos: Point) -> &mut Self {
self.add_attribute(String::from("pos"), AttributeText::attr(pos.to_formatted_string()))
}
/// Edges with the same head and the same samehead value are aimed at the same point on the head.
- pub fn same_head(&mut self, same_head: String) -> &mut Self {
+ fn same_head(&mut self, same_head: String) -> &mut Self {
self.add_attribute(String::from("samehead"), AttributeText::quotted(same_head))
}
/// Edges with the same tail and the same sametail value are aimed at the same point on the tail.
- pub fn same_tail(&mut self, same_tail: String) -> &mut Self {
+ fn same_tail(&mut self, same_tail: String) -> &mut Self {
self.add_attribute(String::from("sametail"), AttributeText::quotted(same_tail))
}
@@ -2115,56 +3001,56 @@ impl<'a> EdgeBuilder<'a> {
/// Print guide boxes in PostScript at the beginning of routesplines if showboxes=1, or at the end if showboxes=2.
/// (Debugging, TB mode only!)
/// default: 0, minimum: 0
- pub fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
+ fn show_boxes(&mut self, show_boxes: u32) -> &mut Self {
self.add_attribute(String::from("showboxes"), AttributeText::attr(show_boxes.to_string()))
}
/// Set style information for components of the graph.
- pub fn style(&mut self, style: String) -> &mut Self {
+ fn style(&mut self, style: String) -> &mut Self {
self.add_attribute(String::from("style"), AttributeText::attr(style))
}
/// Position of an edge’s tail label, in points.
/// The position indicates the center of the label.
- pub fn tail_lp(&mut self, tail_lp: Point) -> &mut Self {
+ fn tail_lp(&mut self, tail_lp: Point) -> &mut Self {
self.add_attribute(String::from("tail_lp"), AttributeText::quotted(tail_lp.to_formatted_string()))
}
/// If true, the tail of an edge is clipped to the boundary of the tail node; otherwise,
/// the end of the edge goes to the center of the node, or the center of a port, if applicable.
- pub fn tail_clip(&mut self, tail_clip: bool) -> &mut Self {
+ fn tail_clip(&mut self, tail_clip: bool) -> &mut Self {
self.add_attribute(String::from("tailclip"), AttributeText::quotted(tail_clip.to_string()))
}
/// Text label to be placed near tail of edge.
- pub fn tail_label(&mut self, tail_label: String) -> &mut Self {
+ fn tail_label(&mut self, tail_label: String) -> &mut Self {
self.add_attribute(String::from("taillabel"), AttributeText::quotted(tail_label))
}
// TODO: portPos struct?
/// Indicates where on the tail node to attach the tail of the edge.
- pub fn tail_port(&mut self, tail_port: String) -> &mut Self {
+ fn tail_port(&mut self, tail_port: String) -> &mut Self {
self.add_attribute(String::from("tailport"), AttributeText::quotted(tail_port))
}
/// If the edge has a tailURL, tailtarget determines which window of the browser is used for the URL.
- pub fn tail_target(&mut self, tail_target: String) -> &mut Self {
+ fn tail_target(&mut self, tail_target: String) -> &mut Self {
self.add_attribute(String::from("tailtarget"), AttributeText::escaped(tail_target))
}
/// Tooltip annotation attached to the tail of an edge.
- pub fn tail_tooltip(&mut self, tail_tooltip: String) -> &mut Self {
+ fn tail_tooltip(&mut self, tail_tooltip: String) -> &mut Self {
self.add_attribute(String::from("tailtooltip"), AttributeText::escaped(tail_tooltip))
}
/// If defined, tailURL is output as part of the tail label of the edge.
/// Also, this value is used near the tail node, overriding any URL value.
- pub fn tail_url(&mut self, tail_url: String) -> &mut Self {
+ fn tail_url(&mut self, tail_url: String) -> &mut Self {
self.add_attribute(String::from("tailURL"), AttributeText::escaped(tail_url))
}
/// If the object has a URL, this attribute determines which window of the browser is used for the URL.
- pub fn target(&mut self, target: String) -> &mut Self {
+ fn target(&mut self, target: String) -> &mut Self {
self.add_attribute(String::from("target"), AttributeText::escaped(target))
}
@@ -2173,12 +3059,12 @@ impl<'a> EdgeBuilder<'a> {
/// Note that if the label is a record specification or an HTML-like label,
/// the resulting tooltip may be unhelpful.
/// In this case, if tooltips will be generated, the user should set a tooltip attribute explicitly.
- pub fn tooltip(&mut self, tooltip: String) -> &mut Self {
+ fn tooltip(&mut self, tooltip: String) -> &mut Self {
self.add_attribute(String::from("tooltip"), AttributeText::escaped(tooltip))
}
/// Hyperlinks incorporated into device-dependent output.
- pub fn url(&mut self, url: String) -> &mut Self {
+ fn url(&mut self, url: String) -> &mut Self {
self.add_attribute(String::from("url"), AttributeText::escaped(url))
}
@@ -2186,7 +3072,7 @@ impl<'a> EdgeBuilder<'a> {
/// Weight of edge.
/// The heavier the weight, the shorter, straighter and more vertical the edge is.
/// default: 1, minimum: 0
- pub fn weight(&mut self, weight: u32) -> &mut Self {
+ fn weight(&mut self, weight: u32) -> &mut Self {
self.add_attribute(String::from("weight"), AttributeText::attr(weight.to_string()))
}
@@ -2196,39 +3082,97 @@ impl<'a> EdgeBuilder<'a> {
/// The labels will be placed so that they do not overlap any node or label.
/// This means it may not be possible to place all of them.
/// To force placing all of them, set forcelabels=true.
- pub fn xlabel(&mut self, width: String) -> &mut Self {
+ fn xlabel(&mut self, width: String) -> &mut Self {
self.add_attribute(String::from("xlabel"), AttributeText::escaped(width))
}
/// Position of an exterior label, in points.
/// The position indicates the center of the label.
- pub fn xlp(&mut self, xlp: Point) -> &mut Self {
+ fn xlp(&mut self, xlp: Point) -> &mut Self {
self.add_attribute(String::from("xlp"), AttributeText::escaped(xlp.to_formatted_string()))
}
- /// Add an attribute to the edge.
- pub fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+
+ fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self;
+
+ // fn add_attribute<S: Into<String>>(
+ // &self,
+ // key: S,
+ // value: AttributeText<'a>
+ // ) {
+ // self.get_attributes().insert(key.into(), value);
+ // }
+
+ // fn get_attributes(&self) -> IndexMap<String, AttributeText<'a>>;
+
+ // fn get_attributes_mut(&self) -> &mut IndexMap<String, AttributeText<'a>>;
+
+ // fn to_dot_string(&self) -> String;
+}
+
+impl<'a> EdgeAttributes<'a> for EdgeAttributeStatementBuilder<'a> {
+
+ fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
self.attributes.insert(key.into(), value);
self
}
- /// Add multiple attribures to the edge.
- pub fn add_attributes(&'a mut self, attributes: HashMap<String, AttributeText<'a>>) -> &mut Self {
- self.attributes.extend(attributes);
- self
+ fn get_attributes_mut(&mut self) -> &mut IndexMap<String, AttributeText<'a>> {
+ &mut self.attributes
}
+}
- pub fn build(&self) -> Edge<'a> {
- Edge {
- // TODO: are these to_owned and clones necessary?
- source: self.source.to_owned(),
- target: self.target.to_owned(),
- comment: self.comment.clone(),
- attributes: self.attributes.clone()
+impl<'a> AttributeStatement<'a> for EdgeAttributeStatement<'a> {
+
+ fn get_attribute_statement_type(&self) -> &'static str {
+ "edge"
+ }
+
+ fn get_attributes(&self) -> &IndexMap<String, AttributeText<'a>> {
+ &self.attributes
+ }
+
+}
+
+// I'm not a huge fan of needing this builder but having a hard time getting around &mut without it
+pub struct EdgeAttributeStatementBuilder<'a> {
+ pub attributes: IndexMap<String, AttributeText<'a>>,
+}
+
+impl<'a> EdgeAttributeStatementBuilder<'a> {
+
+ pub fn new() -> Self {
+ Self {
+ attributes: IndexMap::new(),
+ }
+ }
+
+ pub fn build(&self) -> EdgeAttributeStatement<'a> {
+ EdgeAttributeStatement {
+ attributes: self.attributes.clone(),
}
}
+
}
+#[derive(Clone, Debug)]
+pub struct EdgeAttributeStatement<'a> {
+ pub attributes: IndexMap<String, AttributeText<'a>>,
+}
+
+impl<'a> EdgeAttributeStatement<'a> {
+
+ pub fn new() -> Self {
+ Self {
+ attributes: IndexMap::new(),
+ }
+ }
+
+ pub fn add_attribute<S: Into<String>>(&mut self, key: S, value: AttributeText<'a>) -> &mut Self {
+ self.attributes.insert(key.into(), value);
+ self
+ }
+}
pub enum Shape {
Box,
@@ -2476,6 +3420,31 @@ impl Style {
}
}
+fn set_class<'a>(attributes: &mut IndexMap<String, AttributeText<'a>>, class: String) {
+ add_attribute(attributes, String::from("class"), AttributeText::quotted(class))
+}
+
+fn set_color<'a>(attributes: &mut IndexMap<String, AttributeText<'a>>, color: String) {
+ add_attribute(attributes, String::from("color"), AttributeText::quotted(color))
+}
+
+fn set_color_scheme<'a>(attributes: &mut IndexMap<String, AttributeText<'a>>, color_scheme: String) {
+ add_attribute(attributes, String::from("colorscheme"), AttributeText::quotted(color_scheme))
+}
+
+fn set_fill_color<'a>(attributes: &mut IndexMap<String, AttributeText<'a>>, fill_color: String) {
+ add_attribute(attributes, String::from("fillcolor"), AttributeText::quotted(fill_color))
+}
+
+fn add_attribute<'a, S: Into<String>>(
+ attributes: &mut IndexMap<String, AttributeText<'a>>,
+ key: S,
+ value: AttributeText<'a>
+) {
+ attributes.insert(key.into(), value);
+}
+
+
// fn test_input<Ty>(g: Graph<Ty>) -> io::Result<String>
// where Ty: GraphType
fn test_input(g: Graph) -> io::Result<String>
@@ -2648,10 +3617,14 @@ fn single_edge_with_style() {
#[test]
fn graph_attributes() {
+
+ let edge_attributes = EdgeAttributeStatementBuilder::new().min_len(1).build();
+
let g = GraphBuilder::new_directed(Some("graph_attributes".to_string()))
+ .add_edge_attributes(edge_attributes)
.add_attribute(AttributeType::None, "ranksep".to_string(), AttributeText::attr("0.5"))
.add_attribute(AttributeType::Graph, "rankdir".to_string(), AttributeText::attr("LR"))
- .add_attribute(AttributeType::Edge, "minlen".to_string(), AttributeText::attr("1"))
+ //.add_attribute(AttributeType::Edge, "minlen".to_string(), AttributeText::attr("1"))
.add_attribute(AttributeType::Node, "style".to_string(), AttributeText::attr("filled"))
.build();
@@ -2660,9 +3633,9 @@ fn graph_attributes() {
assert_eq!(
r.unwrap(),
r#"digraph graph_attributes {
+ edge [minlen=1];
ranksep=0.5;
graph [rankdir=LR];
- edge [minlen=1];
node [style=filled];
}
"#