dotavious 0.2.1

A library for generating Graphviz DOT language files for graphs.
Documentation
#![doc(html_root_url = "https://docs.rs/dotavious")]
#![doc(issue_tracker_base_url = "https://github.com/doctavious/dotavious/issues/")]
#![cfg_attr(docsrs, deny(broken_intra_doc_links))]

//! Dotavious provides bindings to generate [DOT](https://graphviz.org/doc/info/lang.html)
//! code used by the [Graphviz](http://graphviz.org/) for visualising graphs.
//! It also provides strongly typed attribute functions and offers almost complete
//! coverage of all Graphviz attributes and syntax.
//!
//! # Examples
//!
//! First example provides a basic directed graph:
//! 2 nodes connected by a single edge
//!
//! ```rust
//! use dotavious::{Dot, Edge, Graph, GraphBuilder, Node};
//!
//! let g = GraphBuilder::new_named_directed("example")
//!         .add_node(Node::new("N0"))
//!         .add_node(Node::new("N1"))
//!         .add_edge(Edge::new("N0", "N1"))
//!         .build()
//!         .unwrap();
//!
//! let dot = Dot { graph: g };
//! println!("{}", dot);
//! ```
//! Produces
//! ```dot
//! digraph example {
//!     N0;
//!     N1;
//!     N0 -> N1;
//! }
//! ```
//!
//! We can also output to a Writer via the `render` function
//! ```rust
//! use dotavious::{Dot, Edge, Graph, GraphBuilder, Node};
//! use std::io;
//! use std::io::Read;
//!
//! let g = GraphBuilder::new_named_directed("example")
//!         .add_node(Node::new("N0".to_string()))
//!         .add_node(Node::new("N1".to_string()))
//!         .add_edge(Edge::new("N0".to_string(), "N1".to_string()))
//!         .build()
//!         .unwrap();
//!
//! let dot = Dot { graph: g };
//! let mut writer= Vec::new();
//! dot.render(&mut writer).unwrap();
//!
//! // output to graphviz DOT formatted string
//! let mut dot_string = String::new();
//! Read::read_to_string(&mut &*writer, &mut dot_string).unwrap();
//! println!("{}", dot_string);
//! ```
//!
//! Second example provides a more complex graph showcasing
//! Dotavious' various builders and strongly typed attribute
//! functions.
//!
//! ```rust
//! use dotavious::attributes::{
//!     AttributeText, Color, CompassPoint, EdgeAttributes, EdgeStyle,
//!     GraphAttributeStatementBuilder, GraphAttributes, GraphStyle, NodeAttributes,
//!     NodeStyle, PortPosition, RankDir, Shape,
//! };
//! use dotavious::{
//!     Dot, Edge, EdgeAttributeStatementBuilder, EdgeBuilder, Graph,
//!     GraphBuilder, Node, NodeAttributeStatementBuilder, NodeBuilder,
//!     SubGraphBuilder
//! };
//! use std::io;
//! use std::io::Read;
//!
//! let cluster_0 = SubGraphBuilder::new_named("cluster_0")
//!     .add_graph_attributes(
//!         GraphAttributeStatementBuilder::new()
//!             .label("process #1")
//!             .style(GraphStyle::Filled)
//!             .color(Color::Named("lightgrey"))
//!             .build()
//!             .unwrap(),
//!     )
//!     .add_node_attributes(
//!         NodeAttributeStatementBuilder::new()
//!             .style(NodeStyle::Filled)
//!             .color(Color::Named("white"))
//!             .build()
//!             .unwrap(),
//!     )
//!     .add_edge(Edge::new("a0", "a1"))
//!     .add_edge(Edge::new("a1", "a2"))
//!     .add_edge(Edge::new("a2", "a3"))
//!     .build()
//!     .unwrap();
//!
//! let cluster_1 = SubGraphBuilder::new_named("cluster_1")
//!     .add_graph_attributes(
//!         GraphAttributeStatementBuilder::new()
//!             .label("process #2")
//!             .style(GraphStyle::Filled)
//!             .color(Color::Named("blue"))
//!             .build()
//!             .unwrap(),
//!     )
//!     .add_node_attributes(
//!         NodeAttributeStatementBuilder::new()
//!             .style(NodeStyle::Filled)
//!             .build()
//!             .unwrap(),
//!     )
//!     .add_edge(Edge::new("b0", "b1"))
//!     .add_edge(Edge::new("b1", "b2"))
//!     .add_edge(Edge::new("b2", "b3"))
//!     .build()
//!     .unwrap();
//!
//! let g = GraphBuilder::new_named_directed("G")
//!     .add_node(
//!         NodeBuilder::new("start".to_string())
//!             .shape(Shape::Mdiamond)
//!             .build()
//!             .unwrap(),
//!     )
//!     .add_node(
//!         NodeBuilder::new("end")
//!             .shape(Shape::Msquare)
//!             .build()
//!             .unwrap(),
//!     )
//!     .add_sub_graph(cluster_0)
//!     .add_sub_graph(cluster_1)
//!     .add_edge(Edge::new("start", "a0"))
//!     .add_edge(Edge::new("start", "b0"))
//!     .add_edge(Edge::new("a1", "b3"))
//!     .add_edge(Edge::new("b2", "a3"))
//!     .add_edge(Edge::new("a3", "a0"))
//!     .add_edge(Edge::new("a3", "end"))
//!     .add_edge(Edge::new("b3", "end"))
//!     .build();
//! ```
//!
//! Produces
//! ```dot
//! digraph G {
//!     subgraph cluster_0 {
//!         graph [label="process #1", style=filled, color="lightgrey"];
//!         node [style=filled, color="white"];
//!         a0 -> a1;
//!         a1 -> a2;
//!         a2 -> a3;
//!     }
//!
//!     subgraph cluster_1 {
//!         graph [label="process #2", style=filled, color="blue"];
//!         node [style=filled];
//!         b0 -> b1;
//!         b1 -> b2;
//!         b2 -> b3;
//!     }
//!
//!     start [shape=Mdiamond];
//!     end [shape=Msquare];
//!     start -> a0;
//!     start -> b0;
//!     a1 -> b3;
//!     b2 -> a3;
//!     a3 -> a0;
//!     a3 -> end;
//!     b3 -> end;
//! }
//! ```

pub mod attributes;
pub mod dot;
pub mod validation;

#[doc(hidden)]
pub use crate::dot::{
    Dot, DotString, Edge, EdgeAttributeStatementBuilder, EdgeBuilder, Graph,
    GraphBuilder, Node, NodeAttributeStatementBuilder, NodeBuilder, SubGraphBuilder,
};

// TODO: support adding edge based on index of nodes?
// TODO: handle render options
// TODO: explicit attribute methods with type safety and enforce constraints
// i'm thinking we have NodeTraits/GraphTraits/EdgeTraits (what about none? is that a graph trait?)
// which will have default methods that use an associated type field called "state" or "attributes" etc
// TODO: implement Clone for Graph
// TODO: see if we can get any insights from Haskell implementation
// https://hackage.haskell.org/package/graphviz-2999.20.1.0/docs/Data-GraphViz-Attributes-Complete.html#t:Point
// - I like this: A summary of known current constraints/limitations/differences:
// Add a DPoint enum?
// /// Either a Double or a (2D) Point (i.e. created with Point::new_2d).
// pub enum DPoint {
//     Double(f32),
//     Point(Point),
// }