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