1use std::ops::Deref;
7
8use crate::{Action, bytes};
9
10use anyhow::Result;
11#[cfg(feature = "serde")]
12use serde::{Deserialize, Serialize};
13
14#[derive(Clone, Debug, Default, PartialEq, Eq)]
20#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
21pub struct Cmdline<'a>(bytes::Cmdline<'a>);
22
23pub type CmdlineOwned = Cmdline<'static>;
25
26impl<'a, T: AsRef<str> + ?Sized> From<&'a T> for Cmdline<'a> {
27 fn from(input: &'a T) -> Self {
31 Self(bytes::Cmdline::from(input.as_ref().as_bytes()))
32 }
33}
34
35impl From<String> for CmdlineOwned {
36 fn from(input: String) -> Self {
40 Self(bytes::Cmdline::from(input.into_bytes()))
41 }
42}
43
44#[derive(Debug)]
48pub struct CmdlineIter<'a>(bytes::CmdlineIter<'a>);
49
50impl<'a> Iterator for CmdlineIter<'a> {
51 type Item = Parameter<'a>;
52
53 fn next(&mut self) -> Option<Self::Item> {
54 self.0.next().map(Parameter::from_bytes)
55 }
56}
57
58#[derive(Debug)]
62pub struct CmdlineIterStr<'a>(bytes::CmdlineIterBytes<'a>);
63
64impl<'a> Iterator for CmdlineIterStr<'a> {
65 type Item = &'a str;
66
67 fn next(&mut self) -> Option<Self::Item> {
68 let bytes = self.0.next()?;
70
71 Some(str::from_utf8(bytes).expect("Parameter bytes come from valid UTF-8 cmdline"))
74 }
75}
76
77impl<'a> Cmdline<'a> {
78 pub fn new() -> CmdlineOwned {
82 Cmdline::default()
83 }
84
85 pub fn from_proc() -> Result<Self> {
92 let cmdline = std::fs::read("/proc/cmdline")?;
93
94 str::from_utf8(&cmdline)?;
99
100 Ok(Self(bytes::Cmdline::from(cmdline)))
101 }
102
103 pub fn iter(&'a self) -> CmdlineIter<'a> {
109 CmdlineIter(self.0.iter())
110 }
111
112 pub fn iter_str(&self) -> CmdlineIterStr<'_> {
117 CmdlineIterStr(self.0.iter_bytes())
118 }
119
120 pub fn find<T: AsRef<str> + ?Sized>(&'a self, key: &T) -> Option<Parameter<'a>> {
125 let key = ParameterKey::from(key.as_ref());
126 self.iter().find(|p| p.key() == key)
127 }
128
129 pub fn find_all_starting_with<T: AsRef<str> + ?Sized>(
133 &'a self,
134 prefix: &'a T,
135 ) -> impl Iterator<Item = Parameter<'a>> + 'a {
136 self.iter()
137 .filter(move |p| p.key().starts_with(prefix.as_ref()))
138 }
139
140 pub fn value_of<T: AsRef<str> + ?Sized>(&'a self, key: &T) -> Option<&'a str> {
145 self.0.value_of(key.as_ref().as_bytes()).map(|v| {
146 str::from_utf8(v).expect("We only construct the underlying bytes from valid UTF-8")
149 })
150 }
151
152 pub fn require_value_of<T: AsRef<str> + ?Sized>(&'a self, key: &T) -> Result<&'a str> {
156 let key = key.as_ref();
157 self.value_of(key)
158 .ok_or_else(|| anyhow::anyhow!("Failed to find kernel argument '{key}'"))
159 }
160
161 pub fn add(&mut self, param: &Parameter) -> Action {
174 self.0.add(¶m.0)
175 }
176
177 pub fn add_or_modify(&mut self, param: &Parameter) -> Action {
190 self.0.add_or_modify(¶m.0)
191 }
192
193 pub fn remove(&mut self, key: &ParameterKey) -> bool {
197 self.0.remove(&key.0)
198 }
199
200 pub fn remove_exact(&mut self, param: &Parameter) -> bool {
205 self.0.remove_exact(¶m.0)
206 }
207
208 #[cfg(test)]
209 pub(crate) fn is_owned(&self) -> bool {
210 self.0.is_owned()
211 }
212
213 #[cfg(test)]
214 pub(crate) fn is_borrowed(&self) -> bool {
215 self.0.is_borrowed()
216 }
217}
218
219impl Deref for Cmdline<'_> {
220 type Target = str;
221
222 fn deref(&self) -> &Self::Target {
223 str::from_utf8(&self.0).expect("We only construct the underlying bytes from valid UTF-8")
226 }
227}
228
229impl<'a, T> AsRef<T> for Cmdline<'a>
230where
231 T: ?Sized,
232 <Cmdline<'a> as Deref>::Target: AsRef<T>,
233{
234 fn as_ref(&self) -> &T {
235 self.deref().as_ref()
236 }
237}
238
239impl<'a> std::fmt::Display for Cmdline<'a> {
240 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
241 f.write_str(self)
242 }
243}
244
245impl<'a> IntoIterator for &'a Cmdline<'a> {
246 type Item = Parameter<'a>;
247 type IntoIter = CmdlineIter<'a>;
248
249 fn into_iter(self) -> Self::IntoIter {
250 self.iter()
251 }
252}
253
254impl<'a, 'other> Extend<Parameter<'other>> for Cmdline<'a> {
255 fn extend<T: IntoIterator<Item = Parameter<'other>>>(&mut self, iter: T) {
263 for param in iter {
264 self.add(¶m);
265 }
266 }
267}
268
269#[derive(Clone, Debug, Eq)]
273pub struct ParameterKey<'a>(bytes::ParameterKey<'a>);
274
275impl Deref for ParameterKey<'_> {
276 type Target = str;
277
278 fn deref(&self) -> &Self::Target {
279 str::from_utf8(&self.0).expect("We only construct the underlying bytes from valid UTF-8")
282 }
283}
284
285impl<'a, T> AsRef<T> for ParameterKey<'a>
286where
287 T: ?Sized,
288 <ParameterKey<'a> as Deref>::Target: AsRef<T>,
289{
290 fn as_ref(&self) -> &T {
291 self.deref().as_ref()
292 }
293}
294
295impl<'a> ParameterKey<'a> {
296 fn from_bytes(input: bytes::ParameterKey<'a>) -> Self {
301 Self(input)
302 }
303}
304
305impl<'a, T: AsRef<str> + ?Sized> From<&'a T> for ParameterKey<'a> {
306 fn from(input: &'a T) -> Self {
307 Self(bytes::ParameterKey(input.as_ref().as_bytes()))
308 }
309}
310
311impl<'a> std::fmt::Display for ParameterKey<'a> {
312 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
313 f.write_str(self)
314 }
315}
316
317impl PartialEq for ParameterKey<'_> {
318 fn eq(&self, other: &Self) -> bool {
323 self.0 == other.0
324 }
325}
326
327#[derive(Clone, Debug, Eq)]
329pub struct Parameter<'a>(bytes::Parameter<'a>);
330
331impl<'a> Parameter<'a> {
332 pub fn parse<T: AsRef<str> + ?Sized>(input: &'a T) -> Option<Self> {
339 bytes::Parameter::parse(input.as_ref().as_bytes()).map(Self)
340 }
341
342 fn from_bytes(bytes: bytes::Parameter<'a>) -> Self {
347 Self(bytes)
348 }
349
350 pub fn key(&'a self) -> ParameterKey<'a> {
352 ParameterKey::from_bytes(self.0.key())
353 }
354
355 pub fn value(&'a self) -> Option<&'a str> {
357 self.0.value().map(|p| {
358 str::from_utf8(p).expect("We only construct the underlying bytes from valid UTF-8")
361 })
362 }
363}
364
365impl<'a> TryFrom<bytes::Parameter<'a>> for Parameter<'a> {
366 type Error = anyhow::Error;
367
368 fn try_from(bytes: bytes::Parameter<'a>) -> Result<Self, Self::Error> {
369 if str::from_utf8(bytes.key().deref()).is_err() {
370 anyhow::bail!("Parameter key is not valid UTF-8");
371 }
372
373 if let Some(value) = bytes.value() {
374 if str::from_utf8(value).is_err() {
375 anyhow::bail!("Parameter value is not valid UTF-8");
376 }
377 }
378
379 Ok(Self(bytes))
380 }
381}
382
383impl<'a> std::fmt::Display for Parameter<'a> {
384 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
385 f.write_str(self)
386 }
387}
388
389impl Deref for Parameter<'_> {
390 type Target = str;
391
392 fn deref(&self) -> &Self::Target {
393 str::from_utf8(&self.0).expect("We only construct the underlying bytes from valid UTF-8")
396 }
397}
398
399impl<'a, T> AsRef<T> for Parameter<'a>
400where
401 T: ?Sized,
402 <Parameter<'a> as Deref>::Target: AsRef<T>,
403{
404 fn as_ref(&self) -> &T {
405 self.deref().as_ref()
406 }
407}
408
409impl<'a> PartialEq for Parameter<'a> {
410 fn eq(&self, other: &Self) -> bool {
411 self.0 == other.0
412 }
413}
414
415#[cfg(test)]
416mod tests {
417 use super::*;
418
419 fn param(s: &str) -> Parameter<'_> {
421 Parameter::parse(s).unwrap()
422 }
423
424 #[test]
425 fn test_parameter_parse() {
426 let p = Parameter::parse("foo").unwrap();
427 assert_eq!(p.key(), "foo".into());
428 assert_eq!(p.value(), None);
429
430 let p = Parameter::parse("foo=bar baz").unwrap();
432 assert_eq!(p.key(), "foo".into());
433 assert_eq!(p.value(), Some("bar"));
434
435 assert!(Parameter::parse("").is_none());
437 assert!(Parameter::parse(" ").is_none());
438 }
439
440 #[test]
441 fn test_parameter_simple() {
442 let switch = param("foo");
443 assert_eq!(switch.key(), "foo".into());
444 assert_eq!(switch.value(), None);
445
446 let kv = param("bar=baz");
447 assert_eq!(kv.key(), "bar".into());
448 assert_eq!(kv.value(), Some("baz"));
449 }
450
451 #[test]
452 fn test_parameter_quoted() {
453 let p = param("foo=\"quoted value\"");
454 assert_eq!(p.value(), Some("quoted value"));
455
456 let p = param("foo=\"unclosed quotes");
457 assert_eq!(p.value(), Some("unclosed quotes"));
458
459 let p = param("foo=trailing_quotes\"");
460 assert_eq!(p.value(), Some("trailing_quotes"));
461
462 let outside_quoted = param("\"foo=quoted value\"");
463 let value_quoted = param("foo=\"quoted value\"");
464 assert_eq!(outside_quoted, value_quoted);
465 }
466
467 #[test]
468 fn test_parameter_display() {
469 assert_eq!(param("foo").to_string(), "foo");
474
475 assert_eq!(param("\"foo\"").to_string(), "\"foo\"");
477 }
478
479 #[test]
480 fn test_parameter_extra_whitespace() {
481 let p = param(" foo=bar ");
482 assert_eq!(p.key(), "foo".into());
483 assert_eq!(p.value(), Some("bar"));
484 }
485
486 #[test]
487 fn test_parameter_internal_key_whitespace() {
488 let p = Parameter::parse("foo bar=baz").unwrap();
490 assert_eq!(p.key(), "foo".into());
491 assert_eq!(p.value(), None);
492 }
493
494 #[test]
495 fn test_parameter_pathological() {
496 let p = param("\"foo\"=bar");
502 assert_eq!(p.key(), ParameterKey::from("foo\""));
503 assert_eq!(p.value(), Some("bar"));
504 assert_ne!(p, param("foo=bar"));
506
507 let p = param("\"foo=\"bar");
510 assert_eq!(p.key(), ParameterKey::from("foo"));
511 assert_eq!(p.value(), Some("bar"));
512 assert_eq!(p, param("foo=bar"));
514
515 let p = param("foo=\"internal\"quotes\"are\"ok\"");
517 assert_eq!(p.value(), Some("internal\"quotes\"are\"ok"));
518 }
519
520 #[test]
521 fn test_parameter_equality() {
522 let foo = param("foo");
524 let bar = param("foobar");
525 assert_ne!(foo, bar);
526 assert_ne!(bar, foo);
527
528 let dashes = param("a-delimited-param");
530 let underscores = param("a_delimited_param");
531 assert_eq!(dashes, underscores);
532
533 let dashes = param("a-delimited-param=same_values");
535 let underscores = param("a_delimited_param=same_values");
536 assert_eq!(dashes, underscores);
537
538 let dashes = param("a-delimited-param=different_values");
540 let underscores = param("a_delimited_param=DiFfErEnT_valUEZ");
541 assert_ne!(dashes, underscores);
542
543 let switch = param("same_key");
545 let keyvalue = param("same_key=but_with_a_value");
546 assert_ne!(switch, keyvalue);
547 }
548
549 #[test]
550 fn test_parameter_tryfrom() {
551 let p = bytes::Parameter::parse(b"foo").unwrap();
553 let utf = Parameter::try_from(p).unwrap();
554 assert_eq!(utf.key(), "foo".into());
555 assert_eq!(utf.value(), None);
556
557 let p = bytes::Parameter::parse(b"foo=bar").unwrap();
559 let utf = Parameter::try_from(p).unwrap();
560 assert_eq!(utf.key(), "foo".into());
561 assert_eq!(utf.value(), Some("bar".into()));
562
563 let p = bytes::Parameter::parse(b"f\xffoo").unwrap();
565 let e = Parameter::try_from(p);
566 assert_eq!(
567 e.unwrap_err().to_string(),
568 "Parameter key is not valid UTF-8"
569 );
570
571 let p = bytes::Parameter::parse(b"foo=b\xffar").unwrap();
573 let e = Parameter::try_from(p);
574 assert_eq!(
575 e.unwrap_err().to_string(),
576 "Parameter value is not valid UTF-8"
577 );
578 }
579
580 #[test]
581 fn test_kargs_simple() {
582 let kargs = Cmdline::from("foo=bar,bar2 baz=fuz wiz");
585 assert!(kargs.is_borrowed());
586 let mut iter = kargs.iter();
587
588 assert_eq!(iter.next(), Some(param("foo=bar,bar2")));
589 assert_eq!(iter.next(), Some(param("baz=fuz")));
590 assert_eq!(iter.next(), Some(param("wiz")));
591 assert_eq!(iter.next(), None);
592
593 assert_eq!(kargs.find("foo").unwrap().value().unwrap(), "bar,bar2");
595 assert!(kargs.find("nothing").is_none());
596 }
597
598 #[test]
599 fn test_cmdline_default() {
600 let kargs: Cmdline = Default::default();
601 assert_eq!(kargs.iter().next(), None);
602 }
603
604 #[test]
605 fn test_cmdline_new() {
606 let kargs = Cmdline::new();
607 assert_eq!(kargs.iter().next(), None);
608 assert!(kargs.is_owned());
609
610 let _static_kargs: CmdlineOwned = Cmdline::new();
612 }
613
614 #[test]
615 fn test_kargs_simple_from_string() {
616 let kargs = Cmdline::from("foo=bar,bar2 baz=fuz wiz".to_string());
617 assert!(kargs.is_owned());
618 let mut iter = kargs.iter();
619
620 assert_eq!(iter.next(), Some(param("foo=bar,bar2")));
621 assert_eq!(iter.next(), Some(param("baz=fuz")));
622 assert_eq!(iter.next(), Some(param("wiz")));
623 assert_eq!(iter.next(), None);
624
625 assert_eq!(kargs.find("foo").unwrap().value().unwrap(), "bar,bar2");
627 assert!(kargs.find("nothing").is_none());
628 }
629
630 #[test]
631 fn test_kargs_from_proc() {
632 let kargs = Cmdline::from_proc().unwrap();
633
634 assert!(kargs.iter().count() > 0);
638 }
639
640 #[test]
641 fn test_kargs_find_dash_hyphen() {
642 let kargs = Cmdline::from("a-b=1 a_b=2");
643 let p = kargs.find("a_b").unwrap();
645 assert_eq!(p.key(), "a-b".into());
646 assert_eq!(p.value().unwrap(), "1");
647 let p = kargs.find("a-b").unwrap();
648 assert_eq!(p.key(), "a-b".into());
649 assert_eq!(p.value().unwrap(), "1");
650
651 let kargs = Cmdline::from("a_b=2 a-b=1");
652 let p = kargs.find("a_b").unwrap();
654 assert_eq!(p.key(), "a_b".into());
655 assert_eq!(p.value().unwrap(), "2");
656 let p = kargs.find("a-b").unwrap();
657 assert_eq!(p.key(), "a_b".into());
658 assert_eq!(p.value().unwrap(), "2");
659 }
660
661 #[test]
662 fn test_kargs_extra_whitespace() {
663 let kargs = Cmdline::from(" foo=bar baz=fuz wiz ");
664 let mut iter = kargs.iter();
665
666 assert_eq!(iter.next(), Some(param("foo=bar")));
667 assert_eq!(iter.next(), Some(param("baz=fuz")));
668 assert_eq!(iter.next(), Some(param("wiz")));
669 assert_eq!(iter.next(), None);
670 }
671
672 #[test]
673 fn test_value_of() {
674 let kargs = Cmdline::from("foo=bar baz=qux switch");
675
676 assert_eq!(kargs.value_of("foo"), Some("bar"));
678 assert_eq!(kargs.value_of("baz"), Some("qux"));
679
680 assert_eq!(kargs.value_of("switch"), None);
682
683 assert_eq!(kargs.value_of("missing"), None);
685
686 let kargs = Cmdline::from("dash-key=value1 under_key=value2");
688 assert_eq!(kargs.value_of("dash_key"), Some("value1"));
689 assert_eq!(kargs.value_of("under-key"), Some("value2"));
690 }
691
692 #[test]
693 fn test_require_value_of() {
694 let kargs = Cmdline::from("foo=bar baz=qux switch");
695
696 assert_eq!(kargs.require_value_of("foo").unwrap(), "bar");
698 assert_eq!(kargs.require_value_of("baz").unwrap(), "qux");
699
700 let err = kargs.require_value_of("switch").unwrap_err();
702 assert!(
703 err.to_string()
704 .contains("Failed to find kernel argument 'switch'")
705 );
706
707 let err = kargs.require_value_of("missing").unwrap_err();
709 assert!(
710 err.to_string()
711 .contains("Failed to find kernel argument 'missing'")
712 );
713
714 let kargs = Cmdline::from("dash-key=value1 under_key=value2");
716 assert_eq!(kargs.require_value_of("dash_key").unwrap(), "value1");
717 assert_eq!(kargs.require_value_of("under-key").unwrap(), "value2");
718 }
719
720 #[test]
721 fn test_find_str() {
722 let kargs = Cmdline::from("foo=bar baz=qux switch rd.break");
723 let p = kargs.find("foo").unwrap();
724 assert_eq!(p, param("foo=bar"));
725 let p = kargs.find("rd.break").unwrap();
726 assert_eq!(p, param("rd.break"));
727 assert!(kargs.find("missing").is_none());
728 }
729
730 #[test]
731 fn test_find_all_str() {
732 let kargs = Cmdline::from("foo=bar rd.foo=a rd.bar=b rd.baz rd.qux=c notrd.val=d");
733 let mut rd_args: Vec<_> = kargs.find_all_starting_with("rd.").collect();
734 rd_args.sort_by(|a, b| a.key().cmp(&b.key()));
735 assert_eq!(rd_args.len(), 4);
736 assert_eq!(rd_args[0], param("rd.bar=b"));
737 assert_eq!(rd_args[1], param("rd.baz"));
738 assert_eq!(rd_args[2], param("rd.foo=a"));
739 assert_eq!(rd_args[3], param("rd.qux=c"));
740 }
741
742 #[test]
743 fn test_param_key_eq() {
744 let k1 = ParameterKey::from("a-b");
745 let k2 = ParameterKey::from("a_b");
746 assert_eq!(k1, k2);
747 let k1 = ParameterKey::from("a-b");
748 let k2 = ParameterKey::from("a-c");
749 assert_ne!(k1, k2);
750 }
751
752 #[test]
753 fn test_add() {
754 let mut kargs = Cmdline::from("console=tty0 console=ttyS1");
755
756 assert!(matches!(kargs.add(¶m("console=ttyS2")), Action::Added));
758 let mut iter = kargs.iter();
759 assert_eq!(iter.next(), Some(param("console=tty0")));
760 assert_eq!(iter.next(), Some(param("console=ttyS1")));
761 assert_eq!(iter.next(), Some(param("console=ttyS2")));
762 assert_eq!(iter.next(), None);
763
764 assert!(matches!(
766 kargs.add(¶m("console=ttyS1")),
767 Action::Existed
768 ));
769 iter = kargs.iter();
770 assert_eq!(iter.next(), Some(param("console=tty0")));
771 assert_eq!(iter.next(), Some(param("console=ttyS1")));
772 assert_eq!(iter.next(), Some(param("console=ttyS2")));
773 assert_eq!(iter.next(), None);
774
775 assert!(matches!(kargs.add(¶m("quiet")), Action::Added));
777 iter = kargs.iter();
778 assert_eq!(iter.next(), Some(param("console=tty0")));
779 assert_eq!(iter.next(), Some(param("console=ttyS1")));
780 assert_eq!(iter.next(), Some(param("console=ttyS2")));
781 assert_eq!(iter.next(), Some(param("quiet")));
782 assert_eq!(iter.next(), None);
783 }
784
785 #[test]
786 fn test_add_empty_cmdline() {
787 let mut kargs = Cmdline::from("");
788 assert!(matches!(kargs.add(¶m("foo")), Action::Added));
789 assert_eq!(&*kargs, "foo");
790 }
791
792 #[test]
793 fn test_add_or_modify() {
794 let mut kargs = Cmdline::from("foo=bar");
795
796 assert!(matches!(kargs.add_or_modify(¶m("baz")), Action::Added));
798 let mut iter = kargs.iter();
799 assert_eq!(iter.next(), Some(param("foo=bar")));
800 assert_eq!(iter.next(), Some(param("baz")));
801 assert_eq!(iter.next(), None);
802
803 assert!(matches!(
805 kargs.add_or_modify(¶m("foo=fuz")),
806 Action::Modified
807 ));
808 iter = kargs.iter();
809 assert_eq!(iter.next(), Some(param("foo=fuz")));
810 assert_eq!(iter.next(), Some(param("baz")));
811 assert_eq!(iter.next(), None);
812
813 assert!(matches!(
816 kargs.add_or_modify(¶m("foo=fuz")),
817 Action::Existed
818 ));
819 iter = kargs.iter();
820 assert_eq!(iter.next(), Some(param("foo=fuz")));
821 assert_eq!(iter.next(), Some(param("baz")));
822 assert_eq!(iter.next(), None);
823 }
824
825 #[test]
826 fn test_add_or_modify_empty_cmdline() {
827 let mut kargs = Cmdline::from("");
828 assert!(matches!(kargs.add_or_modify(¶m("foo")), Action::Added));
829 assert_eq!(&*kargs, "foo");
830 }
831
832 #[test]
833 fn test_add_or_modify_duplicate_parameters() {
834 let mut kargs = Cmdline::from("a=1 a=2");
835 assert!(matches!(
836 kargs.add_or_modify(¶m("a=3")),
837 Action::Modified
838 ));
839 let mut iter = kargs.iter();
840 assert_eq!(iter.next(), Some(param("a=3")));
841 assert_eq!(iter.next(), None);
842 }
843
844 #[test]
845 fn test_remove() {
846 let mut kargs = Cmdline::from("foo bar baz");
847
848 assert!(kargs.remove(&"bar".into()));
850 let mut iter = kargs.iter();
851 assert_eq!(iter.next(), Some(param("foo")));
852 assert_eq!(iter.next(), Some(param("baz")));
853 assert_eq!(iter.next(), None);
854
855 assert!(!kargs.remove(&"missing".into()));
857 iter = kargs.iter();
858 assert_eq!(iter.next(), Some(param("foo")));
859 assert_eq!(iter.next(), Some(param("baz")));
860 assert_eq!(iter.next(), None);
861 }
862
863 #[test]
864 fn test_remove_duplicates() {
865 let mut kargs = Cmdline::from("a=1 b=2 a=3");
866 assert!(kargs.remove(&"a".into()));
867 let mut iter = kargs.iter();
868 assert_eq!(iter.next(), Some(param("b=2")));
869 assert_eq!(iter.next(), None);
870 }
871
872 #[test]
873 fn test_remove_exact() {
874 let mut kargs = Cmdline::from("foo foo=bar foo=baz");
875
876 assert!(kargs.remove_exact(¶m("foo=bar")));
878 let mut iter = kargs.iter();
879 assert_eq!(iter.next(), Some(param("foo")));
880 assert_eq!(iter.next(), Some(param("foo=baz")));
881 assert_eq!(iter.next(), None);
882
883 assert!(!kargs.remove_exact(¶m("foo=wuz")));
885 iter = kargs.iter();
886 assert_eq!(iter.next(), Some(param("foo")));
887 assert_eq!(iter.next(), Some(param("foo=baz")));
888 assert_eq!(iter.next(), None);
889 }
890
891 #[test]
892 fn test_extend() {
893 let mut kargs = Cmdline::from("foo=bar baz");
894 let other = Cmdline::from("qux=quux foo=updated");
895
896 kargs.extend(&other);
897
898 drop(other);
901
902 let mut iter = kargs.iter();
905 assert_eq!(iter.next(), Some(param("foo=bar")));
906 assert_eq!(iter.next(), Some(param("baz")));
907 assert_eq!(iter.next(), Some(param("qux=quux")));
908 assert_eq!(iter.next(), Some(param("foo=updated")));
909 assert_eq!(iter.next(), None);
910 }
911
912 #[test]
913 fn test_extend_empty() {
914 let mut kargs = Cmdline::from("");
915 let other = Cmdline::from("foo=bar baz");
916
917 kargs.extend(&other);
918
919 let mut iter = kargs.iter();
920 assert_eq!(iter.next(), Some(param("foo=bar")));
921 assert_eq!(iter.next(), Some(param("baz")));
922 assert_eq!(iter.next(), None);
923 }
924
925 #[test]
926 fn test_into_iterator() {
927 let kargs = Cmdline::from("foo=bar baz=qux wiz");
928 let params: Vec<_> = (&kargs).into_iter().collect();
929
930 assert_eq!(params.len(), 3);
931 assert_eq!(params[0], param("foo=bar"));
932 assert_eq!(params[1], param("baz=qux"));
933 assert_eq!(params[2], param("wiz"));
934 }
935
936 #[test]
937 fn test_cmdline_eq() {
938 assert_eq!(
942 Cmdline::from("foo bar-with-delim=\"with spaces\""),
943 Cmdline::from("\"bar_with_delim=with spaces\" foo")
944 );
945
946 assert_ne!(Cmdline::from("foo"), Cmdline::from("foo foo"));
950 assert_ne!(Cmdline::from("foo foo"), Cmdline::from("foo"));
951
952 assert_ne!(Cmdline::from("a a b"), Cmdline::from("a b b"));
954 }
955}