use std::{borrow::Cow, num::NonZeroU32};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct NodeId(pub NonZeroU32);
impl NodeId {
pub fn new(one_based: u32) -> Option<Self> {
NonZeroU32::new(one_based).map(Self)
}
pub fn get(self) -> u32 {
self.0.get()
}
#[allow(dead_code)] pub fn index(self) -> usize {
(self.0.get() - 1) as usize
}
}
#[allow(dead_code)]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StackId(pub NonZeroU32);
#[allow(dead_code)]
impl StackId {
pub fn new(one_based: u32) -> Option<Self> {
NonZeroU32::new(one_based).map(Self)
}
pub fn get(self) -> u32 {
self.0.get()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum PortSide {
Input,
Output,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PortId {
pub side: PortSide,
pub index: u16,
}
impl PortId {
pub fn input(index: u16) -> Self {
Self {
side: PortSide::Input,
index,
}
}
pub fn output(index: u16) -> Self {
Self {
side: PortSide::Output,
index,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct PortAddr {
pub node: NodeId,
pub port: PortId,
}
impl PortAddr {
pub fn new(node: NodeId, port: PortId) -> Self {
Self { node, port }
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct Link {
pub from: PortAddr,
pub to: PortAddr,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub struct StackLink {
pub from: StackId,
pub to: StackId,
}
pub type LinkVerdict = Result<(), Cow<'static, str>>;
#[derive(Debug, Clone, Default)]
pub struct PortDesc {
pub label: Cow<'static, str>,
pub color: Option<egui::Color32>,
pub arity: u8,
pub value: Option<Cow<'static, str>>,
pub connectable: bool,
pub expand_height: Option<f64>,
pub collapsible: bool,
}
impl PortDesc {
pub fn new(label: impl Into<Cow<'static, str>>) -> Self {
Self {
label: label.into(),
color: None,
arity: 0,
value: None,
connectable: true,
expand_height: None,
collapsible: false,
}
}
pub fn with_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
self.value = Some(value.into());
self
}
pub fn display_value(mut self, value: impl Into<Cow<'static, str>>) -> Self {
self.value = Some(value.into());
self.connectable = false;
self
}
pub fn with_editor_box(mut self, height: f64) -> Self {
self.value = Some(Cow::Borrowed(""));
self.expand_height = Some(height);
self
}
pub fn display_editor_box(mut self, height: f64) -> Self {
self.value = Some(Cow::Borrowed(""));
self.connectable = false;
self.expand_height = Some(height);
self
}
pub fn collapsible(mut self, expanded_height: Option<f64>) -> Self {
self.value = Some(Cow::Borrowed(""));
self.connectable = false;
self.collapsible = true;
self.expand_height = expanded_height;
self
}
pub fn with_color(mut self, color: egui::Color32) -> Self {
self.color = Some(color);
self
}
pub fn with_arity(mut self, arity: u8) -> Self {
self.arity = arity;
self
}
}
#[derive(Debug, Clone, Default)]
pub struct NodeDesc {
pub title: Cow<'static, str>,
pub inputs: Vec<PortDesc>,
pub outputs: Vec<PortDesc>,
pub accent: Option<egui::Color32>,
pub warning: Option<Cow<'static, str>>,
pub closable: bool,
}
impl NodeDesc {
pub fn new(title: impl Into<Cow<'static, str>>) -> Self {
Self {
title: title.into(),
..Default::default()
}
}
pub fn with_inputs(mut self, inputs: Vec<PortDesc>) -> Self {
self.inputs = inputs;
self
}
pub fn with_outputs(mut self, outputs: Vec<PortDesc>) -> Self {
self.outputs = outputs;
self
}
pub fn with_accent(mut self, accent: egui::Color32) -> Self {
self.accent = Some(accent);
self
}
pub fn with_warning(mut self, text: impl Into<Cow<'static, str>>) -> Self {
self.warning = Some(text.into());
self
}
pub fn closable(mut self) -> Self {
self.closable = true;
self
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct StackDesc {
pub id: StackId,
pub title: Cow<'static, str>,
pub members: Vec<NodeId>,
pub accent: Option<egui::Color32>,
}
#[allow(dead_code)]
impl StackDesc {
pub fn new(id: StackId, title: impl Into<Cow<'static, str>>) -> Self {
Self {
id,
title: title.into(),
members: Vec::new(),
accent: None,
}
}
pub fn with_members(mut self, members: Vec<NodeId>) -> Self {
self.members = members;
self
}
pub fn with_accent(mut self, accent: egui::Color32) -> Self {
self.accent = Some(accent);
self
}
}
pub trait GraphViewer {
fn node_ids(&self) -> Vec<NodeId>;
fn node(&self, id: NodeId) -> NodeDesc;
fn links(&self) -> Vec<Link>;
fn stacks(&self) -> Vec<StackDesc> {
Vec::new()
}
fn stack_links(&self) -> Vec<StackLink> {
Vec::new()
}
fn validate_link(&self, _from: PortAddr, _to: PortAddr) -> LinkVerdict {
Ok(())
}
}