use nadi_plugin::nadi_internal_plugin;
#[nadi_internal_plugin]
mod gviz {
use crate::attrs::FromAttribute;
use crate::functions::FunctionRet;
use crate::nadi_plugin::network_func;
use crate::template::Template;
use crate::{network::Network, node::NodeInner, return_on_err};
use abi_stable::std_types::Tuple2;
use std::{fs::File, io::Write, path::Path};
#[network_func(factor = 0.1)]
fn load_positions(net: &Network, gv_file: &Path, factor: f64) -> Result<(), String> {
let contents = std::fs::read_to_string(gv_file).map_err(|e| e.to_string())?;
let tokens = crate::parser::tokenizer::get_tokens(&contents);
let gvnet = crate::parser::graphviz::parse(tokens).map_err(|e| e.to_string())?;
for Tuple2(name, node) in &net.nodes_map {
let attrs = gvnet
.nodes
.get(name.as_str())
.ok_or(format!("Node {name:?} not found"))?;
let a = attrs.get("pos").ok_or(
"pos attr not found, use a graphviz file generated by a layout engine".to_string(),
)?;
let pos = String::try_from_attr(a).map_err(|e| e.to_string())?;
let (x, y) = pos
.split_once(",")
.ok_or(format!("Invalid position {pos:?}"))?;
let x: f64 = x.trim().parse().ok().ok_or(format!("Invalid x: {x:?}"))?;
let y: f64 = y.trim().parse().ok().ok_or(format!("Invalid y: {y:?}"))?;
node.lock().set_pos((x * factor, y * factor));
}
Ok(())
}
#[network_func(name = "network", global_attrs = "")]
fn save_gv(
net: &mut Network,
outfile: &Path,
name: &str,
global_attrs: &str,
node_attr: Option<Template>,
edge_attr: Option<Template>,
) -> FunctionRet {
let mut file = return_on_err!(File::create(outfile));
return_on_err!(writeln!(file, "digraph {} {{", name));
return_on_err!(writeln!(file, "{}", global_attrs));
for node in net.nodes() {
let (name, outputs) = {
let node: &NodeInner = &node.lock();
(node.name().to_string(), node.outputs().to_vec())
};
if let Some(templ) = &node_attr {
let node: &NodeInner = &node.lock();
let attr = return_on_err!(templ.render(node));
return_on_err!(writeln!(file, "{:?} [{attr}]", name));
}
let attrs = if let Some(templ) = &edge_attr {
let node: &NodeInner = &node.lock();
let attr = return_on_err!(templ.render(node));
format!(" [{attr}]")
} else {
"".into()
};
for out in outputs {
return_on_err!(writeln!(
file,
"{:?} -> {:?}{attrs}",
name,
out.lock().name()
));
}
}
return_on_err!(writeln!(file, "}}"));
FunctionRet::None
}
}