use super::attribute::{Attributes, AttributesList};
use super::writer::{DotWriter, Line, Statement};
/// A [`Scope`] struct represents either a graph, digraph, subgraph or cluster.
/// Its the workhorse of the DOT writing, and can be used to create new sub-scopes,
/// add nodes, add edges, or adjust default attributes for any of the above.
///
/// The only way to construct a top level graph or digraph scope is to call the
/// [`DotWriter::graph`] or [`DotWriter::digraph`] functions on a new [`DotWriter`].
pub struct Scope<'d, 'w> {
writer: &'d mut DotWriter<'w>,
}
impl<'d, 'w> Scope<'d, 'w> {
pub(crate) fn new(writer: &'d mut DotWriter<'w>, prefix: &[u8]) -> Scope<'d, 'w> {
{
let mut line = Line::new(writer);
line.write_with_whitespace(prefix);
line.write(b" {");
}
writer.indent();
Self { writer }
}
/// Starts a new nested subgraph, returning another Scope for writing to that subgraph.
pub fn subgraph(&mut self) -> Scope<'_, 'w> {
Scope::new(self.writer, b"subgraph")
}
/// Starts a new nested cluster subgraph, returning another Scope for writing to it.
/// A cluster is a special case of a subgraph which groups its child nodes together.
/// See the "Subgraphs and Clusters" section of the
/// [Graphviz documentation](https://graphviz.org/doc/info/lang.html)
/// for more information.
pub fn cluster(&mut self) -> Scope<'_, 'w> {
let label = format!("subgraph cluster_{}", self.writer.next_id());
Scope::new(self.writer, label.as_bytes())
}
/// Returns a struct for writing the default attributes for all subgraphs from now on.
pub fn graph_attributes(&mut self) -> AttributesList<'_, 'w> {
let mut statement = Statement::new(self.writer);
statement.write(b"graph");
AttributesList::new(statement)
}
/// Returns a struct for writing the default attributes for all edges from now on.
pub fn edge_attributes(&mut self) -> AttributesList<'_, 'w> {
let mut statement = Statement::new(self.writer);
statement.write(b"edge");
AttributesList::new(statement)
}
/// Returns a struct for writing the default attributes for all nodes from now on.
pub fn node_attributes(&mut self) -> AttributesList<'_, 'w> {
let mut statement = Statement::new(self.writer);
statement.write(b"node");
AttributesList::new(statement)
}
/// Creates a new node, with an automatic default id of the format `node_x`
/// where x is an incerementing integer. You don't have to declare nodes before
/// using them in a call to [`Scope::edge`], but you do have to declare them using this
/// function if you want to set specifc attributes for this node (font etc).
///
/// The returned value can be used to get the automatically generated id,
/// and also to set the attributes.
pub fn node_auto(&mut self) -> Node<'_, 'w> {
let id = self.writer.next_id();
self.node_named(format!("node_{}", id))
}
/// Creates a new node, with the specified id. You don't have to declare nodes before
/// using them in a call to [`Scope::edge`], but you do have to declare them using this
/// function if you want to set specific attributes for this node (font etc).
///
/// The returned value can be used to get the assigned name,
/// and also to set the attributes.
pub fn node_named<S: Into<String>>(&mut self, id: S) -> Node<'_, 'w> {
Node::new(Statement::new(self.writer), id.into())
}
/// Add a new edge joining `start_node_id` and `end_node_id` nodes.
/// Note that nodes do not need to be already defined by [`Scope::node_auto`]
/// or by [`Scope::node_named`] (unless you want to set node-specific attributes).
/// Arguments can be just strings, or you can use the [`Node::id`] of an already
/// defined node:
///
/// ```
/// use dot_writer::DotWriter;
///
/// let mut output_bytes = Vec::new();
/// {
/// let mut writer = DotWriter::from(&mut output_bytes);
/// writer.set_pretty_print(false);
/// let mut digraph = writer.digraph();
/// let a = digraph.node_auto().id();
/// digraph.edge(a, "b");
/// }
/// assert_eq!(
/// std::str::from_utf8(&output_bytes).unwrap(),
/// "digraph{node_0;node_0->b;}"
/// );
/// ```
pub fn edge<S, E>(&mut self, start_node_id: S, end_node_id: E) -> EdgeList<'_, 'w>
where
S: AsRef<[u8]>,
E: AsRef<[u8]>,
{
EdgeList::new(Statement::new(self.writer), start_node_id, end_node_id)
}
/// Add N-1 edges joining all node ids or subgraphs in the iterator,
/// in the same manner as [`Scope::edge`].
/// The return value will be None if less than 2 items are passed in.
///
/// ```
/// use dot_writer::DotWriter;
///
/// let mut output_bytes = Vec::new();
/// {
/// let mut writer = DotWriter::from(&mut output_bytes);
/// writer.set_pretty_print(false);
/// let mut digraph = writer.digraph();
/// digraph.edges(["a", "b", "c"]);
/// }
/// assert_eq!(
/// std::str::from_utf8(&output_bytes).unwrap(),
/// "digraph{a->b->c;}"
/// );
/// ```
pub fn edges<I, E>(&mut self, items: I) -> Option<EdgeList<'_, 'w>>
where
I: IntoIterator<Item = E>,
E: AsRef<[u8]>,
{
let mut iter = items.into_iter();
let mut edge_list = self.edge(iter.next()?, iter.next()?);
for item in iter {
edge_list.edge(item);
}
Some(edge_list)
}
}
impl<'d, 'w> Attributes for Scope<'d, 'w> {
fn set(&mut self, name: &str, value: &str, quote: bool) -> &mut Self {
{
let mut statement = Statement::new(self.writer);
statement.write(name.as_bytes());
statement.write(b"=");
if quote {
statement.write_quoted(value.as_bytes());
} else {
statement.write(value.as_bytes());
}
}
self
}
}
impl<'d, 'w> Drop for Scope<'d, 'w> {
fn drop(&mut self) {
self.writer.unindent();
Line::new(self.writer).write(b"}");
}
}
/// An [`EdgeList`] is returned from [`Scope::edge`] and can be used to set the attributes
/// of an edge, or to chain additional nodes onto the edge statement.
pub struct EdgeList<'d, 'w> {
statement: Statement<'d, 'w>,
}
impl<'d, 'w> EdgeList<'d, 'w> {
fn new<S: AsRef<[u8]>, E: AsRef<[u8]>>(
mut statement: Statement<'d, 'w>,
start_node_id: S,
end_node_id: E,
) -> EdgeList<'d, 'w> {
statement.write(start_node_id.as_ref());
statement.write_edge_operator();
statement.write(end_node_id.as_ref());
EdgeList { statement }
}
/// Adds another edge from the last node added to `node_id`.
/// Note that nodes do not need to be already defined by [`Scope::node_auto`]
/// or by [`Scope::node_named`] (unless you want to set node-specifc attributes).
///
/// ```
/// use dot_writer::DotWriter;
///
/// let mut output_bytes = Vec::new();
/// {
/// let mut writer = DotWriter::from(&mut output_bytes);
/// writer.set_pretty_print(false);
/// writer.digraph().edge("a", "b").edge("c").edge("d");
/// }
/// assert_eq!(
/// std::str::from_utf8(&output_bytes).unwrap(),
/// "digraph{a->b->c->d;}"
/// );
///
/// ```
pub fn edge<N: AsRef<[u8]>>(&mut self, node_id: N) -> &mut Self {
self.statement.write_edge_operator();
self.statement.write(node_id.as_ref());
self
}
/// Start writing the attributes section of an edge definition.
/// See the returned structure for what attributes can be set.
/// Note that no more nodes can be added after this function is called.
pub fn attributes(self) -> AttributesList<'d, 'w> {
AttributesList::new(self.statement)
}
}
/// A [`Node`] is returned from [`Scope::node_named`] or [`Scope::node_auto`],
/// and allows for getting the id of the node for future reference via [`Node::id`],
///
/// Importantly it also implements [`Attributes`] for setting attributes
/// to override existing defaults for this specific node.
pub struct Node<'d, 'w> {
attributes: AttributesList<'d, 'w>,
id: String,
}
impl<'d, 'w> Node<'d, 'w> {
fn new(mut statement: Statement<'d, 'w>, id: String) -> Self {
statement.write(id.as_bytes());
Node {
attributes: AttributesList::new(statement),
id,
}
}
/// Returns a copy of the [`Node`] id, for later use with [`Scope::edge`].
/// Note that as the node definition will not finish writing until after
/// this [`Node`] goes out of scope, you'll need to save the id if you
/// want to draw an edge to it later. This function is most usefull when
/// the edge was automatically generated with [`Scope::node_auto`]:
/// ```
/// use dot_writer::DotWriter;
///
/// let mut output_bytes = Vec::new();
/// {
/// let mut writer = DotWriter::from(&mut output_bytes);
/// writer.set_pretty_print(false);
/// let mut digraph = writer.digraph();
/// let a_id = {
/// let mut sub_graph = digraph.subgraph();
/// let a_id = sub_graph.node_auto().id();
/// // sub_graph goes out of scope here to close bracket
/// // but id is kept for later call
/// a_id
/// };
/// digraph.edge(a_id, "b");
/// }
/// assert_eq!(
/// std::str::from_utf8(&output_bytes).unwrap(),
/// "digraph{subgraph{node_0;}node_0->b;}"
/// );
///
/// ```
pub fn id(&self) -> NodeId {
NodeId {
id: self.id.clone(),
}
}
}
impl<'d, 'w> std::ops::DerefMut for Node<'d, 'w> {
fn deref_mut(&mut self) -> &mut AttributesList<'d, 'w> {
&mut self.attributes
}
}
impl<'d, 'w> std::ops::Deref for Node<'d, 'w> {
type Target = AttributesList<'d, 'w>;
fn deref(&self) -> &AttributesList<'d, 'w> {
&self.attributes
}
}
/// A [`NodeId`] wraps a string storing the Id of a Node
/// It's designed for use with the [`Scope::edge`] function,
/// and for creating [`PortId`] using [`NodeId::port`].
#[derive(Clone, Debug)]
pub struct NodeId {
id: String,
}
impl NodeId {
/// Creates a [`PortId`] for refering to a port. These are specific sub parts of
/// a [`Shape::Record`](`crate::attribute::Shape::Record`)
/// or [`Shape::Mrecord`](`crate::attribute::Shape::Mrecord`)
/// (for more information see "Record-based Nodes"
/// on the [Graphviz documentation](https://graphviz.org/doc/info/shapes.html)).
pub fn port(&self, port_id: &str) -> PortId {
PortId {
id: format!("{}:{}", self.id, port_id),
}
}
}
impl From<String> for NodeId {
fn from(id: String) -> Self {
Self { id }
}
}
impl From<NodeId> for String {
fn from(node_id: NodeId) -> Self {
node_id.id
}
}
impl AsRef<[u8]> for NodeId {
fn as_ref(&self) -> &[u8] {
self.id.as_bytes()
}
}
/// A [`PortId`] wraps a string referning to the nnode id and port of a specifc record
/// or Mrecord node (for more information see "Record-based Nodes"
/// in the [Graphviz documentation](https://graphviz.org/doc/info/shapes.html)).
#[derive(Clone, Debug)]
pub struct PortId {
id: String,
}
impl PortId {
/// Creates a [`PortPosId`] for refering to a specific position on a record or Mrecord
/// port for an edge to attach to.
pub fn position(&self, position: PortPosition) -> PortPosId {
PortPosId {
id: format!("{}:{}", self.id, position.as_ref()),
}
}
}
impl From<String> for PortId {
fn from(id: String) -> Self {
Self { id }
}
}
impl From<PortId> for String {
fn from(port_id: PortId) -> Self {
port_id.id
}
}
impl AsRef<[u8]> for PortId {
fn as_ref(&self) -> &[u8] {
self.id.as_bytes()
}
}
/// A [`PortPosId`] wraps a string referning to the [`NodeId`], [`PortId`] and [`PortPosition`]
/// of a specifc record or Mrecord node (for more information see "portPos"
/// in the [Graphviz documentation](https://graphviz.org/docs/attr-types/portPos/)).
#[derive(Clone, Debug)]
pub struct PortPosId {
id: String,
}
impl From<String> for PortPosId {
fn from(id: String) -> Self {
Self { id }
}
}
impl From<PortPosId> for String {
fn from(port_pos_id: PortPosId) -> Self {
port_pos_id.id
}
}
impl AsRef<[u8]> for PortPosId {
fn as_ref(&self) -> &[u8] {
self.id.as_bytes()
}
}
/// Refers the position to add an edge for of a specifc record or Mrecord node
/// (for more information see "portPos"
/// in the [Graphviz documentation](https://graphviz.org/docs/attr-types/portPos/)).
#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub enum PortPosition {
North,
NorthEast,
East,
SouthEast,
South,
SouthWest,
West,
NorthWest,
Centre,
Auto,
}
impl AsRef<str> for PortPosition {
fn as_ref(&self) -> &str {
match self {
Self::North => "n",
Self::NorthEast => "ne",
Self::East => "e",
Self::SouthEast => "se",
Self::South => "s",
Self::SouthWest => "sw",
Self::West => "w",
Self::NorthWest => "nw",
Self::Centre => "c",
Self::Auto => "_",
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_manual_and_auto_nodes() {
let output = DotWriter::write_string(|writer| {
let mut digraph = writer.digraph();
digraph.node_auto();
let a = digraph.node_named("a").id();
digraph.node_named(NodeId::from(String::from("b")));
digraph.edge(a, "b").edge(String::from("c"));
});
assert_eq!(output, "digraph{node_0;a;b;a->b->c;}")
}
}