use enum_dispatch::enum_dispatch; use std::fmt; use crate::library; use crate::library::*; use crate::types::{Type, TypeSignature}; #[derive(Debug, Copy, Clone)] pub enum CompileError { MissingArguments, InvalidArguments, Codegen(fmt::Error), } impl From for CompileError { fn from(v: fmt::Error) -> Self { Self::Codegen(v) } } /// an instantiable Node, parametrized by its connected input types pub const MAX_INPUTS: usize = 16; #[enum_dispatch] pub trait ConcreteNode { /// given the current connections, which inputs are available? /// /// This can return more inputs than those connected, indicating missing or optional connections. /// This must not fail but fallback to a default signature that allows connections to be made. fn visible_inputs(&self, connected: &[Option; MAX_INPUTS]) -> Vec>; /// given the current connections, what is this node's signature? /// /// This determines the output types shown and is used for compilation. If no valid signature corresponds /// to the set of inputs, this must fail. If the implementation accepts optional inputs, it may validate /// them here or leave that to be done in `compile`. fn signature( &self, connected: &[Option; MAX_INPUTS], ) -> Result; /// generate code for this node. /// /// The number of `inputs` and `outputs` correspond to the types in `signature`. /// This must fail if valid code can not be generated. fn compile( &self, f: &mut dyn fmt::Write, signature: TypeSignature, inputs: Vec>, outputs: Vec, ) -> Result<(), CompileError>; /// optionaly render additional UI in the body of this node. fn show_body(&mut self, ui: &mut egui::Ui) -> egui::Response { ui.response() } } #[enum_dispatch(ConcreteNode)] #[derive(Clone, PartialEq, serde::Serialize, serde::Deserialize)] pub enum AnyNode { Constant(library::AnyConstant), Conversion(library::Conversion), Input(library::Input), Arithmetic(library::BinArithmetic), Builtin(library::BuiltinFunction), Output(library::Output), } impl NodeIndex for AnyNode { const TITLE: &'static str = ""; fn all() -> impl Iterator { (library::AnyConstant::all().map(AnyNode::from)) .chain(library::Conversion::all().map(AnyNode::from)) .chain(library::Input::all().map(AnyNode::from)) .chain(library::Output::all().map(AnyNode::from)) .chain(library::BuiltinFunction::all().map(AnyNode::from)) } fn pick_node(ui: &mut egui::Ui) -> Option { ui.label("Add node"); None.or(library::AnyConstant::pick_node(ui).map(AnyNode::from)) .or(library::Input::pick_node(ui).map(AnyNode::from)) .or(library::Output::pick_node(ui).map(AnyNode::from)) .or({ ui.separator(); None }) .or(library::BinArithmetic::pick_node(ui).map(AnyNode::from)) .or(library::Conversion::pick_node(ui).map(AnyNode::from)) .or(library::BuiltinFunction::pick_node(ui).map(AnyNode::from)) } } impl fmt::Display for AnyNode { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Constant(x) => fmt::Display::fmt(x, f), Self::Conversion(x) => fmt::Display::fmt(x, f), Self::Input(x) => fmt::Display::fmt(x, f), Self::Arithmetic(x) => fmt::Display::fmt(x, f), Self::Builtin(x) => fmt::Display::fmt(x, f), Self::Output(x) => fmt::Display::fmt(x, f), } } }