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}