Skip to main content

vs_installer/
lib.rs

1//! Transactional runtime installs for `vs`.
2
3mod fs;
4mod install;
5mod receipt;
6
7use std::path::PathBuf;
8
9use thiserror::Error;
10
11pub use install::Installer;
12pub use receipt::InstallReceipt;
13
14/// Errors returned by installer services.
15#[derive(Debug, Error)]
16pub enum InstallerError {
17    /// An I/O operation failed.
18    #[error(transparent)]
19    Io(#[from] std::io::Error),
20    /// A download failed.
21    #[error("artifact download failed: {0}")]
22    Download(String),
23    /// A directory walk failed.
24    #[error("failed to walk directory tree: {0}")]
25    Walk(String),
26    /// The source directory was not found.
27    #[error("install source does not exist: {0}")]
28    MissingSource(PathBuf),
29    /// JSON data could not be parsed.
30    #[error("failed to parse JSON file at {path}: {message}")]
31    Json { path: PathBuf, message: String },
32    /// The install validation step failed.
33    #[error("install validation failed: {0}")]
34    Validation(String),
35    /// An archive could not be unpacked.
36    #[error(transparent)]
37    Archive(#[from] zip::result::ZipError),
38}
39
40#[cfg(test)]
41mod tests {
42    use std::error::Error;
43    use std::fs;
44
45    use tempfile::TempDir;
46    use vs_plugin_api::{InstallArtifact, InstallPlan, InstallSource};
47
48    use super::Installer;
49
50    #[test]
51    fn install_should_rollback_when_validation_fails() -> Result<(), Box<dyn Error>> {
52        let temp_dir = TempDir::new()?;
53        let source = temp_dir.path().join("source");
54        fs::create_dir_all(&source)?;
55        fs::write(source.join(".vs-fail-install"), "")?;
56
57        let installer = Installer::new(temp_dir.path().join("home"));
58        let plan = InstallPlan {
59            plugin: String::from("nodejs"),
60            version: String::from("20.11.1"),
61            main: InstallArtifact {
62                name: String::from("nodejs"),
63                version: String::from("20.11.1"),
64                source: InstallSource::Directory { path: source },
65                note: None,
66                checksum: None,
67            },
68            additions: Vec::new(),
69            legacy_filenames: Vec::new(),
70        };
71
72        let error = match installer.install(&plan) {
73            Ok(_) => {
74                return Err(Box::new(std::io::Error::other(
75                    "install unexpectedly succeeded",
76                )));
77            }
78            Err(error) => error,
79        };
80        assert!(error.to_string().contains("validation failed"));
81        assert!(!installer.install_dir("nodejs", "20.11.1").exists());
82        Ok(())
83    }
84}