json_result/
enum.rs

1use serde::de::{DeserializeOwned, Error};
2
3/// A generic enum representing a JSON result that can either be a success (`Ok`) with a value of type `T`
4/// or an error (`Err`) with a value of type `E`.
5///
6/// This enum is designed to be serialized and deserialized using Serde's untagged enum representation,
7/// allowing it to seamlessly handle JSON values that could match either type.
8#[derive(Debug, serde::Serialize, serde::Deserialize)]
9#[serde(untagged)]
10pub enum JsonResult<T, E> {
11    /// Variant representing a successful result containing a value of type `T`.
12    Ok(T),
13    /// Variant representing an error result containing a value of type `E`.
14    Err(E),
15}
16
17impl<T, E> From<JsonResult<T, E>> for serde_json::Value
18where
19    T: serde::Serialize,
20    E: serde::Serialize,
21{
22    /// Converts a `JsonResult<T, E>` into a `serde_json::Value`.
23    ///
24    /// Serializes the contained value in either the `Ok` or `Err` variant into a JSON value.
25    ///
26    /// # Examples
27    ///
28    /// ```
29    /// # use serde_json::json;
30    /// # use json_result::r#enum::JsonResult;
31    /// let res: JsonResult<i32, String> = JsonResult::Ok(42);
32    /// let json_value: serde_json::Value = res.into();
33    /// assert_eq!(json_value, json!(42));
34    /// ```
35    fn from(res: JsonResult<T, E>) -> Self {
36        match res {
37            JsonResult::Ok(v) => serde_json::json!(v),
38            JsonResult::Err(e) => serde_json::json!(e),
39        }
40    }
41}
42
43impl<T, E> TryFrom<serde_json::Value> for JsonResult<T, E>
44where
45    T: DeserializeOwned,
46    E: DeserializeOwned,
47{
48    type Error = serde_json::Error;
49
50    /// Attempts to convert a `serde_json::Value` into a `JsonResult<T, E>` by
51    /// trying to deserialize it first into `T` (success variant), then into `E` (error variant).
52    ///
53    /// If deserialization into both types fails, returns a combined error message detailing both failures.
54    ///
55    /// # Errors
56    ///
57    /// Returns a `serde_json::Error` if the input JSON value cannot be parsed as either `T` or `E`.
58    ///
59    /// # Examples
60    ///
61    /// ```
62    /// # use serde_json::json;
63    /// # use json_result::r#enum::JsonResult;
64    /// let json_val = json!(42);
65    /// let res: JsonResult<i32, String> = json_val.try_into().unwrap();
66    /// match res {
67    ///     JsonResult::Ok(val) => assert_eq!(val, 42),
68    ///     JsonResult::Err(_) => panic!("Expected Ok variant"),
69    /// }
70    /// ```
71    fn try_from(value: serde_json::Value) -> Result<Self, Self::Error> {
72        let t_res = serde_json::from_value::<T>(value.clone());
73        let e_res = serde_json::from_value::<E>(value);
74
75        match (t_res, e_res) {
76            (Ok(v), _) => Ok(JsonResult::Ok(v)),
77            (_, Ok(e)) => Ok(JsonResult::Err(e)),
78            (Err(t_err), Err(e_err)) => {
79                let t_name = std::any::type_name::<T>();
80                let e_name = std::any::type_name::<E>();
81                let message = format!(
82                    "Failed to parse as {}: {}\nFailed to parse as {}: {}",
83                    t_name, t_err, e_name, e_err
84                );
85                Err(serde_json::Error::custom(message))
86            }
87        }
88    }
89}
90
91impl<T, E> From<Result<T, E>> for JsonResult<T, E> {
92    fn from(r: Result<T, E>) -> Self {
93        match r {
94            Ok(v) => JsonResult::Ok(v),
95            Err(e) => JsonResult::Err(e),
96        }
97    }
98}
99
100#[cfg(test)]
101mod tests {
102    use crate::r#enum::JsonResult;
103    use serde::{Deserialize, Serialize};
104
105    #[derive(Debug, Serialize, Deserialize, PartialEq)]
106    struct GoodT {
107        x: u32,
108    }
109
110    #[derive(Debug, Serialize, Deserialize, PartialEq)]
111    struct BadE {
112        msg: String,
113    }
114
115    #[test]
116    fn test_good_case_ok_t() {
117        let original: JsonResult<GoodT, BadE> = JsonResult::Ok(GoodT { x: 123 });
118
119        let json = serde_json::to_value(&original).unwrap();
120        let parsed = JsonResult::<GoodT, BadE>::try_from(json).unwrap();
121
122        match parsed {
123            JsonResult::Ok(v) => assert_eq!(v, GoodT { x: 123 }),
124            _ => panic!("Expected Ok(T)"),
125        }
126    }
127
128    #[test]
129    fn test_good_case_err_e() {
130        let original: JsonResult<GoodT, BadE> = JsonResult::Err(BadE { msg: "fail".into() });
131
132        let json = serde_json::to_value(&original).unwrap();
133        let parsed = JsonResult::<GoodT, BadE>::try_from(json).unwrap();
134
135        match parsed {
136            JsonResult::Err(e) => assert_eq!(e, BadE { msg: "fail".into() }),
137            _ => panic!("Expected Err(E)"),
138        }
139    }
140
141    #[test]
142    fn test_bad_case_neither_matches() {
143        let json = serde_json::json!({ "something": 9999 });
144
145        let result = JsonResult::<GoodT, BadE>::try_from(json);
146
147        assert!(result.is_err());
148
149        let msg = result.unwrap_err().to_string();
150        assert!(msg.contains("GoodT"));
151        assert!(msg.contains("BadE"));
152        assert!(msg.contains("Failed to parse"));
153    }
154
155    #[test]
156    fn test_round_trip_t() {
157        let original: JsonResult<GoodT, BadE> = JsonResult::Ok(GoodT { x: 42 });
158
159        let json: serde_json::Value = original.into();
160        let parsed = JsonResult::<GoodT, BadE>::try_from(json).unwrap();
161
162        match parsed {
163            JsonResult::Ok(v) => assert_eq!(v.x, 42),
164            _ => panic!("Round trip for T failed"),
165        }
166    }
167
168    #[test]
169    fn test_round_trip_e() {
170        let original: JsonResult<GoodT, BadE> = JsonResult::Err(BadE { msg: "boom".into() });
171
172        let json: serde_json::Value = original.into();
173        let parsed = JsonResult::<GoodT, BadE>::try_from(json).unwrap();
174
175        match parsed {
176            JsonResult::Err(v) => assert_eq!(v.msg, "boom"),
177            _ => panic!("Round trip for E failed"),
178        }
179    }
180
181    #[test]
182    fn test_empty_object() {
183        let json = serde_json::json!({});
184
185        // Neither GoodT nor BadE should parse successfully from empty object
186        let result = JsonResult::<GoodT, BadE>::try_from(json);
187        assert!(result.is_err());
188    }
189
190    #[test]
191    fn test_null_value() {
192        let json = serde_json::json!(null);
193
194        let result = JsonResult::<GoodT, BadE>::try_from(json);
195        assert!(result.is_err());
196    }
197
198    #[test]
199    fn test_primitive_value_matches_t() {
200        // If GoodT was just a number, primitive JSON number should parse as T
201        #[derive(Debug, Serialize, Deserialize, PartialEq)]
202        struct NumberT(u32);
203
204        #[derive(Debug, Serialize, Deserialize, PartialEq)]
205        struct StringE(String);
206
207        let json = serde_json::json!(123u32);
208        let parsed = JsonResult::<NumberT, StringE>::try_from(json).unwrap();
209
210        match parsed {
211            JsonResult::Ok(NumberT(n)) => assert_eq!(n, 123),
212            _ => panic!("Expected Ok(NumberT)"),
213        }
214    }
215
216    #[test]
217    fn test_primitive_value_matches_e() {
218        #[derive(Debug, Serialize, Deserialize, PartialEq)]
219        struct NumberT(u32);
220
221        #[derive(Debug, Serialize, Deserialize, PartialEq)]
222        struct StringE(String);
223
224        let json = serde_json::json!("error message");
225        let parsed = JsonResult::<NumberT, StringE>::try_from(json).unwrap();
226
227        match parsed {
228            JsonResult::Err(StringE(s)) => assert_eq!(s, "error message"),
229            _ => panic!("Expected Err(StringE)"),
230        }
231    }
232
233    #[test]
234    fn test_ambiguous_value() {
235        // A JSON value that could deserialize to both T and E if they have same structure
236        #[derive(Debug, Serialize, Deserialize, PartialEq)]
237        struct Ambiguous {
238            value: u32,
239        }
240
241        let json = serde_json::json!({ "value": 55 });
242
243        // Because we try T first, expect Ok variant
244        let parsed = JsonResult::<Ambiguous, Ambiguous>::try_from(json).unwrap();
245
246        match parsed {
247            JsonResult::Ok(Ambiguous { value }) => assert_eq!(value, 55),
248            _ => panic!("Expected Ok variant for ambiguous type"),
249        }
250    }
251
252    #[test]
253    fn test_deeply_nested_json() {
254        #[derive(Debug, Serialize, Deserialize, PartialEq)]
255        struct NestedT {
256            nested: Option<Box<NestedT>>,
257            val: u32,
258        }
259
260        #[derive(Debug, Serialize, Deserialize, PartialEq)]
261        struct NestedE {
262            error: String,
263        }
264
265        let json = serde_json::json!({
266            "nested": {
267                "nested": null,
268                "val": 10
269            },
270            "val": 5
271        });
272
273        let parsed = JsonResult::<NestedT, NestedE>::try_from(json).unwrap();
274
275        match parsed {
276            JsonResult::Ok(n) => {
277                assert_eq!(n.val, 5);
278                assert!(n.nested.is_some());
279                let inner = n.nested.unwrap();
280                assert_eq!(inner.val, 10);
281                assert!(inner.nested.is_none());
282            }
283            _ => panic!("Expected Ok with nested structure"),
284        }
285    }
286
287    #[test]
288    fn test_invalid_json_structure() {
289        // JSON array will not deserialize to GoodT or BadE structs
290        let json = serde_json::json!([1, 2, 3]);
291
292        let result = JsonResult::<GoodT, BadE>::try_from(json);
293        assert!(result.is_err());
294    }
295
296    #[test]
297    fn test_error_message_contains_correct_type_names() {
298        // This triggers error with complex type names to ensure message includes them
299        #[derive(Debug, Serialize, Deserialize)]
300        struct ComplexType {
301            field: String,
302        }
303
304        let json = serde_json::json!("just a string");
305
306        let result = JsonResult::<ComplexType, ComplexType>::try_from(json);
307        assert!(result.is_err());
308
309        let err_str = result.unwrap_err().to_string();
310        assert!(err_str.contains("ComplexType"));
311        assert!(err_str.contains("Failed to parse"));
312    }
313
314    #[derive(Debug, Serialize, Deserialize, PartialEq)]
315    struct OKData {
316        value: i32,
317    }
318
319    #[derive(Debug, Serialize, Deserialize, PartialEq)]
320    struct ErrData {
321        message: String,
322    }
323
324    #[test]
325    fn test_from_result_ok() {
326        let r: Result<OKData, ErrData> = Ok(OKData { value: 123 });
327
328        let jr: JsonResult<OKData, ErrData> = JsonResult::from(r);
329
330        match jr {
331            JsonResult::Ok(ok) => assert_eq!(ok.value, 123),
332            _ => panic!("Expected JsonResult::Ok"),
333        }
334    }
335
336    #[test]
337    fn test_from_result_err() {
338        let r: Result<OKData, ErrData> = Err(ErrData {
339            message: "boom".into(),
340        });
341
342        let jr: JsonResult<OKData, ErrData> = JsonResult::from(r);
343
344        match jr {
345            JsonResult::Err(e) => assert_eq!(e.message, "boom"),
346            _ => panic!("Expected JsonResult::Err"),
347        }
348    }
349
350    #[test]
351    fn test_from_result_type_check() {
352        // Just ensures this compiles & converts correctly
353        let res: Result<i32, &str> = Ok(10);
354        let jr: JsonResult<i32, &str> = res.into();
355
356        assert!(matches!(jr, JsonResult::Ok(10)));
357    }
358
359    #[test]
360    fn test_from_result_error_type_check() {
361        let res: Result<i32, &str> = Err("wrong");
362        let jr: JsonResult<i32, &str> = res.into();
363
364        assert!(matches!(jr, JsonResult::Err("wrong")));
365    }
366}