flarrow_url_scheme/
url_scheme.rs1use std::{collections::HashMap, path::PathBuf, sync::Arc};
2
3use crate::prelude::{thirdparty::libloading::Library, *};
4
5pub struct UrlSchemeManager {
6 pub plugins: HashMap<String, Arc<RuntimeUrlScheme>>,
7}
8
9pub struct UrlSchemeManagerBuilder {
10 pub plugins: HashMap<String, Arc<RuntimeUrlScheme>>,
11}
12
13impl UrlSchemeManager {
14 pub fn new(plugins: HashMap<String, Arc<RuntimeUrlScheme>>) -> Self {
15 UrlSchemeManager { plugins }
16 }
17
18 #[allow(clippy::too_many_arguments)]
19 pub async fn load(
20 &self,
21 url: Url,
22 inputs: Inputs,
23 outputs: Outputs,
24 queries: Queries,
25 queryables: Queryables,
26 configuration: serde_yml::Value,
27 file_ext: Arc<FileExtManager>,
28 ) -> Result<RuntimeNode> {
29 let scheme = url.scheme();
30
31 let plugin = self
32 .plugins
33 .get(scheme)
34 .ok_or_eyre(format!("Plugin not found for scheme '{}'", scheme))?;
35
36 plugin
37 .load(
38 url,
39 inputs,
40 outputs,
41 queries,
42 queryables,
43 configuration,
44 file_ext,
45 )
46 .await
47 }
48}
49
50impl UrlSchemeManagerBuilder {
51 pub async fn new() -> Result<Self> {
52 Ok(Self {
53 plugins: HashMap::new(),
54 })
55 }
56
57 pub async fn load_statically_linked_plugin<T: UrlSchemePlugin + 'static>(
58 &mut self,
59 ) -> Result<()> {
60 let plugin = T::new().await?.wrap_err(format!(
61 "Failed to load static plugin '{}'",
62 std::any::type_name::<T>(),
63 ))?;
64
65 let plugin = Arc::new(RuntimeUrlScheme::StaticallyLinked(plugin));
66
67 for ext in &plugin.target() {
68 self.plugins.insert(ext.to_string(), plugin.clone());
69 }
70
71 tracing::debug!(
72 "Loaded statically linked plugin: {}",
73 std::any::type_name::<T>()
74 );
75
76 Ok(())
77 }
78
79 pub async fn load_dynamically_linked_plugin(&mut self, path: PathBuf) -> Result<()> {
80 match path.extension() {
81 Some(ext) => {
82 if ext == std::env::consts::DLL_EXTENSION {
83 let path_buf = path.clone();
84 let (library, constructor) = tokio::task::spawn_blocking(move || {
85 let library = unsafe {
86 Library::new(path_buf.clone())
87 .wrap_err(format!("Failed to load path {:?}", path_buf))?
88 };
89
90 let constructor = unsafe {
91 library
92 .get::<*mut DynamicallyLinkedUrlSchemePluginInstance>(
93 b"FLARROW_URL_SCHEME_PLUGIN",
94 )
95 .wrap_err(format!(
96 "Failed to load symbol 'FLARROW_URL_SCHEME_PLUGIN' from cdylib {:?}",
97 path_buf
98 ))?
99 .read()
100 };
101
102 Ok::<_, eyre::Report>((library, constructor))
103 })
104 .await??;
105
106 let plugin = Arc::new(RuntimeUrlScheme::DynamicallyLinked(
107 DynamicallyLinkedUrlSchemePlugin {
108 _library: library,
109 handle: (constructor)().await?.wrap_err(format!(
110 "Failed to load dynamically linked plugin '{:?}'",
111 path,
112 ))?,
113 },
114 ));
115
116 for ext in &plugin.target() {
117 self.plugins.insert(ext.to_string(), plugin.clone());
118 }
119
120 tracing::debug!(
121 "Loaded dynamically linked plugin from path: {}",
122 path.display()
123 );
124
125 Ok(())
126 } else {
127 Err(eyre::eyre!("Extension '{:?}' is not supported", ext))
128 }
129 }
130 _ => Err(eyre::eyre!("Unsupported path '{:?}'", path)),
131 }
132 }
133}
134
135pub struct DynamicallyLinkedUrlSchemePlugin {
136 pub handle: Box<dyn UrlSchemePlugin>,
137 pub _library: Library, }
139
140pub enum RuntimeUrlScheme {
141 StaticallyLinked(Box<dyn UrlSchemePlugin>),
142 DynamicallyLinked(DynamicallyLinkedUrlSchemePlugin),
143}
144
145impl RuntimeUrlScheme {
146 pub fn target(&self) -> Vec<String> {
147 match self {
148 RuntimeUrlScheme::StaticallyLinked(plugin) => plugin.target(),
149 RuntimeUrlScheme::DynamicallyLinked(plugin) => plugin.handle.target(),
150 }
151 }
152
153 #[allow(clippy::too_many_arguments)]
154 pub async fn load(
155 &self,
156 url: Url,
157 inputs: Inputs,
158 outputs: Outputs,
159 queries: Queries,
160 queryables: Queryables,
161 configuration: serde_yml::Value,
162 file_ext: Arc<FileExtManager>,
163 ) -> Result<RuntimeNode> {
164 match self {
165 RuntimeUrlScheme::StaticallyLinked(plugin) => {
166 plugin
167 .load(
168 url,
169 inputs,
170 outputs,
171 queries,
172 queryables,
173 configuration,
174 file_ext,
175 )
176 .await?
177 }
178 RuntimeUrlScheme::DynamicallyLinked(plugin) => {
179 plugin
180 .handle
181 .load(
182 url,
183 inputs,
184 outputs,
185 queries,
186 queryables,
187 configuration,
188 file_ext,
189 )
190 .await?
191 }
192 }
193 }
194}