tokmd_ffi_envelope/
lib.rs1#![forbid(unsafe_code)]
7
8use serde_json::Value;
9
10#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum EnvelopeExtractError {
13 JsonParse(String),
15 JsonSerialize(String),
17 InvalidResponseFormat,
19 Upstream(String),
21}
22
23impl std::fmt::Display for EnvelopeExtractError {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 Self::JsonParse(err) => write!(f, "JSON parse error: {err}"),
27 Self::JsonSerialize(err) => write!(f, "JSON serialize error: {err}"),
28 Self::InvalidResponseFormat => write!(f, "Invalid response format"),
29 Self::Upstream(msg) => write!(f, "{msg}"),
30 }
31 }
32}
33
34impl std::error::Error for EnvelopeExtractError {}
35
36pub fn parse_envelope(result_json: &str) -> Result<Value, EnvelopeExtractError> {
51 serde_json::from_str(result_json)
52 .map_err(|err| EnvelopeExtractError::JsonParse(err.to_string()))
53}
54
55pub fn format_error_message(error_obj: Option<&Value>) -> String {
73 let Some(error_obj) = error_obj else {
74 return "Unknown error".to_string();
75 };
76 let Some(error_obj) = error_obj.as_object() else {
77 return "Unknown error".to_string();
78 };
79
80 let code = error_obj
81 .get("code")
82 .and_then(Value::as_str)
83 .unwrap_or("unknown");
84 let message = error_obj
85 .get("message")
86 .and_then(Value::as_str)
87 .unwrap_or("Unknown error");
88 format!("[{code}] {message}")
89}
90
91pub fn extract_data(envelope: Value) -> Result<Value, EnvelopeExtractError> {
113 let Some(obj) = envelope.as_object() else {
114 return Err(EnvelopeExtractError::InvalidResponseFormat);
115 };
116
117 let ok = obj.get("ok").and_then(Value::as_bool).unwrap_or(false);
118 if ok {
119 if let Some(data) = obj.get("data") {
120 return Ok(data.clone());
121 }
122 return Ok(envelope);
123 }
124
125 Err(EnvelopeExtractError::Upstream(format_error_message(
126 obj.get("error"),
127 )))
128}
129
130pub fn extract_data_from_json(result_json: &str) -> Result<Value, EnvelopeExtractError> {
142 let envelope = parse_envelope(result_json)?;
143 extract_data(envelope)
144}
145
146pub fn extract_data_json(result_json: &str) -> Result<String, EnvelopeExtractError> {
159 let data = extract_data_from_json(result_json)?;
160 serde_json::to_string(&data).map_err(|err| EnvelopeExtractError::JsonSerialize(err.to_string()))
161}
162
163#[cfg(test)]
164mod tests {
165 use super::*;
166 use serde_json::json;
167
168 #[test]
169 fn parse_envelope_invalid_json_errors() {
170 let err = parse_envelope("{").unwrap_err();
171 assert!(matches!(err, EnvelopeExtractError::JsonParse(_)));
172 assert!(err.to_string().contains("JSON parse error"));
173 }
174
175 #[test]
176 fn extract_data_success_returns_data() {
177 let envelope = json!({
178 "ok": true,
179 "data": { "mode": "version" }
180 });
181 let data = extract_data(envelope).unwrap();
182 assert_eq!(data["mode"], "version");
183 }
184
185 #[test]
186 fn extract_data_success_without_data_returns_envelope() {
187 let envelope = json!({
188 "ok": true,
189 "mode": "version"
190 });
191 let data = extract_data(envelope.clone()).unwrap();
192 assert_eq!(data, envelope);
193 }
194
195 #[test]
196 fn extract_data_error_formats_message() {
197 let envelope = json!({
198 "ok": false,
199 "error": { "code": "unknown_mode", "message": "Unknown mode: nope" }
200 });
201 let err = extract_data(envelope).unwrap_err();
202 assert_eq!(
203 err,
204 EnvelopeExtractError::Upstream("[unknown_mode] Unknown mode: nope".to_string())
205 );
206 }
207
208 #[test]
209 fn extract_data_non_object_is_invalid_format() {
210 let err = extract_data(json!(["not", "an", "envelope"])).unwrap_err();
211 assert_eq!(err, EnvelopeExtractError::InvalidResponseFormat);
212 }
213
214 #[test]
215 fn format_error_message_defaults_when_missing_fields() {
216 let missing = json!({});
217 assert_eq!(
218 format_error_message(Some(&missing)),
219 "[unknown] Unknown error"
220 );
221 assert_eq!(format_error_message(None), "Unknown error");
222 assert_eq!(format_error_message(Some(&json!("boom"))), "Unknown error");
223 }
224
225 #[test]
226 fn extract_data_json_serializes_payload() {
227 let envelope = json!({
228 "ok": true,
229 "data": { "a": 1, "b": true }
230 });
231 let encoded = extract_data_json(&envelope.to_string()).unwrap();
232 let parsed: Value = serde_json::from_str(&encoded).unwrap();
233 assert_eq!(parsed["a"], 1);
234 assert_eq!(parsed["b"], true);
235 }
236}