locket 0.17.3

Helper tool for secret injection as a process dependency
Documentation
use locket::error::LocketError;
use locket::events::HandlerError;
use locket::provider::ProviderError;
use locket::secrets::SecretError;
use locket::watch::WatchError;
use std::os::unix::process::ExitStatusExt;
use std::process::{ExitCode, Termination};

#[derive(Debug)]
pub struct LocketExitCode(pub LocketError);

impl Termination for LocketExitCode {
    fn report(self) -> ExitCode {
        let code = self.exit_code();
        tracing::error!(exit_code = code, "{}", self.0);
        ExitCode::from(code)
    }
}

impl LocketExitCode {
    fn exit_code(&self) -> u8 {
        match &self.0 {
            LocketError::Secret(e) => match e {
                SecretError::Io(_) => sysexits::ExitCode::IoErr.into(),
                SecretError::Provider(_) => sysexits::ExitCode::Config.into(),
                SecretError::Task(_) => sysexits::ExitCode::Software.into(),
                SecretError::SourceTooLarge { .. } => sysexits::ExitCode::DataErr.into(),
                SecretError::Collision { .. } => sysexits::ExitCode::Usage.into(),
                SecretError::StructureConflict { .. } => sysexits::ExitCode::Usage.into(),
                SecretError::SourceMissing(_) => sysexits::ExitCode::NoInput.into(),
                SecretError::Loop { .. } => sysexits::ExitCode::Usage.into(),
                SecretError::Destructive { .. } => sysexits::ExitCode::Usage.into(),
                SecretError::NoParent(_) => sysexits::ExitCode::IoErr.into(),
                SecretError::Parse(_) => sysexits::ExitCode::DataErr.into(),
                SecretError::Write(_) => sysexits::ExitCode::IoErr.into(),
            },
            LocketError::Provider(e) => match e {
                ProviderError::Network(_) => sysexits::ExitCode::Unavailable.into(),
                ProviderError::NotFound(_) => sysexits::ExitCode::NoInput.into(),
                ProviderError::Unauthorized(_) => sysexits::ExitCode::NoPerm.into(),
                ProviderError::RateLimit => sysexits::ExitCode::TempFail.into(),
                ProviderError::Other(_) => sysexits::ExitCode::Software.into(),
                ProviderError::InvalidConfig(_) => sysexits::ExitCode::Config.into(),
                ProviderError::Io(_) => sysexits::ExitCode::IoErr.into(),
                ProviderError::Exec { .. } => sysexits::ExitCode::Unavailable.into(),
                ProviderError::Url(_) => sysexits::ExitCode::DataErr.into(),
                ProviderError::InvalidId(_) => sysexits::ExitCode::DataErr.into(),
            },
            LocketError::ReferenceParse(_) => sysexits::ExitCode::DataErr.into(),
            LocketError::Watch(e) => match e {
                WatchError::Io(_) => sysexits::ExitCode::IoErr.into(),
                WatchError::Notify(_) => sysexits::ExitCode::Software.into(),
                WatchError::SourceMissing(_) => sysexits::ExitCode::Config.into(),
                WatchError::Disconnected => sysexits::ExitCode::Software.into(),
                WatchError::Handler(h) => Self::handler_exit_code(h),
            },
            LocketError::Handler(e) => Self::handler_exit_code(e),
            LocketError::Health(e) => match e {
                locket::health::HealthError::Unhealthy => 1,
                locket::health::HealthError::Io(_) => sysexits::ExitCode::IoErr.into(),
            },

            #[cfg(feature = "exec")]
            LocketError::Process(e) => Self::process_exit_code(e),

            #[cfg(feature = "compose")]
            LocketError::Compose(e) => match e {
                locket::compose::ComposeError::Io(_) => sysexits::ExitCode::IoErr.into(),
                locket::compose::ComposeError::Provider(_) => {
                    sysexits::ExitCode::Unavailable.into()
                }
                locket::compose::ComposeError::Secret(_) => sysexits::ExitCode::Config.into(),
                locket::compose::ComposeError::Configuration(_) => {
                    sysexits::ExitCode::Config.into()
                }
                locket::compose::ComposeError::Argument(_) => sysexits::ExitCode::Usage.into(),
                locket::compose::ComposeError::Metadata(_) => sysexits::ExitCode::Software.into(),
            },

            #[cfg(any(feature = "exec", feature = "compose"))]
            LocketError::Env(e) => match e {
                locket::env::EnvError::Io(_) => sysexits::ExitCode::IoErr.into(),
                locket::env::EnvError::Secret(_) => sysexits::ExitCode::Config.into(),
                locket::env::EnvError::Provider(_) => sysexits::ExitCode::Unavailable.into(),
                locket::env::EnvError::Parse(_) => sysexits::ExitCode::DataErr.into(),
                locket::env::EnvError::Join(_) => sysexits::ExitCode::Software.into(),
            },

            LocketError::Io(_) => sysexits::ExitCode::IoErr.into(),
            LocketError::Logging(_) => sysexits::ExitCode::Config.into(),
            LocketError::Config(_) => sysexits::ExitCode::Config.into(),
            LocketError::Validation(_) => sysexits::ExitCode::DataErr.into(),
        }
    }

    fn handler_exit_code(e: &HandlerError) -> u8 {
        match e {
            HandlerError::Io(_) => sysexits::ExitCode::IoErr.into(),
            HandlerError::Secret(_) => sysexits::ExitCode::Software.into(),
            HandlerError::Provider(_) => sysexits::ExitCode::Unavailable.into(),
            HandlerError::Exited(status) => {
                if let Some(code) = status.code() {
                    code as u8
                } else if let Some(signal) = status.signal() {
                    (128 + signal) as u8
                } else {
                    sysexits::ExitCode::Unavailable.into()
                }
            }
            HandlerError::Signaled => 128 + 15,
            HandlerError::Interrupted => sysexits::ExitCode::Ok.into(),
            #[cfg(any(feature = "exec", feature = "compose"))]
            HandlerError::Process(e) => Self::process_exit_code(e),
            #[cfg(any(feature = "exec", feature = "compose"))]
            HandlerError::Env(_) => sysexits::ExitCode::Config.into(),
        }
    }

    #[cfg(feature = "exec")]
    fn process_exit_code(e: &locket::process::ProcessError) -> u8 {
        match e {
            locket::process::ProcessError::Env(_) => sysexits::ExitCode::Config.into(),
            locket::process::ProcessError::Io(e) => match e.kind() {
                std::io::ErrorKind::NotFound => 127,
                std::io::ErrorKind::PermissionDenied => 126,
                _ => sysexits::ExitCode::IoErr.into(),
            },
            locket::process::ProcessError::Exited(status) => {
                if let Some(code) = status.code() {
                    code as u8
                } else if let Some(signal) = status.signal() {
                    (128 + signal) as u8
                } else {
                    sysexits::ExitCode::Unavailable.into()
                }
            }
            locket::process::ProcessError::Signaled => 128 + 15,
            locket::process::ProcessError::InvalidCommand(_) => sysexits::ExitCode::Usage.into(),
        }
    }
}