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