node_flow/node/base.rs
1use crate::describe::{Description, DescriptionBase};
2
3/// The `Node` trait serves as the core building block.
4///
5/// It defines how `Input` is processed into an `Output` (and `Error`) with a given `Context`.
6///
7/// # Type Parameters
8/// - `Input`: The type of data consumed by the node.
9/// - `Output`: The type of data produced by the node.
10/// - `Error`: The type representing possible errors.
11/// - `Context`: The type of context used during execution (it should always be a generic).
12///
13/// # Examples
14/// ```
15/// # use node_flow::node::{Node, NodeOutput};
16/// # use node_flow::describe::Description;
17/// #
18/// # struct ExampleNode;
19/// # struct MyError;
20/// #
21/// impl<Context: Send> Node<i32, NodeOutput<String>, MyError, Context> for ExampleNode {
22/// async fn run(
23/// &mut self,
24/// input: i32,
25/// _context: &mut Context,
26/// ) -> Result<NodeOutput<String>, MyError> {
27/// Ok(NodeOutput::Ok(format!("Processed: {}", input)))
28/// }
29///
30/// fn describe(&self) -> Description {
31/// Description::new_node::<Self, i32, String, MyError, Context>(self)
32/// .with_description("My example node")
33/// }
34/// }
35/// ```
36pub trait Node<Input, Output, Error, Context> {
37 /// Runs the node.
38 ///
39 /// This method performs the node's main computation or transformation logic.
40 ///
41 /// # Parameters
42 /// - `input`: The input data to process.
43 /// - `context`: Mutable reference to a context, which may be used for configuration, logging, or shared state.
44 ///
45 /// # Returns
46 /// A [`Future`] that resolves to a `Result<Output, Error>`.
47 ///
48 /// # Examples
49 /// ```
50 /// # use node_flow::node::Node;
51 /// #
52 /// # struct ExampleNode;
53 /// # struct MyError;
54 /// #
55 /// impl<Context: Send> Node<i32, String, MyError, Context> for ExampleNode {
56 /// async fn run(
57 /// &mut self,
58 /// input: i32,
59 /// _context: &mut Context,
60 /// ) -> Result<String, MyError> {
61 /// Ok(format!("Processed: {}", input))
62 /// }
63 /// }
64 /// ```
65 fn run(
66 &mut self,
67 input: Input,
68 context: &mut Context,
69 ) -> impl Future<Output = Result<Output, Error>> + Send;
70
71 /// Describes this node, its type signature and other specifics.
72 ///
73 /// See [`Description`] for more details.
74 ///
75 /// # Examples
76 /// ```
77 /// # use node_flow::node::{Node, NodeOutput};
78 /// # use node_flow::describe::Description;
79 /// #
80 /// # struct ExampleNode;
81 /// # struct MyError;
82 /// #
83 /// impl<Context: Send> Node<i32, NodeOutput<String>, MyError, Context> for ExampleNode {
84 /// async fn run(&mut self, input: i32, _context: &mut Context)
85 /// -> Result<NodeOutput<String>, MyError> { todo!() }
86 ///
87 /// fn describe(&self) -> Description {
88 /// Description::new_node::<Self, i32, String, MyError, Context>(self)
89 /// .with_description("My example node")
90 /// }
91 /// }
92 /// ```
93 // if specialization is ever stabilized this whole function can be removed
94 // and Describe trait with default impl for Node<..> could be used
95 // https://github.com/rust-lang/rust/issues/31844
96 fn describe(&self) -> Description
97 where
98 Self: Sized,
99 {
100 let mut base = DescriptionBase::from::<Self, Input, Output, Error, Context>();
101
102 // remove NodeOutput<> from output name
103 let output_name = &mut base.output.name;
104 if let Some(b_pos) = output_name.find('<')
105 && output_name[..b_pos].contains("NodeOutput")
106 {
107 // remove `..::NodeOutput<`
108 output_name.replace_range(0..=b_pos, "");
109 // remove ending `>`
110 output_name.pop();
111 }
112
113 Description::Node { base }
114 }
115}