viam_bridge_sdk/
loader.rs1use anyhow::{anyhow, Result};
2use libloading::{Library, Symbol};
3use std::any::{Any, TypeId};
4use std::collections::HashMap;
5use std::fs;
6use std::path::Path;
7use std::sync::Arc;
8use serde_json::Value;
9
10use crate::bridge::{
11 BridgeTypeRegistry, ExtensionBridge, ExtensionBridgeWithRegistry, TypedRegistryCallback,
12};
13
14use super::bridge::BridgeContext;
15
16pub type BoxedBridge = Box<dyn ExtensionBridgeWithRegistry>;
18
19pub type CreateBridgeFn = fn() -> BoxedBridge;
21
22pub struct BridgeRegistry {
24 callbacks: HashMap<TypeId, Vec<TypedRegistryCallback>>,
26}
27
28impl BridgeRegistry {
29 pub fn new() -> Self {
31 Self {
32 callbacks: HashMap::new(),
33 }
34 }
35
36 pub fn register_bridge(&mut self, bridge: &mut dyn ExtensionBridgeWithRegistry) -> Result<()> {
38 let registry: &mut dyn BridgeTypeRegistry = bridge;
41
42 let callbacks = registry.get_callbacks_for(TypeId::of::<Self>());
44
45 for callback in callbacks {
47 let host_type_id = callback.host_type_id;
48
49 if !self.callbacks.contains_key(&host_type_id) {
51 self.callbacks.insert(host_type_id, Vec::new());
52 }
53
54 if let Some(callbacks_for_type) = self.callbacks.get_mut(&host_type_id) {
56 }
63 }
64
65 Ok(())
66 }
67
68 pub fn get_callbacks<T: 'static>(&self) -> Vec<&TypedRegistryCallback> {
70 let host_type_id = TypeId::of::<T>();
71 match self.callbacks.get(&host_type_id) {
72 Some(callbacks) => callbacks.iter().collect(),
73 None => Vec::new(),
74 }
75 }
76}
77
78pub trait BridgeLoader {
80 fn load_bridges(&mut self) -> Result<Vec<BoxedBridge>>;
82}
83
84pub struct DynamicBridgeLoader {
86 pub directory: String,
88 registry: BridgeRegistry,
90 context: Option<Arc<BridgeContext>>,
91}
92
93impl DynamicBridgeLoader {
94 pub fn new(directory: &str) -> Self {
96 Self {
97 directory: directory.to_string(),
98 registry: BridgeRegistry::new(),
99 context: None,
100 }
101 }
102
103 pub fn registry(&self) -> &BridgeRegistry {
105 &self.registry
106 }
107
108 pub fn registry_mut(&mut self) -> &mut BridgeRegistry {
110 &mut self.registry
111 }
112
113 pub fn with_context(mut self, context: Arc<BridgeContext>) -> Self {
114 self.context = Some(context);
115 self
116 }
117}
118
119type CreateBridgeWithContextFn = fn(Option<Arc<BridgeContext>>) -> BoxedBridge;
120
121impl BridgeLoader for DynamicBridgeLoader {
122 fn load_bridges(&mut self) -> Result<Vec<BoxedBridge>> {
123 println!("[BRIDGE_LOADER] Starting load_bridges");
124 let mut bridges = Vec::new();
125
126 let directory = Path::new(&self.directory);
128 if !directory.exists() {
129 println!("[BRIDGE_LOADER] Bridge directory does not exist: {}", self.directory);
130 return Err(anyhow!(
131 "Bridge directory does not exist: {}",
132 self.directory
133 ));
134 }
135
136 let mut libraries_to_forget = Vec::new();
138
139 println!("[BRIDGE_LOADER] Scanning directory for bridge libraries: {}", self.directory);
141 for entry in fs::read_dir(directory)? {
142 let entry = entry?;
143 let entry_path = entry.path();
144 println!("[BRIDGE_LOADER] Found entry: {:?}", entry_path);
145
146 let (path, config_value): (std::path::PathBuf, Value) = if entry_path.is_dir() {
148 let config_file = entry_path.join("config.json");
149 let config_value = if config_file.exists() && config_file.is_file() {
150 let s = fs::read_to_string(&config_file)?;
151 serde_json::from_str(&s)?
152 } else {
153 Value::Null
154 };
155 let mut libs = Vec::new();
157 for sub in fs::read_dir(&entry_path)? {
158 let sub = sub?;
159 let p = sub.path();
160 #[cfg(unix)]
161 if p.extension().and_then(|e| e.to_str()) == Some("so") {
162 libs.push(p.clone());
163 }
164 #[cfg(windows)]
165 if p.extension().and_then(|e| e.to_str()) == Some("dll") {
166 libs.push(p.clone());
167 }
168 #[cfg(target_os = "macos")]
169 if p.extension().and_then(|e| e.to_str()) == Some("dylib") {
170 libs.push(p.clone());
171 }
172 }
173 if libs.is_empty() {
174 println!("[BRIDGE_LOADER] No library found in directory: {:?}", entry_path);
175 continue;
176 }
177 (libs.remove(0), config_value)
178 } else {
179 #[cfg(unix)]
180 if !entry_path.is_file() || entry_path.extension().and_then(|e| e.to_str()) != Some("so") {
181 println!("[BRIDGE_LOADER] Skipping non-so file: {:?}", entry_path);
182 continue;
183 }
184 #[cfg(windows)]
185 if !entry_path.is_file() || entry_path.extension().and_then(|e| e.to_str()) != Some("dll") {
186 println!("[BRIDGE_LOADER] Skipping non-dll file: {:?}", entry_path);
187 continue;
188 }
189 #[cfg(target_os = "macos")]
190 if !entry_path.is_file() || entry_path.extension().and_then(|e| e.to_str()) != Some("dylib") {
191 println!("[BRIDGE_LOADER] Skipping non-dylib file: {:?}", entry_path);
192 continue;
193 }
194 (entry_path, Value::Null)
195 };
196
197 #[cfg(unix)]
199 let library = unsafe {
200 println!("[BRIDGE_LOADER] Attempting to load library: {:?}", path);
201 let lib_result = libloading::os::unix::Library::open(
202 Some(path.to_str().unwrap()),
203 libloading::os::unix::RTLD_NOW | libloading::os::unix::RTLD_GLOBAL,
204 );
205
206 match lib_result {
207 Ok(lib) => {
208 println!("[BRIDGE_LOADER] Successfully loaded library: {:?}", path);
209 libloading::Library::from(lib)
210 },
211 Err(e) => {
212 println!("[BRIDGE_LOADER] Failed to load library: {:?}, error: {:?}", path, e);
213 return Err(anyhow!("Failed to load library: {:?}, error: {:?}", path, e));
214 }
215 }
216 };
217
218 #[cfg(not(unix))]
219 let library = unsafe {
220 println!("[BRIDGE_LOADER] Attempting to load library: {:?}", path);
221 let lib_result = Library::new(&path);
222
223 match lib_result {
224 Ok(lib) => {
225 println!("[BRIDGE_LOADER] Successfully loaded library: {:?}", path);
226 lib
227 },
228 Err(e) => {
229 println!("[BRIDGE_LOADER] Failed to load library: {:?}, error: {:?}", path, e);
230 return Err(anyhow!("Failed to load library: {:?}, error: {:?}", path, e));
231 }
232 }
233 };
234
235 println!("[BRIDGE_LOADER] Looking for create_bridge_with_context symbol in: {:?}", path);
237 let create_bridge_result: Result<Symbol<CreateBridgeWithContextFn>, libloading::Error> =
238 unsafe { library.get(b"create_bridge_with_context") };
239
240 let mut bridge = if let Ok(create_bridge) = create_bridge_result {
241 println!("[BRIDGE_LOADER] Found create_bridge_with_context symbol, calling it with context: {:?}", self.context.is_some());
243 let bridge_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
244 create_bridge(self.context.clone())
245 }));
246
247 match bridge_result {
248 Ok(bridge) => {
249 println!("[BRIDGE_LOADER] Successfully created bridge with context");
250 bridge
251 },
252 Err(e) => {
253 println!("[BRIDGE_LOADER] Panic in create_bridge_with_context: {:?}", e);
254 return Err(anyhow!("Panic in create_bridge_with_context"));
255 }
256 }
257 } else {
258 println!("[BRIDGE_LOADER] create_bridge_with_context not found, looking for create_bridge");
260 let create_bridge_result: Result<Symbol<CreateBridgeFn>, libloading::Error> =
261 unsafe { library.get(b"create_bridge") };
262
263 match create_bridge_result {
264 Ok(create_bridge) => {
265 println!("[BRIDGE_LOADER] Found create_bridge symbol, calling it");
266 let bridge_result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
267 create_bridge()
268 }));
269
270 match bridge_result {
271 Ok(bridge) => {
272 println!("[BRIDGE_LOADER] Successfully created bridge");
273 bridge
274 },
275 Err(e) => {
276 println!("[BRIDGE_LOADER] Panic in create_bridge: {:?}", e);
277 return Err(anyhow!("Panic in create_bridge"));
278 }
279 }
280 },
281 Err(e) => {
282 println!("[BRIDGE_LOADER] Failed to find create_bridge symbol: {:?}", e);
283 return Err(anyhow!("Failed to find create_bridge symbol: {:?}", e));
284 }
285 }
286 };
287
288 println!("[BRIDGE_LOADER] Initializing bridge with config");
289 bridge.initialize(config_value)?;
290 println!("[BRIDGE_LOADER] Adding bridge to list");
291 bridges.push(bridge);
292 libraries_to_forget.push(library);
293 }
294
295 println!("[BRIDGE_LOADER] Registering {} bridges", bridges.len());
297 for (i, bridge) in bridges.iter_mut().enumerate() {
298 println!("[BRIDGE_LOADER] Registering bridge {}", i);
299 match self.registry_mut().register_bridge(&mut **bridge) {
300 Ok(_) => println!("[BRIDGE_LOADER] Successfully registered bridge {}", i),
301 Err(e) => println!("[BRIDGE_LOADER] Failed to register bridge {}: {:?}", i, e)
302 }
303 }
304
305 println!("[BRIDGE_LOADER] Leaking {} libraries to keep them loaded", libraries_to_forget.len());
308 for library in libraries_to_forget {
309 std::mem::forget(library);
310 }
311
312 println!("[BRIDGE_LOADER] Completed load_bridges successfully");
313 Ok(bridges)
314 }
315}
316
317pub struct StaticBridgeLoader {
319 factories: Vec<Box<dyn Fn() -> BoxedBridge + Send + Sync>>,
321 registry: BridgeRegistry,
323}
324
325impl StaticBridgeLoader {
326 pub fn new() -> Self {
328 Self {
329 factories: Vec::new(),
330 registry: BridgeRegistry::new(),
331 }
332 }
333
334 pub fn register_factory<F>(&mut self, factory: F)
336 where
337 F: Fn() -> BoxedBridge + Send + Sync + 'static,
338 {
339 self.factories.push(Box::new(factory));
340 }
341
342 pub fn registry(&self) -> &BridgeRegistry {
344 &self.registry
345 }
346
347 pub fn registry_mut(&mut self) -> &mut BridgeRegistry {
349 &mut self.registry
350 }
351}
352
353impl BridgeLoader for StaticBridgeLoader {
354 fn load_bridges(&mut self) -> Result<Vec<BoxedBridge>> {
355 let mut bridges = Vec::new();
357 for factory in &self.factories {
358 let bridge = factory();
359 bridges.push(bridge);
360 }
361
362 for bridge in &mut bridges {
364 self.registry_mut().register_bridge(&mut **bridge)?;
365 }
366
367 Ok(bridges)
368 }
369}