uni-sdk 0.3.0

The official Unimatrix SDK for Rust
Documentation
use reqwest::StatusCode;

/// Result type returned by the SDK.
pub type Result<T> = std::result::Result<T, UniError>;

/// Errors produced while configuring the client or calling the API.
#[derive(Debug, thiserror::Error)]
pub enum UniError {
    /// An API response contained a non-zero business error code.
    #[error("[{code}] {message}{request_suffix}", request_suffix = format_request_id(.request_id.as_deref()))]
    Api {
        /// Business error code returned by the API.
        code: String,
        /// Human-readable error message returned by the API.
        message: String,
        /// HTTP status of the response.
        status: StatusCode,
        /// Unimatrix request ID used for support and diagnostics.
        request_id: Option<String>,
        /// Parsed response body, when one was available.
        body: Option<serde_json::Value>,
    },

    /// The server returned a non-successful HTTP status without an API error.
    #[error("HTTP {status}: {message}{request_suffix}", request_suffix = format_request_id(.request_id.as_deref()))]
    Http {
        /// Non-successful HTTP status.
        status: StatusCode,
        /// HTTP or response message describing the failure.
        message: String,
        /// Unimatrix request ID used for support and diagnostics.
        request_id: Option<String>,
        /// Parsed response body, or the raw body represented as a JSON string.
        body: Option<serde_json::Value>,
    },

    /// A transport-level error occurred.
    #[error("request failed: {0}")]
    Transport(#[source] reqwest::Error),

    /// The endpoint URL or client configuration was invalid.
    #[error("invalid configuration: {0}")]
    Configuration(String),

    /// A response could not be decoded according to the API schema.
    #[error("invalid API response (HTTP {status}){request_suffix}: {message}", request_suffix = format_request_id(.request_id.as_deref()))]
    InvalidResponse {
        /// HTTP status of the response.
        status: StatusCode,
        /// Unimatrix request ID used for support and diagnostics.
        request_id: Option<String>,
        /// Description of the response decoding failure.
        message: String,
        /// Raw response body.
        body: String,
    },
}

impl From<reqwest::Error> for UniError {
    fn from(error: reqwest::Error) -> Self {
        // Signed request URLs contain the access key ID, timestamp, nonce, and
        // signature. Never retain those values in an error that callers may log.
        Self::Transport(error.without_url())
    }
}

impl UniError {
    /// API error code, when the server supplied one.
    pub fn code(&self) -> Option<&str> {
        match self {
            Self::Api { code, .. } => Some(code),
            _ => None,
        }
    }

    /// HTTP status associated with the error, when available.
    pub fn status(&self) -> Option<StatusCode> {
        match self {
            Self::Api { status, .. }
            | Self::Http { status, .. }
            | Self::InvalidResponse { status, .. } => Some(*status),
            _ => None,
        }
    }

    /// Request ID that can be provided to Unimatrix support.
    pub fn request_id(&self) -> Option<&str> {
        match self {
            Self::Api { request_id, .. }
            | Self::Http { request_id, .. }
            | Self::InvalidResponse { request_id, .. } => request_id.as_deref(),
            _ => None,
        }
    }
}

fn format_request_id(request_id: Option<&str>) -> String {
    request_id
        .map(|id| format!(", RequestId: {id}"))
        .unwrap_or_default()
}