graph-error 1.0.0

Graph Api error types and handling for the graph-rs-sdk crate
Documentation
use crate::{ErrorMessage, IdentityResult, WebViewDeviceCodeError};
use std::error::Error;
use tokio::sync::mpsc::error::SendTimeoutError;
use url::ParseError;

pub type AF = AuthorizationFailure;

/// Errors typically from missing or invalid configuration using one of the
/// identity platform clients such as AuthorizationCodeCredential.
#[derive(Debug, thiserror::Error)]
#[non_exhaustive]
pub enum AuthorizationFailure {
    #[error("Required value missing:\n{0:#?}", name)]
    RequiredValue {
        name: String,
        message: Option<String>,
    },

    #[error("{0:#?}")]
    UrlParse(#[from] url::ParseError),

    #[error("{0:#?}")]
    Uuid(#[from] uuid::Error),

    #[error("{0:#?}")]
    Openssl(String),

    #[error("{0:#?}")]
    SerdeJson(#[from] serde_json::Error),
}

impl AuthorizationFailure {
    pub fn required<T: AsRef<str>>(name: T) -> AuthorizationFailure {
        AuthorizationFailure::RequiredValue {
            name: name.as_ref().to_owned(),
            message: None,
        }
    }

    pub fn result<U>(name: impl AsRef<str>) -> IdentityResult<U> {
        Err(AuthorizationFailure::RequiredValue {
            name: name.as_ref().to_owned(),
            message: None,
        })
    }

    pub fn msg_err<T: AsRef<str>>(name: T, message: T) -> AuthorizationFailure {
        AuthorizationFailure::RequiredValue {
            name: name.as_ref().to_owned(),
            message: Some(message.as_ref().to_owned()),
        }
    }

    pub fn msg_internal_err<T: AsRef<str>>(name: T) -> AuthorizationFailure {
        AuthorizationFailure::RequiredValue {
            name: name.as_ref().to_owned(),
            message: Some("Internal error please file an issue on GitHub https://github.com/sreeise/graph-rs-sdk/issues".to_owned()),
        }
    }

    pub fn msg_result<T>(
        name: impl AsRef<str>,
        message: impl ToString,
    ) -> Result<T, AuthorizationFailure> {
        Err(AuthorizationFailure::RequiredValue {
            name: name.as_ref().to_owned(),
            message: Some(message.to_string()),
        })
    }

    pub fn msg_internal_result<T>(name: impl AsRef<str>) -> Result<T, AuthorizationFailure> {
        Err(AF::msg_internal_err(name))
    }

    pub fn condition(cond: bool, name: &str, msg: &str) -> IdentityResult<()> {
        if cond {
            AF::msg_result(name, msg)
        } else {
            Ok(())
        }
    }

    pub fn x509(message: impl ToString) -> AuthorizationFailure {
        AuthorizationFailure::Openssl(message.to_string())
    }

    pub fn x509_result<T>(message: impl ToString) -> Result<T, AuthorizationFailure> {
        Err(AuthorizationFailure::Openssl(message.to_string()))
    }
}

/// Error either from missing or invalid configuration using one of the
/// identity platform clients or an error from the result of executing
/// an http request using the identity platform clients.
#[derive(Debug, thiserror::Error)]
pub enum AuthExecutionError {
    #[error("{0:#?}")]
    Authorization(#[from] AuthorizationFailure),

    #[error("{0:#?}")]
    Request(#[from] reqwest::Error),

    #[error("{0:#?}")]
    Http(#[from] http::Error),

    #[error("message: {0:#?}, response: {1:#?}", message, response)]
    SilentTokenAuth {
        message: String,
        response: http::Response<Result<serde_json::Value, ErrorMessage>>,
    },

    #[error("{0:#?}")]
    JsonWebToken(#[from] jsonwebtoken::errors::Error),

    #[error("{0:#?}")]
    Other(#[from] Box<dyn Error + Send + Sync>),
}

impl AuthExecutionError {
    pub fn silent_token_auth(
        response: http::Response<Result<serde_json::Value, ErrorMessage>>,
    ) -> AuthExecutionError {
        AuthExecutionError::SilentTokenAuth {
            message: "silent token auth failed".into(),
            response,
        }
    }
}

impl From<serde_json::error::Error> for AuthExecutionError {
    fn from(value: serde_json::error::Error) -> Self {
        AuthExecutionError::Authorization(AuthorizationFailure::from(value))
    }
}

impl From<WebViewDeviceCodeError> for AuthExecutionError {
    fn from(value: WebViewDeviceCodeError) -> Self {
        AuthExecutionError::Authorization(AuthorizationFailure::msg_err(
            "Unknown",
            &value.to_string(),
        ))
    }
}

impl From<url::ParseError> for AuthExecutionError {
    fn from(value: ParseError) -> Self {
        AuthExecutionError::Authorization(AuthorizationFailure::UrlParse(value))
    }
}

#[derive(Debug, thiserror::Error)]
pub enum AuthTaskExecutionError<R> {
    #[error("{0:#?}")]
    AuthExecutionError(#[from] AuthExecutionError),
    #[error("Tokio SendTimeoutError - Reason: {0:#?}")]
    SendTimeoutErrorAsync(#[from] SendTimeoutError<R>),
    #[error("{0:#?}")]
    JoinError(#[from] tokio::task::JoinError),
}