1mod 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#[derive(Debug, Error)]
16pub enum InstallerError {
17 #[error(transparent)]
19 Io(#[from] std::io::Error),
20 #[error("artifact download failed: {0}")]
22 Download(String),
23 #[error("failed to walk directory tree: {0}")]
25 Walk(String),
26 #[error("install source does not exist: {0}")]
28 MissingSource(PathBuf),
29 #[error("failed to parse JSON file at {path}: {message}")]
31 Json { path: PathBuf, message: String },
32 #[error("install validation failed: {0}")]
34 Validation(String),
35 #[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}