node_flow/node/output.rs
1/// Represents the outcome of a node execution, with support for soft failures.
2///
3/// # Examples
4/// ```
5/// use node_flow::node::NodeOutput;
6///
7/// let success: NodeOutput<i32> = NodeOutput::Ok(42);
8/// let failure: NodeOutput<i32> = NodeOutput::SoftFail;
9///
10/// assert_eq!(success.ok(), Some(42));
11/// assert_eq!(failure.ok(), None);
12/// ```
13#[derive(Debug, PartialEq, Eq)]
14pub enum NodeOutput<T> {
15 /// Indicates that the node failed in a non-critical way and produced no output.
16 ///
17 /// This is distinct from a hard failure (error) and may simply mean that
18 /// input conditions were not met.
19 SoftFail,
20 /// Indicates that the node successfully produced a value of type `T`.
21 Ok(T),
22}
23
24impl<T> NodeOutput<T> {
25 /// Converts `NodeOutput<T>` into an [`Option<T>`].
26 ///
27 /// - Returns `Some(T)` if the output is [`NodeOutput::Ok`].
28 /// - Returns `None` if the output is [`NodeOutput::SoftFail`].
29 ///
30 /// # Examples
31 /// ```
32 /// use node_flow::node::NodeOutput;
33 ///
34 /// let output = NodeOutput::Ok(5);
35 /// assert_eq!(output.ok(), Some(5));
36 ///
37 /// let failed = NodeOutput::<i32>::SoftFail;
38 /// assert_eq!(failed.ok(), None);
39 /// ```
40 pub fn ok(self) -> Option<T> {
41 match self {
42 Self::SoftFail => None,
43 Self::Ok(val) => Some(val),
44 }
45 }
46
47 /// Converts `NodeOutput<T>` into a [`Result<T, E>`],
48 /// using a provided error value if soft-failed.
49 ///
50 /// - Returns `Ok(T)` if the output is [`NodeOutput::Ok`].
51 /// - Returns `Err(err)` if the output is [`NodeOutput::SoftFail`].
52 ///
53 /// # Errors
54 /// Returns the provided `err` value if the node soft-failed.
55 ///
56 /// # Examples
57 /// ```
58 /// use node_flow::node::NodeOutput;
59 ///
60 /// let ok: Result<i32, &str> = NodeOutput::Ok(42).ok_or("no value");
61 /// assert_eq!(ok, Ok(42));
62 ///
63 /// let soft_fail: Result<i32, &str> = NodeOutput::SoftFail.ok_or("no value");
64 /// assert_eq!(soft_fail, Err("no value"));
65 /// ```
66 pub fn ok_or<E>(self, err: E) -> Result<T, E> {
67 match self {
68 Self::SoftFail => Err(err),
69 Self::Ok(val) => Ok(val),
70 }
71 }
72
73 /// Converts `NodeOutput<T>` into a [`Result<T, E>`],
74 /// lazily computing the error value if soft-failed.
75 ///
76 /// - Returns `Ok(T)` if the output is [`NodeOutput::Ok`].
77 /// - Calls `err()` and returns `Err(err())` if the output is [`NodeOutput::SoftFail`].
78 ///
79 /// This is the lazy variant of [`NodeOutput::ok_or`], avoiding unnecessary error construction
80 /// when the node succeeds.
81 ///
82 /// # Errors
83 /// Calls the provided closure to produce an error if the node soft-failed.
84 ///
85 /// # Examples
86 /// ```
87 /// use node_flow::node::NodeOutput;
88 ///
89 /// let ok: Result<i32, String> = NodeOutput::Ok(10).ok_or_else(|| "soft fail".to_string());
90 /// assert_eq!(ok, Ok(10));
91 ///
92 /// let soft_fail: Result<i32, String> = NodeOutput::SoftFail.ok_or_else(|| "soft fail".to_string());
93 /// assert_eq!(soft_fail, Err("soft fail".to_string()));
94 /// ```
95 pub fn ok_or_else<E>(self, err: impl Fn() -> E) -> Result<T, E> {
96 match self {
97 Self::SoftFail => Err(err()),
98 Self::Ok(val) => Ok(val),
99 }
100 }
101}