armature_graphql_client/
response.rs1use serde::{Deserialize, Serialize};
4use serde_json::Value;
5
6#[derive(Debug, Clone, Deserialize, Serialize)]
8pub struct GraphQLResponse<T = Value> {
9 #[serde(default)]
11 pub data: Option<T>,
12 #[serde(default)]
14 pub errors: Option<Vec<GraphQLResponseError>>,
15 #[serde(default)]
17 pub extensions: Option<Value>,
18}
19
20impl<T> GraphQLResponse<T> {
21 pub fn has_errors(&self) -> bool {
23 self.errors.as_ref().is_some_and(|e| !e.is_empty())
24 }
25
26 pub fn into_result(self) -> crate::Result<T> {
28 if let Some(errors) = self.errors {
29 if !errors.is_empty() {
30 return Err(crate::GraphQLError::GraphQL(errors));
31 }
32 }
33 self.data
34 .ok_or_else(|| crate::GraphQLError::Parse("Response contained no data".to_string()))
35 }
36
37 pub fn data(self) -> Option<T> {
39 self.data
40 }
41
42 pub fn errors(&self) -> Option<&[GraphQLResponseError]> {
44 self.errors.as_deref()
45 }
46}
47
48#[derive(Debug, Clone, Deserialize, Serialize)]
50pub struct GraphQLResponseError {
51 pub message: String,
53 #[serde(default)]
55 pub locations: Option<Vec<ErrorLocation>>,
56 #[serde(default)]
58 pub path: Option<Vec<PathSegment>>,
59 #[serde(default)]
61 pub extensions: Option<Value>,
62}
63
64impl std::fmt::Display for GraphQLResponseError {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 write!(f, "{}", self.message)?;
67 if let Some(locations) = &self.locations {
68 if !locations.is_empty() {
69 write!(f, " at ")?;
70 for (i, loc) in locations.iter().enumerate() {
71 if i > 0 {
72 write!(f, ", ")?;
73 }
74 write!(f, "{}:{}", loc.line, loc.column)?;
75 }
76 }
77 }
78 Ok(())
79 }
80}
81
82#[derive(Debug, Clone, Deserialize, Serialize)]
84pub struct ErrorLocation {
85 pub line: u32,
87 pub column: u32,
89}
90
91#[derive(Debug, Clone, Deserialize, Serialize)]
93#[serde(untagged)]
94pub enum PathSegment {
95 Field(String),
97 Index(usize),
99}
100
101impl std::fmt::Display for PathSegment {
102 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103 match self {
104 Self::Field(name) => write!(f, "{}", name),
105 Self::Index(idx) => write!(f, "[{}]", idx),
106 }
107 }
108}
109
110pub fn format_path(path: &[PathSegment]) -> String {
112 path.iter()
113 .map(|s| s.to_string())
114 .collect::<Vec<_>>()
115 .join(".")
116}