eveng 0.1.1

An API Client for EVE-NG
Documentation
use crate::client::Response;

/// Main error type for the EVE-NG API.
#[derive(Debug, thiserror::Error)]
pub enum Error {
    /// HTTP request error.
    #[error("HTTP request failed: {0}")]
    Http(#[from] reqwest::Error),
    /// JSON serialization or deserialization error.
    #[error("JSON error: {0}")]
    Json(#[from] serde_json::Error),
    /// Errors originating from the EVE-NG API.
    #[error("EVE-NG API error (status {status}): {message}")]
    Api {
        /// HTTP status code.
        code: u16,
        /// API Status message (success/unauthorized/forbidden/fail/error).
        status: String,
        /// Error message from API.
        message: String,
    },
    /// Client-side validation errors, before or after the request has been
    /// made.
    #[error("Client error: {0}")]
    Client(String),
}

impl Error {
    pub(crate) fn from_response(code: reqwest::StatusCode, body: String) -> Self {
        match serde_json::from_str::<Response<()>>(&body) {
            Ok(api) => Error::Api {
                code: api.code,
                status: api.status,
                message: api.message,
            },
            Err(_) => Error::Api {
                code: code.into(),
                status: "error".to_string(),
                message: body.chars().take(200).collect(),
            },
        }
    }
}

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

    #[test]
    fn html_api_error() {
        let body = r#"<html><head><title>Slim Application Error</title><style>body{margin:0;padding:30px;font:12px/1.5 Helvetica,Arial,Verdana,sans-serif;}h1{margin:0;font-size:48px;font-weight:normal;line-height:48px;}strong{display:inline-block;width:65px;}</style></head><body><h1>Slim Application Error</h1><p>The application could not run because of the following error:</p><h2>Details</h2><div><strong>Type:</strong> ErrorException</div><div><strong>Code:</strong> 2</div><div><strong>Message:</strong> Undefined array key \"rows\"</div><div><strong>File:</strong> /opt/unetlab/html/includes/api_authentication.php</div><div><strong>Line:</strong> 193</div><h2>Trace</h2><pre><div>#0 /opt/unetlab/html/includes/api_authentication.php(193): Slim\\Slim::handleErrors()</div><div>#1 /opt/unetlab/html/api.php(129): apiLogin()</div><div>#2 [internal function]: {closure}()</div><div>#3 /opt/unetlab/html/includes/Slim/Route.php(468): call_user_func_array()</div><div>#4 /opt/unetlab/html/includes/Slim/Slim.php(1357): Slim\\Route->dispatch()</div><div>#5 /opt/unetlab/html/includes/Slim/Middleware/Flash.php(85): Slim\\Slim->call()</div><div>#6 /opt/unetlab/html/includes/Slim/Middleware/MethodOverride.php(92): Slim\\Middleware\\Flash->call()</div><div>#7 /opt/unetlab/html/includes/Slim/Middleware/PrettyExceptions.php(67): Slim\\Middleware\\MethodOverride->call()</div><div>#8 /opt/unetlab/html/includes/Slim/Slim.php(1302): Slim\\Middleware\\PrettyExceptions->call()</div><div>#9 /opt/unetlab/html/api.php(1368): Slim\\Slim->run()</div><div>#10 {main}</pre></body></html>"#;

        let err =
            Error::from_response(reqwest::StatusCode::INTERNAL_SERVER_ERROR, body.to_string());
        assert!(matches!(err, Error::Api { code: 500, .. }));
    }

    #[test]
    fn json_api_error() {
        let body = r#"{"code": 404, "message": "Requested folder does not exist (60008).", "status": "fail"}"#;

        let err = Error::from_response(reqwest::StatusCode::NOT_FOUND, body.to_string());
        assert!(matches!(err, Error::Api { code: 404, .. }));
    }
}