intel_dcap_api/
error.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 Matter Labs
3
4use reqwest::{Response, StatusCode};
5use thiserror::Error;
6
7/// Represents all possible errors that can occur when interacting with Intel's DCAP API.
8#[derive(Error, Debug)]
9pub enum IntelApiError {
10    /// Indicates that the requested API version or feature is unsupported.
11    #[error("Unsupported API version or feature: {0}")]
12    UnsupportedApiVersion(String),
13
14    /// Wraps an underlying reqwest error.
15    #[error("Reqwest error: {0}")]
16    Reqwest(#[from] reqwest::Error),
17
18    /// Wraps a URL parsing error.
19    #[error("URL parsing error: {0}")]
20    UrlParse(#[from] url::ParseError),
21
22    /// Wraps a Serde JSON error.
23    #[error("Serde JSON error: {0}")]
24    JsonError(#[from] serde_json::Error),
25
26    /// Represents a general API error, capturing the HTTP status and optional error details.
27    #[error("API Error: Status={status}, Request-ID={request_id}, Code={error_code:?}, Message={error_message:?}")]
28    ApiError {
29        /// HTTP status code returned by the API.
30        status: StatusCode,
31        /// The unique request identifier for tracing errors.
32        request_id: String,
33        /// An optional server-provided error code.
34        error_code: Option<String>,
35        /// An optional server-provided error message.
36        error_message: Option<String>,
37    },
38
39    /// Indicates that a header is missing or invalid.
40    #[error("Header missing or invalid: {0}")]
41    MissingOrInvalidHeader(&'static str),
42
43    /// Represents an invalid subscription key.
44    #[error("Invalid Subscription Key format")]
45    InvalidSubscriptionKey,
46
47    /// Indicates that conflicting parameters were supplied.
48    #[error("Cannot provide conflicting parameters: {0}")]
49    ConflictingParameters(&'static str),
50
51    /// Wraps a standard I/O error.
52    #[error("I/O Error: {0}")]
53    Io(#[from] std::io::Error),
54
55    /// Represents an error while parsing a header's value.
56    #[error("Header value parse error for '{0}': {1}")]
57    HeaderValueParse(&'static str, String),
58
59    /// Indicates an invalid parameter was provided.
60    #[error("Invalid parameter value: {0}")]
61    InvalidParameter(&'static str),
62}
63
64/// Extracts common API error details from response headers.
65pub(crate) fn extract_api_error_details(
66    response: &Response,
67) -> (String, Option<String>, Option<String>) {
68    let request_id = response
69        .headers()
70        .get("Request-ID")
71        .and_then(|v| v.to_str().ok())
72        .unwrap_or("Unknown")
73        .to_string();
74    let error_code = response
75        .headers()
76        .get("Error-Code")
77        .and_then(|v| v.to_str().ok())
78        .map(String::from);
79    let error_message = response
80        .headers()
81        .get("Error-Message")
82        .and_then(|v| v.to_str().ok())
83        .map(String::from);
84    (request_id, error_code, error_message)
85}
86
87/// Checks the response status and returns an ApiError if it's not one of the expected statuses.
88pub(crate) async fn check_status(
89    response: Response,
90    expected_statuses: &[StatusCode],
91) -> Result<Response, IntelApiError> {
92    let status = response.status();
93    if expected_statuses.contains(&status) {
94        Ok(response)
95    } else {
96        let (request_id, error_code, error_message) = extract_api_error_details(&response);
97        Err(IntelApiError::ApiError {
98            status,
99            request_id,
100            error_code,
101            error_message,
102        })
103    }
104}