use crate::attrs::{AttrMap, HasAttributes};
use crate::eval::EvalErrorType;
use crate::expressions::RawExpr;
use crate::node::Node;
use crate::timeseries::{HasSeries, HasTimeSeries, SeriesMap, TsMap};
use abi_stable::std_types::{RDuration, Tuple2};
use abi_stable::{
std_types::{
RHashMap,
ROption::{RNone, RSome},
RString, RVec,
},
StableAbi,
};
use colored::Colorize;
use std::collections::{HashMap, HashSet};
use std::fmt::Debug;
#[repr(C)]
#[derive(StableAbi, Default, Clone, PartialEq)]
pub enum NetworkType {
DAG,
InTree,
OutTree,
WithLoop,
#[default]
Directed,
}
impl std::fmt::Display for NetworkType {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::DAG => write!(fmt, "DAG"),
Self::InTree => write!(fmt, "InTree"),
Self::OutTree => write!(fmt, "OutTree"),
Self::WithLoop => write!(fmt, "WithLoop"),
Self::Directed => write!(fmt, "Directed"),
}
}
}
#[repr(C)]
#[derive(StableAbi, Default, Clone)]
pub struct Network {
ty: NetworkType,
pub(crate) nodes: RVec<RString>,
pub(crate) nodes_ord: RVec<RVec<RString>>,
pub(crate) nodes_map: RHashMap<RString, Node>,
pub(crate) attributes: AttrMap,
pub(crate) series: SeriesMap,
pub(crate) timeseries: TsMap,
pub(crate) outlets: RVec<Node>,
pub(crate) ordered: bool,
}
impl std::fmt::Debug for Network {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Network")
.field("nodes", &self.nodes)
.field("attributes", &self.attributes)
.field("outlets", &self.outlets.len())
.field("ordered", &self.ordered)
.finish()
}
}
impl HasAttributes for Network {
fn attr_map(&self) -> &AttrMap {
&self.attributes
}
fn attr_map_mut(&mut self) -> &mut AttrMap {
&mut self.attributes
}
}
impl HasSeries for Network {
fn series_map(&self) -> &SeriesMap {
&self.series
}
fn series_map_mut(&mut self) -> &mut SeriesMap {
&mut self.series
}
}
impl HasTimeSeries for Network {
fn ts_map(&self) -> &TsMap {
&self.timeseries
}
fn ts_map_mut(&mut self) -> &mut TsMap {
&mut self.timeseries
}
}
impl Network {
pub fn ty(&self) -> &NetworkType {
&self.ty
}
pub fn nodes(&self) -> impl Iterator<Item = &Node> {
self.nodes.iter().map(|n| &self.nodes_map[n])
}
pub fn edges(&self) -> impl Iterator<Item = (&Node, &Node)> + '_ {
self.edges_ind().map(|(s, e)| {
(
&self.nodes_map[&self.nodes[s]],
&self.nodes_map[&self.nodes[e]],
)
})
}
pub fn leaf(&self) -> Option<&Node> {
let mut leaves = self.leaves();
let lf = leaves.next()?;
if leaves.next().is_some() {
None
} else {
Some(lf)
}
}
pub fn leaves(&self) -> impl Iterator<Item = &Node> {
self.nodes
.iter()
.filter(|n| {
self.nodes_map[n.as_str()]
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.is_leaf()
})
.map(|n| &self.nodes_map[n])
}
pub fn root(&self) -> Option<&Node> {
let mut roots = self.roots();
let rt = roots.next()?;
if roots.next().is_some() {
None
} else {
Some(rt)
}
}
pub fn roots(&self) -> impl Iterator<Item = &Node> {
self.nodes
.iter()
.filter(|n| {
self.nodes_map[n.as_str()]
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.is_root()
})
.map(|n| &self.nodes_map[n])
}
pub fn append_edges(&mut self, edges: &[(&str, &str)], _force: bool) -> Result<(), String> {
for (start, end) in edges {
if start == end {
self.ty = NetworkType::WithLoop;
if !self.nodes_map.contains_key(*start) {
self.insert_node_by_name(start);
}
let inp = self.node_by_name(start).expect("Input just inserted");
let out = inp.clone();
inp.try_lock()
.unwrap_or_else(|| panic!("mutex error: {:?} {}", file!(), line!()))
.add_output(out.clone());
inp.try_lock()
.unwrap_or_else(|| panic!("mutex error: {:?} {}", file!(), line!()))
.add_input(out.clone());
} else {
if !self.nodes_map.contains_key(*start) {
self.insert_node_by_name(start);
}
if !self.nodes_map.contains_key(*end) {
self.insert_node_by_name(end);
}
let inp = self.node_by_name(start).expect("Input just inserted");
let out = self.node_by_name(end).expect("Output just inserted");
inp.try_lock()
.unwrap_or_else(|| panic!("mutex error: {:?} {}", file!(), line!()))
.add_output(out.clone());
out.try_lock()
.unwrap_or_else(|| panic!("mutex error: {:?} {}", file!(), line!()))
.add_input(inp.clone());
}
}
self.flatten();
Ok(())
}
pub fn position_nodes(&mut self) {
todo!()
}
pub fn flatten(&mut self) {
self.reorder();
self.set_levels();
self.nodes_map.values().for_each(|n| {
let mut n = n
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()));
let pos = (n.level() as f64, n.index() as f64);
n.set_pos(pos)
})
}
pub fn from_edges(edges: &[(&str, &str)], force: bool) -> Result<Self, String> {
let mut network = Self::default();
network.append_edges(edges, force)?;
Ok(network)
}
pub fn edges_str(&self) -> impl Iterator<Item = (&str, &str)> + '_ {
self.edges_ind()
.map(|(s, e)| (self.nodes[s].as_str(), self.nodes[e].as_str()))
}
pub fn edges_ind(&self) -> impl Iterator<Item = (usize, usize)> + '_ {
self.nodes().filter_map(|n| {
let n = n
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()));
match n.output() {
RSome(o) => Some((
n.index(),
o.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index(),
)),
RNone => None,
}
})
}
pub fn node_names(&self) -> impl Iterator<Item = &str> {
self.nodes.iter().map(|n| n.as_str())
}
pub fn nodes_rev(&self) -> impl Iterator<Item = &Node> {
self.nodes.iter().rev().map(|n| &self.nodes_map[n])
}
pub fn nodes_count(&self) -> usize {
self.nodes.len()
}
pub fn insert_node_by_name(&mut self, name: &str) {
if self.nodes_map.contains_key(name) {
return;
}
let node = Node::new(self.nodes_count(), name);
self.nodes_map.insert(name.into(), node);
self.nodes.push(name.into());
}
pub fn node(&self, ind: usize) -> Option<&Node> {
self.nodes.get(ind).map(|n| &self.nodes_map[n])
}
pub fn node_by_name(&self, name: &str) -> Option<&Node> {
self.nodes_map.get(name)
}
pub fn try_node_by_name(&self, name: &str) -> Result<&Node, EvalErrorType> {
self.nodes_map
.get(name)
.ok_or_else(|| EvalErrorType::NodeNotFound(name.to_string()))
}
pub fn nodes_order(&self, prop: &PropOrder) -> Vec<Node> {
match prop {
PropOrder::Auto | PropOrder::Sequential => self.nodes().cloned().collect(),
PropOrder::OutputFirst => self
.nodes_ord
.iter()
.rev()
.flat_map(|n| n.iter())
.map(|n| self.nodes_map[n].clone())
.collect(),
PropOrder::Inverse => self.nodes_rev().cloned().collect(),
PropOrder::InputsFirst => self
.nodes_ord
.iter()
.flat_map(|n| n.iter())
.map(|n| self.nodes_map[n].clone())
.collect(),
}
}
pub fn nodes_select(
&self,
order: &PropOrder,
prop: &PropNodes,
) -> Result<Vec<Node>, EvalErrorType> {
match (prop, order) {
(PropNodes::All, o) => Ok(self.nodes_order(o)),
(PropNodes::List(lst), PropOrder::Auto) => {
let nodes: Vec<_> = lst.iter().map(|n| self.node_by_name(n.as_str())).collect();
if nodes.iter().any(Option::is_none) {
let not_found: Vec<&str> = lst
.iter()
.zip(nodes)
.filter(|(_, o)| o.is_none())
.map(|(n, _)| n.as_str())
.collect();
Err(EvalErrorType::NodeNotFound(not_found.join(", ")))
} else {
Ok(nodes.into_iter().filter_map(|o| o.cloned()).collect())
}
}
(PropNodes::List(lst), o) => {
let mut sel_lst: HashSet<&str> = lst.iter().map(|n| n.as_str()).collect();
let res = self
.nodes_order(o)
.into_iter()
.filter(|n| sel_lst.remove(n.name()))
.collect();
if sel_lst.is_empty() {
Ok(res)
} else {
Err(EvalErrorType::NodeNotFound(
sel_lst.into_iter().collect::<Vec<&str>>().join(", "),
))
}
}
(PropNodes::Path(p), o) => self.nodes_path(o, p),
}
}
pub fn nodes_path(
&self,
order: &PropOrder,
path: &StrPath,
) -> Result<Vec<Node>, EvalErrorType> {
if self.ty != NetworkType::InTree {
return Err(EvalErrorType::NotImplementedError(
"path between nodes only implemented for in-trees",
));
}
let start = self.try_node_by_name(path.start.as_str())?;
let end = self.try_node_by_name(path.end.as_str())?;
let (start, end, flipped) = if start
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index()
> end.lock().index()
{
(start, end, false)
} else {
(end, start, true)
};
let mut curr = start.clone();
let mut path_nodes = vec![];
let start_name = self.nodes[start
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index()]
.as_str();
let end_name = self.nodes[end
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index()]
.as_str();
loop {
path_nodes.push(curr.clone());
if curr.name() == end_name {
break;
}
let tmp = if let RSome(o) = curr
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.output()
{
o.clone()
} else {
return Err(EvalErrorType::PathNotFound(
start_name.to_string(),
curr.name().to_string(),
end_name.to_string(),
));
};
curr = tmp;
}
match order {
PropOrder::Auto if flipped => Ok(path_nodes.into_iter().rev().collect()),
PropOrder::Auto => Ok(path_nodes),
PropOrder::Sequential | PropOrder::OutputFirst => Ok(path_nodes),
PropOrder::Inverse | PropOrder::InputsFirst => {
Ok(path_nodes.into_iter().rev().collect())
}
}
}
pub fn leaf_nodes(&self) -> impl Iterator<Item = &Node> {
self.nodes_map.iter().map(|n| n.1).filter(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.is_empty()
})
}
pub fn calc_weights(&mut self) {
let mut weights = HashMap::<RString, u64>::with_capacity(self.nodes.len());
self.nodes_map.iter().for_each(|n| {
if n.1
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.is_empty()
{
weights.insert(n.0.clone(), 1);
}
let mut visited = HashSet::new();
visited.insert(
n.1.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index(),
);
let mut n =
n.1.try_lock_for(RDuration::from_secs(1))
.expect("Lock failed for node, maybe branched network")
.output()
.cloned();
while let RSome(out) = n {
*weights.entry(out.name().into()).or_insert(1) += 1;
let o = out
.try_lock_for(RDuration::from_secs(1))
.expect("Lock failed for node, maybe branched network");
if visited.contains(&o.index()) {
self.ty = NetworkType::WithLoop;
break;
}
visited.insert(o.index());
n = o.output().cloned();
}
});
for (node, ord) in weights {
self.nodes_map[&node]
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.set_weight(ord);
}
}
pub fn calc_order(&mut self) {
self.nodes_map.values().for_each(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.set_order(1)
});
let mut nodes_ord_map = HashMap::new();
self.nodes.iter().enumerate().rev().for_each(|(i, n)| {
let nobj = &self.nodes_map[n];
let inputs: Vec<Node> = nobj
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.to_vec();
let ord = inputs
.iter()
.map(|i| {
i.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.order()
})
.max()
.unwrap_or(0);
nobj.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.set_order(ord + 1);
nodes_ord_map.insert(i, ord + 1);
});
let max_ord = *nodes_ord_map.values().max().unwrap_or(&0);
let mut nodes_ord: Vec<RVec<RString>> = (0..=max_ord).map(|_| RVec::new()).collect();
for (i, o) in nodes_ord_map {
nodes_ord
.get_mut(o as usize)
.expect("max taken")
.push(self.nodes[i].clone());
}
self.nodes_ord = nodes_ord
.into_iter()
.filter(|o| !o.is_empty())
.collect::<Vec<_>>()
.into();
}
pub fn reorder(&mut self) {
self.calc_weights();
let weights: HashMap<_, _> = self
.nodes_map
.iter()
.map(|n| {
(
n.0.as_str(),
n.1.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.weight(),
)
})
.collect();
self.outlets = {
let mut outlets: Vec<&str> = self
.nodes_map
.iter()
.filter(|Tuple2(_, n)| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.is_root()
})
.map(|Tuple2(n, _)| n.as_str())
.collect();
outlets.sort_by(|a, b| weights[b].cmp(&weights[a]));
outlets
.into_iter()
.map(|o| self.nodes_map[o].clone())
.collect()
};
let mut visited: HashSet<String> = HashSet::new();
let mut nodes_queue: Vec<String> = Vec::with_capacity(self.nodes.len());
let mut new_nodes: Vec<String> = Vec::with_capacity(self.nodes.len());
for o in self.outlets.iter().rev() {
nodes_queue.push(o.name().to_string());
}
while let Some(curr) = nodes_queue.pop() {
if visited.contains(&curr) {
continue;
}
visited.insert(curr.clone());
let inputs: Vec<Node> = self.nodes_map[curr.as_str()]
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.to_vec();
let mut inps: Vec<String> = inputs.iter().map(|i| i.name().to_string()).collect();
inps.sort_by(|n1, n2| {
weights[n2.as_str()]
.partial_cmp(&weights[n1.as_str()])
.unwrap()
});
self.nodes_map[curr.as_str()]
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs = inps
.iter()
.map(|i| self.nodes_map[i.as_str()].clone())
.collect();
for c in inps {
nodes_queue.push(c.clone());
}
new_nodes.push(curr);
}
let new_nodes: Vec<Node> = new_nodes
.iter()
.map(|n| self.nodes_map[n.as_str()].clone())
.collect();
if new_nodes.len() < self.nodes.len() {
eprintln!(
"Reorder not done, the nodes are not connected: {} connected out of {}",
new_nodes.len(),
self.nodes.len()
);
self.ordered = false;
return;
}
self.nodes = new_nodes
.iter()
.map(|n| n.name().into())
.collect::<Vec<RString>>()
.into();
self.reindex();
self.calc_order();
self.ordered = true;
self.ty = match &self.ty {
NetworkType::Directed => {
let in_ord = self
.nodes_map
.values()
.map(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.len()
})
.max()
.unwrap_or_default();
let out_ord = self
.nodes_map
.values()
.map(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.outputs()
.len()
})
.max()
.unwrap_or_default();
if out_ord < 2 {
NetworkType::InTree
} else if in_ord < 2 {
NetworkType::OutTree
} else {
NetworkType::DAG
}
}
t => t.clone(),
}
}
pub fn reindex(&self) {
for (i, n) in self.nodes().enumerate() {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.set_index(i);
}
}
pub fn set_levels(&mut self) {
fn recc_set(node: &Node, level: u64, visited: &mut HashSet<usize>) {
let ind = node
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index();
if visited.contains(&ind) {
return;
}
visited.insert(ind);
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.set_level(level);
let inputs = node
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.to_vec();
match &inputs[..] {
[] => (),
[first, rest @ ..] => {
recc_set(first, level, visited);
for i in rest {
recc_set(i, level + 1, visited);
}
}
}
}
let mut visited: HashSet<usize> = HashSet::new();
for o in &self.outlets {
recc_set(o, 0, &mut visited);
}
}
fn remove_node_single(&mut self, node: &Node) {
let (ind, outputs): (usize, Vec<Node>) = {
let n = node
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()));
let ind = n.index();
self.nodes.remove(ind);
self.nodes_map.remove(n.name());
(ind, n.outputs().to_vec())
};
for out in &outputs {
let pos = out
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.iter()
.position(|i| {
i.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index()
== ind
})
.expect("Node should be in input list of output");
out.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs_mut()
.remove(pos);
}
let inputs: Vec<Node> = node
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
.to_vec();
for inp in &inputs {
let pos = inp
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.outputs()
.iter()
.position(|i| {
i.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.index()
== ind
})
.expect("Node should be in input list of output");
inp.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.outputs_mut()
.remove(pos);
}
for inp in inputs {
for out in &outputs {
inp.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.add_output(out.clone());
out.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.add_input(inp.clone());
}
}
self.reindex();
}
pub fn remove_node(&mut self, node: &Node) {
self.remove_node_single(node);
}
pub fn subset(&mut self, filter: &[bool], keep: bool) -> Result<(), String> {
let include_nodes: HashMap<String, Node> = self
.nodes()
.zip(self.node_names())
.zip(filter)
.filter(|(_, &f)| !(f ^ keep))
.map(|((n, name), _)| (name.to_string(), n.clone()))
.collect();
include_nodes.values().for_each(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.unset_inputs();
});
for node in include_nodes.values() {
let mut start = node.clone();
loop {
let out = start
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.output()
.cloned();
match out {
RNone => {
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.unset_outputs();
break;
}
RSome(o) => {
if include_nodes.contains_key(o.name()) {
let mut op = o.try_lock().expect(&format!(
"mutex error: {:?} {}",
file!(),
line!()
));
op.add_input(node.clone());
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.unset_outputs();
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.add_output(o.clone());
break;
} else {
start = o.clone();
}
}
}
}
}
self.nodes = include_nodes.keys().map(|n| n.to_string().into()).collect();
self.nodes_map = include_nodes
.into_iter()
.map(|(n, o)| (n.into(), o))
.collect();
self.reorder();
self.set_levels();
Ok(())
}
pub fn new_root(&mut self, node: Node) {
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.unset_outputs();
let mut nodes = Vec::with_capacity(self.nodes.len());
let mut nodes_map = HashMap::with_capacity(self.nodes.len());
fn register(n: &Node, nds: &mut Vec<RString>, nmp: &mut HashMap<RString, Node>) {
let nm: RString = n.name().to_string().into();
nds.push(nm.clone());
nmp.insert(nm, n.clone());
for i in n
.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.inputs()
{
register(i, nds, nmp)
}
}
register(&node, &mut nodes, &mut nodes_map);
self.nodes = nodes.into();
self.nodes_map = nodes_map.into();
self.outlets = vec![node].into();
}
pub fn connections_utf8(&self) -> Vec<String> {
self.nodes()
.map(|node| {
let node =
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()));
let level = node.level();
let par_level = node
.output()
.map(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.level()
})
.unwrap_or(level);
let _merge = level != par_level;
let mut line = String::new();
for _ in 0..level {
line.push_str(" │");
}
if level != par_level {
line.pop();
if node.inputs().is_empty() {
line.push_str("├──");
} else {
line.push_str("├──┐");
}
} else if node.inputs().is_empty() {
line.push_str(" ╵");
} else if node.output().is_none() {
line.push_str(" ╷");
} else {
line.push_str(" │");
}
line
})
.collect()
}
pub fn connections_ascii(&self) -> Vec<String> {
self.nodes()
.map(|node| {
let node =
node.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()));
let level = node.level();
let par_level = node
.output()
.map(|n| {
n.try_lock()
.expect(&format!("mutex error: {:?} {}", file!(), line!()))
.level()
})
.unwrap_or(level);
let _merge = level != par_level;
let mut line = String::new();
for _ in 0..level {
line.push_str(" |");
}
if level != par_level {
line.pop();
line.push_str("|--*");
} else {
line.push_str(" *");
}
line
})
.collect()
}
}
#[repr(C)]
#[derive(StableAbi, Debug, Hash, Default, Clone, Eq, PartialEq)]
pub struct StrPath {
pub start: RString,
pub end: RString,
}
impl std::fmt::Display for StrPath {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(fmt, "{} -> {}", self.start, self.end)
}
}
impl StrPath {
pub fn new(start: RString, end: RString) -> Self {
Self { start, end }
}
pub fn to_colored_string(&self) -> String {
format!(
"{} -> {}",
self.start.to_string().green(),
self.end.to_string().green()
)
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub struct Propagation {
pub order: PropOrder,
pub nodes: PropNodes,
pub condition: PropCondition,
pub start: (usize, usize),
}
impl std::fmt::Display for Propagation {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
write!(fmt, "{}{}{}", self.order, self.nodes, self.condition)
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum PropOrder {
#[default]
Auto,
Sequential,
Inverse,
InputsFirst,
OutputFirst,
}
impl std::fmt::Display for PropOrder {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::Auto => Ok(()),
Self::Sequential => write!(fmt, "<sequential>"),
Self::Inverse => write!(fmt, "<inverse>"),
Self::InputsFirst => write!(fmt, "<inputsfirst>"),
Self::OutputFirst => write!(fmt, "<outputfirst>"),
}
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum PropNodes {
#[default]
All,
List(RVec<RString>),
Path(StrPath),
}
impl std::fmt::Display for PropNodes {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::All => Ok(()),
Self::List(v) => write!(
fmt,
"[{}]",
v.iter()
.map(|a| a.as_str())
.collect::<Vec<&str>>()
.join(", ")
),
Self::Path(p) => write!(fmt, "[{}]", p),
}
}
}
#[derive(Debug, Default, Clone, PartialEq)]
pub enum PropCondition {
#[default]
All,
Expr(RawExpr),
}
impl std::fmt::Display for PropCondition {
fn fmt(&self, fmt: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
match self {
Self::All => Ok(()),
Self::Expr(expr) => write!(fmt, "({})", expr),
}
}
}
impl From<Node> for Network {
fn from(node: Node) -> Self {
let mut net = Self::default();
let mut nodes = vec![];
fn insert_node(n: &Node, nodes: &mut Vec<Node>) {
let ni = n
.try_lock_for(RDuration::from_secs(1))
.expect("Lock failed for node, maybe branched network");
if ni.inputs().is_empty() {
nodes.push(n.clone());
} else {
for i in ni.inputs() {
insert_node(i, nodes);
}
nodes.push(n.clone());
}
}
insert_node(&node, &mut nodes);
net.nodes_map = nodes
.into_iter()
.map(|n| (RString::from(n.name()), n))
.collect::<HashMap<RString, Node>>()
.into();
net.nodes = net.nodes_map.keys().cloned().collect::<Vec<_>>().into();
net.outlets = vec![node].into();
net
}
}