diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface.rs b/editor/src/messages/portfolio/document/utility_types/network_interface.rs index 351e632786..23ab3c64f2 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface.rs @@ -648,7 +648,7 @@ impl NodeNetworkInterface { let export_name = if !export_name.is_empty() { export_name - } else if let Some(export_type_name) = input_type.compiled_nested_type().map(|nested| nested.to_string()) { + } else if let Some(export_type_name) = input_type.compiled_nested_type().map(ToString::to_string) { export_type_name } else { format!("Export #{}", export_index) @@ -658,12 +658,19 @@ impl NodeNetworkInterface { } }; + let valid_types = self.potential_valid_input_types(input_connector, network_path).iter().map(ToString::to_string).collect::>(); + let valid_types = { + // Dedupe while preserving order + let mut found = HashSet::new(); + valid_types.into_iter().filter(|s| found.insert(s.clone())).collect::>() + }; + Some(FrontendGraphInput { data_type, resolved_type, name, description, - valid_types: self.potential_valid_input_types(input_connector, network_path).iter().map(|ty| ty.to_string()).collect(), + valid_types, connected_to, }) } @@ -698,7 +705,7 @@ impl NodeNetworkInterface { let import_name = if !import_name.is_empty() { import_name - } else if let Some(import_type_name) = output_type.compiled_nested_type().map(|nested| nested.to_string()) { + } else if let Some(import_type_name) = output_type.compiled_nested_type().map(ToString::to_string) { import_type_name } else { format!("Import #{}", import_index) diff --git a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs index 97eb99ac14..c24f73ea04 100644 --- a/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs +++ b/editor/src/messages/portfolio/document/utility_types/network_interface/resolved_types.rs @@ -91,8 +91,8 @@ impl TypeSource { /// The type to display in the tooltip label. pub fn resolved_type_tooltip_string(&self) -> String { match self { - TypeSource::Compiled(compiled_type) => format!("Data Type: {:?}", compiled_type.nested_type().to_string()), - TypeSource::TaggedValue(value_type) => format!("Data Type: {:?}", value_type.nested_type().to_string()), + TypeSource::Compiled(compiled_type) => format!("Data Type: {}", compiled_type.nested_type()), + TypeSource::TaggedValue(value_type) => format!("Data Type: {}", value_type.nested_type()), TypeSource::Unknown => "Unknown Data Type".to_string(), TypeSource::Invalid => "Invalid Type Combination".to_string(), TypeSource::Error(_) => "Error Getting Data Type".to_string(), @@ -180,7 +180,7 @@ impl NodeNetworkInterface { self.input_type_not_invalid(input_connector, network_path) } - // Gets the default tagged value for an input. If its not compiled, then it tries to get a valid type. If there are no valid types, then it picks a random implementation + /// Gets the default tagged value for an input. If its not compiled, then it tries to get a valid type. If there are no valid types, then it picks a random implementation. pub fn tagged_value_from_input(&mut self, input_connector: &InputConnector, network_path: &[NodeId]) -> TaggedValue { let guaranteed_type = match self.input_type(input_connector, network_path) { TypeSource::Compiled(compiled) => compiled, @@ -190,12 +190,12 @@ impl NodeNetworkInterface { // TODO: Add a NodeInput::Indeterminate which can be resolved at compile time to be any type that prevents an error. This may require bidirectional typing. self.complete_valid_input_types(input_connector, network_path) .into_iter() - .min_by_key(|ty| ty.nested_type().to_string()) + .min_by_key(|ty| ty.nested_type().identifier_name()) // Pick a random type from the potential valid types .or_else(|| { self.potential_valid_input_types(input_connector, network_path) .into_iter() - .min_by_key(|ty| ty.nested_type().to_string()) + .min_by_key(|ty| ty.nested_type().identifier_name()) }).unwrap_or(concrete!(())) } TypeSource::Error(e) => { diff --git a/node-graph/graph-craft/src/proto.rs b/node-graph/graph-craft/src/proto.rs index 8ceef76873..e9f05053bc 100644 --- a/node-graph/graph-craft/src/proto.rs +++ b/node-graph/graph-craft/src/proto.rs @@ -794,7 +794,14 @@ impl TypingContext { .into_iter() .chain(&inputs) .enumerate() - .filter_map(|(i, t)| if i == 0 { None } else { Some(format!("• Input {}: {t}", i + convert_node_index_offset)) }) + .filter_map(|(i, t)| { + if i == 0 { + None + } else { + let number = i + convert_node_index_offset; + Some(format!("• Input {number}: {t}")) + } + }) .collect::>() .join("\n"); Err(vec![GraphError::new(node, GraphErrorType::InvalidImplementations { inputs, error_inputs })]) @@ -821,13 +828,13 @@ impl TypingContext { return Ok(node_io.clone()); } } - let inputs = [call_argument].into_iter().chain(&inputs).map(|t| t.to_string()).collect::>().join(", "); + let inputs = [call_argument].into_iter().chain(&inputs).map(ToString::to_string).collect::>().join(", "); let valid = valid_output_types.into_iter().cloned().collect(); Err(vec![GraphError::new(node, GraphErrorType::MultipleImplementations { inputs, valid })]) } _ => { - let inputs = [call_argument].into_iter().chain(&inputs).map(|t| t.to_string()).collect::>().join(", "); + let inputs = [call_argument].into_iter().chain(&inputs).map(ToString::to_string).collect::>().join(", "); let valid = valid_output_types.into_iter().cloned().collect(); Err(vec![GraphError::new(node, GraphErrorType::MultipleImplementations { inputs, valid })]) } @@ -871,9 +878,7 @@ fn check_generic(types: &NodeIOTypes, input: &Type, parameters: &[Type], generic /// Returns a list of all generic types used in the node fn replace_generics(types: &mut NodeIOTypes, lookup: &HashMap) { let replace = |ty: &Type| { - let Type::Generic(ident) = ty else { - return None; - }; + let Type::Generic(ident) = ty else { return None }; lookup.get(ident.as_ref()).cloned() }; types.call_argument.replace_nested(replace); diff --git a/node-graph/libraries/core-types/src/types.rs b/node-graph/libraries/core-types/src/types.rs index cdef025ae4..38e8f09ffa 100644 --- a/node-graph/libraries/core-types/src/types.rs +++ b/node-graph/libraries/core-types/src/types.rs @@ -118,11 +118,10 @@ impl NodeIOTypes { impl std::fmt::Debug for NodeIOTypes { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_fmt(format_args!( - "node({}) → {}", - [&self.call_argument].into_iter().chain(&self.inputs).map(|input| input.to_string()).collect::>().join(", "), - self.return_value - )) + let inputs = self.inputs.iter().map(ToString::to_string).collect::>().join(", "); + let return_value = &self.return_value; + let call_argument = &self.call_argument; + f.write_fmt(format_args!("({inputs}) → {return_value} called with {call_argument}")) } } @@ -202,7 +201,7 @@ impl std::hash::Hash for TypeDescriptor { impl std::fmt::Display for TypeDescriptor { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let text = make_type_user_readable(&format_type(&self.name)); + let text = make_type_user_readable(&simplify_identifier_name(&self.name)); write!(f, "{text}") } } @@ -337,15 +336,17 @@ impl Type { } } - pub fn to_cow_string(&self) -> Cow<'static, str> { + pub fn identifier_name(&self) -> String { match self { - Type::Generic(name) => name.clone(), - _ => Cow::Owned(self.to_string()), + Type::Generic(name) => name.to_string(), + Type::Concrete(ty) => simplify_identifier_name(&ty.name), + Type::Fn(call_arg, return_value) => format!("{} called with {}", return_value.identifier_name(), call_arg.identifier_name()), + Type::Future(ty) => ty.identifier_name(), } } } -pub fn format_type(ty: &str) -> String { +pub fn simplify_identifier_name(ty: &str) -> String { ty.split('<') .map(|path| path.split(',').map(|path| path.split("::").last().unwrap_or(path)).collect::>().join(",")) .collect::>() @@ -361,42 +362,26 @@ pub fn make_type_user_readable(ty: &str) -> String { impl std::fmt::Debug for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let text = match self { - Self::Generic(name) => name.to_string(), - #[cfg(feature = "type_id_logging")] - Self::Concrete(ty) => format!("Concrete<{}, {:?}>", ty.name, ty.id), - #[cfg(not(feature = "type_id_logging"))] - Self::Concrete(ty) => format_type(&ty.name), - Self::Fn(call_arg, return_value) => format!("{return_value:?} called with {call_arg:?}"), - Self::Future(ty) => format!("{ty:?}"), - }; - let text = make_type_user_readable(&text); - write!(f, "{text}") + write!(f, "{self}") } } +// Display impl std::fmt::Display for Type { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - if self == &concrete!(glam::DVec2) { - return write!(f, "vec2"); - } - if self == &concrete!(glam::DAffine2) { - return write!(f, "transform"); - } - if self == &concrete!(Footprint) { - return write!(f, "footprint"); - } - if self == &concrete!(&str) || self == &concrete!(String) { - return write!(f, "string"); - } + use glam::*; - let text = match self { - Type::Generic(name) => name.to_string(), - Type::Concrete(ty) => format_type(&ty.name), - Type::Fn(call_arg, return_value) => format!("{return_value} called with {call_arg}"), - Type::Future(ty) => ty.to_string(), - }; - let text = make_type_user_readable(&text); - write!(f, "{text}") + match self { + Type::Generic(name) => write!(f, "{}", make_type_user_readable(name)), + Type::Concrete(ty) => match () { + () if self == &concrete!(DVec2) || self == &concrete!(Vec2) || self == &concrete!(IVec2) || self == &concrete!(UVec2) => write!(f, "Vec2"), + () if self == &concrete!(glam::DAffine2) => write!(f, "Transform"), + () if self == &concrete!(Footprint) => write!(f, "Footprint"), + () if self == &concrete!(&str) || self == &concrete!(String) => write!(f, "String"), + _ => write!(f, "{}", make_type_user_readable(&simplify_identifier_name(&ty.name))), + }, + Type::Fn(call_arg, return_value) => write!(f, "{return_value} called with {call_arg}"), + Type::Future(ty) => write!(f, "{ty}"), + } } } diff --git a/node-graph/preprocessor/src/lib.rs b/node-graph/preprocessor/src/lib.rs index b945466a57..0f9bf398e4 100644 --- a/node-graph/preprocessor/src/lib.rs +++ b/node-graph/preprocessor/src/lib.rs @@ -36,7 +36,7 @@ pub fn generate_node_substitutions() -> HashMap = implementations.iter().map(|(_, node_io)| node_io.call_argument.clone()).collect(); let first_node_io = implementations.first().map(|(_, node_io)| node_io).unwrap_or(const { &NodeIOTypes::empty() }); let mut node_io_types = vec![HashSet::new(); fields.len()]; @@ -69,8 +69,8 @@ pub fn generate_node_substitutions() -> HashMap", input_ty.clone())); - let convert_node_identifier = ProtoNodeIdentifier::with_owned_string(format!("graphene_core::ops::ConvertNode<{}>", input_ty.clone())); + let into_node_identifier = ProtoNodeIdentifier::with_owned_string(format!("graphene_core::ops::IntoNode<{}>", input_ty.identifier_name())); + let convert_node_identifier = ProtoNodeIdentifier::with_owned_string(format!("graphene_core::ops::ConvertNode<{}>", input_ty.identifier_name())); let proto_node = if into_node_registry.keys().any(|ident: &ProtoNodeIdentifier| ident.as_str() == into_node_identifier.as_str()) { generated_nodes += 1;