robinpath_modules/modules/
graphql_mod.rs1use robinpath::{RobinPath, Value};
2
3pub fn register(rp: &mut RobinPath) {
4 rp.register_builtin("graphql.buildQuery", |args, _| {
6 let query_name = args.first().map(|v| v.to_display_string()).unwrap_or_default();
7 let fields = args.get(1);
8 let variables = args.get(2);
9
10 let fields_str = format_fields(fields);
11
12 let vars_decl = if let Some(vars) = variables {
13 if let Value::Object(map) = vars {
14 if map.is_empty() {
15 String::new()
16 } else {
17 let params: Vec<String> = map
18 .iter()
19 .map(|(k, v)| format!("${}: {}", k, graphql_type_of(v)))
20 .collect();
21 let args_inner: Vec<String> = map
22 .keys()
23 .map(|k| format!("{}: ${}", k, k))
24 .collect();
25 format!(
26 "({})",
27 params.join(", ")
28 ) + &format!(" {{ {}({}) {{ {} }} }}", query_name, args_inner.join(", "), fields_str)
29 }
30 } else {
31 String::new()
32 }
33 } else {
34 String::new()
35 };
36
37 if vars_decl.is_empty() {
38 Ok(Value::String(format!(
39 "query {} {{ {} }}",
40 query_name, fields_str
41 )))
42 } else {
43 Ok(Value::String(format!("query {}{}", query_name, vars_decl)))
44 }
45 });
46
47 rp.register_builtin("graphql.buildMutation", |args, _| {
49 let mutation_name = args.first().map(|v| v.to_display_string()).unwrap_or_default();
50 let input = args.get(1);
51 let fields = args.get(2);
52
53 let fields_str = format_fields(fields);
54 let input_str = format_input(input);
55
56 Ok(Value::String(format!(
57 "mutation {{ {}(input: {}) {{ {} }} }}",
58 mutation_name, input_str, fields_str
59 )))
60 });
61
62 rp.register_builtin("graphql.parseResponse", |args, _| {
64 let resp = args.first().cloned().unwrap_or(Value::Null);
65 match resp {
66 Value::Object(map) => {
67 if let Some(errors) = map.get("errors") {
69 if let Value::Array(arr) = errors {
70 if !arr.is_empty() {
71 let mut result = indexmap::IndexMap::new();
72 result.insert("data".to_string(), map.get("data").cloned().unwrap_or(Value::Null));
73 result.insert("errors".to_string(), Value::Array(arr.clone()));
74 return Ok(Value::Object(result));
75 }
76 }
77 }
78 Ok(map.get("data").cloned().unwrap_or(Value::Null))
80 }
81 _ => Ok(Value::Null),
82 }
83 });
84
85 rp.register_builtin("graphql.extractErrors", |args, _| {
87 let resp = args.first().cloned().unwrap_or(Value::Null);
88 match resp {
89 Value::Object(map) => {
90 if let Some(errors) = map.get("errors") {
91 if let Value::Array(arr) = errors {
92 if !arr.is_empty() {
93 let messages: Vec<Value> = arr
94 .iter()
95 .map(|e| {
96 if let Value::Object(err_obj) = e {
97 err_obj
98 .get("message")
99 .cloned()
100 .unwrap_or_else(|| Value::String(e.to_display_string()))
101 } else {
102 Value::String(e.to_display_string())
103 }
104 })
105 .collect();
106 return Ok(Value::Array(messages));
107 }
108 }
109 }
110 Ok(Value::Null)
111 }
112 _ => Ok(Value::Null),
113 }
114 });
115}
116
117fn format_fields(fields: Option<&Value>) -> String {
118 match fields {
119 Some(Value::Array(arr)) => {
120 arr.iter().map(|v| v.to_display_string()).collect::<Vec<_>>().join(" ")
121 }
122 Some(Value::String(s)) => s.clone(),
123 Some(v) => v.to_display_string(),
124 None => String::new(),
125 }
126}
127
128fn format_input(input: Option<&Value>) -> String {
129 match input {
130 Some(Value::Object(map)) => {
131 let pairs: Vec<String> = map
132 .iter()
133 .map(|(k, v)| format!("{}: {}", k, format_graphql_value(v)))
134 .collect();
135 format!("{{ {} }}", pairs.join(", "))
136 }
137 Some(v) => v.to_display_string(),
138 None => "{}".to_string(),
139 }
140}
141
142fn format_graphql_value(v: &Value) -> String {
143 match v {
144 Value::String(s) => format!("\"{}\"", s),
145 Value::Number(n) => {
146 if *n == (*n as i64) as f64 {
147 format!("{}", *n as i64)
148 } else {
149 format!("{}", n)
150 }
151 }
152 Value::Bool(b) => format!("{}", b),
153 Value::Null => "null".to_string(),
154 Value::Array(arr) => {
155 let items: Vec<String> = arr.iter().map(format_graphql_value).collect();
156 format!("[{}]", items.join(", "))
157 }
158 Value::Object(map) => {
159 let pairs: Vec<String> = map
160 .iter()
161 .map(|(k, v)| format!("{}: {}", k, format_graphql_value(v)))
162 .collect();
163 format!("{{ {} }}", pairs.join(", "))
164 }
165 Value::Undefined => "null".to_string(),
166 }
167}
168
169fn graphql_type_of(v: &Value) -> &'static str {
170 match v {
171 Value::String(_) => "String",
172 Value::Number(n) => {
173 if *n == (*n as i64) as f64 {
174 "Int"
175 } else {
176 "Float"
177 }
178 }
179 Value::Bool(_) => "Boolean",
180 _ => "String",
181 }
182}