Skip to main content

vs_core/service/
add.rs

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    /// Adds a plugin to the local home.
10    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}