mod fs;
mod install;
mod receipt;
use std::path::PathBuf;
use thiserror::Error;
pub use install::Installer;
pub use receipt::InstallReceipt;
#[derive(Debug, Error)]
pub enum InstallerError {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error("artifact download failed: {0}")]
Download(String),
#[error("failed to walk directory tree: {0}")]
Walk(String),
#[error("install source does not exist: {0}")]
MissingSource(PathBuf),
#[error("failed to parse JSON file at {path}: {message}")]
Json { path: PathBuf, message: String },
#[error("install validation failed: {0}")]
Validation(String),
#[error(transparent)]
Archive(#[from] zip::result::ZipError),
}
#[cfg(test)]
mod tests {
use std::error::Error;
use std::fs;
use tempfile::TempDir;
use vs_plugin_api::{InstallArtifact, InstallPlan, InstallSource};
use super::Installer;
#[test]
fn install_should_rollback_when_validation_fails() -> Result<(), Box<dyn Error>> {
let temp_dir = TempDir::new()?;
let source = temp_dir.path().join("source");
fs::create_dir_all(&source)?;
fs::write(source.join(".vs-fail-install"), "")?;
let installer = Installer::new(temp_dir.path().join("home"));
let plan = InstallPlan {
plugin: String::from("nodejs"),
version: String::from("20.11.1"),
main: InstallArtifact {
name: String::from("nodejs"),
version: String::from("20.11.1"),
source: InstallSource::Directory { path: source },
note: None,
checksum: None,
},
additions: Vec::new(),
legacy_filenames: Vec::new(),
};
let error = match installer.install(&plan) {
Ok(_) => {
return Err(Box::new(std::io::Error::other(
"install unexpectedly succeeded",
)));
}
Err(error) => error,
};
assert!(error.to_string().contains("validation failed"));
assert!(!installer.install_dir("nodejs", "20.11.1").exists());
Ok(())
}
}