1use crate::{
7 Input,
8 error::{DeserializeError, EXPECTED_INPUT_TYPES, i128_to_isize, invalid_type},
9 position::InputPath,
10};
11use serde::{
12 Deserializer, Serializer,
13 de::{self, Deserialize, DeserializeSeed, MapAccess, SeqAccess, Visitor},
14 ser::{Serialize, SerializeMap, SerializeSeq},
15};
16use std::{collections::HashMap, fmt};
17
18impl Input {
19 pub fn serialize<S: Serializer>(&self, serializer: S) -> S::Ok {
21 serialize_infallible(self, serializer)
22 }
23}
24
25impl Serialize for Input {
26 fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
27 Ok(serialize_infallible(self, serializer))
28 }
29}
30
31fn serialize_infallible<S: Serializer>(input: &Input, serializer: S) -> S::Ok {
32 match input {
33 Input::Bool(value) => serializer.serialize_bool(*value).unwrap_infallible(),
34 Input::Int(value) => serializer.serialize_i64(*value as i64).unwrap_infallible(),
35 Input::Float(value) => serializer.serialize_f64(*value).unwrap_infallible(),
36 Input::Str(value) => serializer.serialize_str(value).unwrap_infallible(),
37 Input::List(values) => {
38 let mut seq = serializer
39 .serialize_seq(Some(values.len()))
40 .unwrap_infallible();
41 for value in values {
42 seq.serialize_element(value).unwrap_infallible();
43 }
44 seq.end().unwrap_infallible()
45 }
46 Input::Map(values) => {
47 let mut map = serializer
48 .serialize_map(Some(values.len()))
49 .unwrap_infallible();
50 for (key, value) in values {
51 map.serialize_key(key).unwrap_infallible();
52 map.serialize_value(value).unwrap_infallible();
53 }
54 map.end().unwrap_infallible()
55 }
56 }
57}
58
59trait UnwrapInfallible<T> {
60 fn unwrap_infallible(self) -> T;
61}
62
63impl<T, E: fmt::Debug> UnwrapInfallible<T> for Result<T, E> {
64 fn unwrap_infallible(self) -> T {
65 self.unwrap_or_else(|error| panic!("Input serialization cannot fail: {error:?}"))
66 }
67}
68
69impl<'de> Deserialize<'de> for Input {
70 fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
71 deserializer.deserialize_any(InputSeed {
72 path: InputPath::root(),
73 })
74 }
75}
76
77struct InputSeed {
78 path: InputPath,
79}
80
81impl Clone for InputSeed {
82 fn clone(&self) -> Self {
83 Self {
84 path: self.path.clone(),
85 }
86 }
87}
88
89impl<'de> DeserializeSeed<'de> for InputSeed {
90 type Value = Input;
91
92 fn deserialize<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
93 deserializer.deserialize_any(self)
94 }
95}
96
97impl InputSeed {
98 fn invalid_type<E: de::Error>(&self, found: impl Into<String>) -> E {
99 E::custom(invalid_type(self.path.clone(), found).to_string())
100 }
101
102 fn integer_out_of_range<E: de::Error>(&self, value: i128) -> E {
103 E::custom(
104 DeserializeError::IntegerOutOfRange {
105 path: self.path.clone(),
106 value,
107 }
108 .to_string(),
109 )
110 }
111
112 fn int_from_i128<E: de::Error>(&self, value: i128) -> Result<Input, E> {
113 match i128_to_isize(value) {
114 Some(value) => Ok(Input::Int(value)),
115 None => Err(self.integer_out_of_range(value)),
116 }
117 }
118}
119
120impl<'de> Visitor<'de> for InputSeed {
121 type Value = Input;
122
123 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
124 write!(formatter, "a {EXPECTED_INPUT_TYPES}")
125 }
126
127 fn visit_bool<E: de::Error>(self, value: bool) -> Result<Self::Value, E> {
128 Ok(Input::Bool(value))
129 }
130
131 fn visit_i64<E: de::Error>(self, value: i64) -> Result<Self::Value, E> {
132 self.int_from_i128(value as i128)
133 }
134
135 fn visit_u64<E: de::Error>(self, value: u64) -> Result<Self::Value, E> {
136 self.int_from_i128(value as i128)
137 }
138
139 fn visit_i128<E: de::Error>(self, value: i128) -> Result<Self::Value, E> {
140 self.int_from_i128(value)
141 }
142
143 fn visit_u128<E: de::Error>(self, value: u128) -> Result<Self::Value, E> {
144 self.int_from_i128(value as i128)
145 }
146
147 fn visit_f32<E: de::Error>(self, value: f32) -> Result<Self::Value, E> {
148 Ok(Input::Float(value as f64))
149 }
150
151 fn visit_f64<E: de::Error>(self, value: f64) -> Result<Self::Value, E> {
152 Ok(Input::Float(value))
153 }
154
155 fn visit_str<E: de::Error>(self, value: &str) -> Result<Self::Value, E> {
156 Ok(Input::Str(value.to_owned()))
157 }
158
159 fn visit_string<E: de::Error>(self, value: String) -> Result<Self::Value, E> {
160 Ok(Input::Str(value))
161 }
162
163 fn visit_seq<A: SeqAccess<'de>>(self, mut seq: A) -> Result<Self::Value, A::Error> {
164 let mut values = Vec::new();
165 let mut index = 0;
166 while let Some(value) = seq.next_element_seed(InputSeed {
167 path: self.path.clone().with_index(index),
168 })? {
169 values.push(value);
170 index += 1;
171 }
172 Ok(Input::List(values))
173 }
174
175 fn visit_map<A: MapAccess<'de>>(self, mut map: A) -> Result<Self::Value, A::Error> {
176 let mut values = HashMap::new();
177 while let Some(key) = map.next_key::<String>()? {
178 let value = map.next_value_seed(InputSeed {
179 path: self.path.clone().with_key(&key),
180 })?;
181 values.insert(key, value);
182 }
183 Ok(Input::Map(values))
184 }
185
186 fn visit_unit<E: de::Error>(self) -> Result<Self::Value, E> {
187 Err(self.invalid_type("null"))
188 }
189
190 fn visit_none<E: de::Error>(self) -> Result<Self::Value, E> {
191 Err(self.invalid_type("null"))
192 }
193
194 fn visit_some<D: Deserializer<'de>>(self, deserializer: D) -> Result<Self::Value, D::Error> {
195 deserializer.deserialize_any(self)
196 }
197
198 fn visit_newtype_struct<D: Deserializer<'de>>(
199 self,
200 deserializer: D,
201 ) -> Result<Self::Value, D::Error> {
202 deserializer.deserialize_any(self)
203 }
204
205 fn visit_enum<A: de::EnumAccess<'de>>(self, _data: A) -> Result<Self::Value, A::Error> {
206 Err(self.invalid_type("enum"))
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use super::*;
213 use crate::error::InputSerializeError;
214 use serde_json::{Value, json};
215
216 #[test]
217 fn serialize_is_infallible() {
218 let input = Input::Map(HashMap::from([
219 ("list".to_string(), Input::from([1, 2, 3])),
220 ("flag".to_string(), Input::from(true)),
221 ]));
222 let _: Result<Value, InputSerializeError> =
223 Ok(input.serialize(serde_json::value::Serializer));
224 }
225
226 #[test]
227 fn rejects_null_with_clean_error() {
228 let error = serde_json::from_str::<Input>("null").unwrap_err();
229 assert!(
230 error
231 .to_string()
232 .starts_with("expected boolean, integer, float, string, list, or map, found null")
233 );
234 }
235
236 #[test]
237 fn rejects_invalid_type_at_path() {
238 let error = serde_json::from_str::<Input>(r#"{"foo": null}"#).unwrap_err();
239 assert!(error.to_string().starts_with(
240 "[foo]: expected boolean, integer, float, string, list, or map, found null"
241 ));
242 }
243
244 #[test]
245 fn rejects_out_of_range_integer_at_path() {
246 let value = if cfg!(target_pointer_width = "64") {
247 json!(i64::MAX as i128 + 1)
248 } else {
249 json!(i64::MAX)
250 };
251 let json = format!(r#"{{"count": {value}}}"#);
252 let error = serde_json::from_str::<Input>(&json).unwrap_err();
253 assert!(error.to_string().starts_with(&format!(
254 "[count]: integer {value} is out of range for isize"
255 )));
256 }
257
258 #[test]
259 fn roundtrip() {
260 let input = Input::from(HashMap::from([
261 ("name".to_string(), Input::from("plugx")),
262 ("enabled".to_string(), Input::from(true)),
263 ("count".to_string(), Input::from(3)),
264 ("ratio".to_string(), Input::from(0.5)),
265 ]));
266 let json = serde_json::to_string(&input).unwrap();
267 let decoded: Input = serde_json::from_str(&json).unwrap();
268 assert_eq!(input, decoded);
269 }
270
271 #[test]
272 fn deep_json_roundtrip() {
273 let json = r#"{
274 "app": {
275 "server": {
276 "host": "localhost",
277 "port": 8080,
278 "routes": [
279 {
280 "path": "/api",
281 "handler": {
282 "type": "proxy",
283 "target": {
284 "url": "http://example.com",
285 "headers": ["accept", "authorization"]
286 }
287 }
288 }
289 ]
290 },
291 "logging": {
292 "level": "debug",
293 "filters": {
294 "modules": {
295 "plugx": {
296 "input": {
297 "serde": true
298 }
299 }
300 }
301 }
302 }
303 }
304 }"#;
305
306 let decoded: Input = serde_json::from_str(json).unwrap();
307 let encoded = serde_json::to_string(&decoded).unwrap();
308 let roundtripped: Input = serde_json::from_str(&encoded).unwrap();
309 assert_eq!(decoded, roundtripped);
310
311 assert_eq!(
312 decoded
313 .as_map()
314 .unwrap()
315 .get("app")
316 .unwrap()
317 .as_map()
318 .unwrap()
319 .get("logging")
320 .unwrap()
321 .as_map()
322 .unwrap()
323 .get("filters")
324 .unwrap()
325 .as_map()
326 .unwrap()
327 .get("modules")
328 .unwrap()
329 .as_map()
330 .unwrap()
331 .get("plugx")
332 .unwrap()
333 .as_map()
334 .unwrap()
335 .get("input")
336 .unwrap()
337 .as_map()
338 .unwrap()
339 .get("serde")
340 .unwrap(),
341 &Input::Bool(true)
342 );
343 }
344
345 #[test]
346 fn rejects_invalid_type_at_deep_path() {
347 let error = serde_json::from_str::<Input>(
348 r#"{"app": {"logging": {"filters": {"modules": {"plugx": null}}}}}"#,
349 )
350 .unwrap_err();
351 assert!(
352 error.to_string().starts_with(
353 "[app][logging][filters][modules][plugx]: expected boolean, integer, float, string, list, or map, found null"
354 )
355 );
356 }
357}