cloudiful-notifier 0.2.1

Async notification delivery library for ntfy, generic webhooks, DingTalk robots, and SMTP email.
Documentation
use std::fmt::{Display, Formatter};

#[derive(Debug, Clone, PartialEq, Eq)]
pub enum NotifierError {
    InvalidUrl {
        url: String,
        message: String,
    },
    UnsupportedUrlScheme {
        scheme: String,
    },
    InvalidHeaderName {
        header: String,
        message: String,
    },
    InvalidHeaderValue {
        header: String,
        message: String,
    },
    ReservedHeader {
        header: String,
    },
    InvalidSecret {
        provider: &'static str,
        message: String,
    },
    HttpRequest {
        provider: &'static str,
        message: String,
    },
    Transport {
        provider: &'static str,
        message: String,
    },
    HttpStatus {
        provider: &'static str,
        status: u16,
    },
    ProviderRejected {
        provider: &'static str,
        code: String,
        message: String,
    },
    InvalidMessage {
        provider: &'static str,
        message: String,
    },
    ResponseDecode {
        provider: &'static str,
        message: String,
    },
}

impl Display for NotifierError {
    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
        match self {
            Self::InvalidUrl { url, message } => {
                write!(f, "invalid url `{url}`: {message}")
            }
            Self::UnsupportedUrlScheme { scheme } => {
                write!(f, "unsupported url scheme `{scheme}`")
            }
            Self::InvalidHeaderName { header, message } => {
                write!(f, "invalid header name `{header}`: {message}")
            }
            Self::InvalidHeaderValue { header, message } => {
                write!(f, "invalid header value for `{header}`: {message}")
            }
            Self::ReservedHeader { header } => {
                write!(f, "reserved header `{header}` cannot be overridden")
            }
            Self::InvalidSecret { provider, message } => {
                write!(f, "{provider} secret is invalid: {message}")
            }
            Self::HttpRequest { provider, message } => {
                write!(f, "{provider} request failed: {message}")
            }
            Self::Transport { provider, message } => {
                write!(f, "{provider} transport failed: {message}")
            }
            Self::HttpStatus { provider, status } => {
                write!(f, "{provider} request failed with status {status}")
            }
            Self::ProviderRejected {
                provider,
                code,
                message,
            } => {
                write!(f, "{provider} rejected request with code {code}: {message}")
            }
            Self::InvalidMessage { provider, message } => {
                write!(f, "{provider} message is invalid: {message}")
            }
            Self::ResponseDecode { provider, message } => {
                write!(f, "failed to decode {provider} response: {message}")
            }
        }
    }
}

impl std::error::Error for NotifierError {}

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

    #[test]
    fn renders_invalid_url_error() {
        let error = NotifierError::InvalidUrl {
            url: "ftp://example.com".to_string(),
            message: "relative URL without a base".to_string(),
        };

        assert!(error.to_string().contains("invalid url"));
    }

    #[test]
    fn renders_provider_rejected_error() {
        let error = NotifierError::ProviderRejected {
            provider: "dingtalk",
            code: "310000".to_string(),
            message: "signature error".to_string(),
        };

        assert!(error.to_string().contains("dingtalk rejected request"));
    }
}