use std::path::{Path, PathBuf};
use std::sync::Arc;
use ferridriver_script::{compile_and_extract_plugins, walk_source_files};
use super::manifest::PluginManifest;
#[derive(Debug, Clone)]
pub struct LoadedPlugin {
pub tools: Vec<PluginManifest>,
pub bytecode: Arc<[u8]>,
pub path: PathBuf,
}
#[derive(Debug)]
pub enum PluginLoadError {
Io {
path: PathBuf,
error: std::io::Error,
},
Bundle {
path: PathBuf,
message: String,
},
ManifestInvalid {
path: PathBuf,
error: serde_json::Error,
},
ManifestNoTools {
path: PathBuf,
},
}
impl std::fmt::Display for PluginLoadError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Io { path, error } => write!(f, "read {}: {error}", path.display()),
Self::Bundle { path, message } => write!(f, "bundle {}: {message}", path.display()),
Self::ManifestInvalid { path, error } => write!(f, "{}: manifest invalid: {error}", path.display()),
Self::ManifestNoTools { path } => write!(f, "{}: no tools declared in globalThis.exports", path.display()),
}
}
}
impl std::error::Error for PluginLoadError {}
pub async fn load_all(files: &[PathBuf]) -> (Vec<LoadedPlugin>, Vec<PluginLoadError>) {
let (compiled, bundle_failures) = compile_and_extract_plugins(files).await;
let mut loaded = Vec::with_capacity(compiled.len());
let mut errors: Vec<PluginLoadError> = bundle_failures
.into_iter()
.map(|(path, e)| PluginLoadError::Bundle {
path,
message: e.message,
})
.collect();
for cp in compiled {
let tools: Vec<PluginManifest> = match serde_json::from_str(&cp.manifests_json) {
Ok(t) => t,
Err(error) => {
errors.push(PluginLoadError::ManifestInvalid { path: cp.path, error });
continue;
},
};
if tools.is_empty() {
errors.push(PluginLoadError::ManifestNoTools { path: cp.path });
continue;
}
loaded.push(LoadedPlugin {
tools,
bytecode: cp.bytecode,
path: cp.path,
});
}
(loaded, errors)
}
pub fn discover(path: &Path) -> Result<Vec<PathBuf>, PluginLoadError> {
let meta = std::fs::metadata(path).map_err(|error| PluginLoadError::Io {
path: path.to_path_buf(),
error,
})?;
if meta.is_file() {
return Ok(vec![path.to_path_buf()]);
}
if !meta.is_dir() {
return Ok(Vec::new());
}
Ok(walk_source_files(path))
}