Skip to main content

chalk_client/
error.rs

1//! Error types for the Chalk client SDK.
2//!
3//! This module defines a single error enum, [`ChalkClientError`](crate::error::ChalkClientError), that covers
4//! everything that can go wrong when using the client — from configuration
5//! mistakes to network failures to server-side errors.
6
7use crate::types::ChalkError;
8
9/// Everything that can go wrong when using the Chalk client.
10#[derive(Debug, thiserror::Error)]
11pub enum ChalkClientError {
12    /// A configuration problem — missing credentials, bad YAML, etc.
13    #[error("configuration error: {0}")]
14    Config(String),
15
16    /// An authentication failure — could not exchange credentials for a token.
17    #[error("authentication error: {0}")]
18    Auth(String),
19
20    /// An HTTP-level error from the `reqwest` library (DNS failure, timeout,
21    /// TLS handshake error, etc.).
22    #[error("HTTP error: {0}")]
23    Http(#[from] reqwest::Error),
24
25    /// The Chalk API returned an HTTP success status but the response body
26    /// contained an error we couldn't map to `ServerErrors`. This is a
27    /// catch-all for unexpected API-level failures.
28    #[error("API error (status {status}): {message}")]
29    Api {
30        /// The HTTP status code returned by the server.
31        status: u16,
32        /// The error message from the response body.
33        message: String,
34    },
35
36    /// Failed to serialize or deserialize JSON.
37    #[error("JSON error: {0}")]
38    Json(#[from] serde_json::Error),
39
40    /// Failed to parse a YAML configuration file (e.g. `~/.chalk.yml`).
41    #[error("YAML error: {0}")]
42    Yaml(#[from] serde_yaml::Error),
43
44    /// An error from the Apache Arrow library (e.g. when encoding/decoding
45    /// Arrow IPC for bulk queries).
46    #[error("Arrow error: {0}")]
47    Arrow(#[from] arrow::error::ArrowError),
48
49    /// A standard I/O error (file not found, permission denied, etc.).
50    #[error("I/O error: {0}")]
51    Io(#[from] std::io::Error),
52
53    /// gRPC protocol error (e.g. deadline exceeded, unavailable).
54    #[error("gRPC error: {0}")]
55    Grpc(Box<tonic::Status>),
56
57    /// gRPC transport/connection error.
58    #[error("gRPC transport error: {0}")]
59    GrpcTransport(#[from] tonic::transport::Error),
60
61    /// The Chalk server returned one or more structured errors in its response.
62    #[error("server returned {} error(s): {}", .0.len(), .0.first().map(|e| e.message.as_str()).unwrap_or("unknown"))]
63    ServerErrors(Vec<ChalkError>),
64}
65
66impl From<tonic::Status> for ChalkClientError {
67    fn from(status: tonic::Status) -> Self {
68        ChalkClientError::Grpc(Box::new(status))
69    }
70}
71
72/// A convenience type alias so we can write `Result<T>` instead of
73/// `std::result::Result<T, ChalkClientError>` everywhere in this crate.
74pub type Result<T> = std::result::Result<T, ChalkClientError>;
75
76#[cfg(test)]
77mod tests {
78    use super::*;
79
80    #[test]
81    fn test_error_display() {
82        let err = ChalkClientError::Config("missing client_id".into());
83        assert!(err.to_string().contains("missing client_id"));
84
85        let err = ChalkClientError::Api {
86            status: 401,
87            message: "unauthorized".into(),
88        };
89        assert!(err.to_string().contains("401"));
90        assert!(err.to_string().contains("unauthorized"));
91    }
92
93    #[test]
94    fn test_server_errors_display() {
95        let errors = vec![
96            ChalkError {
97                code: "RESOLVER_FAILED".into(),
98                category: "FIELD".into(),
99                message: "resolver timed out".into(),
100                feature: Some("user.age".into()),
101                resolver: Some("get_user_age".into()),
102                exception: None,
103            },
104            ChalkError {
105                code: "RESOLVER_FAILED".into(),
106                category: "FIELD".into(),
107                message: "another error".into(),
108                feature: None,
109                resolver: None,
110                exception: None,
111            },
112        ];
113        let err = ChalkClientError::ServerErrors(errors);
114        let msg = err.to_string();
115        assert!(msg.contains("2 error(s)"));
116        assert!(msg.contains("resolver timed out"));
117    }
118}