zenoh_flow/runtime/dataflow/
loader.rs1use super::instance::builtin::zenoh::{get_zenoh_sink_declaration, get_zenoh_source_declaration};
16use super::node::{
17 ConstructorFn, OperatorConstructor, OperatorFn, SinkConstructor, SinkFn, SourceConstructor,
18 SourceFn,
19};
20use crate::model::record::{OperatorRecord, SinkRecord, SourceRecord};
21use crate::model::{Middleware, ZFUri};
22use crate::types::Configuration;
23use crate::utils::parse_uri;
24use crate::zfresult::ErrorKind;
25use crate::Result;
26use crate::{bail, zferror};
27use serde::{Deserialize, Serialize};
28use std::path::{Path, PathBuf};
29use std::sync::Arc;
30
31#[cfg(target_family = "unix")]
32use libloading::os::unix::Library;
33#[cfg(target_family = "windows")]
34use libloading::Library;
35
36#[cfg(target_family = "unix")]
37static LOAD_FLAGS: std::os::raw::c_int =
38 libloading::os::unix::RTLD_NOW | libloading::os::unix::RTLD_LOCAL;
39
40pub static CORE_VERSION: &str = env!("CARGO_PKG_VERSION");
44pub static RUSTC_VERSION: &str = env!("RUSTC_VERSION");
48
49pub static EXT_FILE_EXTENSION: &str = "zfext";
50
51pub(crate) enum NodeSymbol {
53 Source,
54 Operator,
55 Sink,
56}
57
58impl NodeSymbol {
59 pub(crate) fn to_bytes(&self) -> &[u8] {
67 match self {
68 NodeSymbol::Source => b"_zf_export_source\0",
69 NodeSymbol::Operator => b"_zf_export_operator\0",
70 NodeSymbol::Sink => b"_zf_export_sink\0",
71 }
72 }
73}
74
75pub struct NodeDeclaration<C> {
77 pub rustc_version: &'static str,
78 pub core_version: &'static str,
79 pub constructor: C,
80}
81
82pub type SourceDeclaration = NodeDeclaration<SourceFn>;
83pub type OperatorDeclaration = NodeDeclaration<OperatorFn>;
84pub type SinkDeclaration = NodeDeclaration<SinkFn>;
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
101pub struct ExtensibleImplementation {
102 pub(crate) name: String,
103 pub(crate) file_extension: String,
104 pub(crate) source_lib: String,
105 pub(crate) sink_lib: String,
106 pub(crate) operator_lib: String,
107 pub(crate) config_lib_key: String,
108}
109
110#[derive(Debug, Clone, Serialize, Deserialize)]
125pub struct LoaderConfig {
126 extensions: Vec<ExtensibleImplementation>,
127}
128
129impl LoaderConfig {
130 pub fn new() -> Self {
132 Self { extensions: vec![] }
133 }
134
135 pub fn try_add_extension(&mut self, ext: ExtensibleImplementation) -> Result<()> {
140 if self.extensions.iter().any(|e| e.name == ext.name) {
141 return Err(zferror!(ErrorKind::Duplicate).into());
142 }
143 self.extensions.push(ext);
144 Ok(())
145 }
146
147 pub fn remove_extension(&mut self, name: &str) -> Option<ExtensibleImplementation> {
149 if let Some(index) = self.extensions.iter().position(|e| e.name == name) {
150 let ext = self.extensions.remove(index);
151 return Some(ext);
152 }
153 None
154 }
155
156 pub fn get_extension_by_file_extension(
158 &self,
159 file_extension: &str,
160 ) -> Option<&ExtensibleImplementation> {
161 if let Some(ext) = self
162 .extensions
163 .iter()
164 .find(|e| e.file_extension == file_extension)
165 {
166 return Some(ext);
167 }
168 None
169 }
170
171 pub fn get_extension_by_name(&self, name: &str) -> Option<&ExtensibleImplementation> {
173 if let Some(ext) = self.extensions.iter().find(|e| e.name == name) {
174 return Some(ext);
175 }
176 None
177 }
178}
179
180impl Default for LoaderConfig {
181 fn default() -> Self {
182 Self::new()
183 }
184}
185
186pub struct Loader {
197 pub(crate) config: LoaderConfig,
198}
199
200impl Loader {
201 pub fn new(config: LoaderConfig) -> Self {
203 Self { config }
204 }
205
206 unsafe fn load_node_from_file<T: ConstructorFn>(
217 &self,
218 node_symbol: NodeSymbol,
219 file_path: PathBuf,
220 configuration: &mut Option<Configuration>,
221 ) -> Result<(Library, T)> {
222 let file_extension = crate::utils::get_file_extension(&file_path).ok_or_else(|| {
223 zferror!(
224 ErrorKind::LoadingError,
225 "Missing file extension for < {:?} >",
226 file_path,
227 )
228 })?;
229
230 let library_path = if crate::utils::is_dynamic_library(&file_extension) {
231 file_path
232 } else {
233 match self.config.get_extension_by_file_extension(&file_extension) {
234 Some(e) => {
235 Self::wrap_configuration(configuration, e.config_lib_key.clone(), &file_path)?;
236 let lib = match node_symbol {
237 NodeSymbol::Source => &e.source_lib,
238 NodeSymbol::Operator => &e.operator_lib,
239 NodeSymbol::Sink => &e.sink_lib,
240 };
241 std::fs::canonicalize(lib)?
242 }
243 _ => bail!(ErrorKind::Unimplemented),
244 }
245 };
246
247 log::trace!("[Loader] loading library {:?}", library_path);
248
249 #[cfg(target_family = "unix")]
250 let library = Library::open(Some(library_path.clone()), LOAD_FLAGS)?;
251
252 #[cfg(target_family = "windows")]
253 let library = Library::new(library_path)?;
254
255 let decl = library
256 .get::<*mut NodeDeclaration<T>>(node_symbol.to_bytes())?
257 .read();
258
259 if decl.rustc_version != RUSTC_VERSION || decl.core_version != CORE_VERSION {
261 return Err(zferror!(
262 ErrorKind::VersionMismatch,
263 "Library {} rustc expected {} rustc found {} - Zenoh-Flow expected {} Zenoh-Flow found {}",
264 library_path.display(),
265 RUSTC_VERSION,
266 decl.rustc_version,
267 CORE_VERSION,
268 decl.core_version
269 )
270 .into());
271 }
272
273 Ok((library, decl.constructor))
274 }
275
276 fn load_source_from_builtin(&self, middleware: Middleware) -> Result<SourceFn> {
283 match middleware {
284 Middleware::Zenoh => {
285 let declaration = get_zenoh_source_declaration();
286 Ok(declaration.constructor)
287 }
288 }
289 }
290
291 fn load_sink_from_builtin(&self, middleware: Middleware) -> Result<SinkFn> {
298 match middleware {
299 Middleware::Zenoh => {
300 let declaration = get_zenoh_sink_declaration();
301 Ok(declaration.constructor)
302 }
303 }
304 }
305
306 pub(crate) fn load_source_constructor(
318 &self,
319 mut record: SourceRecord,
320 ) -> Result<SourceConstructor> {
321 if let Some(uri) = &record.uri {
322 match parse_uri(uri)? {
323 ZFUri::File(file_path) => {
324 let (library, constructor) = unsafe {
325 self.load_node_from_file::<SourceFn>(
326 NodeSymbol::Source,
327 file_path,
328 &mut record.configuration,
329 )?
330 };
331
332 Ok(SourceConstructor::new_dynamic(
333 record,
334 constructor,
335 Arc::new(library),
336 ))
337 }
338 ZFUri::Builtin(mw) => {
339 let constructor = self.load_source_from_builtin(mw)?;
340 Ok(SourceConstructor::new_static(record, constructor))
341 }
342 }
343 } else {
344 bail!(
345 ErrorKind::LoadingError,
346 "Missing URI for dynamically loaded Source < {} >.",
347 record.id.clone()
348 )
349 }
350 }
351
352 pub(crate) fn load_operator_constructor(
365 &self,
366 mut record: OperatorRecord,
367 ) -> Result<OperatorConstructor> {
368 if let Some(uri) = &record.uri {
369 match parse_uri(uri)? {
370 ZFUri::File(file_path) => {
371 let (library, constructor) = unsafe {
372 self.load_node_from_file::<OperatorFn>(
373 NodeSymbol::Operator,
374 file_path,
375 &mut record.configuration,
376 )?
377 };
378
379 Ok(OperatorConstructor::new_dynamic(
380 record,
381 constructor,
382 Arc::new(library),
383 ))
384 }
385 ZFUri::Builtin(_mw) => {
386 bail!(
387 ErrorKind::Unimplemented,
388 "Loading builtin operators is not supported < {} >.",
389 record.id.clone()
390 )
391 }
392 }
393 } else {
394 bail!(
395 ErrorKind::LoadingError,
396 "Missing URI for dynamically loaded Operator < {} >.",
397 record.id.clone()
398 )
399 }
400 }
401
402 pub(crate) fn load_sink_constructor(&self, mut record: SinkRecord) -> Result<SinkConstructor> {
414 if let Some(uri) = &record.uri {
415 match parse_uri(uri)? {
416 ZFUri::File(file_path) => {
417 let (library, constructor) = unsafe {
418 self.load_node_from_file::<SinkFn>(
419 NodeSymbol::Sink,
420 file_path,
421 &mut record.configuration,
422 )?
423 };
424
425 Ok(SinkConstructor::new_dynamic(
426 record,
427 constructor,
428 Arc::new(library),
429 ))
430 }
431 ZFUri::Builtin(mw) => {
432 let constructor = self.load_sink_from_builtin(mw)?;
433 Ok(SinkConstructor::new_static(record, constructor))
434 }
435 }
436 } else {
437 bail!(
438 ErrorKind::LoadingError,
439 "Missing URI for dynamically loaded Sink < {} >.",
440 record.id.clone()
441 )
442 }
443 }
444
445 fn wrap_configuration(
452 configuration: &mut Option<Configuration>,
453 config_key: String,
454 file_path: &Path,
455 ) -> Result<()> {
456 let mut new_config: serde_json::map::Map<String, Configuration> =
457 serde_json::map::Map::new();
458 let config = configuration.take();
459 new_config.insert(
460 config_key,
461 file_path
462 .to_str()
463 .ok_or_else(|| {
464 zferror!(
465 ErrorKind::LoadingError,
466 "Unable parse file path < {:?} >.",
467 file_path,
468 )
469 })?
470 .into(),
471 );
472
473 if let Some(config) = config {
474 new_config.insert(String::from("configuration"), config);
475 }
476
477 *configuration = Some(new_config.into());
478 Ok(())
479 }
480}