standard_plugin_cli/
project.rs1use std::path::{Path, PathBuf};
6
7use standard_plugin_manifest::{MANIFEST_MAX_BYTES, Manifest};
8
9#[derive(Clone, Debug)]
11pub struct Plugin {
12 pub dir: PathBuf,
13 pub manifest_path: PathBuf,
15 pub bundle: PathBuf,
17}
18
19impl Plugin {
20 fn at(dir: &Path) -> Option<Self> {
21 let bundle = dir.join("bundle");
22 [
23 bundle.join("standard-plugin.json"),
24 dir.join("standard-plugin.json"),
25 ]
26 .into_iter()
27 .find(|path| path.is_file())
28 .map(|manifest_path| Self {
29 dir: dir.to_path_buf(),
30 manifest_path,
31 bundle,
32 })
33 }
34
35 pub fn manifest_bytes(&self) -> Result<Vec<u8>, String> {
36 let bytes = std::fs::read(&self.manifest_path)
37 .map_err(|error| format!("{}: {error}", self.manifest_path.display()))?;
38 if bytes.len() as u64 > MANIFEST_MAX_BYTES {
39 return Err(format!(
40 "{} is larger than {MANIFEST_MAX_BYTES} bytes",
41 self.manifest_path.display()
42 ));
43 }
44 Ok(bytes)
45 }
46
47 pub fn manifest(&self) -> Result<Manifest, String> {
48 Manifest::parse(&self.manifest_bytes()?)
49 .map_err(|error| format!("{}: {error}", self.manifest_path.display()))
50 }
51
52 pub fn stage_manifest(&self) -> std::io::Result<()> {
54 std::fs::create_dir_all(&self.bundle)?;
55 let staged = self.bundle.join("standard-plugin.json");
56 if self.manifest_path != staged {
57 std::fs::copy(&self.manifest_path, staged)?;
58 }
59 Ok(())
60 }
61}
62
63pub fn plugins(dir: &Path) -> Result<Vec<Plugin>, String> {
67 if let Some(plugin) = Plugin::at(dir) {
68 return Ok(vec![plugin]);
69 }
70 let mut found: Vec<Plugin> = std::fs::read_dir(dir)
71 .map_err(|error| format!("{}: {error}", dir.display()))?
72 .filter_map(Result::ok)
73 .map(|entry| entry.path())
74 .filter(|path| path.is_dir())
75 .filter_map(|path| Plugin::at(&path))
76 .collect();
77 found.sort_by(|a, b| a.dir.cmp(&b.dir));
78 if found.is_empty() {
79 return Err(format!(
80 "no plugin in {}: expected bundle/standard-plugin.json or standard-plugin.json",
81 dir.display()
82 ));
83 }
84 Ok(found)
85}