opendal/services/gcs/
error.rs1use http::Response;
19use http::StatusCode;
20use serde::Deserialize;
21use serde_json::de;
22
23use crate::raw::*;
24use crate::*;
25
26#[derive(Default, Debug, Deserialize)]
27#[serde(default, rename_all = "camelCase")]
28struct GcsErrorResponse {
29 error: GcsError,
30}
31
32#[derive(Default, Debug, Deserialize)]
33#[serde(default, rename_all = "camelCase")]
34struct GcsError {
35 code: usize,
36 message: String,
37 errors: Vec<GcsErrorDetail>,
38}
39
40#[derive(Default, Debug, Deserialize)]
41#[serde(default, rename_all = "camelCase")]
42struct GcsErrorDetail {
43 domain: String,
44 location: String,
45 location_type: String,
46 message: String,
47 reason: String,
48}
49
50pub(super) fn parse_error(resp: Response<Buffer>) -> Error {
52 let (parts, body) = resp.into_parts();
53 let bs = body.to_bytes();
54
55 let (kind, retryable) = match parts.status {
56 StatusCode::NOT_FOUND => (ErrorKind::NotFound, false),
57 StatusCode::FORBIDDEN => (ErrorKind::PermissionDenied, false),
58 StatusCode::PRECONDITION_FAILED | StatusCode::NOT_MODIFIED => {
59 (ErrorKind::ConditionNotMatch, false)
60 }
61 StatusCode::TOO_MANY_REQUESTS => (ErrorKind::RateLimited, true),
62 StatusCode::INTERNAL_SERVER_ERROR
63 | StatusCode::BAD_GATEWAY
64 | StatusCode::SERVICE_UNAVAILABLE
65 | StatusCode::GATEWAY_TIMEOUT => (ErrorKind::Unexpected, true),
66 _ => (ErrorKind::Unexpected, false),
67 };
68
69 let message = match de::from_slice::<GcsErrorResponse>(&bs) {
70 Ok(gcs_err) => format!("{gcs_err:?}"),
71 Err(_) => String::from_utf8_lossy(&bs).into_owned(),
72 };
73
74 let mut err = Error::new(kind, message);
75
76 err = with_error_response_context(err, parts);
77
78 if retryable {
79 err = err.set_temporary();
80 }
81
82 err
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88
89 #[test]
90 fn test_parse_error() {
91 let bs = bytes::Bytes::from(
92 r#"
93{
94"error": {
95 "errors": [
96 {
97 "domain": "global",
98 "reason": "required",
99 "message": "Login Required",
100 "locationType": "header",
101 "location": "Authorization"
102 }
103 ],
104 "code": 401,
105 "message": "Login Required"
106 }
107}
108"#,
109 );
110
111 let out: GcsErrorResponse = de::from_slice(&bs).expect("must success");
112 println!("{out:?}");
113
114 assert_eq!(out.error.code, 401);
115 assert_eq!(out.error.message, "Login Required");
116 assert_eq!(out.error.errors[0].domain, "global");
117 assert_eq!(out.error.errors[0].reason, "required");
118 assert_eq!(out.error.errors[0].message, "Login Required");
119 assert_eq!(out.error.errors[0].location_type, "header");
120 assert_eq!(out.error.errors[0].location, "Authorization");
121 }
122}