Skip to main content

lineark_sdk/
error.rs

1use serde::{Deserialize, Serialize};
2use std::fmt;
3
4/// A single GraphQL error from the API response.
5#[derive(Debug, Clone, Serialize, Deserialize)]
6pub struct GraphQLError {
7    pub message: String,
8    #[serde(default)]
9    pub extensions: Option<serde_json::Value>,
10}
11
12/// Errors that can occur when interacting with the Linear API.
13#[derive(Debug)]
14pub enum LinearError {
15    /// Authentication failed (invalid or expired token).
16    Authentication(String),
17    /// Request was rate-limited.
18    RateLimited {
19        retry_after: Option<f64>,
20        message: String,
21    },
22    /// Invalid input (bad arguments to a mutation).
23    InvalidInput(String),
24    /// Forbidden (insufficient permissions).
25    Forbidden(String),
26    /// Network or HTTP transport error.
27    Network(reqwest::Error),
28    /// GraphQL errors returned by the API.
29    GraphQL(Vec<GraphQLError>),
30    /// The requested data path was not found in the response.
31    MissingData(String),
32    /// Non-2xx HTTP response not covered by a more specific variant.
33    HttpError { status: u16, body: String },
34    /// Auth configuration error (no token found).
35    AuthConfig(String),
36}
37
38impl fmt::Display for LinearError {
39    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
40        match self {
41            Self::Authentication(msg) => write!(f, "Authentication error: {}", msg),
42            Self::RateLimited { message, .. } => write!(f, "Rate limited: {}", message),
43            Self::InvalidInput(msg) => write!(f, "Invalid input: {}", msg),
44            Self::Forbidden(msg) => write!(f, "Forbidden: {}", msg),
45            Self::Network(e) => write!(f, "Network error: {}", e),
46            Self::GraphQL(errors) => {
47                let msgs: Vec<String> = errors
48                    .iter()
49                    .map(|e| {
50                        if let Some(ext) = &e.extensions {
51                            format!("{} ({})", e.message, ext)
52                        } else {
53                            e.message.clone()
54                        }
55                    })
56                    .collect();
57                write!(f, "GraphQL errors: {}", msgs.join("; "))
58            }
59            Self::HttpError { status, body } => {
60                write!(f, "HTTP error {}: {}", status, body)
61            }
62            Self::MissingData(path) => write!(f, "Missing data at path: {}", path),
63            Self::AuthConfig(msg) => write!(f, "Auth configuration error: {}", msg),
64        }
65    }
66}
67
68impl std::error::Error for LinearError {
69    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
70        match self {
71            Self::Network(e) => Some(e),
72            _ => None,
73        }
74    }
75}
76
77impl From<reqwest::Error> for LinearError {
78    fn from(e: reqwest::Error) -> Self {
79        Self::Network(e)
80    }
81}