1use serde::Deserialize;
4use std::collections::HashMap;
5use std::fmt;
6use thiserror::Error;
7
8#[derive(Debug, Error)]
10#[non_exhaustive]
11pub enum Error {
12 #[error("Request error: {0}")]
14 Request(#[from] reqwest::Error),
15
16 #[error("Invalid URL: {0}")]
18 InvalidUrl(#[from] url::ParseError),
19
20 #[error("Serialization error: {0}")]
22 Serialization(String),
23
24 #[error("Deserialization error: {0}")]
26 Deserialization(String),
27
28 #[error("Missing expected data: {0}")]
30 MissingData(&'static str),
31}
32
33impl From<base64ct::Error> for Error {
34 fn from(err: base64ct::Error) -> Self {
35 Self::Deserialization(format!("base64 decode: {err}"))
36 }
37}
38
39impl From<bcs::Error> for Error {
40 fn from(err: bcs::Error) -> Self {
41 Self::Deserialization(format!("bcs decode: {err}"))
42 }
43}
44
45impl From<sui_sdk_types::TypeParseError> for Error {
46 fn from(err: sui_sdk_types::TypeParseError) -> Self {
47 Self::Deserialization(format!("type parse: {err}"))
48 }
49}
50
51impl From<std::num::ParseIntError> for Error {
52 fn from(err: std::num::ParseIntError) -> Self {
53 Self::Deserialization(format!("integer parse: {err}"))
54 }
55}
56
57impl From<sui_sdk_types::DigestParseError> for Error {
58 fn from(err: sui_sdk_types::DigestParseError) -> Self {
59 Self::Deserialization(format!("digest parse: {err}"))
60 }
61}
62
63#[derive(Debug, Deserialize)]
70pub struct GraphQLError {
71 message: String,
72 locations: Option<Vec<Location>>,
73 path: Option<Vec<PathFragment>>,
74 extensions: Option<HashMap<String, serde_json::Value>>,
75}
76
77impl fmt::Display for GraphQLError {
78 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
79 write!(f, "{}", self.message)
80 }
81}
82
83impl GraphQLError {
84 pub fn message(&self) -> &str {
86 &self.message
87 }
88
89 pub fn locations(&self) -> Option<&[Location]> {
91 self.locations.as_deref()
92 }
93
94 pub fn path(&self) -> Option<&[PathFragment]> {
96 self.path.as_deref()
97 }
98
99 pub fn extensions(&self) -> Option<&HashMap<String, serde_json::Value>> {
101 self.extensions.as_ref()
102 }
103
104 pub fn code(&self) -> Option<&str> {
106 self.extensions.as_ref()?.get("code")?.as_str()
107 }
108}
109
110#[derive(Debug, Deserialize)]
112#[serde(untagged)]
113pub enum PathFragment {
114 Key(String),
116 Index(i32),
118}
119
120#[derive(Debug, Deserialize)]
122pub struct Location {
123 pub line: i32,
125 pub column: i32,
127}
128
129#[cfg(test)]
130mod tests {
131 use super::*;
132
133 #[test]
134 fn test_error_display() {
135 let err = Error::Request(
137 reqwest::Client::new()
138 .get("not a valid url")
139 .build()
140 .unwrap_err(),
141 );
142 assert!(err.to_string().contains("Request error"));
143 }
144
145 #[test]
146 fn test_graphql_error_display() {
147 let err: GraphQLError =
148 serde_json::from_value(serde_json::json!({"message": "Field not found"})).unwrap();
149 assert_eq!(err.to_string(), "Field not found");
150 }
151
152 #[test]
153 fn test_graphql_error_code() {
154 let err: GraphQLError = serde_json::from_value(serde_json::json!({
155 "message": "Unknown field \"foo\" on type \"Query\".",
156 "extensions": {"code": "GRAPHQL_VALIDATION_FAILED"}
157 }))
158 .unwrap();
159 assert_eq!(err.code(), Some("GRAPHQL_VALIDATION_FAILED"));
160 }
161
162 #[test]
163 fn test_graphql_error_no_code() {
164 let err: GraphQLError =
165 serde_json::from_value(serde_json::json!({"message": "Error"})).unwrap();
166 assert_eq!(err.code(), None);
167 }
168
169 #[test]
170 fn test_path_fragment_deserialization() {
171 let json = r#"["object", 0, "field"]"#;
172 let path: Vec<PathFragment> = serde_json::from_str(json).unwrap();
173
174 assert!(matches!(&path[0], PathFragment::Key(k) if k == "object"));
175 assert!(matches!(&path[1], PathFragment::Index(0)));
176 assert!(matches!(&path[2], PathFragment::Key(k) if k == "field"));
177 }
178}