Skip to main content

supabase_client_functions/
error.rs

1use serde::Deserialize;
2use supabase_client_core::SupabaseError;
3
4/// Error response format from Supabase Edge Functions.
5#[derive(Debug, Clone, Deserialize)]
6pub struct FunctionsApiErrorResponse {
7    #[serde(default)]
8    pub error: Option<String>,
9    #[serde(default)]
10    pub message: Option<String>,
11}
12
13impl FunctionsApiErrorResponse {
14    /// Extract the most informative error message from the response.
15    pub fn error_message(&self) -> String {
16        self.message
17            .as_deref()
18            .or(self.error.as_deref())
19            .unwrap_or("Unknown error")
20            .to_string()
21    }
22}
23
24/// Edge Functions-specific errors.
25#[derive(Debug, thiserror::Error)]
26pub enum FunctionsError {
27    /// HTTP transport error from reqwest.
28    #[error("HTTP error: {0}")]
29    Http(#[from] reqwest::Error),
30
31    /// Function returned non-2xx status (JS: FunctionsHttpError).
32    #[error("Functions HTTP error ({status}): {message}")]
33    HttpError { status: u16, message: String },
34
35    /// Relay/infrastructure error, detected via `x-relay-error: true` header (JS: FunctionsRelayError).
36    #[error("Functions relay error ({status}): {message}")]
37    RelayError { status: u16, message: String },
38
39    /// Invalid configuration (missing URL or key).
40    #[error("Invalid functions configuration: {0}")]
41    InvalidConfig(String),
42
43    /// JSON serialization/deserialization error.
44    #[error("Serialization error: {0}")]
45    Serialization(#[from] serde_json::Error),
46
47    /// URL parsing error.
48    #[error("URL parse error: {0}")]
49    UrlParse(#[from] url::ParseError),
50}
51
52impl From<FunctionsError> for SupabaseError {
53    fn from(err: FunctionsError) -> Self {
54        SupabaseError::Functions(err.to_string())
55    }
56}
57
58#[cfg(test)]
59mod tests {
60    use super::*;
61
62    #[test]
63    fn error_display_http_error() {
64        let err = FunctionsError::HttpError {
65            status: 400,
66            message: "Bad Request".into(),
67        };
68        assert_eq!(err.to_string(), "Functions HTTP error (400): Bad Request");
69    }
70
71    #[test]
72    fn error_display_relay_error() {
73        let err = FunctionsError::RelayError {
74            status: 502,
75            message: "Function not found".into(),
76        };
77        assert_eq!(
78            err.to_string(),
79            "Functions relay error (502): Function not found"
80        );
81    }
82
83    #[test]
84    fn error_display_invalid_config() {
85        let err = FunctionsError::InvalidConfig("missing url".into());
86        assert_eq!(
87            err.to_string(),
88            "Invalid functions configuration: missing url"
89        );
90    }
91
92    #[test]
93    fn error_converts_to_supabase_error() {
94        let err = FunctionsError::HttpError {
95            status: 500,
96            message: "Internal".into(),
97        };
98        let supa: SupabaseError = err.into();
99        match supa {
100            SupabaseError::Functions(msg) => assert!(msg.contains("Internal")),
101            other => panic!("Expected Functions variant, got: {:?}", other),
102        }
103    }
104
105    #[test]
106    fn api_error_response_deserialization() {
107        let json = r#"{"error":"not_found","message":"Function not found"}"#;
108        let resp: FunctionsApiErrorResponse = serde_json::from_str(json).unwrap();
109        assert_eq!(resp.error.as_deref(), Some("not_found"));
110        assert_eq!(resp.message.as_deref(), Some("Function not found"));
111        assert_eq!(resp.error_message(), "Function not found");
112    }
113
114    #[test]
115    fn api_error_response_fallback_to_error() {
116        let json = r#"{"error":"Something went wrong"}"#;
117        let resp: FunctionsApiErrorResponse = serde_json::from_str(json).unwrap();
118        assert_eq!(resp.error_message(), "Something went wrong");
119    }
120
121    #[test]
122    fn api_error_response_unknown() {
123        let json = r#"{}"#;
124        let resp: FunctionsApiErrorResponse = serde_json::from_str(json).unwrap();
125        assert_eq!(resp.error_message(), "Unknown error");
126    }
127}