use std::path::Path;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum VmSpectError {
#[error("I/O error: {0}")]
Io(#[from] std::io::Error),
#[error("Parse error: {0}")]
Parse(String),
#[error("Unsupported operating system: {0}")]
UnsupportedOs(String),
#[error("Unsupported image format: {0}")]
UnsupportedFormat(String),
#[error("Disk image not found: {0}")]
ImageNotFound(String),
#[error("Inspection was cancelled by the user")]
Cancelled,
#[error("Missing {component_type} '{declared_name}' referenced by '{descriptor_path}'. Resolved path: '{resolved_path}'. OS error: {source}")]
MissingDiskComponent {
descriptor_path: String,
declared_name: String,
resolved_path: String,
component_type: String,
#[source]
source: std::io::Error,
},
#[error("QEMU tool not available: {0}")]
QemuNotFound(String),
#[error("NBD protocol error: {0}")]
Nbd(String),
#[error("File system error: {0}")]
FileSystem(String),
#[error("Windows Registry error: {0}")]
WindowsRegistry(String),
#[error("Configuration error: {0}")]
Config(String),
#[error("Inspection error: {0}")]
Other(String),
}
impl VmSpectError {
pub(crate) fn from_disk_component_io(
descriptor_path: &Path,
declared_name: &str,
resolved_path: &Path,
component_type: &str,
operation: &str,
source: std::io::Error,
) -> Self {
if source.kind() == std::io::ErrorKind::NotFound {
Self::MissingDiskComponent {
descriptor_path: descriptor_path.display().to_string(),
declared_name: declared_name.to_string(),
resolved_path: resolved_path.display().to_string(),
component_type: component_type.to_string(),
source,
}
} else {
let message = format!(
"Could not {operation} {component_type} '{declared_name}' referenced by '{}'. Resolved path: '{}': {source}",
descriptor_path.display(),
resolved_path.display(),
);
Self::Io(std::io::Error::new(source.kind(), message))
}
}
}
impl From<String> for VmSpectError {
fn from(msg: String) -> Self {
VmSpectError::Other(msg)
}
}
impl From<&str> for VmSpectError {
fn from(msg: &str) -> Self {
VmSpectError::Other(msg.to_string())
}
}
pub type Result<T> = std::result::Result<T, VmSpectError>;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_error_display() {
let err_io = VmSpectError::Io(std::io::Error::new(
std::io::ErrorKind::NotFound,
"file not found",
));
assert!(err_io.to_string().contains("I/O error"));
let err_cancel = VmSpectError::Cancelled;
assert_eq!(
err_cancel.to_string(),
"Inspection was cancelled by the user"
);
let err_other: VmSpectError = "something failed".into();
assert_eq!(err_other.to_string(), "Inspection error: something failed");
}
}