Skip to main content

qubit_function/tasks/
runnable.rs

1/*******************************************************************************
2 *
3 *    Copyright (c) 2025 - 2026.
4 *    Haixing Hu, Qubit Co. Ltd.
5 *
6 *    All rights reserved.
7 *
8 ******************************************************************************/
9//! # Runnable Types
10//!
11//! Provides fallible, one-time, zero-argument actions.
12//!
13//! A `Runnable<E>` is equivalent to `FnOnce() -> Result<(), E>`, but uses
14//! task-oriented vocabulary. Use it when the operation's side effect matters
15//! and only success or failure should be reported.
16//!
17//! The trait itself does not require `Send`; concurrent executors should add
18//! `+ Send + 'static` at their API boundary.
19//!
20//! # Author
21//!
22//! Haixing Hu
23
24use std::fmt;
25
26use crate::{
27    suppliers::supplier_once::SupplierOnce,
28    tasks::callable::BoxCallable,
29};
30
31// ============================================================================
32// Runnable Trait
33// ============================================================================
34
35/// A fallible one-time action.
36///
37/// `Runnable<E>` consumes itself and returns `Result<(), E>`. It is a semantic
38/// specialization of `SupplierOnce<Result<(), E>>` for executable actions and
39/// deferred side effects.
40///
41/// # Type Parameters
42///
43/// * `E` - The error value returned when the action fails.
44///
45/// # Examples
46///
47/// ```rust
48/// use qubit_function::Runnable;
49///
50/// let task = || Ok::<(), String>(());
51/// assert_eq!(task.run(), Ok(()));
52/// ```
53///
54/// # Author
55///
56/// Haixing Hu
57pub trait Runnable<E> {
58    /// Executes the action, consuming `self`.
59    ///
60    /// # Returns
61    ///
62    /// Returns `Ok(())` when the action succeeds, or `Err(E)` when it fails.
63    /// The exact error meaning is defined by the concrete runnable.
64    fn run(self) -> Result<(), E>;
65
66    /// Converts this runnable into a boxed runnable.
67    ///
68    /// # Returns
69    ///
70    /// A `BoxRunnable<E>` that executes this runnable when `run()` is invoked.
71    fn into_box(self) -> BoxRunnable<E>
72    where
73        Self: Sized + 'static,
74    {
75        BoxRunnable::new(move || self.run())
76    }
77
78    /// Converts this runnable into a closure.
79    ///
80    /// # Returns
81    ///
82    /// A closure implementing `FnOnce() -> Result<(), E>`.
83    fn into_fn(self) -> impl FnOnce() -> Result<(), E>
84    where
85        Self: Sized + 'static,
86    {
87        move || self.run()
88    }
89
90    /// Converts this runnable into a boxed runnable without consuming `self`.
91    ///
92    /// The method clones `self` and boxes the clone. Use this for cloneable
93    /// runnable values that need to be reused after boxing.
94    ///
95    /// # Returns
96    ///
97    /// A new `BoxRunnable<E>` built from a clone of this runnable.
98    fn to_box(&self) -> BoxRunnable<E>
99    where
100        Self: Clone + Sized + 'static,
101    {
102        self.clone().into_box()
103    }
104
105    /// Converts this runnable into a closure without consuming `self`.
106    ///
107    /// The method clones `self` and returns a one-time closure that executes
108    /// the clone.
109    ///
110    /// # Returns
111    ///
112    /// A closure implementing `FnOnce() -> Result<(), E>`.
113    fn to_fn(&self) -> impl FnOnce() -> Result<(), E>
114    where
115        Self: Clone + Sized + 'static,
116    {
117        self.clone().into_fn()
118    }
119
120    /// Converts this runnable into a callable returning unit.
121    ///
122    /// # Returns
123    ///
124    /// A `BoxCallable<(), E>` that executes this runnable and returns
125    /// `Ok(())` on success.
126    fn into_callable(self) -> BoxCallable<(), E>
127    where
128        Self: Sized + 'static,
129    {
130        BoxCallable::new(move || self.run())
131    }
132}
133
134// ============================================================================
135// BoxRunnable
136// ============================================================================
137
138/// Box-based one-time runnable.
139///
140/// `BoxRunnable<E>` stores a `Box<dyn FnOnce() -> Result<(), E>>` and can be
141/// executed only once. It is the boxed concrete implementation of
142/// [`Runnable`].
143///
144/// # Type Parameters
145///
146/// * `E` - The error value returned when the action fails.
147///
148/// # Examples
149///
150/// ```rust
151/// use qubit_function::{BoxRunnable, Runnable};
152///
153/// let task = BoxRunnable::new(|| Ok::<(), String>(()));
154/// assert_eq!(task.run(), Ok(()));
155/// ```
156///
157/// # Author
158///
159/// Haixing Hu
160pub struct BoxRunnable<E> {
161    /// The one-time closure executed by this runnable.
162    function: Box<dyn FnOnce() -> Result<(), E>>,
163    /// The optional name of this runnable.
164    name: Option<String>,
165}
166
167impl<E> BoxRunnable<E> {
168    /// Creates a new boxed runnable.
169    ///
170    /// # Parameters
171    ///
172    /// * `function` - The one-time closure executed by this runnable.
173    ///
174    /// # Returns
175    ///
176    /// A new unnamed `BoxRunnable<E>`.
177    #[inline]
178    pub fn new<F>(function: F) -> Self
179    where
180        F: FnOnce() -> Result<(), E> + 'static,
181    {
182        Self {
183            function: Box::new(function),
184            name: None,
185        }
186    }
187
188    /// Creates a new named boxed runnable.
189    ///
190    /// # Parameters
191    ///
192    /// * `name` - Name used by `Debug` and `Display`.
193    /// * `function` - The one-time closure executed by this runnable.
194    ///
195    /// # Returns
196    ///
197    /// A new named `BoxRunnable<E>`.
198    #[inline]
199    pub fn new_with_name<F>(name: &str, function: F) -> Self
200    where
201        F: FnOnce() -> Result<(), E> + 'static,
202    {
203        Self {
204            function: Box::new(function),
205            name: Some(name.to_string()),
206        }
207    }
208
209    /// Creates a new boxed runnable with an optional name.
210    ///
211    /// # Parameters
212    ///
213    /// * `function` - The one-time closure executed by this runnable.
214    /// * `name` - Optional name used by `Debug` and `Display`.
215    ///
216    /// # Returns
217    ///
218    /// A new `BoxRunnable<E>`.
219    #[inline]
220    pub fn new_with_optional_name<F>(function: F, name: Option<String>) -> Self
221    where
222        F: FnOnce() -> Result<(), E> + 'static,
223    {
224        Self {
225            function: Box::new(function),
226            name,
227        }
228    }
229
230    /// Creates a boxed runnable from a one-time supplier.
231    ///
232    /// This is an explicit bridge from `SupplierOnce<Result<(), E>>` to
233    /// `Runnable<E>`.
234    ///
235    /// # Parameters
236    ///
237    /// * `supplier` - The supplier that produces the runnable result.
238    ///
239    /// # Returns
240    ///
241    /// A new `BoxRunnable<E>`.
242    #[inline]
243    pub fn from_supplier<S>(supplier: S) -> Self
244    where
245        S: SupplierOnce<Result<(), E>> + 'static,
246    {
247        Self::new(move || supplier.get())
248    }
249
250    /// Gets the optional runnable name.
251    ///
252    /// # Returns
253    ///
254    /// Returns `Some(&str)` if a name was set, or `None` otherwise.
255    #[inline]
256    pub fn name(&self) -> Option<&str> {
257        self.name.as_deref()
258    }
259
260    /// Sets the runnable name.
261    ///
262    /// # Parameters
263    ///
264    /// * `name` - The new name.
265    #[inline]
266    pub fn set_name(&mut self, name: &str) {
267        if self.name.as_deref() != Some(name) {
268            self.name = Some(name.to_owned());
269        }
270    }
271
272    /// Clears the runnable name.
273    #[inline]
274    pub fn clear_name(&mut self) {
275        self.name = None;
276    }
277
278    /// Chains another runnable after this runnable succeeds.
279    ///
280    /// The second runnable is not executed if this runnable returns `Err`.
281    ///
282    /// # Parameters
283    ///
284    /// * `next` - The runnable to execute after this runnable succeeds.
285    ///
286    /// # Returns
287    ///
288    /// A new runnable executing both actions in sequence.
289    #[inline]
290    pub fn and_then<N>(self, next: N) -> BoxRunnable<E>
291    where
292        N: Runnable<E> + 'static,
293        E: 'static,
294    {
295        let name = self.name;
296        let function = self.function;
297        BoxRunnable::new_with_optional_name(
298            move || {
299                function()?;
300                next.run()
301            },
302            name,
303        )
304    }
305
306    /// Runs this runnable before a callable.
307    ///
308    /// The callable is not executed if this runnable returns `Err`.
309    ///
310    /// # Parameters
311    ///
312    /// * `callable` - The callable to execute after this runnable succeeds.
313    ///
314    /// # Returns
315    ///
316    /// A callable producing the second computation's result.
317    #[inline]
318    pub fn then_callable<R, C>(self, callable: C) -> BoxCallable<R, E>
319    where
320        C: crate::tasks::callable::Callable<R, E> + 'static,
321        R: 'static,
322        E: 'static,
323    {
324        let name = self.name;
325        let function = self.function;
326        BoxCallable::new_with_optional_name(
327            move || {
328                function()?;
329                callable.call()
330            },
331            name,
332        )
333    }
334}
335
336impl<E> Runnable<E> for BoxRunnable<E> {
337    /// Executes the boxed runnable.
338    #[inline]
339    fn run(self) -> Result<(), E> {
340        (self.function)()
341    }
342
343    /// Returns this boxed runnable without re-boxing it.
344    #[inline]
345    fn into_box(self) -> BoxRunnable<E> {
346        self
347    }
348
349    /// Extracts the underlying one-time closure.
350    #[inline]
351    fn into_fn(self) -> impl FnOnce() -> Result<(), E> {
352        self.function
353    }
354
355    /// Converts this boxed runnable into a boxed callable while preserving its
356    /// name.
357    #[inline]
358    fn into_callable(self) -> BoxCallable<(), E>
359    where
360        Self: Sized + 'static,
361    {
362        let name = self.name;
363        let function = self.function;
364        BoxCallable::new_with_optional_name(function, name)
365    }
366}
367
368impl<E> SupplierOnce<Result<(), E>> for BoxRunnable<E> {
369    /// Executes the boxed runnable as a one-time supplier of `Result<(), E>`.
370    #[inline]
371    fn get(self) -> Result<(), E> {
372        self.run()
373    }
374}
375
376impl<F, E> Runnable<E> for F
377where
378    F: FnOnce() -> Result<(), E>,
379{
380    /// Executes the closure as a runnable.
381    #[inline]
382    fn run(self) -> Result<(), E> {
383        self()
384    }
385
386    /// Converts the closure to a boxed runnable.
387    #[inline]
388    fn into_box(self) -> BoxRunnable<E>
389    where
390        Self: Sized + 'static,
391    {
392        BoxRunnable::new(self)
393    }
394
395    /// Returns the closure unchanged.
396    #[inline]
397    fn into_fn(self) -> impl FnOnce() -> Result<(), E>
398    where
399        Self: Sized + 'static,
400    {
401        self
402    }
403}
404
405impl<E> fmt::Debug for BoxRunnable<E> {
406    /// Formats this boxed runnable for debugging.
407    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
408        f.debug_struct("BoxRunnable")
409            .field("name", &self.name)
410            .field("function", &"<function>")
411            .finish()
412    }
413}
414
415impl<E> fmt::Display for BoxRunnable<E> {
416    /// Formats this boxed runnable for display.
417    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
418        match &self.name {
419            Some(name) => write!(f, "BoxRunnable({name})"),
420            None => write!(f, "BoxRunnable"),
421        }
422    }
423}