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}