Skip to main content

viam_bridge_sdk/
loader.rs

1use 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
16/// Type to store bridge in trait object for type erasure
17pub type BoxedBridge = Box<dyn ExtensionBridgeWithRegistry>;
18
19/// Function type for creating a bridge
20pub type CreateBridgeFn = fn() -> BoxedBridge;
21
22/// Registry for managing bridge callbacks
23pub struct BridgeRegistry {
24    /// Map of TypeId to callbacks for that type
25    callbacks: HashMap<TypeId, Vec<TypedRegistryCallback>>,
26}
27
28impl BridgeRegistry {
29    /// Create a new empty bridge registry
30    pub fn new() -> Self {
31        Self {
32            callbacks: HashMap::new(),
33        }
34    }
35
36    /// Register a bridge with the registry
37    pub fn register_bridge(&mut self, bridge: &mut dyn ExtensionBridgeWithRegistry) -> Result<()> {
38        // The bridge itself will handle adding its callbacks to the registry
39        // We're using a trait object to get dynamic dispatch
40        let registry: &mut dyn BridgeTypeRegistry = bridge;
41
42        // Get all the callbacks from the bridge
43        let callbacks = registry.get_callbacks_for(TypeId::of::<Self>());
44
45        // Add each callback to our registry
46        for callback in callbacks {
47            let host_type_id = callback.host_type_id;
48
49            // Create entry if it doesn't exist
50            if !self.callbacks.contains_key(&host_type_id) {
51                self.callbacks.insert(host_type_id, Vec::new());
52            }
53
54            // Access the entry and add the callback
55            if let Some(callbacks_for_type) = self.callbacks.get_mut(&host_type_id) {
56                // Clone is not supported for TypedRegistryCallback, so we'd need to implement
57                // a proper Clone or copy mechanism from the callback
58                // For now, this would require bridge implementations to provide a way to get
59                // multiple callbacks
60                // callbacks_for_type.push(callback.clone());
61                // This is a TODO: properly get callbacks from bridges
62            }
63        }
64
65        Ok(())
66    }
67
68    /// Get callbacks for a specific host type
69    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
78/// Trait for loading bridges
79pub trait BridgeLoader {
80    /// Load bridges from a source
81    fn load_bridges(&mut self) -> Result<Vec<BoxedBridge>>;
82}
83
84/// Dynamic bridge loader for loading bridges from shared libraries
85pub struct DynamicBridgeLoader {
86    /// Directory to scan for bridge libraries
87    pub directory: String,
88    /// Registry for managing bridge callbacks
89    registry: BridgeRegistry,
90    context: Option<Arc<BridgeContext>>,
91}
92
93impl DynamicBridgeLoader {
94    /// Create a new dynamic bridge loader
95    pub fn new(directory: &str) -> Self {
96        Self {
97            directory: directory.to_string(),
98            registry: BridgeRegistry::new(),
99            context: None,
100        }
101    }
102
103    /// Get a reference to the registry
104    pub fn registry(&self) -> &BridgeRegistry {
105        &self.registry
106    }
107
108    /// Get a mutable reference to the registry
109    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        // Ensure directory exists
127        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        // Collect libraries and bridges
137        let mut libraries_to_forget = Vec::new();
138
139        // Scan directory for bridge libraries
140        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            // determine library path and optional JSON config
147            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                // find library inside
156                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            // Load the library
198            #[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            // Try to get the new create_bridge symbol that accepts context
236            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                // Call the new function with context
242                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                // Fall back to the original create_bridge function
259                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        // Now register all bridges
296        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        // We intentionally leak the libraries to keep them loaded
306        // This is necessary because the bridge instances depend on the libraries
307        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
317/// Static bridge loader for loading bridges from compiled code
318pub struct StaticBridgeLoader {
319    /// Functions that create bridge instances
320    factories: Vec<Box<dyn Fn() -> BoxedBridge + Send + Sync>>,
321    /// Registry for managing bridge callbacks
322    registry: BridgeRegistry,
323}
324
325impl StaticBridgeLoader {
326    /// Create a new static bridge loader
327    pub fn new() -> Self {
328        Self {
329            factories: Vec::new(),
330            registry: BridgeRegistry::new(),
331        }
332    }
333
334    /// Register a factory function that creates a bridge
335    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    /// Get a reference to the registry
343    pub fn registry(&self) -> &BridgeRegistry {
344        &self.registry
345    }
346
347    /// Get a mutable reference to the registry
348    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        // First, create all bridges without registering them
356        let mut bridges = Vec::new();
357        for factory in &self.factories {
358            let bridge = factory();
359            bridges.push(bridge);
360        }
361
362        // Now register all bridges
363        for bridge in &mut bridges {
364            self.registry_mut().register_bridge(&mut **bridge)?;
365        }
366
367        Ok(bridges)
368    }
369}