Skip to main content

vs_core/service/
install.rs

1//! Services for planning and performing plugin installations.
2
3use crate::{App, CoreError, InstalledVersion};
4
5impl App {
6    /// Installs a plugin version, choosing the first available version when omitted.
7    pub fn install_plugin_version(
8        &self,
9        plugin_name: &str,
10        version: Option<&str>,
11    ) -> Result<InstalledVersion, CoreError> {
12        let entry = self.resolve_registry_entry(plugin_name)?;
13        let plugin = self.load_plugin(&entry)?;
14        let available_versions = self.cached_available_versions(plugin_name, &[])?;
15        let selected_version = version
16            .map(str::to_string)
17            .or_else(|| {
18                available_versions
19                    .first()
20                    .map(|candidate| candidate.version.clone())
21            })
22            .ok_or_else(|| {
23                CoreError::Unsupported(format!(
24                    "plugin {plugin_name} does not expose installable versions"
25                ))
26            })?;
27
28        let plan = plugin.install_plan(&selected_version)?;
29        let runtime = self.installer.install(&plan)?;
30
31        // Run PostInstall inside a logical transaction: if it fails, roll back
32        // the freshly committed install directory so we never leave a half-
33        // configured runtime on disk.
34        if let Err(error) = plugin.post_install(&runtime) {
35            let _ = std::fs::remove_dir_all(&runtime.root_dir);
36            return Err(error.into());
37        }
38
39        Ok(InstalledVersion {
40            plugin: plugin_name.to_string(),
41            version: runtime.version,
42            install_dir: runtime.root_dir,
43        })
44    }
45
46    /// Returns the version requested by the project config for a plugin, when present.
47    pub fn project_tool_version(&self, plugin_name: &str) -> Result<Option<String>, CoreError> {
48        Ok(self
49            .resolve_project_tool_version_internal(plugin_name)?
50            .map(|resolved| resolved.version))
51    }
52
53    /// Lists configured tools that should be installed for the current context.
54    pub fn configured_tools_for_install(&self) -> Result<Vec<(String, String)>, CoreError> {
55        Ok(self
56            .collect_current_tools()?
57            .into_iter()
58            .map(|tool| (tool.plugin, tool.version))
59            .collect())
60    }
61}