ggen_cli_lib/
runtime.rs

1//! Runtime utilities for bridging async/sync boundaries
2//!
3//! This module provides utilities for executing async code in sync contexts,
4//! particularly for CLI commands that need to call async domain functions.
5
6use ggen_utils::error::Result;
7use std::future::Future;
8
9/// Execute an async function in a sync context
10///
11/// This creates a new Tokio runtime and blocks on the provided future.
12/// Used by CLI commands to bridge to async domain functions.
13///
14/// # Examples
15///
16/// ```rust,ignore
17/// use ggen_utils::error::Result;
18///
19/// fn sync_command() -> Result<()> {
20///     crate::runtime::execute(async {
21///         // Async domain logic here
22///         Ok(())
23///     })
24/// }
25/// ```
26pub fn execute<F>(future: F) -> Result<()>
27where
28    F: Future<Output = Result<()>>,
29{
30    let runtime = match tokio::runtime::Runtime::new() {
31        Ok(rt) => rt,
32        Err(e) => {
33            let msg = format!("Failed to create Tokio runtime: {}", e);
34            log::error!("{}", msg);
35            return Err(ggen_utils::error::Error::new(&msg));
36        }
37    };
38
39    runtime.block_on(future)
40}
41
42/// Block on an async function with a generic return type
43///
44/// Similar to `execute` but supports any return type, not just Result<()>.
45/// Used for domain functions that return values.
46///
47/// # Examples
48///
49/// ```rust,ignore
50/// use ggen_utils::error::Result;
51///
52/// fn get_data() -> Result<String> {
53///     crate::runtime::block_on(async {
54///         Ok("data".to_string())
55///     })
56/// }
57/// ```
58pub fn block_on<F, T>(async_op: F) -> Result<T>
59where
60    F: Future<Output = T> + Send,
61    T: Send,
62{
63    // Check if we're already in a runtime
64    match tokio::runtime::Handle::try_current() {
65        Ok(_) => {
66            // Already in runtime - spawn new thread with new runtime to avoid nesting
67            std::thread::scope(|s| {
68                s.spawn(move || {
69                    let rt = match tokio::runtime::Runtime::new() {
70                        Ok(runtime) => runtime,
71                        Err(e) => {
72                            let msg = format!("Failed to create Tokio runtime: {}", e);
73                            log::error!("{}", msg);
74                            return Err(ggen_utils::error::Error::new(&msg));
75                        }
76                    };
77                    Ok(rt.block_on(async_op))
78                })
79                .join()
80                .map_err(|_| ggen_utils::error::Error::new("Runtime thread panicked"))?
81            })
82        }
83        Err(_) => {
84            // Not in runtime - create one on current thread
85            match tokio::runtime::Runtime::new() {
86                Ok(runtime) => Ok(runtime.block_on(async_op)),
87                Err(e) => {
88                    let msg = format!("Failed to create Tokio runtime: {}", e);
89                    log::error!("{}", msg);
90                    Err(ggen_utils::error::Error::new(&msg))
91                }
92            }
93        }
94    }
95}