use super::attribute::{Attributes, AttributesList};
use super::writer::{DotWriter, Line, Statement};
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 }
}
pub fn subgraph(&mut self) -> Scope<'_, 'w> {
Scope::new(self.writer, b"subgraph")
}
pub fn cluster(&mut self) -> Scope<'_, 'w> {
let label = format!("subgraph cluster_{}", self.writer.next_id());
Scope::new(self.writer, label.as_bytes())
}
pub fn graph_attributes(&mut self) -> AttributesList<'_, 'w> {
let mut statement = Statement::new(self.writer);
statement.write(b"graph");
AttributesList::new(statement)
}
pub fn edge_attributes(&mut self) -> AttributesList<'_, 'w> {
let mut statement = Statement::new(self.writer);
statement.write(b"edge");
AttributesList::new(statement)
}
pub fn node_attributes(&mut self) -> AttributesList<'_, 'w> {
let mut statement = Statement::new(self.writer);
statement.write(b"node");
AttributesList::new(statement)
}
pub fn node_auto(&mut self) -> Node<'_, 'w> {
let id = self.writer.next_id();
self.node_named(format!("node_{}", id))
}
pub fn node_named<S: Into<String>>(&mut self, id: S) -> Node<'_, 'w> {
Node::new(Statement::new(self.writer), id.into())
}
pub fn edge<F, T>(&mut self, from_node_id: F, to_node_id: T) -> EdgeList<'_, 'w>
where
F: AsRef<[u8]>,
T: AsRef<[u8]>,
{
EdgeList::new(Statement::new(self.writer), from_node_id, to_node_id)
}
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"}");
}
}
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 }
}
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
}
pub fn attributes(self) -> AttributesList<'d, 'w> {
AttributesList::new(self.statement)
}
}
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,
}
}
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
}
}
#[derive(Clone, Debug)]
pub struct NodeId {
id: String,
}
impl NodeId {
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()
}
}
#[derive(Clone, Debug)]
pub struct PortId {
id: String,
}
impl PortId {
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()
}
}
#[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()
}
}
#[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;}")
}
}