node_flow/describe/
design.rs

1use std::any::type_name;
2
3use crate::node::{Node, NodeOutput};
4
5/// Represents a description of either a single [`Node`] or an entire flow of connected nodes.
6///
7/// This enum is primarily used for introspection and visualization of a flow.
8#[derive(Debug, Clone)]
9pub enum Description {
10    /// Single node description.
11    Node {
12        /// The base description containing type information and metadata.
13        base: DescriptionBase,
14    },
15    /// Description of a flow.
16    ///
17    /// Flow description is a collection of nodes connected by edges.
18    /// It's used for describing composite node structures and pipelines.
19    Flow {
20        /// The base description containing type information and metadata.
21        base: DescriptionBase,
22        /// The collection of node descriptions that make up this flow.
23        nodes: Vec<Description>,
24        /// The connections between nodes within this flow.
25        edges: Vec<Edge>,
26    },
27}
28
29impl Description {
30    /// Creates a new [`Description::Node`] from a given [`Node`] instance.
31    #[must_use]
32    pub fn new_node<NodeType, Input, Output, Error, Context>(node: &NodeType) -> Self
33    where
34        NodeType: Node<Input, NodeOutput<Output>, Error, Context>,
35    {
36        Self::Node {
37            base: DescriptionBase::from_node(node),
38        }
39    }
40
41    /// Creates a new [`Description::Flow`] from a given [`Node`] instance.
42    ///
43    /// # Parameters
44    /// - `node`: The flow-level node.
45    /// - `nodes`: The list of contained node descriptions.
46    /// - `edges`: The connections between nodes.
47    #[must_use]
48    pub fn new_flow<NodeType, Input, Output, Error, Context>(
49        node: &NodeType,
50        nodes: Vec<Self>,
51        edges: Vec<Edge>,
52    ) -> Self
53    where
54        NodeType: Node<Input, NodeOutput<Output>, Error, Context>,
55    {
56        Self::Flow {
57            base: DescriptionBase::from_node(node),
58            edges,
59            nodes,
60        }
61    }
62
63    /// Returns a mutable reference to the underlying [`DescriptionBase`].
64    #[must_use]
65    pub const fn get_base_mut(&mut self) -> &mut DescriptionBase {
66        match self {
67            Self::Node { base } | Self::Flow { base, .. } => base,
68        }
69    }
70
71    /// Returns an immutable reference to the underlying [`DescriptionBase`].
72    #[must_use]
73    pub const fn get_base_ref(&self) -> &DescriptionBase {
74        match self {
75            Self::Node { base } | Self::Flow { base, .. } => base,
76        }
77    }
78
79    /// Sets a description on this node or flow.
80    ///
81    /// This is primarily used to provide additional documentation or context.
82    #[must_use]
83    pub fn with_description(mut self, description: impl Into<String>) -> Self {
84        self.get_base_mut().description = Some(description.into());
85        self
86    }
87
88    /// Sets the external resources used by this node or flow.
89    ///
90    /// External resources could include remote calls, file accesses or other types of external services.
91    #[must_use]
92    pub fn with_externals(mut self, externals: Vec<ExternalResource>) -> Self {
93        self.get_base_mut().externals = Some(externals);
94        self
95    }
96
97    /// Modifies the name using a provided function.
98    ///
99    /// This is useful when you only want to modify the name.
100    #[must_use]
101    pub fn modify_name(mut self, func: impl FnOnce(&mut String)) -> Self {
102        let name = &mut self.get_base_mut().r#type.name;
103        func(name);
104        self
105    }
106}
107
108/// The base structure describing a node's type signature and metadata.
109///
110/// Contains information about the node's input, output, error, and context types,
111/// along with optional description and external resource metadata.
112#[derive(Debug, Clone)]
113pub struct DescriptionBase {
114    /// The type of the node or flow itself.
115    pub r#type: Type,
116    /// The type of input accepted by the node.
117    pub input: Type,
118    /// The type of output produced by the node.
119    pub output: Type,
120    /// The type of error that may be returned by the node.
121    pub error: Type,
122    /// The type of context used when executing the node.
123    pub context: Type,
124    /// An optional description of the node or flow.
125    pub description: Option<String>,
126    /// Optional list of external resources the node uses.
127    pub externals: Option<Vec<ExternalResource>>,
128}
129
130impl DescriptionBase {
131    /// Creates a [`DescriptionBase`] from type parameters.
132    #[must_use]
133    pub fn from<NodeType, Input, Output, Error, Context>() -> Self {
134        Self {
135            r#type: Type::of::<NodeType>(),
136            input: Type::of::<Input>(),
137            output: Type::of::<Output>(),
138            error: Type::of::<Error>(),
139            context: Type::of::<Context>(),
140            description: None,
141            externals: None,
142        }
143    }
144
145    /// Creates a [`DescriptionBase`] from a given [`Node`] instance.
146    #[must_use]
147    pub fn from_node<NodeType, Input, Output, Error, Context>(_node: &NodeType) -> Self
148    where
149        NodeType: Node<Input, NodeOutput<Output>, Error, Context>,
150    {
151        Self::from::<NodeType, Input, Output, Error, Context>()
152    }
153
154    /// Sets a description.
155    ///
156    /// This is primarily used to provide additional documentation or context.
157    #[must_use]
158    pub fn with_description(mut self, description: impl Into<String>) -> Self {
159        self.description = Some(description.into());
160        self
161    }
162
163    /// Sets the external resources.
164    ///
165    /// External resources could include remote calls, file accesses or other types of external services.
166    #[must_use]
167    pub fn with_externals(mut self, externals: Vec<ExternalResource>) -> Self {
168        self.externals = Some(externals);
169        self
170    }
171}
172
173/// Represents a type.
174#[derive(Debug, Clone)]
175pub struct Type {
176    /// The name of a type.
177    ///
178    /// By default it should be the fully qualified type name returned by `std::any::type_name::<T>()`.
179    pub name: String,
180}
181
182impl Type {
183    /// Creates a [`Type`] representing the type `T`.
184    #[must_use]
185    pub fn of<T>() -> Self {
186        Self {
187            name: type_name::<T>().to_owned(),
188        }
189    }
190
191    /// Creates a [`Type`] based on the value's type.
192    #[must_use]
193    pub fn of_val<T>(_: &T) -> Self {
194        Self::of::<T>()
195    }
196
197    /// Returns a simplified version of the type name.
198    ///
199    /// Instead of `std::option::Option<std::string::String>` it returns `Option<String>`.
200    #[cfg(feature = "describe_get_name_simple")]
201    #[must_use]
202    pub fn get_name_simple(&self) -> String {
203        tynm::TypeName::from(self.name.as_str()).as_str()
204    }
205}
206
207/// Represents a directional connection between nodes in a flow.
208///
209/// Each edge connects two [`EdgeEnding`]s, which can be either a node or the flow itself.
210#[derive(Debug, Clone)]
211pub struct Edge {
212    /// The starting point of the edge.
213    pub start: EdgeEnding,
214    /// The ending point of the edge.
215    pub end: EdgeEnding,
216}
217
218/// Represents one end of an [`Edge`].
219///
220/// An `EdgeEnding` can either connect to the flow or to a specific node.
221#[derive(Debug, Clone)]
222pub enum EdgeEnding {
223    /// The edge connects to the flow.
224    ToFlow,
225    /// The edge connects to a specific node.
226    ToNode {
227        /// The index of the node within the flow.
228        node_index: usize,
229    },
230}
231
232impl Edge {
233    /// Creates an edge that passes directly through the flow without connecting to any nodes.
234    #[must_use]
235    pub const fn passthrough() -> Self {
236        Self {
237            start: EdgeEnding::ToFlow,
238            end: EdgeEnding::ToFlow,
239        }
240    }
241
242    /// Creates an edge connecting the flow to a specific node.
243    #[must_use]
244    pub const fn flow_to_node(node_idx: usize) -> Self {
245        Self {
246            start: EdgeEnding::ToFlow,
247            end: EdgeEnding::ToNode {
248                node_index: node_idx,
249            },
250        }
251    }
252
253    /// Creates an edge connecting a node to the flow.
254    #[must_use]
255    pub const fn node_to_flow(node_idx: usize) -> Self {
256        Self {
257            start: EdgeEnding::ToNode {
258                node_index: node_idx,
259            },
260            end: EdgeEnding::ToFlow,
261        }
262    }
263
264    /// Creates an edge connecting one node to another.
265    #[must_use]
266    pub const fn node_to_node(start_node_idx: usize, end_node_idx: usize) -> Self {
267        Self {
268            start: EdgeEnding::ToNode {
269                node_index: start_node_idx,
270            },
271            end: EdgeEnding::ToNode {
272                node_index: end_node_idx,
273            },
274        }
275    }
276}
277
278/// Represents an external resource dependency.
279///
280/// These resources may represent things like files, APIs, or external data sources.
281#[derive(Debug, Clone)]
282pub struct ExternalResource {
283    /// The type of the external resource.
284    pub r#type: Type,
285    /// An optional description of the external resource.
286    pub description: Option<String>,
287    /// The type of data produced by this resource.
288    pub output: Type,
289}
290
291impl ExternalResource {
292    /// Creates a new [`ExternalResource`] description.
293    #[must_use]
294    pub fn new<ResourceType, Output>() -> Self {
295        Self {
296            r#type: Type::of::<ResourceType>(),
297            description: None,
298            output: Type::of::<Output>(),
299        }
300    }
301
302    /// Sets a description.
303    ///
304    /// This is primarily used to provide additional documentation or context.
305    #[must_use]
306    pub fn with_description(mut self, description: String) -> Self {
307        self.description = Some(description);
308        self
309    }
310}