Alioth Code Coverage

de.rs98.72%

1// Copyright 2024 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use std::collections::HashMap;
16
17use serde::Deserialize;
18use serde::de::{self, DeserializeSeed, EnumAccess, MapAccess, SeqAccess, VariantAccess, Visitor};
19
20use crate::error::{Error, Result};
21
22#[derive(Debug)]
23pub struct Deserializer<'s, 'o> {
24 input: &'s str,
25 objects: Option<&'o HashMap<&'s str, &'s str>>,
26 top_level: bool,
27 key: &'s str,
28}
29
30impl<'s> de::Deserializer<'s> for &mut Deserializer<'s, '_> {
31 type Error = Error;
32
33 fn deserialize_any<V>(self, _visitor: V) -> Result<V::Value>
34 where
35 V: Visitor<'s>,
36 {
37 Err(Error::UnknownType)
38 }
39
40 fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>91x
41 where91x
42 V: Visitor<'s>,91x
43 {
44 let s = self.consume_input();91x
45 match s {91x
46 "on" | "true" => visitor.visit_bool(true),91x
47 "off" | "false" => visitor.visit_bool(false),21x
48 _ => Err(Error::ExpectedBool),3x
49 }
50 }91x
51
52 fn deserialize_i8<V>(self, visitor: V) -> Result<V::Value>21x
53 where21x
54 V: Visitor<'s>,21x
55 {
56 visitor.visit_i8(self.parse_signed()?)21x
57 }21x
58
59 fn deserialize_i16<V>(self, visitor: V) -> Result<V::Value>3x
60 where3x
61 V: Visitor<'s>,3x
62 {
63 visitor.visit_i16(self.parse_signed()?)3x
64 }3x
65
66 fn deserialize_i32<V>(self, visitor: V) -> Result<V::Value>9x
67 where9x
68 V: Visitor<'s>,9x
69 {
70 visitor.visit_i32(self.parse_signed()?)9x
71 }9x
72
73 fn deserialize_i64<V>(self, visitor: V) -> Result<V::Value>6x
74 where6x
75 V: Visitor<'s>,6x
76 {
77 visitor.visit_i64(self.parse_signed()?)6x
78 }6x
79
80 fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>87x
81 where87x
82 V: Visitor<'s>,87x
83 {
84 visitor.visit_u8(self.parse_unsigned()?)87x
85 }87x
86
87 fn deserialize_u16<V>(self, visitor: V) -> Result<V::Value>53x
88 where53x
89 V: Visitor<'s>,53x
90 {
91 visitor.visit_u16(self.parse_unsigned()?)53x
92 }53x
93
94 fn deserialize_u32<V>(self, visitor: V) -> Result<V::Value>93x
95 where93x
96 V: Visitor<'s>,93x
97 {
98 visitor.visit_u32(self.parse_unsigned()?)93x
99 }93x
100
101 fn deserialize_u64<V>(self, visitor: V) -> Result<V::Value>32x
102 where32x
103 V: Visitor<'s>,32x
104 {
105 visitor.visit_u64(self.parse_unsigned()?)32x
106 }32x
107
108 fn deserialize_f32<V>(self, visitor: V) -> Result<V::Value>3x
109 where3x
110 V: Visitor<'s>,3x
111 {
112 let s = self.consume_input();3x
113 visitor.visit_f32(s.parse().map_err(|_| Error::ExpectedFloat)?)3x
114 }3x
115
116 fn deserialize_f64<V>(self, visitor: V) -> Result<V::Value>3x
117 where3x
118 V: Visitor<'s>,3x
119 {
120 let s = self.consume_input();3x
121 visitor.visit_f64(s.parse().map_err(|_| Error::ExpectedFloat)?)3x
122 }3x
123
124 fn deserialize_char<V>(self, visitor: V) -> Result<V::Value>27x
125 where27x
126 V: Visitor<'s>,27x
127 {
128 self.deserialize_str(visitor)27x
129 }27x
130
131 fn deserialize_str<V>(self, visitor: V) -> Result<V::Value>178x
132 where178x
133 V: Visitor<'s>,178x
134 {
135 if self.top_level {178x
136 visitor.visit_borrowed_str(self.consume_all())21x
137 } else {
138 let id = self.consume_input();157x
139 visitor.visit_borrowed_str(self.deref_id(id)?)157x
140 }
141 }178x
142
143 fn deserialize_string<V>(self, visitor: V) -> Result<V::Value>135x
144 where135x
145 V: Visitor<'s>,135x
146 {
147 self.deserialize_str(visitor)135x
148 }135x
149
150 fn deserialize_bytes<V>(self, visitor: V) -> Result<V::Value>12x
151 where12x
152 V: Visitor<'s>,12x
153 {
154 self.deserialize_seq(visitor)12x
155 }12x
156
157 fn deserialize_byte_buf<V>(self, visitor: V) -> Result<V::Value>3x
158 where3x
159 V: Visitor<'s>,3x
160 {
161 self.deserialize_bytes(visitor)3x
162 }3x
163
164 fn deserialize_option<V>(self, visitor: V) -> Result<V::Value>68x
165 where68x
166 V: Visitor<'s>,68x
167 {
168 let id = self.consume_input();68x
169 let s = self.deref_id(id)?;68x
170 if id.starts_with("id_") && s.is_empty() {65x
171 visitor.visit_none()12x
172 } else {
173 let mut sub_de = Deserializer { input: s, ..*self };53x
174 visitor.visit_some(&mut sub_de)53x
175 }
176 }68x
177
178 fn deserialize_unit<V>(self, visitor: V) -> Result<V::Value>18x
179 where18x
180 V: Visitor<'s>,18x
181 {
182 let s = self.consume_input();18x
183 if s.is_empty() {18x
184 visitor.visit_unit()9x
185 } else {
186 Err(Error::ExpectedUnit)9x
187 }
188 }18x
189
190 fn deserialize_unit_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>12x
191 where12x
192 V: Visitor<'s>,12x
193 {
194 self.deserialize_unit(visitor)12x
195 }12x
196
197 fn deserialize_newtype_struct<V>(self, _name: &'static str, visitor: V) -> Result<V::Value>3x
198 where3x
199 V: Visitor<'s>,3x
200 {
201 visitor.visit_newtype_struct(self)3x
202 }3x
203
204 fn deserialize_seq<V>(self, visitor: V) -> Result<V::Value>62x
205 where62x
206 V: Visitor<'s>,62x
207 {
208 self.deserialize_nested(|de| visitor.visit_seq(CommaSeparated::new(de)))62x
209 }62x
210
211 fn deserialize_tuple<V>(self, _len: usize, visitor: V) -> Result<V::Value>12x
212 where12x
213 V: Visitor<'s>,12x
214 {
215 self.deserialize_seq(visitor)12x
216 }12x
217
218 fn deserialize_tuple_struct<V>(3x
219 self,3x
220 _name: &'static str,3x
221 _len: usize,3x
222 visitor: V,3x
223 ) -> Result<V::Value>3x
224 where3x
225 V: Visitor<'s>,3x
226 {
227 self.deserialize_seq(visitor)3x
228 }3x
229
230 fn deserialize_map<V>(self, visitor: V) -> Result<V::Value>192x
231 where192x
232 V: Visitor<'s>,192x
233 {
234 self.deserialize_nested(|de| visitor.visit_map(CommaSeparated::new(de)))192x
235 }192x
236
237 fn deserialize_struct<V>(177x
238 self,177x
239 _name: &'static str,177x
240 _fields: &'static [&'static str],177x
241 visitor: V,177x
242 ) -> Result<V::Value>177x
243 where177x
244 V: Visitor<'s>,177x
245 {
246 self.deserialize_map(visitor)177x
247 }177x
248
249 fn deserialize_enum<V>(86x
250 self,86x
251 _name: &'static str,86x
252 _variants: &'static [&'static str],86x
253 visitor: V,86x
254 ) -> Result<V::Value>86x
255 where86x
256 V: Visitor<'s>,86x
257 {
258 self.deserialize_nested(|de| visitor.visit_enum(Enum::new(de)))86x
259 }86x
260
261 fn deserialize_identifier<V>(self, visitor: V) -> Result<V::Value>446x
262 where446x
263 V: Visitor<'s>,446x
264 {
265 visitor.visit_borrowed_str(self.consume_input())446x
266 }446x
267
268 fn deserialize_ignored_any<V>(self, _visitor: V) -> Result<V::Value>15x
269 where15x
270 V: Visitor<'s>,15x
271 {
272 Err(Error::Ignored(self.key.to_owned()))15x
273 }15x
274}
275
276impl<'s, 'o> Deserializer<'s, 'o> {
277 pub fn from_args(input: &'s str, objects: &'o HashMap<&'s str, &'s str>) -> Self {124x
278 Deserializer {124x
279 input,124x
280 objects: Some(objects),124x
281 top_level: true,124x
282 key: "",124x
283 }124x
284 }124x
285
286 pub fn from_arg(input: &'s str) -> Self {207x
287 Deserializer {207x
288 input,207x
289 objects: None,207x
290 top_level: true,207x
291 key: "",207x
292 }207x
293 }207x
294
295 fn end(&self) -> Result<()> {522x
296 if self.input.is_empty() {522x
297 Ok(())504x
298 } else {
299 Err(Error::Trailing(self.input.to_owned()))18x
300 }
301 }522x
302
303 fn deserialize_nested<F, V>(&mut self, f: F) -> Result<V>340x
304 where340x
305 F: FnOnce(&mut Self) -> Result<V>,340x
306 {
307 let mut sub_de;
308 let de = if !self.top_level {340x
309 let id = self.consume_input();91x
310 let sub_input = self.deref_id(id)?;91x
311 sub_de = Deserializer {85x
312 input: sub_input,85x
313 ..*self85x
314 };85x
315 &mut sub_de85x
316 } else {
317 self.top_level = false;249x
318 self249x
319 };
320 let val = f(de)?;334x
321 de.end()?;282x
322 Ok(val)267x
323 }340x
324
325 fn consume_input_until(&mut self, end: char) -> Option<&'s str> {1586x
326 let len = self.input.find(end)?;1586x
327 let s = &self.input[..len];809x
328 self.input = &self.input[len + end.len_utf8()..];809x
329 Some(s)809x
330 }1586x
331
332 fn consume_all(&mut self) -> &'s str {792x
333 let s = self.input;792x
334 self.input = "";792x
335 s792x
336 }792x
337
338 fn consume_input(&mut self) -> &'s str {1181x
339 match self.consume_input_until(',') {1181x
340 Some(s) => s,410x
341 None => self.consume_all(),771x
342 }
343 }1181x
344
345 fn deref_id(&self, id: &'s str) -> Result<&'s str> {316x
346 if id.starts_with("id_") {316x
347 if let Some(s) = self.objects.and_then(|objects| objects.get(id)) {98x
348 Ok(s)89x
349 } else {
350 Err(Error::IdNotFound(id.to_owned()))9x
351 }
352 } else {
353 Ok(id)218x
354 }
355 }316x
356
357 fn parse_unsigned<T>(&mut self) -> Result<T>295x
358 where295x
359 T: TryFrom<u64>,295x
360 {
361 let s = self.consume_input();295x
362 let (num, shift) = if let Some((num, "")) = s.split_once(['k', 'K']) {295x
363 (num, 10)6x
364 } else if let Some((num, "")) = s.split_once(['m', 'M']) {289x
365 (num, 20)3x
366 } else if let Some((num, "")) = s.split_once(['g', 'G']) {286x
367 (num, 30)32x
368 } else if let Some((num, "")) = s.split_once(['t', 'T']) {254x
369 (num, 40)3x
370 } else {
371 (s, 0)251x
372 };
373 let n = if let Some(num_h) = num.strip_prefix("0x") {295x
374 u64::from_str_radix(num_h, 16)78x
375 } else if let Some(num_o) = num.strip_prefix("0o") {217x
376 u64::from_str_radix(num_o, 8)3x
377 } else if let Some(num_b) = num.strip_prefix("0b") {214x
378 u64::from_str_radix(num_b, 2)3x
379 } else {
380 num.parse::<u64>()211x
381 }
382 .map_err(|_| Error::ExpectedInteger)?;295x
383
384 let shifted_n = n.checked_shl(shift).ok_or(Error::Overflow)?;289x
385
386 T::try_from(shifted_n).map_err(|_| Error::Overflow)289x
387 }295x
388
389 fn parse_signed<T>(&mut self) -> Result<T>39x
390 where39x
391 T: TryFrom<i64>,39x
392 {
393 let i = if self.input.starts_with('-') {39x
394 let s = self.consume_input();9x
395 s.parse().map_err(|_| Error::ExpectedInteger)9x
396 } else {
397 let n = self.parse_unsigned::<u64>()?;30x
398 i64::try_from(n).map_err(|_| Error::Overflow)27x
399 }?;
400 T::try_from(i).map_err(|_| Error::Overflow)36x
401 }39x
402}
403
404pub fn from_args<'s, 'o, T>(s: &'s str, objects: &'o HashMap<&'s str, &'s str>) -> Result<T>124x
405where124x
406 T: Deserialize<'s>,124x
407{
408 let mut deserializer = Deserializer::from_args(s, objects);124x
409 let value = T::deserialize(&mut deserializer)?;124x
410 deserializer.end()?;102x
411 Ok(value)102x
412}124x
413
414pub fn from_arg<'s, T>(s: &'s str) -> Result<T>207x
415where207x
416 T: Deserialize<'s>,207x
417{
418 let mut deserializer = Deserializer::from_arg(s);207x
419 let value = T::deserialize(&mut deserializer)?;207x
420 deserializer.end()?;138x
421 Ok(value)135x
422}207x
423
424struct CommaSeparated<'a, 's: 'a, 'o: 'a> {
425 de: &'a mut Deserializer<'s, 'o>,
426}
427
428impl<'a, 's, 'o> CommaSeparated<'a, 's, 'o> {
429 fn new(de: &'a mut Deserializer<'s, 'o>) -> Self {272x
430 CommaSeparated { de }272x
431 }272x
432}
433
434impl<'s> SeqAccess<'s> for CommaSeparated<'_, 's, '_> {
435 type Error = Error;
436
437 fn next_element_seed<T>(&mut self, seed: T) -> Result<Option<T::Value>>219x
438 where219x
439 T: DeserializeSeed<'s>,219x
440 {
441 if self.de.input.is_empty() {219x
442 return Ok(None);38x
443 }181x
444 seed.deserialize(&mut *self.de).map(Some)181x
445 }219x
446}
447
448impl<'s> MapAccess<'s> for CommaSeparated<'_, 's, '_> {
449 type Error = Error;
450
451 fn next_key_seed<K>(&mut self, seed: K) -> Result<Option<K::Value>>564x
452 where564x
453 K: DeserializeSeed<'s>,564x
454 {
455 if self.de.input.is_empty() {564x
456 return Ok(None);159x
457 }405x
458 let Some(key) = self.de.consume_input_until('=') else {405x
459 return Err(Error::ExpectedMapEq);6x
460 };
461 if key.contains(',') {399x
462 return Err(Error::ExpectedMapEq);3x
463 }396x
464 self.de.key = key;396x
465 let mut sub_de = Deserializer {396x
466 input: key,396x
467 key: "",396x
468 ..*self.de396x
469 };396x
470 seed.deserialize(&mut sub_de).map(Some)396x
471 }564x
472
473 fn next_value_seed<V>(&mut self, seed: V) -> Result<V::Value>396x
474 where396x
475 V: DeserializeSeed<'s>,396x
476 {
477 seed.deserialize(&mut *self.de)396x
478 }396x
479}
480
481struct Enum<'a, 's: 'a, 'o: 'a> {
482 de: &'a mut Deserializer<'s, 'o>,
483}
484
485impl<'a, 's, 'o> Enum<'a, 's, 'o> {
486 fn new(de: &'a mut Deserializer<'s, 'o>) -> Self {80x
487 Enum { de }80x
488 }80x
489}
490
491impl<'s> EnumAccess<'s> for Enum<'_, 's, '_> {
492 type Error = Error;
493 type Variant = Self;
494
495 fn variant_seed<V>(self, seed: V) -> Result<(V::Value, Self::Variant)>80x
496 where80x
497 V: DeserializeSeed<'s>,80x
498 {
499 let val = seed.deserialize(&mut *self.de)?;80x
500 Ok((val, self))70x
501 }80x
502}
503
504impl<'s> VariantAccess<'s> for Enum<'_, 's, '_> {
505 type Error = Error;
506
507 fn unit_variant(self) -> Result<()> {23x
508 Ok(())23x
509 }23x
510
511 fn newtype_variant_seed<T>(self, seed: T) -> Result<T::Value>29x
512 where29x
513 T: DeserializeSeed<'s>,29x
514 {
515 self.de.top_level = true;29x
516 seed.deserialize(self.de)29x
517 }29x
518
519 fn tuple_variant<V>(self, _len: usize, visitor: V) -> Result<V::Value>9x
520 where9x
521 V: Visitor<'s>,9x
522 {
523 visitor.visit_seq(CommaSeparated::new(self.de))9x
524 }9x
525
526 fn struct_variant<V>(self, _fields: &'static [&'static str], visitor: V) -> Result<V::Value>9x
527 where9x
528 V: Visitor<'s>,9x
529 {
530 visitor.visit_map(CommaSeparated::new(self.de))9x
531 }9x
532}
533
534#[cfg(test)]
535mod test {
536 use std::collections::HashMap;
537 use std::marker::PhantomData;
538
539 use assert_matches::assert_matches;
540 use serde::Deserialize;
541 use serde_bytes::{ByteArray, ByteBuf};
542
543 use crate::{Error, from_arg, from_args};
544
545 #[test]
546 fn test_option() {3x
547 assert_matches!(from_arg::<Option<u32>>(""), Err(Error::ExpectedInteger));3x
548 assert_eq!(from_arg::<Option<u32>>("12").unwrap(), Some(12));3x
549
550 assert_eq!(from_arg::<Option<&'static str>>("").unwrap(), Some(""));3x
551 assert_eq!(3x
552 from_args::<Option<&'static str>>("id_1", &HashMap::from([("id_1", "")])).unwrap(),3x
553 None
554 );
555 assert_eq!(from_arg::<Option<&'static str>>("12").unwrap(), Some("12"));3x
556 assert_matches!(3x
557 from_arg::<Option<&'static str>>("id_1"),3x
558 Err(Error::IdNotFound(id)) if id == "id_1"3x
559 );
560 assert_eq!(3x
561 from_args::<Option<&'static str>>("id_1", &HashMap::from([("id_1", "id_2")])).unwrap(),3x
562 Some("id_2")
563 );
564
565 let map_none = HashMap::from([("id_none", "")]);3x
566 assert_eq!(from_arg::<Vec<Option<u32>>>("").unwrap(), vec![]);3x
567 assert_eq!(3x
568 from_args::<Vec<Option<u32>>>("id_none,", &map_none).unwrap(),3x
569 vec![None]3x
570 );
571 assert_eq!(from_arg::<Vec<Option<u32>>>("1,").unwrap(), vec![Some(1)]);3x
572 assert_eq!(3x
573 from_arg::<Vec<Option<u32>>>("1,2,").unwrap(),3x
574 vec![Some(1), Some(2)]3x
575 );
576 assert_eq!(3x
577 from_args::<Vec<Option<u32>>>("1,2,id_none,", &map_none).unwrap(),3x
578 vec![Some(1), Some(2), None]3x
579 );
580 assert_eq!(3x
581 from_args::<Vec<Option<u32>>>("id_none,2", &map_none).unwrap(),3x
582 vec![None, Some(2)]3x
583 );
584 }3x
585
586 #[test]
587 fn test_unit() {3x
588 assert!(from_arg::<()>("").is_ok());3x
589 assert_matches!(from_arg::<()>("unit"), Err(Error::ExpectedUnit));3x
590
591 assert!(from_arg::<PhantomData<u8>>("").is_ok());3x
592 assert_matches!(from_arg::<PhantomData<u8>>("12"), Err(Error::ExpectedUnit));3x
593
594 #[derive(Debug, Deserialize, PartialEq, Eq)]
595 struct Param {
596 p: PhantomData<u8>,
597 }
598 assert_eq!(from_arg::<Param>("p=").unwrap(), Param { p: PhantomData });3x
599 assert_matches!(from_arg::<Param>("p=1,"), Err(Error::ExpectedUnit));3x
600 }3x
601
602 #[test]
603 fn test_numbers() {3x
604 assert_eq!(from_arg::<i8>("0").unwrap(), 0);3x
605 assert_eq!(from_arg::<i8>("1").unwrap(), 1);3x
606 assert_eq!(from_arg::<i8>("127").unwrap(), 127);3x
607 assert_matches!(from_arg::<i8>("128"), Err(Error::Overflow));3x
608 assert_eq!(from_arg::<i8>("-1").unwrap(), -1);3x
609 assert_eq!(from_arg::<i8>("-128").unwrap(), -128);3x
610 assert_matches!(from_arg::<i8>("-129"), Err(Error::Overflow));3x
611
612 assert_eq!(from_arg::<i16>("1k").unwrap(), 1 << 10);3x
613
614 assert_eq!(from_arg::<i32>("1g").unwrap(), 1 << 30);3x
615 assert_matches!(from_arg::<i32>("2g"), Err(Error::Overflow));3x
616 assert_matches!(from_arg::<i32>("0xffffffff"), Err(Error::Overflow));3x
617
618 assert_eq!(from_arg::<i64>("0xffffffff").unwrap(), 0xffffffff);3x
619
620 assert_matches!(from_arg::<i64>("gg"), Err(Error::ExpectedInteger));3x
621
622 assert_matches!(from_arg::<f32>("0.125").unwrap(), 0.125);3x
623
624 assert_matches!(from_arg::<f64>("-0.5").unwrap(), -0.5);3x
625 }3x
626
627 #[test]
628 fn test_char() {3x
629 assert_eq!(from_arg::<char>("=").unwrap(), '=');3x
630 assert_eq!(from_arg::<char>("a").unwrap(), 'a');3x
631 assert_matches!(from_arg::<char>("an"), Err(Error::Message(_)));3x
632
633 assert_eq!(3x
634 from_args::<HashMap<char, char>>(3x
635 "id_1=a,b=id_2,id_2=id_1",3x
636 &HashMap::from([("id_1", ","), ("id_2", "="),])3x
637 )
638 .unwrap(),3x
639 HashMap::from([(',', 'a'), ('b', '='), ('=', ',')])3x
640 );
641 }3x
642
643 #[test]
644 fn test_bytes() {3x
645 assert!(from_arg::<ByteArray<6>>("0xea,0xd7,0xa8,0xe8,0xc6,0x2f").is_ok());3x
646 assert_matches!(3x
647 from_arg::<ByteArray<5>>("0xea,0xd7,0xa8,0xe8,0xc6,0x2f"),3x
648 Err(Error::Trailing(t)) if t == "0x2f"3x
649 );
650 assert_eq!(3x
651 from_arg::<ByteBuf>("0xea,0xd7,0xa8,0xe8,0xc6,0x2f").unwrap(),3x
652 vec![0xea, 0xd7, 0xa8, 0xe8, 0xc6, 0x2f]3x
653 );
654
655 #[derive(Debug, Deserialize, Eq, PartialEq)]
656 struct MacAddr {
657 addr: ByteArray<6>,
658 }
659 assert_eq!(3x
660 from_args::<MacAddr>(3x
661 "addr=id_addr",3x
662 &HashMap::from([("id_addr", "0xea,0xd7,0xa8,0xe8,0xc6,0x2f")])3x
663 )
664 .unwrap(),3x
665 MacAddr {3x
666 addr: ByteArray::new([0xea, 0xd7, 0xa8, 0xe8, 0xc6, 0x2f])3x
667 }3x
668 )
669 }3x
670
671 #[test]
672 fn test_string() {3x
673 assert_eq!(3x
674 from_arg::<String>("test,s=1,c").unwrap(),3x
675 "test,s=1,c".to_owned()3x
676 );
677 assert_eq!(3x
678 from_args::<HashMap<String, String>>(3x
679 "cmd=id_1",3x
680 &HashMap::from([("id_1", "console=ttyS0")])3x
681 )
682 .unwrap(),3x
683 HashMap::from([("cmd".to_owned(), "console=ttyS0".to_owned())])3x
684 )
685 }3x
686
687 #[test]
688 fn test_seq() {3x
689 assert_eq!(from_arg::<Vec<u32>>("").unwrap(), vec![]);3x
690
691 assert_eq!(from_arg::<Vec<u32>>("1").unwrap(), vec![1]);3x
692
693 assert_eq!(from_arg::<Vec<u32>>("1,2,3,4").unwrap(), vec![1, 2, 3, 4]);3x
694
695 assert_eq!(from_arg::<(u16, bool)>("12,true").unwrap(), (12, true));3x
696 assert_matches!(3x
697 from_arg::<(u16, bool)>("12,true,false"),3x
698 Err(Error::Trailing(t)) if t == "false"3x
699 );
700
701 #[derive(Debug, Deserialize, PartialEq, Eq)]
702 struct TestStruct {
703 a: (u16, bool),
704 }
705 assert_eq!(3x
706 from_args::<TestStruct>("a=id_a", &HashMap::from([("id_a", "12,true")])).unwrap(),3x
707 TestStruct { a: (12, true) }
708 );
709 assert_matches!(3x
710 from_args::<TestStruct>("a=id_a", &HashMap::from([("id_a", "12,true,true")])),3x
711 Err(Error::Trailing(t)) if t == "true"3x
712 );
713
714 #[derive(Debug, Deserialize, PartialEq, Eq)]
715 struct Node {
716 #[serde(default)]
717 name: String,
718 #[serde(default)]
719 start: u64,
720 size: u64,
721 }
722 #[derive(Debug, Deserialize, PartialEq, Eq)]
723 struct Numa {
724 nodes: Vec<Node>,
725 }
726
727 assert_eq!(3x
728 from_args::<Numa>(3x
729 "nodes=id_nodes",3x
730 &HashMap::from([3x
731 ("id_nodes", "id_node1,id_node2"),3x
732 ("id_node1", "name=a,start=0,size=2g"),3x
733 ("id_node2", "name=b,start=4g,size=2g"),3x
734 ])3x
735 )
736 .unwrap(),3x
737 Numa {3x
738 nodes: vec![3x
739 Node {3x
740 name: "a".to_owned(),3x
741 start: 0,3x
742 size: 2 << 303x
743 },3x
744 Node {3x
745 name: "b".to_owned(),3x
746 start: 4 << 30,3x
747 size: 2 << 303x
748 }3x
749 ]3x
750 }3x
751 );
752
753 assert_eq!(3x
754 from_arg::<Numa>("nodes=size=2g,").unwrap(),3x
755 Numa {3x
756 nodes: vec![Node {3x
757 name: "".to_owned(),3x
758 start: 0,3x
759 size: 2 << 303x
760 }]3x
761 }3x
762 );
763
764 #[derive(Debug, Deserialize, PartialEq, Eq)]
765 struct Info(bool, u32);
766
767 assert_eq!(from_arg::<Info>("true,32").unwrap(), Info(true, 32));3x
768 }3x
769
770 #[test]
771 fn test_map() {3x
772 #[derive(Debug, Deserialize, PartialEq, Eq, Hash)]
773 struct MapKey {
774 name: String,
775 id: u32,
776 }
777 #[derive(Debug, Deserialize, PartialEq, Eq)]
778 struct MapVal {
779 addr: String,
780 info: HashMap<String, String>,
781 }
782
783 assert_matches!(3x
784 from_arg::<MapKey>("name=a,id=1,addr=b"),3x
785 Err(Error::Ignored(k)) if k == "addr"3x
786 );
787 assert_matches!(3x
788 from_arg::<MapKey>("name=a,addr=b,id=1"),3x
789 Err(Error::Ignored(k)) if k == "addr"3x
790 );
791 assert_matches!(from_arg::<MapKey>("name=a,ids=b"), Err(Error::Ignored(k)) if k == "ids");3x
792 assert_matches!(from_arg::<MapKey>("name=a,ids=b,id=1"), Err(Error::Ignored(k)) if k == "ids");3x
793
794 assert_eq!(3x
795 from_args::<HashMap<MapKey, MapVal>>(3x
796 "id_key1=id_val1,id_key2=id_val2",3x
797 &HashMap::from([3x
798 ("id_key1", "name=gic,id=1"),3x
799 ("id_key2", "name=pci,id=2"),3x
800 ("id_val1", "addr=0xff,info=id_info1"),3x
801 ("id_info1", "compatible=id_gic,msi-controller=,#msi-cells=1"),3x
802 ("id_gic", "arm,gic-v3-its"),3x
803 ("id_val2", "addr=0xcc,info=compatible=pci-host-ecam-generic"),3x
804 ])3x
805 )
806 .unwrap(),3x
807 HashMap::from([3x
808 (3x
809 MapKey {3x
810 name: "gic".to_owned(),3x
811 id: 13x
812 },3x
813 MapVal {3x
814 addr: "0xff".to_owned(),3x
815 info: HashMap::from([3x
816 ("compatible".to_owned(), "arm,gic-v3-its".to_owned()),3x
817 ("msi-controller".to_owned(), "".to_owned()),3x
818 ("#msi-cells".to_owned(), "1".to_owned())3x
819 ])3x
820 }3x
821 ),3x
822 (3x
823 MapKey {3x
824 name: "pci".to_owned(),3x
825 id: 23x
826 },3x
827 MapVal {3x
828 addr: "0xcc".to_owned(),3x
829 info: HashMap::from([(3x
830 "compatible".to_owned(),3x
831 "pci-host-ecam-generic".to_owned()3x
832 )])3x
833 }3x
834 )3x
835 ])3x
836 );
837 }3x
838
839 #[test]
840 fn test_nested_struct() {3x
841 #[derive(Debug, Deserialize, PartialEq, Eq)]
842 struct Param {
843 byte: u8,
844 word: u16,
845 dw: u32,
846 long: u64,
847 enable_1: bool,
848 enable_2: bool,
849 enable_3: Option<bool>,
850 sub: SubParam,
851 addr: Addr,
852 }
853
854 #[derive(Debug, Deserialize, PartialEq, Eq)]
855 struct SubParam {
856 b: u8,
857 w: u16,
858 enable: Option<bool>,
859 s: String,
860 }
861
862 #[derive(Debug, Deserialize, PartialEq, Eq)]
863 struct Addr(u32);
864
865 assert_eq!(3x
866 from_args::<Param>(3x
867 "byte=0b10,word=0o7k,dw=0x8m,long=10t,enable_1=on,enable_2=off,sub=id_1,addr=1g",3x
868 &[("id_1", "b=1,w=2,s=s1,enable=on")].into()3x
869 )
870 .unwrap(),3x
871 Param {3x
872 byte: 0b10,3x
873 word: 0o7 << 10,3x
874 dw: 0x8 << 20,3x
875 long: 10 << 40,3x
876 enable_1: true,3x
877 enable_2: false,3x
878 enable_3: None,3x
879 sub: SubParam {3x
880 b: 1,3x
881 w: 2,3x
882 enable: Some(true),3x
883 s: "s1".to_owned(),3x
884 },3x
885 addr: Addr(1 << 30)3x
886 }3x
887 );
888 assert_matches!(
889 from_arg::<SubParam>("b=1,w=2,enable,s=s1"),3x
890 Err(Error::ExpectedMapEq)
891 );
892 assert_matches!(
893 from_arg::<SubParam>("b=1,w=2,s=s1,enable"),3x
894 Err(Error::ExpectedMapEq)
895 );
896 }3x
897
898 #[test]
899 fn test_bool() {3x
900 assert_matches!(from_arg::<bool>("on"), Ok(true));3x
901 assert_matches!(from_arg::<bool>("off"), Ok(false));3x
902 assert_matches!(from_arg::<bool>("true"), Ok(true));3x
903 assert_matches!(from_arg::<bool>("false"), Ok(false));3x
904 assert_matches!(from_arg::<bool>("on,off"), Err(Error::Trailing(t)) if t == "off");3x
905
906 #[derive(Debug, Deserialize, PartialEq, Eq)]
907 struct BoolStruct {
908 val: bool,
909 }
910 assert_eq!(3x
911 from_arg::<BoolStruct>("val=on").unwrap(),3x
912 BoolStruct { val: true }
913 );
914 assert_eq!(3x
915 from_arg::<BoolStruct>("val=off").unwrap(),3x
916 BoolStruct { val: false }
917 );
918 assert_eq!(3x
919 from_arg::<BoolStruct>("val=true").unwrap(),3x
920 BoolStruct { val: true }
921 );
922 assert_eq!(3x
923 from_arg::<BoolStruct>("val=false").unwrap(),3x
924 BoolStruct { val: false }
925 );
926 assert_matches!(from_arg::<BoolStruct>("val=a"), Err(Error::ExpectedBool));3x
927
928 assert_matches!(3x
929 from_arg::<BoolStruct>("val=on,key=off"),3x
930 Err(Error::Ignored(k)) if k == "key"3x
931 );
932 }3x
933
934 #[test]
935 fn test_enum() {3x
936 #[derive(Debug, Deserialize, PartialEq, Eq)]
937 struct SubStruct {
938 a: u32,
939 b: bool,
940 }
941
942 #[derive(Debug, Deserialize, PartialEq, Eq)]
943 enum TestEnum {
944 A {
945 #[serde(default)]
946 val: u32,
947 },
948 B(u64),
949 C(u8, u8),
950 D,
951 #[serde(alias = "e")]
952 E,
953 F(SubStruct),
954 G(u16, String, bool),
955 }
956
957 #[derive(Debug, Deserialize, PartialEq, Eq)]
958 struct TestStruct {
959 num: u32,
960 e: TestEnum,
961 }
962
963 assert_eq!(3x
964 from_args::<TestStruct>("num=3,e=id_a", &[("id_a", "A,val=1")].into()).unwrap(),3x
965 TestStruct {
966 num: 3,
967 e: TestEnum::A { val: 1 }
968 }
969 );
970 assert_eq!(3x
971 from_arg::<TestStruct>("num=4,e=A").unwrap(),3x
972 TestStruct {
973 num: 4,
974 e: TestEnum::A { val: 0 },
975 }
976 );
977 assert_eq!(3x
978 from_args::<TestStruct>("num=4,e=id_a", &[("id_a", "A")].into()).unwrap(),3x
979 TestStruct {
980 num: 4,
981 e: TestEnum::A { val: 0 },
982 }
983 );
984 assert_eq!(3x
985 from_arg::<TestStruct>("num=4,e=D").unwrap(),3x
986 TestStruct {
987 num: 4,
988 e: TestEnum::D,
989 }
990 );
991 assert_eq!(3x
992 from_args::<TestStruct>("num=4,e=id_d", &[("id_d", "D")].into()).unwrap(),3x
993 TestStruct {
994 num: 4,
995 e: TestEnum::D,
996 }
997 );
998 assert_eq!(3x
999 from_arg::<TestStruct>("num=3,e=e").unwrap(),3x
1000 TestStruct {
1001 num: 3,
1002 e: TestEnum::E
1003 }
1004 );
1005 assert_matches!(3x
1006 from_arg::<TestStruct>("num=4,e=id_d"),3x
1007 Err(Error::IdNotFound(id)) if id == "id_d"3x
1008 );
1009 assert_matches!(3x
1010 from_args::<TestStruct>("num=4,e=id_d", &[].into()),3x
1011 Err(Error::IdNotFound(id)) if id == "id_d"3x
1012 );
1013 assert_eq!(from_arg::<TestEnum>("B,1").unwrap(), TestEnum::B(1));3x
1014 assert_eq!(from_arg::<TestEnum>("D").unwrap(), TestEnum::D);3x
1015 assert_eq!(3x
1016 from_arg::<TestEnum>("F,a=1,b=on").unwrap(),3x
1017 TestEnum::F(SubStruct { a: 1, b: true })
1018 );
1019 assert_eq!(3x
1020 from_arg::<TestEnum>("G,1,a,true").unwrap(),3x
1021 TestEnum::G(1, "a".to_owned(), true)3x
1022 );
1023 assert_matches!(3x
1024 from_arg::<TestEnum>("G,1,a,true,false"),3x
1025 Err(Error::Trailing(t)) if t == "false"3x
1026 );
1027 assert_matches!(3x
1028 from_args::<TestStruct>(3x
1029 "num=4,e=id_e",3x
1030 &HashMap::from([("id_e", "G,1,a,true,false")])3x
1031 ),
1032 Err(Error::Trailing(t)) if t == "false"3x
1033 );
1034 }3x
1035}
1036