1use crate::FaucetError;
4use jsonpath_rust::JsonPath;
5use serde_json::Value;
6
7pub fn quote_ident(name: &str) -> String {
20 format!("\"{}\"", name.replace('"', "\"\""))
21}
22
23pub fn extract_records(body: &Value, path: Option<&str>) -> Result<Vec<Value>, FaucetError> {
32 match path {
33 Some(p) => {
34 let results = body
35 .query(p)
36 .map_err(|e| FaucetError::JsonPath(format!("invalid JSONPath '{p}': {e}")))?;
37 Ok(results.into_iter().cloned().collect())
38 }
39 None => match body {
40 Value::Array(arr) => Ok(arr.clone()),
41 other => Ok(vec![other.clone()]),
42 },
43 }
44}
45
46pub async fn check_http_response(
54 resp: reqwest::Response,
55 max_body_len: usize,
56) -> Result<reqwest::Response, FaucetError> {
57 if resp.status().is_success() {
58 return Ok(resp);
59 }
60
61 let status = resp.status().as_u16();
62 let url = resp.url().to_string();
63 let body_text = resp.text().await.unwrap_or_default();
64
65 let body = if body_text.len() > max_body_len {
66 let end = body_text.floor_char_boundary(max_body_len);
67 format!("{}...(truncated)", &body_text[..end])
68 } else {
69 body_text
70 };
71
72 Err(FaucetError::HttpStatus { status, url, body })
73}
74
75pub const DEFAULT_ERROR_BODY_MAX_LEN: usize = 2048;
77
78#[cfg(test)]
79mod tests {
80 use super::*;
81 use serde_json::json;
82
83 #[test]
86 fn quote_ident_simple() {
87 assert_eq!(quote_ident("my_table"), "\"my_table\"");
88 }
89
90 #[test]
91 fn quote_ident_with_embedded_quotes() {
92 assert_eq!(quote_ident("has\"quote"), "\"has\"\"quote\"");
93 }
94
95 #[test]
96 fn quote_ident_empty() {
97 assert_eq!(quote_ident(""), "\"\"");
98 }
99
100 #[test]
101 fn quote_ident_special_chars() {
102 assert_eq!(quote_ident("table; DROP"), "\"table; DROP\"");
103 }
104
105 #[test]
108 fn extract_with_path() {
109 let body = json!({"data": [{"id": 1}, {"id": 2}]});
110 let records = extract_records(&body, Some("$.data[*]")).unwrap();
111 assert_eq!(records.len(), 2);
112 assert_eq!(records[0]["id"], 1);
113 }
114
115 #[test]
116 fn extract_without_path_array() {
117 let body = json!([{"id": 1}, {"id": 2}]);
118 let records = extract_records(&body, None).unwrap();
119 assert_eq!(records.len(), 2);
120 }
121
122 #[test]
123 fn extract_without_path_object() {
124 let body = json!({"id": 1});
125 let records = extract_records(&body, None).unwrap();
126 assert_eq!(records.len(), 1);
127 }
128
129 #[test]
130 fn extract_empty_result() {
131 let body = json!({"data": []});
132 let records = extract_records(&body, Some("$.data[*]")).unwrap();
133 assert!(records.is_empty());
134 }
135
136 #[test]
137 fn extract_invalid_path_returns_error() {
138 let body = json!({"data": 1});
139 let result = extract_records(&body, Some("$.data[*]"));
141 let _ = result;
144 }
145}