batch_mode_process_response/
deserialize_json_with_optional_fields_wrapper.rs

1crate::ix!();
2
3/// Attempt to deserialize `json_value` into `T`.  
4/// If the top‐level attempt fails, the function looks for a nested `"fields"`
5/// object and tries again.  
6/// All paths are heavily traced so we can see exactly where deserialization
7/// succeeds or fails.
8#[instrument(level = "trace", skip(json_value))]
9pub fn deserialize_json_with_optional_fields_wrapper<T>(
10    json_value: &serde_json::Value,
11) -> Result<T, serde_json::Error>
12where
13    T: DeserializeOwned,
14{
15    trace!("Attempting direct deserialization into target struct …");
16    match serde_json::from_value::<T>(json_value.clone()) {
17        Ok(t) => {
18            debug!("Direct deserialization succeeded.");
19            return Ok(t);
20        }
21        Err(e_direct) => {
22            debug!("Direct deserialization failed: {:?}", e_direct);
23
24            trace!("Checking for `fields` wrapper …");
25            if let Some(inner) = json_value.get("fields") {
26                trace!("`fields` wrapper found — trying inner value deserialization …");
27                match serde_json::from_value::<T>(inner.clone()) {
28                    Ok(t) => {
29                        info!("Deserialization succeeded after unwrapping `fields`.");
30                        Ok(t)
31                    }
32                    Err(e_inner) => {
33                        error!(
34                            "Deserialization failed after unwrapping `fields`: {:?}",
35                            e_inner
36                        );
37                        // Return the *inner* error because that is the final failure.
38                        Err(e_inner)
39                    }
40                }
41            } else {
42                error!("No `fields` wrapper present; cannot recover.");
43                // Return the *direct* error: that is the best information we have.
44                Err(e_direct)
45            }
46        }
47    }
48}
49
50#[cfg(test)]
51mod deserialize_json_with_optional_fields_wrapper_tests {
52    use super::*;
53
54    /// A minimal structure that mirrors the fields we expect to be present
55    /// in the JSON used by `AiReadmeWriterDesiredOutput`.  Using a tiny struct
56    /// keeps the tests focussed on (de)serialisation semantics rather than the
57    /// production‑size type.
58    #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
59    struct TestStruct {
60        crate_name: String,
61        answer:     u32,
62    }
63
64    /// Helper that converts a raw JSON string into a `serde_json::Value` so the
65    /// call‑sites stay tidy.
66    fn json_val(s: &str) -> serde_json::Value {
67        serde_json::from_str::<serde_json::Value>(s).expect("JSON must parse for test setup")
68    }
69
70    #[traced_test]
71    fn direct_deserialization_succeeds() {
72        trace!("===== BEGIN TEST: direct_deserialization_succeeds =====");
73
74        // Top‑level keys exactly match `TestStruct`.
75        let input = json_val(r#"{ "crate_name": "foo", "answer": 42 }"#);
76
77        let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
78
79        assert!(
80            result.is_ok(),
81            "Direct deserialization should succeed when keys are at the top level"
82        );
83        assert_eq!(
84            result.unwrap(),
85            TestStruct {
86                crate_name: "foo".into(),
87                answer: 42
88            }
89        );
90
91        trace!("===== END TEST: direct_deserialization_succeeds =====");
92    }
93
94    #[traced_test]
95    fn fields_wrapper_deserialization_succeeds() {
96        trace!("===== BEGIN TEST: fields_wrapper_deserialization_succeeds =====");
97
98        // Production assistant output often nests user fields under `"fields"`.
99        let input = json_val(
100            r#"
101            {
102                "struct_name":"Whatever",
103                "fields": {
104                    "crate_name":"bar",
105                    "answer":1337
106                }
107            }"#,
108        );
109
110        let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
111
112        assert!(
113            result.is_ok(),
114            "`fields` wrapper should be detected and unwrapped automatically"
115        );
116        assert_eq!(
117            result.unwrap(),
118            TestStruct {
119                crate_name: "bar".into(),
120                answer: 1337
121            }
122        );
123
124        trace!("===== END TEST: fields_wrapper_deserialization_succeeds =====");
125    }
126
127    #[traced_test]
128    fn fails_when_neither_top_level_nor_wrapper_matches() {
129        trace!("===== BEGIN TEST: fails_when_neither_top_level_nor_wrapper_matches =====");
130
131        // Top‑level keys wrong *and* no `"fields"` key.
132        let input = json_val(r#"{ "not_it": true }"#);
133
134        let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
135
136        assert!(
137            result.is_err(),
138            "Should fail because required keys are missing and no wrapper is present"
139        );
140
141        trace!("===== END TEST: fails_when_neither_top_level_nor_wrapper_matches =====");
142    }
143
144    #[traced_test]
145    fn fails_when_wrapper_present_but_incorrect() {
146        trace!("===== BEGIN TEST: fails_when_wrapper_present_but_incorrect =====");
147
148        // `"fields"` exists but does NOT contain the expected keys.
149        let input = json_val(
150            r#"
151            {
152                "fields": {
153                    "wrong":"shape"
154                }
155            }"#,
156        );
157
158        let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
159
160        assert!(
161            result.is_err(),
162            "Should fail because inner object lacks required keys"
163        );
164
165        trace!("===== END TEST: fails_when_wrapper_present_but_incorrect =====");
166    }
167
168    #[traced_test]
169    fn succeeds_with_additional_unrelated_top_level_keys() {
170        trace!("===== BEGIN TEST: succeeds_with_additional_unrelated_top_level_keys =====");
171
172        // Extra keys should be ignored by Serde by default.
173        let input = json_val(
174            r#"
175            {
176                "crate_name": "baz",
177                "answer": 7,
178                "extraneous": "ignored"
179            }"#,
180        );
181
182        let result = deserialize_json_with_optional_fields_wrapper::<TestStruct>(&input);
183
184        assert!(
185            result.is_ok(),
186            "Serde should ignore unrelated top‑level keys when `deny_unknown_fields` is not used"
187        );
188        assert_eq!(
189            result.unwrap(),
190            TestStruct {
191                crate_name: "baz".into(),
192                answer: 7
193            }
194        );
195
196        trace!("===== END TEST: succeeds_with_additional_unrelated_top_level_keys =====");
197    }
198}