use std::collections::HashMap;
use std::ops::AddAssign;
#[cfg(feature = "display")]
use std::fmt::{Display, Formatter};
use crate::subgraph_free::{
AttrStmt as AstAttrStmt, EdgeStmt, Graph as AstGraph, NodeID, NodeStmt, Port, Stmt,
};
pub use crate::ast::AList;
#[derive(Debug, Clone)]
pub struct Graph<A> {
pub strict: bool,
pub is_digraph: bool,
pub name: Option<String>,
pub attr: Vec<AttrStmt<A>>,
pub nodes: NodeSet<A>,
pub edges: EdgeSet<A>,
pub ideqs: Vec<IDEq>,
}
#[cfg(feature = "display")]
impl<A> Display for Graph<A>
where
A: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
if self.strict {
write!(f, "strict ")?;
}
if self.is_digraph {
write!(f, "digraph ")?;
} else {
write!(f, "graph ")?;
}
if let Some(name) = &self.name {
write!(f, "{}", name)?;
}
writeln!(f, "{{")?;
for stmt in &self.attr {
writeln!(f, "{}", stmt)?;
}
for ideq in &self.ideqs {
writeln!(f, "{}", ideq)?;
}
write!(f, "{}", self.nodes)?;
write!(f, "{}", self.edges)?;
writeln!(f, "}}")?;
Result::Ok(())
}
}
impl<A> Graph<A> {
pub fn filter_map<F, B>(self, f: F) -> Graph<B>
where
F: Fn(A) -> Option<B>,
{
let new_attr = self
.attr
.into_iter()
.filter_map(|attr_stmt| attr_stmt.filter_map(&f))
.collect();
let new_nodes = self.nodes.map(&f);
let new_edges = self.edges.map(&f);
Graph {
strict: self.strict,
is_digraph: self.is_digraph,
name: self.name,
attr: new_attr,
nodes: new_nodes,
edges: new_edges,
ideqs: self.ideqs,
}
}
}
impl<A, G> From<G> for Graph<A>
where
G: Into<AstGraph<A>>,
A: Clone,
{
fn from(graph: G) -> Self {
let graph = graph.into();
let mut attrs: Vec<AstAttrStmt<_>> = Vec::new();
let mut nodes = Vec::new();
let mut edges = Vec::new();
let mut ideqs = Vec::new();
for stmt in graph.stmts {
match stmt {
Stmt::NodeStmt(node) => nodes.push(node),
Stmt::EdgeStmt(edge) => edges.push(edge),
Stmt::AttrStmt(attr) => attrs.push(attr),
Stmt::IDEq(lhs, rhs) => ideqs.push(IDEq {lhs, rhs}),
}
}
let mut nodes: NodeSet<A> = nodes.into();
let edges: EdgeSet<A> = (edges, &mut nodes).into();
let attr: Vec<AttrStmt<A>> = attrs
.into_iter()
.flat_map(|stmt: AstAttrStmt<_>| {
let attr_l: Vec<AttrStmt<A>> = stmt.into();
attr_l
})
.collect();
Graph {
strict: graph.strict,
is_digraph: graph.is_digraph,
name: graph.name,
attr,
nodes,
edges,
ideqs,
}
}
}
#[cfg(feature = "petgraph")]
use petgraph::graph::Graph as PetGraph;
#[cfg(feature = "petgraph")]
impl<A> From<Graph<A>> for PetGraph<Node<A>, AList<A>> {
fn from(val: Graph<A>) -> Self {
let mut graph = PetGraph::new();
let mut node_indices = HashMap::new();
for node in val.nodes.set {
let ni = graph.add_node(node.1);
node_indices.insert(node.0, ni);
}
for edge in val.edges.set {
let from_ni = node_indices.get(&edge.from).unwrap();
let to_ni = node_indices.get(&edge.to).unwrap();
graph.add_edge(*from_ni, *to_ni, edge.attr);
}
graph
}
}
#[derive(Debug, Clone)]
pub struct Node<A> {
pub id: String,
pub port: Option<Port>,
pub attr: AList<A>,
}
impl<A> From<NodeStmt<A>> for Node<A> {
fn from(stmt: NodeStmt<A>) -> Self {
Node {
id: stmt.node.id.to_string(),
port: stmt.node.port,
attr: stmt.attr.map(|list| list.into()).unwrap_or(AList::empty()),
}
}
}
impl<A> From<&NodeID> for Node<A> {
fn from(node: &NodeID) -> Self {
Node {
id: node.id.to_string(),
port: node.port.clone(),
attr: AList::empty(),
}
}
}
impl<A> Node<A> {
fn map<F, B>(self, f: F) -> Node<B>
where
F: Fn(A) -> Option<B>,
{
Node {
id: self.id,
port: self.port,
attr: self.attr.filter_map_attr(&f),
}
}
}
#[cfg(feature = "display")]
impl<A> Display for Node<A>
where
A: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "\"{}\" ", self.id)?;
if let Some(port) = &self.port {
write!(f, "{}", port)?;
}
if !self.attr.is_empty() {
write!(f, "[{}]", self.attr)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct NodeSet<A> {
pub set: HashMap<String, Node<A>>,
}
impl<A> NodeSet<A> {
fn insert_if_absent(&mut self, id: String, or: Node<A>) {
if self.set.get(&id).is_none() {
self.set.insert(id, or);
}
}
fn map<F, B>(self, f: F) -> NodeSet<B>
where
F: Fn(A) -> Option<B>,
{
let new_set = self
.set
.into_iter()
.map(|(name, node)| (name, node.map(&f)))
.collect();
NodeSet { set: new_set }
}
}
impl<A, I> From<I> for NodeSet<A>
where
I: IntoIterator<Item = NodeStmt<A>>,
{
fn from(nodes: I) -> Self {
let set: HashMap<_, _> = nodes
.into_iter()
.map(|node| (node.node.id.to_string(), node.into()))
.collect();
NodeSet { set }
}
}
#[cfg(feature = "display")]
impl<A> Display for NodeSet<A>
where
A: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
for node in self.set.values() {
writeln!(f, "{}", node)?;
}
Ok(())
}
}
#[derive(Debug, Clone)]
pub struct EdgeSet<A> {
pub set: Vec<Edge<A>>,
}
impl<A> EdgeSet<A> {
fn empty() -> Self {
Self { set: Vec::new() }
}
fn map<F, B>(self, f: F) -> EdgeSet<B>
where
F: Fn(A) -> Option<B>,
{
let new_set = self.set.into_iter().map(|edge| edge.map(&f)).collect();
EdgeSet { set: new_set }
}
}
impl<A> AddAssign for EdgeSet<A> {
fn add_assign(&mut self, mut rhs: Self) {
self.set.append(&mut rhs.set)
}
}
#[cfg(feature = "display")]
impl<A> Display for EdgeSet<A>
where
A: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
for edge in &self.set {
writeln!(f, "{}", edge)?;
}
Ok(())
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Edge<A> {
pub from: String,
pub to: String,
pub attr: AList<A>,
}
#[cfg(feature = "display")]
impl<A> Display for Edge<A>
where
A: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "\"{}\" -> \"{}\" [{}]", self.from, self.to, self.attr)
}
}
impl<A> Edge<A> {
fn map<F, B>(self, f: F) -> Edge<B>
where
F: Fn(A) -> Option<B>,
{
Edge {
from: self.from,
to: self.to,
attr: self.attr.filter_map_attr(&f),
}
}
}
impl<A> From<(EdgeStmt<A>, &mut NodeSet<A>)> for EdgeSet<A>
where
A: Clone,
{
fn from(tuple: (EdgeStmt<A>, &mut NodeSet<A>)) -> Self {
let (stmt, nodes) = tuple;
let mut from = stmt.from;
let mut rhs = stmt.next;
let mut set = Vec::new();
let attr = stmt.attr.map(|list| list.into()).unwrap_or(AList::empty());
loop {
let to = rhs.to;
let from_id = from.id.clone();
let to_id = to.id.clone();
nodes.insert_if_absent(from.id.to_string(), (&from).into());
nodes.insert_if_absent(to.id.to_string(), (&to).into());
let edge = Edge {
from: from_id.to_string(),
to: to_id.to_string(),
attr: attr.clone(),
};
set.push(edge);
if rhs.next.is_none() {
return EdgeSet { set };
}
from = to;
rhs = *(rhs.next.unwrap());
}
}
}
impl<A, I> From<(I, &mut NodeSet<A>)> for EdgeSet<A>
where
I: IntoIterator<Item = EdgeStmt<A>>,
A: Clone,
{
fn from(tuple: (I, &mut NodeSet<A>)) -> Self {
let (stmts, nodes) = tuple;
let mut set = EdgeSet::empty();
for stmt in stmts {
set += (stmt, &mut *nodes).into();
}
set
}
}
#[derive(Debug, Copy, Clone)]
pub enum AttrStmt<A> {
Graph(A),
Node(A),
Edge(A),
}
impl<A> AttrStmt<A> {
fn filter_map<F, B>(self, f: F) -> Option<AttrStmt<B>>
where
F: Fn(A) -> Option<B>,
{
match self {
AttrStmt::Graph(a) => {
f(a).map(AttrStmt::Graph)
}
AttrStmt::Node(a) => {
f(a).map(AttrStmt::Node)
}
AttrStmt::Edge(a) => {
f(a).map(AttrStmt::Edge)
}
}
}
}
impl<A> From<AstAttrStmt<A>> for Vec<AttrStmt<A>> {
fn from(val: AstAttrStmt<A>) -> Self {
match val {
AstAttrStmt::Graph(list) => {
let alist: AList<A> = list.into();
alist
.into_iter()
.map(|attr| AttrStmt::Graph(attr))
.collect()
}
AstAttrStmt::Node(list) => {
let alist: AList<A> = list.into();
alist
.into_iter()
.map(|attr| AttrStmt::Node(attr))
.collect()
}
AstAttrStmt::Edge(list) => {
let alist: AList<A> = list.into();
alist
.into_iter()
.map(|attr| AttrStmt::Edge(attr))
.collect()
}
}
}
}
#[cfg(feature = "display")]
impl<A> Display for AttrStmt<A>
where
A: Display,
{
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
match self {
AttrStmt::Graph(attr) => write!(f, "graph [{}]", attr),
AttrStmt::Edge(attr) => write!(f, "edge [{}]", attr),
AttrStmt::Node(attr) => write!(f, "node [{}]", attr),
}
}
}
#[derive(Clone, Debug)]
pub struct IDEq {
pub lhs: String,
pub rhs: String,
}
#[cfg(feature = "display")]
impl Display for IDEq {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), std::fmt::Error> {
write!(f, "{} = {}", self.lhs, self.rhs)
}
}