cel_cxx/
maybe_future.rs

1//! Conditional future types for async/sync compatibility.
2//!
3//! This module provides the [`MaybeFuture`] type, which has **different definitions**
4//! depending on whether the `async` feature is enabled. This allows the same API to work
5//! seamlessly in both synchronous and asynchronous contexts.
6//!
7//! # ⚠️ Feature-Dependent Type Definition
8//!
9//! **Critical**: [`MaybeFuture`] is implemented differently based on feature flags:
10//!
11//! | Feature State | Type Definition | Behavior |
12//! |---------------|-----------------|----------|
13//! | **No `async`** | `type MaybeFuture<'a, T, E> = Result<T, E>` | Simple type alias, zero overhead |
14//! | **With `async`** | `enum MaybeFuture<'a, T, E> { Result(..), Future(..) }` | Can hold immediate results or futures |
15//!
16//! # Documentation Generation
17//!
18//! **Important**: When building documentation with `--all-features` (as on docs.rs), only the
19//! async variant is shown because the `async` feature is enabled. The synchronous variant
20//! (simple type alias) is only visible when building docs without the `async` feature.
21//!
22//! - **On docs.rs**: Shows the enum variant with `Result` and `Future` cases
23//! - **Without async feature**: `MaybeFuture` is just a type alias for `Result<T, E>`
24//! - Each definition is marked with appropriate `#[cfg(...)]` attributes
25//!
26//! # Usage Guidelines
27//!
28//! ## For Library Authors
29//!
30//! When returning [`MaybeFuture`] from your APIs:
31//! ```ignore
32//! use cel_cxx::MaybeFuture;
33//!
34//! // This signature works regardless of async feature
35//! fn your_function() -> MaybeFuture<'_, YourType, YourError> {
36//!     // Implementation varies by feature
37//!     # unimplemented!()
38//! }
39//! ```
40//!
41//! ## For Library Users
42//!
43//! When consuming [`MaybeFuture`] values:
44//! ```rust,no_run
45//! # use cel_cxx::{Error, MaybeFuture};
46//! // Sync mode (no async feature)
47//! #[cfg(not(feature = "async"))]
48//! fn example_usage<'a>(maybe_future: MaybeFuture<'a, i32, Error>) -> Result<(), Error> {
49//!     let result = maybe_future?; // It's just Result<T, E>
50//!     Ok(())
51//! }
52//!
53//! // Async mode (with async feature)
54//! #[cfg(feature = "async")]
55//! async fn example_usage<'a>(maybe_future: MaybeFuture<'a, i32, Error>) -> Result<(), Error> {
56//!     match maybe_future {
57//!         MaybeFuture::Result(result) => {
58//!             let value = result?; // Immediate result
59//!         }
60//!         MaybeFuture::Future(future) => {
61//!             let result = future.await?; // Await the future
62//!         }
63//!     }
64//!     Ok(())
65//! }
66//! ```
67//!
68//! # Examples
69//!
70//! ## Synchronous mode (without `async` feature)
71//!
72//! ```rust
73//! # #[cfg(not(feature = "async"))]
74//! # {
75//! use cel_cxx::{Error, MaybeFuture};
76//!
77//! // MaybeFuture is just Result<T, E> in sync mode
78//! let result: MaybeFuture<'_, i32, Error> = Ok(42);
79//! assert_eq!(result.unwrap(), 42);
80//! # }
81//! ```
82//!
83//! ## Asynchronous mode (with `async` feature)
84//!
85//! ```rust
86//! # #[cfg(feature = "async")]
87//! # async fn example() {
88//! use cel_cxx::MaybeFuture;
89//!
90//! // Can hold either immediate results or futures
91//! let immediate: MaybeFuture<'_, i32, &str> = MaybeFuture::Result(Ok(42));
92//! let future_result: MaybeFuture<'_, i32, &str> = MaybeFuture::Future(
93//!     Box::pin(async { Ok(100) })
94//! );
95//!
96//! assert!(immediate.is_result());
97//! assert!(future_result.is_future());
98//! # }
99//! ```
100
101//! # Type Definitions
102pub use imp::*;
103
104#[cfg(not(feature = "async"))]
105mod imp {
106    /// A type alias for immediate results when async features are disabled.
107    ///
108    /// When the `async` feature is not enabled, `MaybeFuture` is simply a type alias
109    /// for `Result<T, E>`, meaning all operations return immediately without any
110    /// async overhead.
111    ///
112    /// # Type Parameters
113    ///
114    /// * `'a` - Lifetime parameter (unused in sync mode but kept for API compatibility)
115    /// * `T` - The success type
116    /// * `E` - The error type (defaults to [`crate::Error`])
117    ///
118    /// # Feature-Dependent Behavior
119    ///
120    /// This type has different definitions depending on feature flags:
121    /// - **Without `async` feature**: Type alias to `Result<T, E>` (this definition)
122    /// - **With `async` feature**: Enum with `Result` and `Future` variants
123    ///
124    /// See the [module documentation](crate::maybe_future) for more details about
125    /// feature-dependent behavior.
126    ///
127    /// # Examples
128    ///
129    /// ```rust,no_run
130    /// # #[cfg(not(feature = "async"))]
131    /// # {
132    /// use cel_cxx::MaybeFuture;
133    ///
134    /// // In sync mode, MaybeFuture is just Result<T, E>
135    /// let result: MaybeFuture<'_, i32> = Ok(42);
136    /// assert_eq!(result.unwrap(), 42);
137    /// # }
138    /// ```
139    #[cfg_attr(docsrs, doc(cfg(not(feature = "async"))))]
140    pub type MaybeFuture<'a, T, E = crate::Error> = Result<T, E>;
141}
142
143#[cfg(feature = "async")]
144#[cfg_attr(docsrs, doc(cfg(feature = "async")))]
145mod imp {
146    use futures::future::{BoxFuture, Future};
147    use std::pin::Pin;
148
149    /// A type that can represent either an immediate result or a future.
150    ///
151    /// When the `async` feature is enabled, `MaybeFuture` is an enum that can hold
152    /// either an immediate `Result<T, E>` or a boxed future that will eventually
153    /// resolve to a `Result<T, E>`. This allows the same API to handle both
154    /// synchronous and asynchronous operations seamlessly.
155    ///
156    /// # Type Parameters
157    ///
158    /// * `'a` - The lifetime of the future
159    /// * `T` - The success type
160    /// * `E` - The error type
161    ///
162    /// # Variants
163    ///
164    /// * `Result(Result<T, E>)` - An immediate result
165    /// * `Future(BoxFuture<'a, Result<T, E>>)` - A future that will resolve to a result
166    ///
167    /// # Feature-Dependent Behavior
168    ///
169    /// This type has different definitions depending on feature flags:
170    /// - **Without `async` feature**: Type alias to `Result<T, E>`
171    /// - **With `async` feature**: Enum with `Result` and `Future` variants (this definition)
172    ///
173    /// See the [module documentation](crate::maybe_future) for more details about
174    /// feature-dependent behavior.
175    ///
176    /// # Examples
177    ///
178    /// ## Working with immediate results
179    ///
180    /// ```rust,no_run
181    /// # #[cfg(feature = "async")]
182    /// # {
183    /// use cel_cxx::MaybeFuture;
184    ///
185    /// let immediate: MaybeFuture<'_, i32, &str> = MaybeFuture::Result(Ok(42));
186    /// assert!(immediate.is_result());
187    /// assert_eq!(immediate.into_result().unwrap().unwrap(), 42);
188    /// # }
189    /// ```
190    ///
191    /// ## Working with futures
192    ///
193    /// ```rust,no_run
194    /// # #[cfg(feature = "async")]
195    /// # async fn example() {
196    /// use cel_cxx::MaybeFuture;
197    /// use futures::future::BoxFuture;
198    ///
199    /// let future_result: BoxFuture<'_, Result<i32, &str>> =
200    ///     Box::pin(async { Ok(42) });
201    /// let maybe_future: MaybeFuture<'_, i32, &str> =
202    ///     MaybeFuture::Future(future_result);
203    ///
204    /// assert!(maybe_future.is_future());
205    /// let result = maybe_future.into_future().unwrap().await.unwrap();
206    /// assert_eq!(result, 42);
207    /// # }
208    /// ```
209    #[cfg_attr(docsrs, doc(cfg(feature = "async")))]
210    pub enum MaybeFuture<'a, T, E> {
211        /// An immediate result value.
212        Result(Result<T, E>),
213        /// A future that will resolve to a result value.
214        Future(BoxFuture<'a, Result<T, E>>),
215    }
216
217    impl<'a, T, E> MaybeFuture<'a, T, E> {
218        /// Returns `true` if this is an immediate result.
219        ///
220        /// # Examples
221        ///
222        /// ```rust,no_run
223        /// # #[cfg(feature = "async")]
224        /// # {
225        /// use cel_cxx::MaybeFuture;
226        ///
227        /// let immediate: MaybeFuture<'_, i32, &str> = MaybeFuture::Result(Ok(42));
228        /// assert!(immediate.is_result());
229        /// # }
230        /// ```
231        pub fn is_result(&self) -> bool {
232            matches!(self, MaybeFuture::Result(_))
233        }
234
235        /// Returns `true` if this is a future.
236        ///
237        /// # Examples
238        ///
239        /// ```rust,no_run
240        /// # #[cfg(feature = "async")]
241        /// # {
242        /// use cel_cxx::MaybeFuture;
243        /// use futures::future::BoxFuture;
244        ///
245        /// let future: BoxFuture<'_, Result<i32, &str>> = Box::pin(async { Ok(42) });
246        /// let maybe_future: MaybeFuture<'_, i32, &str> = MaybeFuture::Future(future);
247        /// assert!(maybe_future.is_future());
248        /// # }
249        /// ```
250        pub fn is_future(&self) -> bool {
251            matches!(self, MaybeFuture::Future(_))
252        }
253
254        /// Returns a reference to the result if this is an immediate result.
255        ///
256        /// # Examples
257        ///
258        /// ```rust,no_run
259        /// # #[cfg(feature = "async")]
260        /// # {
261        /// use cel_cxx::MaybeFuture;
262        ///
263        /// let immediate: MaybeFuture<'_, i32, &str> = MaybeFuture::Result(Ok(42));
264        /// assert_eq!(immediate.result_ref().unwrap().as_ref().unwrap(), &42);
265        /// # }
266        /// ```
267        pub fn result_ref(&self) -> Option<&Result<T, E>> {
268            match self {
269                MaybeFuture::Result(t) => Some(t),
270                MaybeFuture::Future(_) => None,
271            }
272        }
273
274        /// Returns a reference to the future if this is a future.
275        ///
276        /// # Examples
277        ///
278        /// ```rust,no_run
279        /// # #[cfg(feature = "async")]
280        /// # {
281        /// use cel_cxx::MaybeFuture;
282        /// use futures::future::BoxFuture;
283        ///
284        /// let future: BoxFuture<'_, Result<i32, &str>> = Box::pin(async { Ok(42) });
285        /// let maybe_future: MaybeFuture<'_, i32, &str> = MaybeFuture::Future(future);
286        /// assert!(maybe_future.future_ref().is_some());
287        /// # }
288        /// ```
289        pub fn future_ref(&self) -> Option<&BoxFuture<'a, Result<T, E>>> {
290            match self {
291                MaybeFuture::Result(_) => None,
292                MaybeFuture::Future(f) => Some(f),
293            }
294        }
295
296        /// Returns a mutable reference to the result if this is an immediate result.
297        pub fn result_mut(&mut self) -> Option<&mut Result<T, E>> {
298            match self {
299                MaybeFuture::Result(t) => Some(t),
300                MaybeFuture::Future(_) => None,
301            }
302        }
303
304        /// Returns a mutable reference to the future if this is a future.
305        pub fn future_mut(&mut self) -> Option<&mut BoxFuture<'a, Result<T, E>>> {
306            match self {
307                MaybeFuture::Result(_) => None,
308                MaybeFuture::Future(f) => Some(f),
309            }
310        }
311
312        /// Consumes this value and returns the result if it's an immediate result.
313        ///
314        /// # Examples
315        ///
316        /// ```rust,no_run
317        /// # #[cfg(feature = "async")]
318        /// # {
319        /// use cel_cxx::MaybeFuture;
320        ///
321        /// let immediate: MaybeFuture<'_, i32, &str> = MaybeFuture::Result(Ok(42));
322        /// let result = immediate.into_result().unwrap();
323        /// assert_eq!(result.unwrap(), 42);
324        /// # }
325        /// ```
326        pub fn into_result(self) -> Option<Result<T, E>> {
327            match self {
328                MaybeFuture::Result(t) => Some(t),
329                MaybeFuture::Future(_) => None,
330            }
331        }
332
333        /// Consumes this value and returns the future if it's a future.
334        ///
335        /// # Examples
336        ///
337        /// ```rust,no_run
338        /// # #[cfg(feature = "async")]
339        /// # async fn example() {
340        /// use cel_cxx::MaybeFuture;
341        /// use futures::future::BoxFuture;
342        ///
343        /// let future: BoxFuture<'_, Result<i32, &str>> = Box::pin(async { Ok(42) });
344        /// let maybe_future: MaybeFuture<'_, i32, &str> = MaybeFuture::Future(future);
345        /// let extracted_future = maybe_future.into_future().unwrap();
346        /// let result = extracted_future.await.unwrap();
347        /// assert_eq!(result, 42);
348        /// # }
349        /// ```
350        pub fn into_future(self) -> Option<BoxFuture<'a, Result<T, E>>> {
351            match self {
352                MaybeFuture::Result(_) => None,
353                MaybeFuture::Future(f) => Some(f),
354            }
355        }
356
357        /// Extracts the result, panicking if this is a future.
358        ///
359        /// # Panics
360        ///
361        /// Panics if this `MaybeFuture` contains a future rather than an immediate result.
362        ///
363        /// # Examples
364        ///
365        /// ```rust,no_run
366        /// # #[cfg(feature = "async")]
367        /// # {
368        /// use cel_cxx::MaybeFuture;
369        ///
370        /// let immediate: MaybeFuture<'_, i32, &str> = MaybeFuture::Result(Ok(42));
371        /// let result = immediate.expect_result("Expected immediate result");
372        /// assert_eq!(result.unwrap(), 42);
373        /// # }
374        /// ```
375        pub fn expect_result(self, msg: &str) -> Result<T, E> {
376            self.into_result().expect(msg)
377        }
378
379        /// Extracts the future, panicking if this is an immediate result.
380        ///
381        /// # Panics
382        ///
383        /// Panics if this `MaybeFuture` contains an immediate result rather than a future.
384        ///
385        /// # Examples
386        ///
387        /// ```rust,no_run
388        /// # #[cfg(feature = "async")]
389        /// # async fn example() {
390        /// use cel_cxx::MaybeFuture;
391        /// use futures::future::BoxFuture;
392        ///
393        /// let future: BoxFuture<'_, Result<i32, &str>> = Box::pin(async { Ok(42) });
394        /// let maybe_future: MaybeFuture<'_, i32, &str> = MaybeFuture::Future(future);
395        /// let extracted_future = maybe_future.expect_future("Expected future");
396        /// let result = extracted_future.await.unwrap();
397        /// assert_eq!(result, 42);
398        /// # }
399        /// ```
400        pub fn expect_future(self, msg: &str) -> BoxFuture<'a, Result<T, E>> {
401            self.into_future().expect(msg)
402        }
403
404        /// Extracts the result, panicking if this is a future.
405        ///
406        /// This is equivalent to `expect_result` but with a default panic message.
407        ///
408        /// # Panics
409        ///
410        /// Panics if this `MaybeFuture` contains a future rather than an immediate result.
411        pub fn unwrap_result(self) -> Result<T, E> {
412            self.into_result().unwrap()
413        }
414
415        /// Extracts the future, panicking if this is an immediate result.
416        ///
417        /// This is equivalent to `expect_future` but with a default panic message.
418        ///
419        /// # Panics
420        ///
421        /// Panics if this `MaybeFuture` contains an immediate result rather than a future.
422        pub fn unwrap_future(self) -> BoxFuture<'a, Result<T, E>> {
423            self.into_future().unwrap()
424        }
425    }
426
427    impl<'a, T: std::fmt::Debug, E: std::fmt::Debug> std::fmt::Debug for MaybeFuture<'a, T, E> {
428        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
429            match self {
430                MaybeFuture::Result(t) => t.fmt(f),
431                MaybeFuture::Future(_) => f.write_str("<future>"),
432            }
433        }
434    }
435
436    impl<'a, T, E> From<Result<T, E>> for MaybeFuture<'a, T, E> {
437        fn from(t: Result<T, E>) -> Self {
438            MaybeFuture::Result(t)
439        }
440    }
441
442    impl<'a, Fut, T, E> From<Pin<Box<Fut>>> for MaybeFuture<'a, T, E>
443    where
444        Fut: Future<Output = Result<T, E>> + Send + 'a,
445    {
446        fn from(f: Pin<Box<Fut>>) -> Self {
447            MaybeFuture::Future(f)
448        }
449    }
450}