diff options
| author | s-ol <s+removethis@s-ol.nu> | 2026-01-23 18:10:39 +0000 |
|---|---|---|
| committer | s-ol <s+removethis@s-ol.nu> | 2026-01-23 18:10:39 +0000 |
| commit | d4366a4c12224da7ef3c08b9ca9d119da11b302a (patch) | |
| tree | 4b3f746e6fb071863bec80cf2e763e830226a9eb /src | |
| parent | variadic arithmetic (diff) | |
| download | nodetoy-d4366a4c12224da7ef3c08b9ca9d119da11b302a.tar.gz nodetoy-d4366a4c12224da7ef3c08b9ca9d119da11b302a.zip | |
Error checking
Diffstat (limited to 'src')
| -rw-r--r-- | src/library.rs | 50 | ||||
| -rw-r--r-- | src/main.rs | 59 | ||||
| -rw-r--r-- | src/node.rs | 14 | ||||
| -rw-r--r-- | src/snarl_ext.rs | 21 |
4 files changed, 108 insertions, 36 deletions
diff --git a/src/library.rs b/src/library.rs index cd3b36c..65929a0 100644 --- a/src/library.rs +++ b/src/library.rs @@ -3,7 +3,7 @@ use itertools::{Itertools, izip}; use serde::{Deserialize, Serialize}; use std::fmt; -use crate::node::ConcreteNode; +use crate::node::{CompileError, ConcreteNode}; use crate::types::{ Dimension, Dimension::*, FloatPrecision::*, ScalarType, ScalarType::*, Type, Type::*, TypeSignature, @@ -70,7 +70,7 @@ trait FixedNode: fmt::Debug { inputs: Vec<String>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let mut pairs = signature.outputs.iter().zip(outputs.iter()); let (out_typ, out_name) = pairs.next().expect("no output"); @@ -82,7 +82,8 @@ trait FixedNode: fmt::Debug { let first = func[..1].to_lowercase(); let rest = func[1..].to_string(); let params = inputs.iter().chain(outputs.iter().skip(1)).join(", "); - writeln!(f, "{out_typ} {out_name} = {first}{rest}({params});") + writeln!(f, "{out_typ} {out_name} = {first}{rest}({params});")?; + Ok(()) } } @@ -111,12 +112,12 @@ impl<T: FixedNode> ConcreteNode for T { inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let inputs: Option<Vec<String>> = inputs.into_iter().collect(); if let Some(inputs) = inputs { self.compile(signature, inputs, outputs, f) } else { - Err(fmt::Error) + Err(CompileError::MissingArguments) } } } @@ -135,7 +136,7 @@ impl ConcreteNode for ProvideImpl { trait OutputOnlyNode: fmt::Debug { fn output_type(&self) -> Type; - fn compile(&self, out_type: Type, output: &str, f: &mut dyn fmt::Write) -> fmt::Result; + fn compile(&self, out_type: Type, output: &str, f: &mut dyn fmt::Write) -> Result<(), CompileError>; } impl<T: OutputOnlyNode> ConcreteNode for T { @@ -160,7 +161,7 @@ impl<T: OutputOnlyNode> ConcreteNode for T { inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { self.compile(signature.outputs[0], &outputs[0], f) } } @@ -235,7 +236,7 @@ impl ConcreteNode for BinArithmetic { inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let out_typ = &signature.outputs[0]; let out_name = &outputs[0]; let sym = match self { @@ -245,7 +246,8 @@ impl ConcreteNode for BinArithmetic { Self::Divide => " / ", }; let expr = inputs.iter().filter_map(|s| s.as_ref()).join(sym); - writeln!(f, "{out_typ} {out_name} = {expr};") + writeln!(f, "{out_typ} {out_name} = {expr};")?; + Ok(()) } } @@ -791,14 +793,15 @@ impl FixedNode for Output { inputs: Vec<String>, _outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { match signature.inputs[0] { - Vector(_, D2) => writeln!(f, "out_color = vec4({}, 0.0, 1.0);", inputs[0]), - Vector(_, D3) => writeln!(f, "out_color = vec4({}, 1.0);", inputs[0]), - Vector(_, D4) => writeln!(f, "out_color = {};", inputs[0]), - Scalar(_) => writeln!(f, "out_color = vec4(vec3({}), 1.0);", inputs[0]), + Vector(_, D2) => writeln!(f, "out_color = vec4({}, 0.0, 1.0);", inputs[0])?, + Vector(_, D3) => writeln!(f, "out_color = vec4({}, 1.0);", inputs[0])?, + Vector(_, D4) => writeln!(f, "out_color = {};", inputs[0])?, + Scalar(_) => writeln!(f, "out_color = vec4(vec3({}), 1.0);", inputs[0])?, _ => unreachable!("invalid output type"), - } + }; + Ok(()) } } impl NodeIndex for Output { @@ -968,10 +971,11 @@ where _inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let out_typ = Self::TYPE; let out_name = &outputs[0]; - writeln!(f, "{out_typ} {out_name} = {};", self) + writeln!(f, "{out_typ} {out_name} = {};", self)?; + Ok(()) } fn show_body(&mut self, ui: &mut egui::Ui) -> egui::Response { @@ -1122,7 +1126,7 @@ impl ConcreteNode for Input { _inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let out_typ = &signature.outputs[0]; let out_name = &outputs[0]; writeln!( @@ -1133,7 +1137,8 @@ impl ConcreteNode for Input { Self::Resolution => "in_resolution", Self::Time => "in_time", } - ) + )?; + Ok(()) } } impl NodeIndex for Input { @@ -1173,7 +1178,7 @@ impl FixedNode for SplitVector { inputs: Vec<String>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let input = &inputs[0]; for (typ, name, component) in izip!( signature.outputs.into_iter(), @@ -1223,11 +1228,12 @@ impl FixedNode for CombineVector { inputs: Vec<String>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result { + ) -> Result<(), CompileError> { let out_typ = &signature.outputs[0]; let out_name = &outputs[0]; let params = inputs.iter().join(", "); - writeln!(f, "{out_typ} {out_name} = {out_typ}({params});") + writeln!(f, "{out_typ} {out_name} = {out_typ}({params});")?; + Ok(()) } } impl fmt::Display for CombineVector { diff --git a/src/main.rs b/src/main.rs index 35a3ca3..ad81cea 100644 --- a/src/main.rs +++ b/src/main.rs @@ -19,7 +19,7 @@ mod wasm; use library::NodeIndex; use node::{AnyNode, ConcreteNode}; -use snarl_ext::{Compilable, WithSignatures}; +use snarl_ext::{Compilable, GraphError, WithSignatures}; use types::{Dimension, FloatPrecision, ScalarType, Type}; impl Into<PinInfo> for Type { @@ -41,12 +41,30 @@ impl Into<PinInfo> for Type { } } +static ERROR_COLOR: Color32 = Color32::from_rgb(0xc0, 0x00, 0x00); + pub struct Viewer { dirty: bool, filename: Option<PathBuf>, + error: Option<GraphError>, + + #[cfg(target_arch = "wasm32")] save_as_dialog: Option<String>, } +impl Default for Viewer { + fn default() -> Self { + Self { + dirty: true, + filename: None, + error: None, + + #[cfg(target_arch = "wasm32")] + save_as_dialog: None, + } + } +} + impl SnarlViewer<AnyNode> for Viewer { #[inline] fn connect(&mut self, from: &OutPin, to: &InPin, snarl: &mut Snarl<AnyNode>) { @@ -126,6 +144,23 @@ impl SnarlViewer<AnyNode> for Viewer { } } + fn node_frame( + &mut self, + mut default: egui::Frame, + node: NodeId, + _inputs: &[InPin], + _outputs: &[OutPin], + _snarl: &Snarl<AnyNode>, + ) -> egui::Frame { + if Some(node) == self.error.and_then(|e| e.1) { + default.stroke = egui::Stroke { + width: 2.0, + color: ERROR_COLOR, + }; + } + + default + } fn has_graph_menu(&mut self, _pos: egui::Pos2, _snarl: &mut Snarl<AnyNode>) -> bool { true } @@ -133,6 +168,7 @@ impl SnarlViewer<AnyNode> for Viewer { fn show_graph_menu(&mut self, pos: egui::Pos2, ui: &mut Ui, snarl: &mut Snarl<AnyNode>) { if let Some(node) = AnyNode::pick_node(ui) { snarl.insert_node(pos, node); + self.dirty = true; ui.close(); } } @@ -152,6 +188,7 @@ impl SnarlViewer<AnyNode> for Viewer { ui.label("Node menu"); if ui.button("Remove").clicked() { snarl.remove_node(node); + self.dirty = true; ui.close(); } } @@ -221,11 +258,7 @@ impl App { App { snarl, - viewer: Viewer { - dirty: true, - filename: None, - save_as_dialog: None, - }, + viewer: Default::default(), preview: preview::Preview::new(cx).expect("Failed to init preview"), } } @@ -371,10 +404,13 @@ impl eframe::App for App { match self.snarl.compile(&mut buf) { Ok(()) => { info!("{}", buf); + self.viewer.error = None; Some(buf) } Err(x) => { - error!("Error compiling: {}", x); + error!("Error compiling: {:?}", x); + self.viewer.dirty = false; + self.viewer.error = Some(x); None } } @@ -388,6 +424,15 @@ impl eframe::App for App { self.viewer.dirty = false; } }); + + if let Some(err) = &self.viewer.error { + let message = if let Some(node) = err.1 { + format!("Error in node {}: {:?}", node.0, err.0) + } else { + format!("Error while compiling: {:?}", err.0) + }; + ui.colored_label(Color32::from_rgb(0xff, 0, 0), message); + } }); egui::CentralPanel::default().show(ctx, |ui| { diff --git a/src/node.rs b/src/node.rs index 3bcd73b..b63270e 100644 --- a/src/node.rs +++ b/src/node.rs @@ -5,6 +5,18 @@ use crate::library; use crate::library::*; use crate::types::{Type, TypeSignature}; +#[derive(Debug, Copy, Clone)] +pub enum CompileError { + MissingArguments, + Codegen(fmt::Error), +} + +impl From<fmt::Error> for CompileError { + fn from(v: fmt::Error) -> Self { + Self::Codegen(v) + } +} + /// an instantiable Node, parametrized by its connected input types #[enum_dispatch] pub trait ConcreteNode { @@ -24,7 +36,7 @@ pub trait ConcreteNode { inputs: Vec<Option<String>>, outputs: Vec<String>, f: &mut dyn fmt::Write, - ) -> fmt::Result; + ) -> Result<(), CompileError>; fn show_body(&mut self, ui: &mut egui::Ui) -> egui::Response { ui.response() diff --git a/src/snarl_ext.rs b/src/snarl_ext.rs index e36eb2e..5cbb065 100644 --- a/src/snarl_ext.rs +++ b/src/snarl_ext.rs @@ -2,7 +2,7 @@ use egui_snarl::{InPinId, NodeId, OutPinId, Snarl}; use std::fmt; use topological_sort::TopologicalSort; -use crate::node::{AnyNode, ConcreteNode}; +use crate::node::{AnyNode, CompileError, ConcreteNode}; use crate::types::{Type, TypeSignature}; pub trait WithSignatures { @@ -13,9 +13,17 @@ pub trait WithSignatures { fn get_num_inputs(&self, node: NodeId) -> usize; } +#[derive(fmt::Debug, Copy, Clone)] +pub struct GraphError(pub CompileError, pub Option<NodeId>); +impl From<fmt::Error> for GraphError { + fn from(v: fmt::Error) -> Self { + Self(v.into(), None) + } +} + pub trait Compilable { - fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> fmt::Result; - fn compile(&self, f: &mut dyn fmt::Write) -> fmt::Result; + fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> Result<(), CompileError>; + fn compile(&self, f: &mut dyn fmt::Write) -> Result<(), GraphError>; } impl WithSignatures for Snarl<AnyNode> { @@ -57,7 +65,7 @@ fn compile_output(pin: OutPinId) -> String { } impl Compilable for Snarl<AnyNode> { - fn compile(&self, f: &mut dyn fmt::Write) -> fmt::Result { + fn compile(&self, f: &mut dyn fmt::Write) -> Result<(), GraphError> { let mut order = TopologicalSort::<NodeId>::new(); for (out, inp) in self.wires() { order.add_dependency(out.node, inp.node); @@ -80,14 +88,15 @@ void main() {{ " )?; while let Some(id) = order.pop() { - self.compile_node(id, f)?; + self.compile_node(id, f) + .map_err(|inner| GraphError(inner, Some(id)))?; } writeln!(f, "}}")?; Ok(()) } - fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> fmt::Result { + fn compile_node(&self, node: NodeId, f: &mut dyn fmt::Write) -> Result<(), CompileError> { let signature = self.get_node_signature(node); let inputs: Vec<Option<String>> = (0..signature.inputs.len()) |
