//! A seam in a widget from outside flowrs-core warns, as a builtin's did. //! //! The builtins have no seams left (`seam_visibility.rs` locks that census), so an //! [`install`]ed widget is the one place a program that compiles and then stops at //! `unimplemented!` can still come from. `install` puts a widget everywhere a builtin is; //! the diagnostics bar has to be one of those places, or its seam warning could never //! fire again and an extension's panic would arrive unannounced. //! //! Its own test binary, and one test: installing is process-wide and refused once the //! node catalog is built, so nothing else may run first. use flowrs_core::Graph; use flowrs_core::graph::diagnostics::{Severity, check, is_buildable}; use flowrs_core::graph::widget::{ CodegenContext, ParameterDef, PortSpec, Widget, WidgetCodegenResult, WidgetVisual, generates_seam, install, seam_widget_ids, }; use std::collections::HashMap; struct Stub { id: &'static str, process: &'static str, } impl Widget for Stub { fn id(&self) -> String { self.id.into() } fn name(&self) -> String { self.id.into() } fn category(&self) -> String { "Extension".into() } fn inputs(&self) -> Vec { vec![] } fn outputs(&self) -> Vec { vec![] } fn parameters(&self) -> Vec { vec![] } fn visual(&self) -> WidgetVisual { WidgetVisual { icon: String::new(), color: "#000".into(), min_size: (1, 1), resizable: false, } } fn codegen(&self, _: &CodegenContext, _: HashMap) -> WidgetCodegenResult { WidgetCodegenResult { outputs: HashMap::new(), imports: vec![], init: None, process: self.process.into(), cleanup: None, state: vec![], } } } #[test] fn an_installed_seam_warns_and_an_installed_implementation_does_not() { install(Box::new(Stub { id: "ext.seam", process: "unimplemented!(\"ext.seam: call the vendor SDK here\");", })) .expect("installs before the catalog is built"); install(Box::new(Stub { id: "ext.done", process: "let done = 1;", })) .expect("installs before the catalog is built"); assert!(generates_seam("ext.seam")); assert!(!generates_seam("ext.done")); assert!(!generates_seam("image.grayscale"), "a builtin that ships"); assert!( !seam_widget_ids().contains("ext.seam"), "an installed widget stays out of the builtin census" ); let mut g = Graph::new(); let seam = g.add_node("ext.seam", 0.0, 0.0); g.add_node("ext.done", 40.0, 0.0); let warned: Vec<_> = check(&g) .into_iter() .filter(|d| d.message.contains("generates a seam")) .collect(); assert_eq!(warned.len(), 1, "only the seam is flagged: {warned:?}"); let d = &warned[0]; assert_eq!( d.node, seam, "the warning points at the node that has the seam" ); assert_eq!( d.severity, Severity::Warning, "a seam is a warning, not an error" ); assert!(d.message.contains("`ext.seam`"), "{}", d.message); assert!(d.message.contains("unimplemented!"), "{}", d.message); assert!(is_buildable(&g), "a seam must not block the build"); // The warning describes the program that is really generated. let src = g.to_rust().expect("the graph generates"); assert!(src.contains("unimplemented!(\"ext.seam"), "{src}"); }