flow-graph 0.23.0

A representation of a graph data structure for flow-based programming.
Documentation
use super::*;
use crate::node::NodePort;
use crate::port::PortDirection;
use crate::{NodeIndex, PortReference};

#[derive(Debug, Clone)]
#[must_use]
pub struct Port<'graph, DATA> {
  schematic: &'graph Schematic<DATA>,
  pub(super) port: PortReference,
}

impl<'graph, DATA> Port<'graph, DATA>
where
  DATA: Clone,
{
  pub const fn new(schematic: &'graph Schematic<DATA>, port: PortReference) -> Self {
    Self { schematic, port }
  }

  pub const fn node(&self) -> NodeHop<DATA> {
    NodeHop::new(self.schematic, self.port.node_index)
  }

  pub fn connections(&self) -> Connections<DATA> {
    get_port_connections(self.schematic, &self.port)
  }

  #[must_use]
  pub fn name(&self) -> &str {
    get_port_name(self.schematic, &self.port)
  }

  pub fn inner(&self) -> &NodePort {
    get_ports_node(self.schematic, &self.port)
  }

  pub const fn direction(&self) -> PortDirection {
    self.port.direction
  }
}

impl<'graph, DATA> AsRef<PortReference> for Port<'graph, DATA> {
  fn as_ref(&self) -> &PortReference {
    &self.port
  }
}

impl<'graph, DATA> std::fmt::Display for Port<'graph, DATA>
where
  DATA: Clone,
{
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    write!(f, "{}", display(self.schematic, &self.port))
  }
}

#[derive(Debug, Clone)]
#[must_use]
pub struct Ports<'graph, DATA> {
  pub(super) direction: Option<PortDirection>,
  pub(super) ports: Vec<PortReference>,
  pub(super) cur_index: usize,
  pub(super) node_index: NodeIndex,
  schematic: &'graph Schematic<DATA>,
}

impl<'graph, DATA> Ports<'graph, DATA> {
  pub(crate) fn new(schematic: &'graph Schematic<DATA>, node_index: NodeIndex, ports: Vec<PortReference>) -> Self {
    Self {
      direction: ports.get(0).map(|p| *p.direction()),
      ports,
      cur_index: 0,
      node_index,
      schematic,
    }
  }

  #[must_use]
  pub fn len(&self) -> usize {
    self.ports.len()
  }

  #[must_use]
  pub fn is_empty(&self) -> bool {
    self.ports.is_empty()
  }
}

impl<'graph, DATA> Iterator for Ports<'graph, DATA>
where
  DATA: Clone,
{
  type Item = Port<'graph, DATA>;

  fn next(&mut self) -> Option<Port<'graph, DATA>> {
    let result = self
      .ports
      .get(self.cur_index)
      .map(|index| Port::new(self.schematic, *index));
    self.cur_index += 1;
    result
  }
}

fn display<DATA>(schematic: &Schematic<DATA>, port: &PortReference) -> String
where
  DATA: Clone,
{
  let node = &schematic.nodes[port.node_index];
  let (port, dir) = match port.direction {
    PortDirection::In => (&node.inputs()[port.port_index], "IN"),
    PortDirection::Out => (&node.outputs()[port.port_index], "OUT"),
  };
  format!("{}.{}.{}", node, dir, port)
}

impl<'graph, DATA> std::fmt::Display for Ports<'graph, DATA>
where
  DATA: Clone,
{
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    for (index, port) in self.ports.iter().enumerate() {
      let comma = if index < (self.ports.len() - 1) { ", " } else { "" };
      if index == self.cur_index {
        write!(f, ">>>{}<<<{}", display(self.schematic, port), comma)?;
      } else {
        write!(f, "{}{}", display(self.schematic, port), comma)?;
      }
    }
    if self.cur_index >= self.ports.len() {
      write!(f, ", >>>DONE<<<")?;
    }
    Ok(())
  }
}