bubba_core/runtime/
mod.rs1use anyhow::Result;
7use std::sync::Arc;
8use tokio::runtime::Runtime as TokioRuntime;
9use crate::navigation::{global_stack, navigate_to};
10use crate::ui::Screen;
11
12pub struct Runtime {
14 tokio: Arc<TokioRuntime>,
15}
16
17impl Runtime {
18 pub fn new() -> Result<Self> {
20 let tokio = tokio::runtime::Builder::new_multi_thread()
21 .worker_threads(2)
22 .enable_all()
23 .build()?;
24 Ok(Self {
25 tokio: Arc::new(tokio),
26 })
27 }
28
29 pub fn launch(&self, root_name: &'static str, root: fn() -> Screen) {
31 log::info!("[Bubba] Runtime launching. Root screen: {}", root_name);
32 navigate_to(root_name, root);
33
34 #[cfg(target_os = "android")]
36 self.run_android_loop();
37
38 #[cfg(not(target_os = "android"))]
39 self.run_host_loop();
40 }
41
42 pub fn render_current(&self) {
44 if let Some(screen) = global_stack().current() {
45 log::debug!("[Bubba] Rendering screen:\n{}", screen.root.debug_render(0));
46
47 #[cfg(not(target_os = "android"))]
50 println!("{}", screen.root.debug_render(0));
51 }
52 }
53
54 pub fn spawn_task<F>(&self, fut: F)
63 where
64 F: std::future::Future<Output = ()> + Send + 'static,
65 {
66 self.tokio.spawn(fut);
67 }
68
69 #[cfg(not(target_os = "android"))]
72 fn run_host_loop(&self) {
73 log::info!("[Bubba] Running in host mode (no Android runtime).");
74 self.render_current();
75 }
76
77 #[cfg(target_os = "android")]
80 fn run_android_loop(&self) {
81 log::info!("[Bubba] Android runtime active.");
82 }
85}
86
87impl Default for Runtime {
88 fn default() -> Self {
89 Self::new().expect("Failed to boot Bubba runtime")
90 }
91}
92
93pub fn alert(message: impl Into<String>) {
101 let msg = message.into();
102 log::info!("[Bubba alert] {}", msg);
103
104 #[cfg(target_os = "android")]
105 android_alert(&msg);
106
107 #[cfg(not(target_os = "android"))]
108 println!("[ALERT] {}", msg);
109}
110
111pub fn log_msg(message: impl Into<String>) {
117 log::debug!("[Bubba log] {}", message.into());
118}
119
120pub fn spawn<F>(fut: F)
123where
124 F: std::future::Future<Output = ()> + Send + 'static,
125{
126 GLOBAL_RUNTIME.with(|rt| {
127 if let Some(r) = rt.borrow().as_ref() {
128 r.tokio.spawn(fut);
129 }
130 });
131}
132
133use std::cell::RefCell;
136
137thread_local! {
138 static GLOBAL_RUNTIME: RefCell<Option<Arc<Runtime>>> = RefCell::new(None);
139}
140
141pub fn set_global_runtime(rt: Arc<Runtime>) {
143 GLOBAL_RUNTIME.with(|r| {
144 *r.borrow_mut() = Some(rt);
145 });
146}
147
148#[cfg(all(target_os = "android", feature = "android"))]
151mod android {
152 use jni::JNIEnv;
153 use jni::objects::{JClass, JString};
154
155 #[no_mangle]
157 pub extern "system" fn Java_rs_bubba_BubbaBridge_nativeOnEvent(
158 env: JNIEnv,
159 _class: JClass,
160 element_id: JString,
161 event_kind: JString,
162 value: JString,
163 ) {
164 let element_id: String = env.get_string(element_id).unwrap().into();
165 let event_kind: String = env.get_string(event_kind).unwrap().into();
166 let value: String = env.get_string(value).unwrap().into();
167
168 log::debug!("[JNI] event: {} on #{}", event_kind, element_id);
169
170 }
173
174 pub fn android_alert(message: &str) {
176 log::info!("[Android] Alert: {}", message);
178 }
179}
180
181#[cfg(not(all(target_os = "android", feature = "android")))]
182#[allow(dead_code)]
183fn android_alert(_msg: &str) {}
184
185pub const BUBBA_VERSION: &str = env!("CARGO_PKG_VERSION");
189
190#[cfg(test)]
191mod tests {
192 use super::*;
193
194 #[test]
195 fn runtime_boots() {
196 let rt = Runtime::new().expect("runtime should boot");
197 drop(rt);
199 }
200
201 #[test]
202 fn alert_does_not_panic() {
203 alert("test alert");
204 }
205
206 #[test]
207 fn log_does_not_panic() {
208 log_msg("test log");
209 }
210}