use std::path::{Path, PathBuf};
use standard_plugin_manifest::{MANIFEST_MAX_BYTES, Manifest};
#[derive(Clone, Debug)]
pub struct Plugin {
pub dir: PathBuf,
pub manifest_path: PathBuf,
pub bundle: PathBuf,
}
impl Plugin {
fn at(dir: &Path) -> Option<Self> {
let bundle = dir.join("bundle");
[
bundle.join("standard-plugin.json"),
dir.join("standard-plugin.json"),
]
.into_iter()
.find(|path| path.is_file())
.map(|manifest_path| Self {
dir: dir.to_path_buf(),
manifest_path,
bundle,
})
}
pub fn manifest_bytes(&self) -> Result<Vec<u8>, String> {
let bytes = std::fs::read(&self.manifest_path)
.map_err(|error| format!("{}: {error}", self.manifest_path.display()))?;
if bytes.len() as u64 > MANIFEST_MAX_BYTES {
return Err(format!(
"{} is larger than {MANIFEST_MAX_BYTES} bytes",
self.manifest_path.display()
));
}
Ok(bytes)
}
pub fn manifest(&self) -> Result<Manifest, String> {
Manifest::parse(&self.manifest_bytes()?)
.map_err(|error| format!("{}: {error}", self.manifest_path.display()))
}
pub fn stage_manifest(&self) -> std::io::Result<()> {
std::fs::create_dir_all(&self.bundle)?;
let staged = self.bundle.join("standard-plugin.json");
if self.manifest_path != staged {
std::fs::copy(&self.manifest_path, staged)?;
}
Ok(())
}
}
pub fn plugins(dir: &Path) -> Result<Vec<Plugin>, String> {
if let Some(plugin) = Plugin::at(dir) {
return Ok(vec![plugin]);
}
let mut found: Vec<Plugin> = std::fs::read_dir(dir)
.map_err(|error| format!("{}: {error}", dir.display()))?
.filter_map(Result::ok)
.map(|entry| entry.path())
.filter(|path| path.is_dir())
.filter_map(|path| Plugin::at(&path))
.collect();
found.sort_by(|a, b| a.dir.cmp(&b.dir));
if found.is_empty() {
return Err(format!(
"no plugin in {}: expected bundle/standard-plugin.json or standard-plugin.json",
dir.display()
));
}
Ok(found)
}