Skip to main content

heyo_sdk/
errors.rs

1use std::time::Duration;
2
3use thiserror::Error;
4
5/// Every fallible call in the SDK returns `Result<T, HeyoError>`. Variants
6/// mirror the TypeScript SDK's error classes so the same recovery patterns
7/// translate across languages.
8#[derive(Debug, Error)]
9pub enum HeyoError {
10    /// No API key was provided and `HEYO_API_KEY` is unset.
11    #[error("Missing API key. Pass `api_key` or set HEYO_API_KEY.")]
12    Authentication,
13
14    /// 4xx other than 401/403/404 (e.g. 400 Bad Request, 422 Unprocessable).
15    #[error("{0}")]
16    InvalidArgument(String),
17
18    /// 404 Not Found.
19    #[error("not found: {0}")]
20    NotFound(String),
21
22    /// 5xx, network error, or non-classified HTTP failure.
23    #[error("api error ({status}): {message}")]
24    Api {
25        status: u16,
26        message: String,
27        body: Option<serde_json::Value>,
28    },
29
30    /// A `wait_for_*` call exceeded its budget.
31    #[error("timeout after {0:?}: {1}")]
32    Timeout(Duration, String),
33
34    /// Sandbox provisioning ended in `failed`.
35    #[error("sandbox {sandbox_id} failed: {reason}")]
36    SandboxFailed {
37        sandbox_id: String,
38        reason: String,
39    },
40
41    /// Shell-stream WebSocket could not be (re)established.
42    #[error("connection error: {0}")]
43    Connection(String),
44
45    /// Server refused to resume the shell session.
46    #[error("shell session expired{}", .session_id.as_deref().map(|s| format!(" ({})", s)).unwrap_or_default())]
47    SessionExpired { session_id: Option<String> },
48
49    /// Remote shell exited unexpectedly.
50    #[error("shell exited with code {0}")]
51    ShellExit(i32),
52
53    /// `Database::checkin` rejected because `data_version` advanced.
54    #[error("checkin conflict: expected={expected:?} current={current}")]
55    CheckinConflict {
56        expected: Option<i64>,
57        current: i64,
58    },
59}
60
61impl HeyoError {
62    pub(crate) fn invalid(msg: impl Into<String>) -> Self {
63        HeyoError::InvalidArgument(msg.into())
64    }
65    pub(crate) fn api(status: u16, message: impl Into<String>) -> Self {
66        HeyoError::Api {
67            status,
68            message: message.into(),
69            body: None,
70        }
71    }
72    pub(crate) fn api_with_body(
73        status: u16,
74        message: impl Into<String>,
75        body: Option<serde_json::Value>,
76    ) -> Self {
77        HeyoError::Api {
78            status,
79            message: message.into(),
80            body,
81        }
82    }
83}