1use vs_plugin_api::PluginBackendKind;
2use vs_registry::RegistryEntry;
3
4use crate::plugin_source::is_remote_source;
5use crate::registry_source::fetch_plugin_manifest;
6use crate::{App, CoreError};
7
8impl App {
9 pub fn add_plugin(
11 &self,
12 name: Option<&str>,
13 source: Option<String>,
14 backend: Option<PluginBackendKind>,
15 alias: Option<&str>,
16 ) -> Result<RegistryEntry, CoreError> {
17 let entry = if let Some(source) = source {
18 let backend = backend.unwrap_or(self.default_backend()?);
19 self.ensure_backend_supported(backend)?;
20 let requested_name = name.or(alias).unwrap_or("plugin");
21 let seed_entry = RegistryEntry {
22 name: requested_name.to_string(),
23 source: if is_remote_source(&source) {
24 source
25 } else {
26 self.normalize_source_path(&source).display().to_string()
27 },
28 backend,
29 description: None,
30 aliases: Vec::new(),
31 };
32 let materialized = self.materialize_plugin_entry(&seed_entry)?;
33 let plugin = self.load_plugin(&materialized)?;
34 let manifest = plugin.manifest().clone();
35 let final_name = alias.or(name).unwrap_or(&manifest.name).to_string();
36 let mut aliases = manifest.aliases;
37 push_alias(&mut aliases, name);
38 push_alias(&mut aliases, Some(&manifest.name));
39 aliases.retain(|entry_alias| entry_alias != &final_name);
40 RegistryEntry {
41 name: final_name,
42 source: materialized.source,
43 backend,
44 description: manifest.description,
45 aliases,
46 }
47 } else {
48 let name = name.ok_or_else(|| {
49 CoreError::Unsupported(String::from(
50 "add requires a plugin name unless --source is provided",
51 ))
52 })?;
53 let mut entry = self.resolve_registry_entry(name)?;
54 if entry.backend == PluginBackendKind::Lua {
55 let config = self.app_config()?;
56 if !config.registry.address.is_empty() {
57 if let Ok(manifest) =
58 fetch_plugin_manifest(&config.registry.address, &entry.name)
59 {
60 entry.source = manifest.download_url;
61 entry.description = manifest.description.or(entry.description);
62 }
63 }
64 }
65 let materialized = self.materialize_plugin_entry(&entry)?;
66 let plugin = self.load_plugin(&materialized)?;
67 let manifest = plugin.manifest().clone();
68 let final_name = alias.unwrap_or(name).to_string();
69 let mut aliases = entry.aliases;
70 push_alias(&mut aliases, Some(name));
71 push_alias(&mut aliases, Some(&manifest.name));
72 aliases.extend(manifest.aliases);
73 aliases.sort();
74 aliases.dedup();
75 aliases.retain(|entry_alias| entry_alias != &final_name);
76 RegistryEntry {
77 name: final_name,
78 source: materialized.source,
79 backend: entry.backend,
80 description: manifest.description.or(entry.description),
81 aliases,
82 }
83 };
84
85 self.registry.add_plugin(entry.clone())?;
86 Ok(entry)
87 }
88}
89
90fn push_alias(aliases: &mut Vec<String>, alias: Option<&str>) {
91 if let Some(alias) = alias {
92 let alias = alias.trim();
93 if !alias.is_empty() {
94 aliases.push(alias.to_string());
95 }
96 }
97}