1use crate::{Error, Kind, MessageKind, Result, VersionRange};
16use serde_json::Value;
17use std::str::FromStr;
18
19#[derive(Clone, Debug, Eq, PartialEq)]
21pub struct Wv<'a>(&'a Value);
22
23impl<'a, 'b: 'a> From<&'b Value> for Wv<'a> {
24 fn from(value: &'b Value) -> Self {
25 Self(value)
26 }
27}
28
29pub trait As<'a, T>
30where
31 T: 'a,
32{
33 fn as_a(&'a self, name: &str) -> Result<T>;
34}
35
36impl<'a> As<'a, &'a str> for Wv<'a> {
37 fn as_a(&'a self, name: &str) -> Result<&'a str> {
38 self.as_option(name)
39 .and_then(|maybe| maybe.ok_or(Error::Message(name.into())))
40 }
41}
42
43impl<'a> As<'a, String> for Wv<'a> {
44 fn as_a(&'a self, name: &str) -> Result<String> {
45 self.as_option(name)
46 .and_then(|maybe| maybe.ok_or(Error::Message(name.into())))
47 }
48}
49
50impl<'a> As<'a, &'a [Value]> for Wv<'a> {
51 fn as_a(&'a self, name: &str) -> Result<&'a [Value]> {
52 self.0[name]
53 .as_array()
54 .map(|v| &v[..])
55 .ok_or(Error::Message(String::from(name)))
56 }
57}
58
59impl<'a> As<'a, u64> for Wv<'a> {
60 fn as_a(&'a self, name: &str) -> Result<u64> {
61 self.0[name]
62 .as_u64()
63 .ok_or(Error::Message(String::from(name)))
64 }
65}
66
67impl<'a> As<'a, u32> for Wv<'a> {
68 fn as_a(&'a self, name: &str) -> Result<u32> {
69 As::<'a, u64>::as_a(self, name).and_then(|u| u32::try_from(u).map_err(Into::into))
70 }
71}
72
73impl<'a> As<'a, u16> for Wv<'a> {
74 fn as_a(&'a self, name: &str) -> Result<u16> {
75 As::<'a, u64>::as_a(self, name).and_then(|u| u16::try_from(u).map_err(Into::into))
76 }
77}
78
79impl<'a> As<'a, i64> for Wv<'a> {
80 fn as_a(&'a self, name: &str) -> Result<i64> {
81 self.0[name]
82 .as_i64()
83 .ok_or(Error::Message(String::from(name)))
84 }
85}
86
87impl<'a> As<'a, i16> for Wv<'a> {
88 fn as_a(&'a self, name: &str) -> Result<i16> {
89 As::<'a, i64>::as_a(self, name).and_then(|u| i16::try_from(u).map_err(Into::into))
90 }
91}
92
93impl<'a> As<'a, MessageKind> for Wv<'a> {
94 fn as_a(&'a self, name: &str) -> Result<MessageKind> {
95 self.as_a(name).and_then(MessageKind::from_str)
96 }
97}
98
99impl<'a> As<'a, VersionRange> for Wv<'a> {
100 fn as_a(&'a self, name: &str) -> Result<VersionRange> {
101 self.as_a(name).and_then(VersionRange::from_str)
102 }
103}
104
105impl<'a> As<'a, Kind> for Wv<'a> {
106 fn as_a(&'a self, name: &str) -> Result<Kind> {
107 self.as_a(name).and_then(Kind::from_str)
108 }
109}
110
111pub trait AsOption<'a, T>
112where
113 T: 'a,
114{
115 fn as_option(&'a self, name: &str) -> Result<Option<T>>;
116}
117
118impl<'a> AsOption<'a, bool> for Wv<'a> {
119 fn as_option(&'a self, name: &str) -> Result<Option<bool>> {
120 Ok(self.0[name].as_bool())
121 }
122}
123
124impl<'a> AsOption<'a, u64> for Wv<'a> {
125 fn as_option(&'a self, name: &str) -> Result<Option<u64>> {
126 Ok(self.0[name].as_u64())
127 }
128}
129
130impl<'a> AsOption<'a, u32> for Wv<'a> {
131 fn as_option(&'a self, name: &str) -> Result<Option<u32>> {
132 self.0[name]
133 .as_u64()
134 .map_or(Ok(None), |v| v.try_into().map_err(Into::into).map(Some))
135 }
136}
137
138impl<'a> AsOption<'a, u16> for Wv<'a> {
139 fn as_option(&'a self, name: &str) -> Result<Option<u16>> {
140 self.0[name]
141 .as_u64()
142 .map_or(Ok(None), |v| v.try_into().map_err(Into::into).map(Some))
143 }
144}
145
146impl<'a> AsOption<'a, u8> for Wv<'a> {
147 fn as_option(&'a self, name: &str) -> Result<Option<u8>> {
148 self.0[name]
149 .as_u64()
150 .map_or(Ok(None), |v| v.try_into().map_err(Into::into).map(Some))
151 }
152}
153
154impl<'a> AsOption<'a, i64> for Wv<'a> {
155 fn as_option(&'a self, name: &str) -> Result<Option<i64>> {
156 Ok(self.0[name].as_i64())
157 }
158}
159
160impl<'a> AsOption<'a, i32> for Wv<'a> {
161 fn as_option(&'a self, name: &str) -> Result<Option<i32>> {
162 self.0[name]
163 .as_i64()
164 .map_or(Ok(None), |v| v.try_into().map_err(Into::into).map(Some))
165 }
166}
167
168impl<'a> AsOption<'a, i16> for Wv<'a> {
169 fn as_option(&'a self, name: &str) -> Result<Option<i16>> {
170 self.0[name]
171 .as_i64()
172 .map_or(Ok(None), |v| v.try_into().map_err(Into::into).map(Some))
173 }
174}
175
176impl<'a> AsOption<'a, i8> for Wv<'a> {
177 fn as_option(&'a self, name: &str) -> Result<Option<i8>> {
178 self.0[name]
179 .as_i64()
180 .map_or(Ok(None), |v| v.try_into().map_err(Into::into).map(Some))
181 }
182}
183
184impl<'a> AsOption<'a, &'a str> for Wv<'a> {
185 fn as_option(&'a self, name: &str) -> Result<Option<&'a str>> {
186 Ok(self.0[name].as_str())
187 }
188}
189
190impl<'a> AsOption<'a, String> for Wv<'a> {
191 fn as_option(&'a self, name: &str) -> Result<Option<String>> {
192 Ok(self.0[name].as_str().map(String::from))
193 }
194}
195
196impl<'a> AsOption<'a, &'a [Value]> for Wv<'a> {
197 fn as_option(&'a self, name: &str) -> Result<Option<&'a [Value]>> {
198 Ok(self.0[name].as_array().map(|v| &v[..]))
199 }
200}
201
202impl<'a> AsOption<'a, VersionRange> for Wv<'a> {
203 fn as_option(&'a self, name: &str) -> Result<Option<VersionRange>> {
204 self.0[name]
205 .as_str()
206 .map_or(Ok(None), |s| VersionRange::from_str(s).map(Some))
207 }
208}
209
210#[cfg(test)]
211mod tests {
212 use serde_json::json;
213
214 use super::*;
215
216 #[test]
217 fn as_a_str() -> Result<()> {
218 let v = serde_json::from_str::<Value>(
219 r#"
220 {
221 "hello": "world"
222 }
223 "#,
224 )?;
225
226 let wv = Wv::from(&v);
227 let s: &str = wv.as_a("hello")?;
228 assert_eq!("world", s);
229
230 Ok(())
231 }
232
233 #[test]
234 fn as_a_string() -> Result<()> {
235 let v = serde_json::from_str::<Value>(
236 r#"
237 {
238 "hello": "world"
239 }
240 "#,
241 )?;
242
243 let wv = Wv::from(&v);
244 let s: String = wv.as_a("hello")?;
245 assert_eq!("world", s);
246
247 Ok(())
248 }
249
250 #[test]
251 fn as_a_kind() -> Result<()> {
252 let v = serde_json::from_str::<Value>(
253 r#"
254 {
255 "type": "request"
256 }
257 "#,
258 )?;
259
260 let wv = Wv::from(&v);
261 let s: MessageKind = wv.as_a("type")?;
262 assert_eq!(MessageKind::Request, s);
263
264 Ok(())
265 }
266
267 #[test]
268 fn as_option() -> Result<()> {
269 fn as_option<'a, 'b, T>(
270 instance: &'a Wv<'b>,
271 operation: impl Fn(&'a Wv<'b>, &'static str) -> Result<Option<T>>,
272 tests: &[(&'static str, Option<T>)],
273 ) -> Result<()>
274 where
275 T: 'a + PartialEq + std::fmt::Debug,
276 {
277 for (name, expected) in tests {
278 assert_eq!(*expected, operation(instance, name)?, "name: {name}");
279 }
280 Ok(())
281 }
282
283 let v = serde_json::from_str::<Value>(
284 r#"
285 {
286 "u64": 18446744073709551615,
287 "u32": 4294967295,
288 "u16": 65535,
289 "u8": 255,
290
291 "i64_max": 9223372036854775807,
292 "i64_min": -9223372036854775808,
293 "i32_max": 2147483647,
294 "i32_min": -2147483648,
295 "i16_max": 32767,
296 "i16_min": -32768,
297 "i8_max": 127,
298 "i8_min": -128
299 }
300 "#,
301 )?;
302
303 let wv = Wv::from(&v);
304
305 as_option(
306 &wv,
307 <Wv<'_> as AsOption<u64>>::as_option,
308 &[
309 ("u64", Some(u64::MAX)),
310 ("u32", Some(u32::MAX.into())),
311 ("u16", Some(u16::MAX.into())),
312 ("u8", Some(u8::MAX.into())),
313 ("i64_max", Some(i64::MAX.try_into()?)),
314 ("i64_min", None),
315 ("i32_max", Some(i32::MAX.try_into()?)),
316 ("i32_min", None),
317 ("i16_max", Some(i16::MAX.try_into()?)),
318 ("i16_min", None),
319 ("i8_max", Some(i8::MAX.try_into()?)),
320 ("i8_min", None),
321 ],
322 )?;
323
324 assert!(matches!(
325 <Wv<'_> as AsOption<u32>>::as_option(&wv, "u64"),
326 Err(Error::TryFromInt(_)),
327 ));
328 assert!(matches!(
329 <Wv<'_> as AsOption<u32>>::as_option(&wv, "i64_max"),
330 Err(Error::TryFromInt(_)),
331 ));
332
333 as_option(
334 &wv,
335 <Wv<'_> as AsOption<u32>>::as_option,
336 &[
337 ("u32", Some(u32::MAX)),
339 ("u16", Some(u16::MAX.into())),
340 ("u8", Some(u8::MAX.into())),
341 ("i64_min", None),
343 ("i32_max", Some(i32::MAX.try_into()?)),
344 ("i32_min", None),
345 ("i16_max", Some(i16::MAX.try_into()?)),
346 ("i16_min", None),
347 ("i8_max", Some(i8::MAX.try_into()?)),
348 ("i8_min", None),
349 ],
350 )?;
351
352 Ok(())
353 }
354
355 #[test]
356 fn as_a_u64() -> Result<()> {
357 let v = serde_json::from_str::<Value>(
358 r#"
359 {
360 "value": 32123
361 }
362 "#,
363 )?;
364
365 let wv = Wv::from(&v);
366 let s: u64 = wv.as_a("value")?;
367 assert_eq!(32123, s);
368
369 Ok(())
370 }
371
372 #[test]
373 fn as_a_version_range() -> Result<()> {
374 let v = serde_json::from_str::<Value>(
375 r#"
376 {
377 "flexibleVersions": "2+"
378 }
379 "#,
380 )?;
381
382 let wv = Wv::from(&v);
383 let flexible: VersionRange = wv.as_a("flexibleVersions")?;
384
385 assert_eq!(
386 VersionRange {
387 start: 2,
388 end: i16::MAX
389 },
390 flexible
391 );
392
393 Ok(())
394 }
395
396 #[test]
397 fn as_array_value() -> Result<()> {
398 let v = serde_json::from_str::<Value>(
399 r#"
400 {
401 "x": [1,2,3]
402 }
403 "#,
404 )?;
405
406 let wv = Wv::from(&v);
407 let array: &[Value] = wv.as_a("x")?;
408 assert_eq!([json!(1), json!(2), json!(3)], array);
409
410 Ok(())
411 }
412}