rusta-cli 1.0.18

macOS arm64 CLI for creating and managing Ubuntu VMs on Tart
use std::fmt;

#[derive(Debug)]
pub struct Error {
    pub message: String,
    pub exit_code: u8,
}

impl Error {
    pub fn msg<S: Into<String>>(m: S) -> Self {
        Self { message: m.into(), exit_code: 1 }
    }
    pub fn with_code<S: Into<String>>(m: S, code: u8) -> Self {
        Self { message: m.into(), exit_code: code }
    }
    pub fn not_found<S: Into<String>>(m: S) -> Self {
        Self::with_code(m, 2)
    }
    pub fn cmd(what: &str, e: std::io::Error) -> Self {
        Self::msg(format!("failed to run {}: {}", what, e))
    }
    pub fn exit_code(&self) -> u8 {
        self.exit_code
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str(&self.message)
    }
}

impl From<std::io::Error> for Error {
    fn from(e: std::io::Error) -> Self {
        Self::msg(e.to_string())
    }
}

pub type Result<T> = std::result::Result<T, Error>;

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn defaults_exit_code_one() {
        let e = Error::msg("x");
        assert_eq!(e.exit_code(), 1);
        assert_eq!(format!("{e}"), "x");
    }

    #[test]
    fn not_found_uses_code_two() {
        assert_eq!(Error::not_found("nope").exit_code(), 2);
    }

    #[test]
    fn with_code_uses_provided() {
        assert_eq!(Error::with_code("x", 7).exit_code(), 7);
    }

    #[test]
    fn cmd_wraps_io_error() {
        let io_err = std::io::Error::new(std::io::ErrorKind::Other, "boom");
        let e = Error::cmd("widget", io_err);
        assert!(e.message.contains("widget"));
        assert!(e.message.contains("boom"));
    }

    #[test]
    fn from_io_error_default_exit_one() {
        let io_err = std::io::Error::new(std::io::ErrorKind::Other, "bad");
        let e: Error = io_err.into();
        assert_eq!(e.exit_code(), 1);
    }
}