1use std::ops::Deref;
7
8use crate::{Action, bytes};
9
10use anyhow::Result;
11use serde::{Deserialize, Serialize};
12
13#[derive(Clone, Debug, Default, PartialEq, Eq, Serialize, Deserialize)]
19pub struct Cmdline<'a>(bytes::Cmdline<'a>);
20
21pub type CmdlineOwned = Cmdline<'static>;
23
24impl<'a, T: AsRef<str> + ?Sized> From<&'a T> for Cmdline<'a> {
25 fn from(input: &'a T) -> Self {
29 Self(bytes::Cmdline::from(input.as_ref().as_bytes()))
30 }
31}
32
33impl From<String> for CmdlineOwned {
34 fn from(input: String) -> Self {
38 Self(bytes::Cmdline::from(input.into_bytes()))
39 }
40}
41
42#[derive(Debug)]
46pub struct CmdlineIter<'a>(bytes::CmdlineIter<'a>);
47
48impl<'a> Iterator for CmdlineIter<'a> {
49 type Item = Parameter<'a>;
50
51 fn next(&mut self) -> Option<Self::Item> {
52 self.0.next().map(Parameter::from_bytes)
53 }
54}
55
56#[derive(Debug)]
60pub struct CmdlineIterStr<'a>(bytes::CmdlineIterBytes<'a>);
61
62impl<'a> Iterator for CmdlineIterStr<'a> {
63 type Item = &'a str;
64
65 fn next(&mut self) -> Option<Self::Item> {
66 let bytes = self.0.next()?;
68
69 Some(str::from_utf8(bytes).expect("Parameter bytes come from valid UTF-8 cmdline"))
72 }
73}
74
75impl<'a> Cmdline<'a> {
76 pub fn new() -> CmdlineOwned {
80 Cmdline::default()
81 }
82
83 pub fn from_proc() -> Result<Self> {
90 let cmdline = std::fs::read("/proc/cmdline")?;
91
92 str::from_utf8(&cmdline)?;
97
98 Ok(Self(bytes::Cmdline::from(cmdline)))
99 }
100
101 pub fn iter(&'a self) -> CmdlineIter<'a> {
107 CmdlineIter(self.0.iter())
108 }
109
110 pub fn iter_str(&self) -> CmdlineIterStr<'_> {
115 CmdlineIterStr(self.0.iter_bytes())
116 }
117
118 pub fn find<T: AsRef<str> + ?Sized>(&'a self, key: &T) -> Option<Parameter<'a>> {
123 let key = ParameterKey::from(key.as_ref());
124 self.iter().find(|p| p.key() == key)
125 }
126
127 pub fn find_all_starting_with<T: AsRef<str> + ?Sized>(
131 &'a self,
132 prefix: &'a T,
133 ) -> impl Iterator<Item = Parameter<'a>> + 'a {
134 self.iter()
135 .filter(move |p| p.key().starts_with(prefix.as_ref()))
136 }
137
138 pub fn value_of<T: AsRef<str> + ?Sized>(&'a self, key: &T) -> Option<&'a str> {
143 self.0.value_of(key.as_ref().as_bytes()).map(|v| {
144 str::from_utf8(v).expect("We only construct the underlying bytes from valid UTF-8")
147 })
148 }
149
150 pub fn require_value_of<T: AsRef<str> + ?Sized>(&'a self, key: &T) -> Result<&'a str> {
154 let key = key.as_ref();
155 self.value_of(key)
156 .ok_or_else(|| anyhow::anyhow!("Failed to find kernel argument '{key}'"))
157 }
158
159 pub fn add(&mut self, param: &Parameter) -> Action {
172 self.0.add(¶m.0)
173 }
174
175 pub fn add_or_modify(&mut self, param: &Parameter) -> Action {
188 self.0.add_or_modify(¶m.0)
189 }
190
191 pub fn remove(&mut self, key: &ParameterKey) -> bool {
195 self.0.remove(&key.0)
196 }
197
198 pub fn remove_exact(&mut self, param: &Parameter) -> bool {
203 self.0.remove_exact(¶m.0)
204 }
205
206 #[cfg(test)]
207 pub(crate) fn is_owned(&self) -> bool {
208 self.0.is_owned()
209 }
210
211 #[cfg(test)]
212 pub(crate) fn is_borrowed(&self) -> bool {
213 self.0.is_borrowed()
214 }
215}
216
217impl Deref for Cmdline<'_> {
218 type Target = str;
219
220 fn deref(&self) -> &Self::Target {
221 str::from_utf8(&self.0).expect("We only construct the underlying bytes from valid UTF-8")
224 }
225}
226
227impl<'a, T> AsRef<T> for Cmdline<'a>
228where
229 T: ?Sized,
230 <Cmdline<'a> as Deref>::Target: AsRef<T>,
231{
232 fn as_ref(&self) -> &T {
233 self.deref().as_ref()
234 }
235}
236
237impl<'a> std::fmt::Display for Cmdline<'a> {
238 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
239 f.write_str(self)
240 }
241}
242
243impl<'a> IntoIterator for &'a Cmdline<'a> {
244 type Item = Parameter<'a>;
245 type IntoIter = CmdlineIter<'a>;
246
247 fn into_iter(self) -> Self::IntoIter {
248 self.iter()
249 }
250}
251
252impl<'a, 'other> Extend<Parameter<'other>> for Cmdline<'a> {
253 fn extend<T: IntoIterator<Item = Parameter<'other>>>(&mut self, iter: T) {
261 for param in iter {
262 self.add(¶m);
263 }
264 }
265}
266
267#[derive(Clone, Debug, Eq)]
271pub struct ParameterKey<'a>(bytes::ParameterKey<'a>);
272
273impl Deref for ParameterKey<'_> {
274 type Target = str;
275
276 fn deref(&self) -> &Self::Target {
277 str::from_utf8(&self.0).expect("We only construct the underlying bytes from valid UTF-8")
280 }
281}
282
283impl<'a, T> AsRef<T> for ParameterKey<'a>
284where
285 T: ?Sized,
286 <ParameterKey<'a> as Deref>::Target: AsRef<T>,
287{
288 fn as_ref(&self) -> &T {
289 self.deref().as_ref()
290 }
291}
292
293impl<'a> ParameterKey<'a> {
294 fn from_bytes(input: bytes::ParameterKey<'a>) -> Self {
299 Self(input)
300 }
301}
302
303impl<'a, T: AsRef<str> + ?Sized> From<&'a T> for ParameterKey<'a> {
304 fn from(input: &'a T) -> Self {
305 Self(bytes::ParameterKey(input.as_ref().as_bytes()))
306 }
307}
308
309impl<'a> std::fmt::Display for ParameterKey<'a> {
310 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
311 f.write_str(self)
312 }
313}
314
315impl PartialEq for ParameterKey<'_> {
316 fn eq(&self, other: &Self) -> bool {
321 self.0 == other.0
322 }
323}
324
325#[derive(Clone, Debug, Eq)]
327pub struct Parameter<'a>(bytes::Parameter<'a>);
328
329impl<'a> Parameter<'a> {
330 pub fn parse<T: AsRef<str> + ?Sized>(input: &'a T) -> Option<Self> {
337 bytes::Parameter::parse(input.as_ref().as_bytes()).map(Self)
338 }
339
340 fn from_bytes(bytes: bytes::Parameter<'a>) -> Self {
345 Self(bytes)
346 }
347
348 pub fn key(&'a self) -> ParameterKey<'a> {
350 ParameterKey::from_bytes(self.0.key())
351 }
352
353 pub fn value(&'a self) -> Option<&'a str> {
355 self.0.value().map(|p| {
356 str::from_utf8(p).expect("We only construct the underlying bytes from valid UTF-8")
359 })
360 }
361}
362
363impl<'a> TryFrom<bytes::Parameter<'a>> for Parameter<'a> {
364 type Error = anyhow::Error;
365
366 fn try_from(bytes: bytes::Parameter<'a>) -> Result<Self, Self::Error> {
367 if str::from_utf8(bytes.key().deref()).is_err() {
368 anyhow::bail!("Parameter key is not valid UTF-8");
369 }
370
371 if let Some(value) = bytes.value() {
372 if str::from_utf8(value).is_err() {
373 anyhow::bail!("Parameter value is not valid UTF-8");
374 }
375 }
376
377 Ok(Self(bytes))
378 }
379}
380
381impl<'a> std::fmt::Display for Parameter<'a> {
382 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
383 f.write_str(self)
384 }
385}
386
387impl Deref for Parameter<'_> {
388 type Target = str;
389
390 fn deref(&self) -> &Self::Target {
391 str::from_utf8(&self.0).expect("We only construct the underlying bytes from valid UTF-8")
394 }
395}
396
397impl<'a, T> AsRef<T> for Parameter<'a>
398where
399 T: ?Sized,
400 <Parameter<'a> as Deref>::Target: AsRef<T>,
401{
402 fn as_ref(&self) -> &T {
403 self.deref().as_ref()
404 }
405}
406
407impl<'a> PartialEq for Parameter<'a> {
408 fn eq(&self, other: &Self) -> bool {
409 self.0 == other.0
410 }
411}
412
413#[cfg(test)]
414mod tests {
415 use super::*;
416
417 fn param(s: &str) -> Parameter<'_> {
419 Parameter::parse(s).unwrap()
420 }
421
422 #[test]
423 fn test_parameter_parse() {
424 let p = Parameter::parse("foo").unwrap();
425 assert_eq!(p.key(), "foo".into());
426 assert_eq!(p.value(), None);
427
428 let p = Parameter::parse("foo=bar baz").unwrap();
430 assert_eq!(p.key(), "foo".into());
431 assert_eq!(p.value(), Some("bar"));
432
433 assert!(Parameter::parse("").is_none());
435 assert!(Parameter::parse(" ").is_none());
436 }
437
438 #[test]
439 fn test_parameter_simple() {
440 let switch = param("foo");
441 assert_eq!(switch.key(), "foo".into());
442 assert_eq!(switch.value(), None);
443
444 let kv = param("bar=baz");
445 assert_eq!(kv.key(), "bar".into());
446 assert_eq!(kv.value(), Some("baz"));
447 }
448
449 #[test]
450 fn test_parameter_quoted() {
451 let p = param("foo=\"quoted value\"");
452 assert_eq!(p.value(), Some("quoted value"));
453
454 let p = param("foo=\"unclosed quotes");
455 assert_eq!(p.value(), Some("unclosed quotes"));
456
457 let p = param("foo=trailing_quotes\"");
458 assert_eq!(p.value(), Some("trailing_quotes"));
459
460 let outside_quoted = param("\"foo=quoted value\"");
461 let value_quoted = param("foo=\"quoted value\"");
462 assert_eq!(outside_quoted, value_quoted);
463 }
464
465 #[test]
466 fn test_parameter_display() {
467 assert_eq!(param("foo").to_string(), "foo");
472
473 assert_eq!(param("\"foo\"").to_string(), "\"foo\"");
475 }
476
477 #[test]
478 fn test_parameter_extra_whitespace() {
479 let p = param(" foo=bar ");
480 assert_eq!(p.key(), "foo".into());
481 assert_eq!(p.value(), Some("bar"));
482 }
483
484 #[test]
485 fn test_parameter_internal_key_whitespace() {
486 let p = Parameter::parse("foo bar=baz").unwrap();
488 assert_eq!(p.key(), "foo".into());
489 assert_eq!(p.value(), None);
490 }
491
492 #[test]
493 fn test_parameter_pathological() {
494 let p = param("\"foo\"=bar");
500 assert_eq!(p.key(), ParameterKey::from("foo\""));
501 assert_eq!(p.value(), Some("bar"));
502 assert_ne!(p, param("foo=bar"));
504
505 let p = param("\"foo=\"bar");
508 assert_eq!(p.key(), ParameterKey::from("foo"));
509 assert_eq!(p.value(), Some("bar"));
510 assert_eq!(p, param("foo=bar"));
512
513 let p = param("foo=\"internal\"quotes\"are\"ok\"");
515 assert_eq!(p.value(), Some("internal\"quotes\"are\"ok"));
516 }
517
518 #[test]
519 fn test_parameter_equality() {
520 let foo = param("foo");
522 let bar = param("foobar");
523 assert_ne!(foo, bar);
524 assert_ne!(bar, foo);
525
526 let dashes = param("a-delimited-param");
528 let underscores = param("a_delimited_param");
529 assert_eq!(dashes, underscores);
530
531 let dashes = param("a-delimited-param=same_values");
533 let underscores = param("a_delimited_param=same_values");
534 assert_eq!(dashes, underscores);
535
536 let dashes = param("a-delimited-param=different_values");
538 let underscores = param("a_delimited_param=DiFfErEnT_valUEZ");
539 assert_ne!(dashes, underscores);
540
541 let switch = param("same_key");
543 let keyvalue = param("same_key=but_with_a_value");
544 assert_ne!(switch, keyvalue);
545 }
546
547 #[test]
548 fn test_parameter_tryfrom() {
549 let p = bytes::Parameter::parse(b"foo").unwrap();
551 let utf = Parameter::try_from(p).unwrap();
552 assert_eq!(utf.key(), "foo".into());
553 assert_eq!(utf.value(), None);
554
555 let p = bytes::Parameter::parse(b"foo=bar").unwrap();
557 let utf = Parameter::try_from(p).unwrap();
558 assert_eq!(utf.key(), "foo".into());
559 assert_eq!(utf.value(), Some("bar".into()));
560
561 let p = bytes::Parameter::parse(b"f\xffoo").unwrap();
563 let e = Parameter::try_from(p);
564 assert_eq!(
565 e.unwrap_err().to_string(),
566 "Parameter key is not valid UTF-8"
567 );
568
569 let p = bytes::Parameter::parse(b"foo=b\xffar").unwrap();
571 let e = Parameter::try_from(p);
572 assert_eq!(
573 e.unwrap_err().to_string(),
574 "Parameter value is not valid UTF-8"
575 );
576 }
577
578 #[test]
579 fn test_kargs_simple() {
580 let kargs = Cmdline::from("foo=bar,bar2 baz=fuz wiz");
583 assert!(kargs.is_borrowed());
584 let mut iter = kargs.iter();
585
586 assert_eq!(iter.next(), Some(param("foo=bar,bar2")));
587 assert_eq!(iter.next(), Some(param("baz=fuz")));
588 assert_eq!(iter.next(), Some(param("wiz")));
589 assert_eq!(iter.next(), None);
590
591 assert_eq!(kargs.find("foo").unwrap().value().unwrap(), "bar,bar2");
593 assert!(kargs.find("nothing").is_none());
594 }
595
596 #[test]
597 fn test_cmdline_default() {
598 let kargs: Cmdline = Default::default();
599 assert_eq!(kargs.iter().next(), None);
600 }
601
602 #[test]
603 fn test_cmdline_new() {
604 let kargs = Cmdline::new();
605 assert_eq!(kargs.iter().next(), None);
606 assert!(kargs.is_owned());
607
608 let _static_kargs: CmdlineOwned = Cmdline::new();
610 }
611
612 #[test]
613 fn test_kargs_simple_from_string() {
614 let kargs = Cmdline::from("foo=bar,bar2 baz=fuz wiz".to_string());
615 assert!(kargs.is_owned());
616 let mut iter = kargs.iter();
617
618 assert_eq!(iter.next(), Some(param("foo=bar,bar2")));
619 assert_eq!(iter.next(), Some(param("baz=fuz")));
620 assert_eq!(iter.next(), Some(param("wiz")));
621 assert_eq!(iter.next(), None);
622
623 assert_eq!(kargs.find("foo").unwrap().value().unwrap(), "bar,bar2");
625 assert!(kargs.find("nothing").is_none());
626 }
627
628 #[test]
629 fn test_kargs_from_proc() {
630 let kargs = Cmdline::from_proc().unwrap();
631
632 assert!(kargs.iter().count() > 0);
636 }
637
638 #[test]
639 fn test_kargs_find_dash_hyphen() {
640 let kargs = Cmdline::from("a-b=1 a_b=2");
641 let p = kargs.find("a_b").unwrap();
643 assert_eq!(p.key(), "a-b".into());
644 assert_eq!(p.value().unwrap(), "1");
645 let p = kargs.find("a-b").unwrap();
646 assert_eq!(p.key(), "a-b".into());
647 assert_eq!(p.value().unwrap(), "1");
648
649 let kargs = Cmdline::from("a_b=2 a-b=1");
650 let p = kargs.find("a_b").unwrap();
652 assert_eq!(p.key(), "a_b".into());
653 assert_eq!(p.value().unwrap(), "2");
654 let p = kargs.find("a-b").unwrap();
655 assert_eq!(p.key(), "a_b".into());
656 assert_eq!(p.value().unwrap(), "2");
657 }
658
659 #[test]
660 fn test_kargs_extra_whitespace() {
661 let kargs = Cmdline::from(" foo=bar baz=fuz wiz ");
662 let mut iter = kargs.iter();
663
664 assert_eq!(iter.next(), Some(param("foo=bar")));
665 assert_eq!(iter.next(), Some(param("baz=fuz")));
666 assert_eq!(iter.next(), Some(param("wiz")));
667 assert_eq!(iter.next(), None);
668 }
669
670 #[test]
671 fn test_value_of() {
672 let kargs = Cmdline::from("foo=bar baz=qux switch");
673
674 assert_eq!(kargs.value_of("foo"), Some("bar"));
676 assert_eq!(kargs.value_of("baz"), Some("qux"));
677
678 assert_eq!(kargs.value_of("switch"), None);
680
681 assert_eq!(kargs.value_of("missing"), None);
683
684 let kargs = Cmdline::from("dash-key=value1 under_key=value2");
686 assert_eq!(kargs.value_of("dash_key"), Some("value1"));
687 assert_eq!(kargs.value_of("under-key"), Some("value2"));
688 }
689
690 #[test]
691 fn test_require_value_of() {
692 let kargs = Cmdline::from("foo=bar baz=qux switch");
693
694 assert_eq!(kargs.require_value_of("foo").unwrap(), "bar");
696 assert_eq!(kargs.require_value_of("baz").unwrap(), "qux");
697
698 let err = kargs.require_value_of("switch").unwrap_err();
700 assert!(
701 err.to_string()
702 .contains("Failed to find kernel argument 'switch'")
703 );
704
705 let err = kargs.require_value_of("missing").unwrap_err();
707 assert!(
708 err.to_string()
709 .contains("Failed to find kernel argument 'missing'")
710 );
711
712 let kargs = Cmdline::from("dash-key=value1 under_key=value2");
714 assert_eq!(kargs.require_value_of("dash_key").unwrap(), "value1");
715 assert_eq!(kargs.require_value_of("under-key").unwrap(), "value2");
716 }
717
718 #[test]
719 fn test_find_str() {
720 let kargs = Cmdline::from("foo=bar baz=qux switch rd.break");
721 let p = kargs.find("foo").unwrap();
722 assert_eq!(p, param("foo=bar"));
723 let p = kargs.find("rd.break").unwrap();
724 assert_eq!(p, param("rd.break"));
725 assert!(kargs.find("missing").is_none());
726 }
727
728 #[test]
729 fn test_find_all_str() {
730 let kargs = Cmdline::from("foo=bar rd.foo=a rd.bar=b rd.baz rd.qux=c notrd.val=d");
731 let mut rd_args: Vec<_> = kargs.find_all_starting_with("rd.").collect();
732 rd_args.sort_by(|a, b| a.key().cmp(&b.key()));
733 assert_eq!(rd_args.len(), 4);
734 assert_eq!(rd_args[0], param("rd.bar=b"));
735 assert_eq!(rd_args[1], param("rd.baz"));
736 assert_eq!(rd_args[2], param("rd.foo=a"));
737 assert_eq!(rd_args[3], param("rd.qux=c"));
738 }
739
740 #[test]
741 fn test_param_key_eq() {
742 let k1 = ParameterKey::from("a-b");
743 let k2 = ParameterKey::from("a_b");
744 assert_eq!(k1, k2);
745 let k1 = ParameterKey::from("a-b");
746 let k2 = ParameterKey::from("a-c");
747 assert_ne!(k1, k2);
748 }
749
750 #[test]
751 fn test_add() {
752 let mut kargs = Cmdline::from("console=tty0 console=ttyS1");
753
754 assert!(matches!(kargs.add(¶m("console=ttyS2")), Action::Added));
756 let mut iter = kargs.iter();
757 assert_eq!(iter.next(), Some(param("console=tty0")));
758 assert_eq!(iter.next(), Some(param("console=ttyS1")));
759 assert_eq!(iter.next(), Some(param("console=ttyS2")));
760 assert_eq!(iter.next(), None);
761
762 assert!(matches!(
764 kargs.add(¶m("console=ttyS1")),
765 Action::Existed
766 ));
767 iter = kargs.iter();
768 assert_eq!(iter.next(), Some(param("console=tty0")));
769 assert_eq!(iter.next(), Some(param("console=ttyS1")));
770 assert_eq!(iter.next(), Some(param("console=ttyS2")));
771 assert_eq!(iter.next(), None);
772
773 assert!(matches!(kargs.add(¶m("quiet")), Action::Added));
775 iter = kargs.iter();
776 assert_eq!(iter.next(), Some(param("console=tty0")));
777 assert_eq!(iter.next(), Some(param("console=ttyS1")));
778 assert_eq!(iter.next(), Some(param("console=ttyS2")));
779 assert_eq!(iter.next(), Some(param("quiet")));
780 assert_eq!(iter.next(), None);
781 }
782
783 #[test]
784 fn test_add_empty_cmdline() {
785 let mut kargs = Cmdline::from("");
786 assert!(matches!(kargs.add(¶m("foo")), Action::Added));
787 assert_eq!(&*kargs, "foo");
788 }
789
790 #[test]
791 fn test_add_or_modify() {
792 let mut kargs = Cmdline::from("foo=bar");
793
794 assert!(matches!(kargs.add_or_modify(¶m("baz")), Action::Added));
796 let mut iter = kargs.iter();
797 assert_eq!(iter.next(), Some(param("foo=bar")));
798 assert_eq!(iter.next(), Some(param("baz")));
799 assert_eq!(iter.next(), None);
800
801 assert!(matches!(
803 kargs.add_or_modify(¶m("foo=fuz")),
804 Action::Modified
805 ));
806 iter = kargs.iter();
807 assert_eq!(iter.next(), Some(param("foo=fuz")));
808 assert_eq!(iter.next(), Some(param("baz")));
809 assert_eq!(iter.next(), None);
810
811 assert!(matches!(
814 kargs.add_or_modify(¶m("foo=fuz")),
815 Action::Existed
816 ));
817 iter = kargs.iter();
818 assert_eq!(iter.next(), Some(param("foo=fuz")));
819 assert_eq!(iter.next(), Some(param("baz")));
820 assert_eq!(iter.next(), None);
821 }
822
823 #[test]
824 fn test_add_or_modify_empty_cmdline() {
825 let mut kargs = Cmdline::from("");
826 assert!(matches!(kargs.add_or_modify(¶m("foo")), Action::Added));
827 assert_eq!(&*kargs, "foo");
828 }
829
830 #[test]
831 fn test_add_or_modify_duplicate_parameters() {
832 let mut kargs = Cmdline::from("a=1 a=2");
833 assert!(matches!(
834 kargs.add_or_modify(¶m("a=3")),
835 Action::Modified
836 ));
837 let mut iter = kargs.iter();
838 assert_eq!(iter.next(), Some(param("a=3")));
839 assert_eq!(iter.next(), None);
840 }
841
842 #[test]
843 fn test_remove() {
844 let mut kargs = Cmdline::from("foo bar baz");
845
846 assert!(kargs.remove(&"bar".into()));
848 let mut iter = kargs.iter();
849 assert_eq!(iter.next(), Some(param("foo")));
850 assert_eq!(iter.next(), Some(param("baz")));
851 assert_eq!(iter.next(), None);
852
853 assert!(!kargs.remove(&"missing".into()));
855 iter = kargs.iter();
856 assert_eq!(iter.next(), Some(param("foo")));
857 assert_eq!(iter.next(), Some(param("baz")));
858 assert_eq!(iter.next(), None);
859 }
860
861 #[test]
862 fn test_remove_duplicates() {
863 let mut kargs = Cmdline::from("a=1 b=2 a=3");
864 assert!(kargs.remove(&"a".into()));
865 let mut iter = kargs.iter();
866 assert_eq!(iter.next(), Some(param("b=2")));
867 assert_eq!(iter.next(), None);
868 }
869
870 #[test]
871 fn test_remove_exact() {
872 let mut kargs = Cmdline::from("foo foo=bar foo=baz");
873
874 assert!(kargs.remove_exact(¶m("foo=bar")));
876 let mut iter = kargs.iter();
877 assert_eq!(iter.next(), Some(param("foo")));
878 assert_eq!(iter.next(), Some(param("foo=baz")));
879 assert_eq!(iter.next(), None);
880
881 assert!(!kargs.remove_exact(¶m("foo=wuz")));
883 iter = kargs.iter();
884 assert_eq!(iter.next(), Some(param("foo")));
885 assert_eq!(iter.next(), Some(param("foo=baz")));
886 assert_eq!(iter.next(), None);
887 }
888
889 #[test]
890 fn test_extend() {
891 let mut kargs = Cmdline::from("foo=bar baz");
892 let other = Cmdline::from("qux=quux foo=updated");
893
894 kargs.extend(&other);
895
896 drop(other);
899
900 let mut iter = kargs.iter();
903 assert_eq!(iter.next(), Some(param("foo=bar")));
904 assert_eq!(iter.next(), Some(param("baz")));
905 assert_eq!(iter.next(), Some(param("qux=quux")));
906 assert_eq!(iter.next(), Some(param("foo=updated")));
907 assert_eq!(iter.next(), None);
908 }
909
910 #[test]
911 fn test_extend_empty() {
912 let mut kargs = Cmdline::from("");
913 let other = Cmdline::from("foo=bar baz");
914
915 kargs.extend(&other);
916
917 let mut iter = kargs.iter();
918 assert_eq!(iter.next(), Some(param("foo=bar")));
919 assert_eq!(iter.next(), Some(param("baz")));
920 assert_eq!(iter.next(), None);
921 }
922
923 #[test]
924 fn test_into_iterator() {
925 let kargs = Cmdline::from("foo=bar baz=qux wiz");
926 let params: Vec<_> = (&kargs).into_iter().collect();
927
928 assert_eq!(params.len(), 3);
929 assert_eq!(params[0], param("foo=bar"));
930 assert_eq!(params[1], param("baz=qux"));
931 assert_eq!(params[2], param("wiz"));
932 }
933
934 #[test]
935 fn test_cmdline_eq() {
936 assert_eq!(
940 Cmdline::from("foo bar-with-delim=\"with spaces\""),
941 Cmdline::from("\"bar_with_delim=with spaces\" foo")
942 );
943
944 assert_ne!(Cmdline::from("foo"), Cmdline::from("foo foo"));
948 assert_ne!(Cmdline::from("foo foo"), Cmdline::from("foo"));
949
950 assert_ne!(Cmdline::from("a a b"), Cmdline::from("a b b"));
952 }
953}