flarrow_url/
plugin.rs

1use std::path::PathBuf;
2
3use libloading::Library;
4use tokio::task::JoinHandle;
5use url::Url;
6
7use crate::prelude::*;
8
9pub trait UrlPlugin: Send {
10    #[allow(clippy::new_ret_no_self)]
11    fn new() -> JoinHandle<Result<Box<dyn UrlPlugin>>>
12    where
13        Self: Sized;
14
15    fn load(
16        &self,
17        url: Url,
18        inputs: Inputs,
19        outputs: Outputs,
20        configuration: serde_yml::Value,
21    ) -> JoinHandle<Result<RuntimeNode>>;
22}
23
24pub type DynamicallyLinkedUrlPluginInstance = fn() -> JoinHandle<Result<Box<dyn UrlPlugin>>>;
25
26pub struct DynamicallyLinkedUrlPlugin {
27    handle: Box<dyn UrlPlugin>, // The order is really important, the _library must be dropped after the handle
28    _library: Library,
29}
30
31pub enum RuntimeUrlPlugin {
32    StaticallyLinked(Box<dyn UrlPlugin>),
33    DynamicallyLinked(DynamicallyLinkedUrlPlugin),
34}
35
36impl RuntimeUrlPlugin {
37    pub async fn new_statically_linked<T: UrlPlugin + 'static>() -> Result<Self> {
38        Ok(Self::StaticallyLinked(
39            T::new()
40                .await
41                .wrap_err("Failed to await for statically linked url plugin")?
42                .wrap_err("Failed to create statically linked url plugin")?,
43        ))
44    }
45
46    pub async fn new_dynamically_linked(path: PathBuf) -> Result<Self> {
47        let library = unsafe { Library::new(path)? };
48        let constructor = unsafe {
49            library
50                .get::<*mut DynamicallyLinkedUrlPluginInstance>(b"FLARROW_URL_PLUGIN")?
51                .read()
52        };
53
54        Ok(RuntimeUrlPlugin::DynamicallyLinked(
55            DynamicallyLinkedUrlPlugin {
56                _library: library,
57                handle: (constructor)()
58                    .await
59                    .wrap_err("Failed to await for dynamically linked url plugin")?
60                    .wrap_err("Failed to create dynamically linked url plugin")?,
61            },
62        ))
63    }
64
65    pub fn load(
66        &self,
67        url: Url,
68        inputs: Inputs,
69        outputs: Outputs,
70        configuration: serde_yml::Value,
71    ) -> JoinHandle<Result<RuntimeNode>> {
72        match self {
73            RuntimeUrlPlugin::StaticallyLinked(plugin) => {
74                plugin.load(url, inputs, outputs, configuration)
75            }
76            RuntimeUrlPlugin::DynamicallyLinked(plugin) => {
77                plugin.handle.load(url, inputs, outputs, configuration)
78            }
79        }
80    }
81}