opendal/services/upyun/
error.rs1use bytes::Buf;
19use http::Response;
20use quick_xml::de;
21use serde::Deserialize;
22
23use crate::raw::*;
24use crate::*;
25
26#[derive(Default, Debug, Deserialize)]
28#[serde(default, rename_all = "PascalCase")]
29struct UpyunError {
30 code: i64,
31 msg: String,
32 id: String,
33}
34
35pub(super) fn parse_error(resp: Response<Buffer>) -> Error {
37 let (parts, body) = resp.into_parts();
38 let bs = body.to_bytes();
39
40 let (kind, retryable) = match parts.status.as_u16() {
41 403 => (ErrorKind::PermissionDenied, false),
42 404 => (ErrorKind::NotFound, false),
43 304 | 412 => (ErrorKind::ConditionNotMatch, false),
44 499 => (ErrorKind::Unexpected, true),
47 500 | 502 | 503 | 504 => (ErrorKind::Unexpected, true),
48 _ => (ErrorKind::Unexpected, false),
49 };
50
51 let (message, _upyun_err) = de::from_reader::<_, UpyunError>(bs.clone().reader())
52 .map(|upyun_err| (format!("{upyun_err:?}"), Some(upyun_err)))
53 .unwrap_or_else(|_| (String::from_utf8_lossy(&bs).into_owned(), None));
54
55 let mut err = Error::new(kind, message);
56
57 err = with_error_response_context(err, parts);
58
59 if retryable {
60 err = err.set_temporary();
61 }
62
63 err
64}
65
66#[cfg(test)]
67mod test {
68 use http::StatusCode;
69
70 use super::*;
71
72 #[tokio::test]
73 async fn test_parse_error() {
74 let err_res = vec![
75 (
76 r#"{"code": 40100016, "msg": "invalid date value in header", "id": "f5b30c720ddcecc70abd2f5c1c64bde8"}"#,
77 ErrorKind::Unexpected,
78 StatusCode::UNAUTHORIZED,
79 ),
80 (
81 r#"{"code": 40300010, "msg": "file type error", "id": "f5b30c720ddcecc70abd2f5c1c64bde7"}"#,
82 ErrorKind::PermissionDenied,
83 StatusCode::FORBIDDEN,
84 ),
85 ];
86
87 for res in err_res {
88 let bs = bytes::Bytes::from(res.0);
89 let body = Buffer::from(bs);
90 let resp = Response::builder().status(res.2).body(body).unwrap();
91
92 let err = parse_error(resp);
93
94 assert_eq!(err.kind(), res.1);
95 }
96 }
97}