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}