use std::{borrow::Cow, sync::Arc};
use masonry::kurbo::Point;
use petgraph::{prelude::*, visit::IntoEdgeReferences};
pub type Graph<N> = StableDiGraph<Arc<N>, Edge>;
pub trait Node: Clone {
type Icon;
type Widget;
type PortShape;
fn in_arity(&self) -> u16;
fn out_arity(&self) -> u16;
fn in_port(&self, index: u16) -> Port<'_, Self::PortShape, InputLabel<'_, Self::Widget>>;
fn out_port(&self, index: u16) -> Port<'_, Self::PortShape, OutputLabel<'_>>;
fn name(&self) -> Cow<'_, str>;
fn position(&self) -> Point;
fn position_mut(&mut self) -> Option<&mut Point>;
#[inline]
fn style(&self) -> NodeStyle { NodeStyle::Large }
#[inline]
fn width(&self) -> f64 {
match self.style() {
NodeStyle::Small => 84.0,
NodeStyle::Medium => 96.0,
NodeStyle::Large => 192.0,
}
}
#[inline]
fn label(&self) -> NodeLabel<'_, Self::Icon, Self::Widget> { NodeLabel::Name }
}
#[derive(Debug, Clone, Default)]
pub struct Port<'n, S, L> {
pub name: Cow<'n, str>,
pub shape: S,
pub label: L,
}
#[derive(Debug, Clone, Default)]
pub enum InputLabel<'n, W> {
#[default]
Name,
Text(Cow<'n, str>),
Widget(W),
}
#[derive(Debug, Clone, Default)]
pub enum OutputLabel<'n> {
#[default]
Name,
Text(Cow<'n, str>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NodeStyle {
Small,
Medium,
Large,
}
#[derive(Debug, Clone)]
pub enum NodeLabel<'n, I, W> {
Name,
Text(Cow<'n, str>),
Icon(I),
Widget(W),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum NoWidget {}
impl masonry::core::Widget for NoWidget {
#[inline]
fn register_children(&mut self, _: &mut masonry::core::RegisterCtx) { match *self {} }
#[inline]
fn layout(
&mut self,
_: &mut masonry::core::LayoutCtx,
_: &mut masonry::core::PropertiesMut<'_>,
_: &masonry::core::BoxConstraints,
) -> masonry::kurbo::Size {
match *self {}
}
#[inline]
fn paint(
&mut self,
_: &mut masonry::core::PaintCtx,
_: &masonry::core::PropertiesRef<'_>,
_: &mut masonry::vello::Scene,
) {
match *self {}
}
#[inline]
fn accessibility_role(&self) -> accesskit::Role { match *self {} }
#[inline]
fn accessibility(
&mut self,
_: &mut masonry::core::AccessCtx,
_: &masonry::core::PropertiesRef<'_>,
_: &mut accesskit::Node,
) {
match *self {}
}
#[inline]
fn children_ids(&self) -> smallvec::SmallVec<[masonry::core::WidgetId; 16]> { match *self {} }
}
#[derive(Debug, Clone, Copy)]
pub struct Edge {
pub from_port: u16,
pub to_port: u16,
}
pub const PORT_MAX: u16 = u16::MAX;
#[inline]
#[expect(clippy::must_use_candidate, reason = "Using this is impossible")]
pub fn port_overflow<T>() -> T { panic!("Port number exceeded {PORT_MAX}") }
#[derive(Debug)]
pub struct Checked<G>(Arc<G>);
impl<G> Checked<G> {
#[expect(
clippy::missing_safety_doc,
reason = "WIP, adding this will suppress missing-docs warnings"
)]
#[inline]
#[must_use]
pub const unsafe fn new_unchecked(graph: Arc<G>) -> Self { Self(graph) }
#[must_use]
pub fn into_inner(self) -> Arc<G> { self.0 }
}
impl<N: Node> Checked<Graph<N>> {
#[must_use]
pub fn new(graph: Arc<Graph<N>>) -> Self {
for edge in graph.edge_references() {
let weight = edge.weight();
assert!(
weight.from_port < graph[edge.source()].out_arity(),
"Invalid edge source port index"
);
assert!(
weight.to_port < graph[edge.target()].in_arity(),
"Invalid edge target port index"
);
}
Self(graph)
}
}