Skip to content

Commit 0232a41

Browse files
committed
kernel_cmdline: Add Cmdline::add method for both bytes and utf8 modules
Add a new `add` method to both `bytes::Cmdline` and `utf8::Cmdline` that appends parameters without modifying existing ones. Unlike `add_or_modify`, this method preserves duplicate keys with different values (e.g., multiple `console=` parameters), which is valid for certain kernel parameters. The method returns `Action::Added` when a new parameter is appended, and `Action::Existed` when the exact parameter already exists. Assisted-by: Claude Code (Sonnet 4.5) Signed-off-by: John Eckersberg <[email protected]>
1 parent f2a1e4f commit 0232a41

File tree

2 files changed

+132
-0
lines changed

2 files changed

+132
-0
lines changed

crates/kernel_cmdline/src/bytes.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,36 @@ impl<'a> Cmdline<'a> {
132132
})
133133
}
134134

135+
/// Add a parameter to the command line if it doesn't already exist
136+
///
137+
/// Returns `Action::Added` if the parameter did not already exist
138+
/// and was added.
139+
///
140+
/// Returns `Action::Existed` if the exact parameter (same key and value)
141+
/// already exists. No modification was made.
142+
///
143+
/// Unlike `add_or_modify`, this method will not modify existing
144+
/// parameters. If a parameter with the same key exists but has a
145+
/// different value, the new parameter is still added, allowing
146+
/// duplicate keys (e.g., multiple `console=` parameters).
147+
pub fn add(&mut self, param: &Parameter) -> Action {
148+
// Check if the exact parameter already exists
149+
for p in self.iter() {
150+
if p == *param {
151+
// Exact match found, don't add duplicate
152+
return Action::Existed;
153+
}
154+
}
155+
156+
// The exact parameter was not found, so we append it.
157+
let self_mut = self.0.to_mut();
158+
if !self_mut.is_empty() && !self_mut.last().unwrap().is_ascii_whitespace() {
159+
self_mut.push(b' ');
160+
}
161+
self_mut.extend_from_slice(param.parameter);
162+
Action::Added
163+
}
164+
135165
/// Add or modify a parameter to the command line
136166
///
137167
/// Returns `Action::Added` if the parameter did not exist before
@@ -678,6 +708,49 @@ mod tests {
678708
assert_eq!(rd_args[3], param("rd.qux=c"));
679709
}
680710

711+
#[test]
712+
fn test_add() {
713+
let mut kargs = Cmdline::from(b"console=tty0 console=ttyS1");
714+
715+
// add new parameter with duplicate key but different value
716+
assert!(matches!(
717+
kargs.add(&param("console=ttyS2")),
718+
Action::Added
719+
));
720+
let mut iter = kargs.iter();
721+
assert_eq!(iter.next(), Some(param("console=tty0")));
722+
assert_eq!(iter.next(), Some(param("console=ttyS1")));
723+
assert_eq!(iter.next(), Some(param("console=ttyS2")));
724+
assert_eq!(iter.next(), None);
725+
726+
// try to add exact duplicate - should return Existed
727+
assert!(matches!(
728+
kargs.add(&param("console=ttyS1")),
729+
Action::Existed
730+
));
731+
iter = kargs.iter();
732+
assert_eq!(iter.next(), Some(param("console=tty0")));
733+
assert_eq!(iter.next(), Some(param("console=ttyS1")));
734+
assert_eq!(iter.next(), Some(param("console=ttyS2")));
735+
assert_eq!(iter.next(), None);
736+
737+
// add completely new parameter
738+
assert!(matches!(kargs.add(&param("quiet")), Action::Added));
739+
iter = kargs.iter();
740+
assert_eq!(iter.next(), Some(param("console=tty0")));
741+
assert_eq!(iter.next(), Some(param("console=ttyS1")));
742+
assert_eq!(iter.next(), Some(param("console=ttyS2")));
743+
assert_eq!(iter.next(), Some(param("quiet")));
744+
assert_eq!(iter.next(), None);
745+
}
746+
747+
#[test]
748+
fn test_add_empty_cmdline() {
749+
let mut kargs = Cmdline::from(b"");
750+
assert!(matches!(kargs.add(&param("foo")), Action::Added));
751+
assert_eq!(kargs.0, b"foo".as_slice());
752+
}
753+
681754
#[test]
682755
fn test_add_or_modify() {
683756
let mut kargs = Cmdline::from(b"foo=bar");

crates/kernel_cmdline/src/utf8.rs

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ impl<'a> Cmdline<'a> {
118118
.ok_or_else(|| anyhow::anyhow!("Failed to find kernel argument '{key}'"))
119119
}
120120

121+
/// Add a parameter to the command line if it doesn't already exist
122+
///
123+
/// Returns `Action::Added` if the parameter did not already exist
124+
/// and was added.
125+
///
126+
/// Returns `Action::Existed` if the exact parameter (same key and value)
127+
/// already exists. No modification was made.
128+
///
129+
/// Unlike `add_or_modify`, this method will not modify existing
130+
/// parameters. If a parameter with the same key exists but has a
131+
/// different value, the new parameter is still added, allowing
132+
/// duplicate keys (e.g., multiple `console=` parameters).
133+
pub fn add(&mut self, param: &Parameter) -> Action {
134+
self.0.add(&param.0)
135+
}
136+
121137
/// Add or modify a parameter to the command line
122138
///
123139
/// Returns `Action::Added` if the parameter did not exist before
@@ -636,6 +652,49 @@ mod tests {
636652
assert_ne!(k1, k2);
637653
}
638654

655+
#[test]
656+
fn test_add() {
657+
let mut kargs = Cmdline::from("console=tty0 console=ttyS1");
658+
659+
// add new parameter with duplicate key but different value
660+
assert!(matches!(
661+
kargs.add(&param("console=ttyS2")),
662+
Action::Added
663+
));
664+
let mut iter = kargs.iter();
665+
assert_eq!(iter.next(), Some(param("console=tty0")));
666+
assert_eq!(iter.next(), Some(param("console=ttyS1")));
667+
assert_eq!(iter.next(), Some(param("console=ttyS2")));
668+
assert_eq!(iter.next(), None);
669+
670+
// try to add exact duplicate - should return Existed
671+
assert!(matches!(
672+
kargs.add(&param("console=ttyS1")),
673+
Action::Existed
674+
));
675+
iter = kargs.iter();
676+
assert_eq!(iter.next(), Some(param("console=tty0")));
677+
assert_eq!(iter.next(), Some(param("console=ttyS1")));
678+
assert_eq!(iter.next(), Some(param("console=ttyS2")));
679+
assert_eq!(iter.next(), None);
680+
681+
// add completely new parameter
682+
assert!(matches!(kargs.add(&param("quiet")), Action::Added));
683+
iter = kargs.iter();
684+
assert_eq!(iter.next(), Some(param("console=tty0")));
685+
assert_eq!(iter.next(), Some(param("console=ttyS1")));
686+
assert_eq!(iter.next(), Some(param("console=ttyS2")));
687+
assert_eq!(iter.next(), Some(param("quiet")));
688+
assert_eq!(iter.next(), None);
689+
}
690+
691+
#[test]
692+
fn test_add_empty_cmdline() {
693+
let mut kargs = Cmdline::from("");
694+
assert!(matches!(kargs.add(&param("foo")), Action::Added));
695+
assert_eq!(kargs.as_ref(), "foo");
696+
}
697+
639698
#[test]
640699
fn test_add_or_modify() {
641700
let mut kargs = Cmdline::from("foo=bar");

0 commit comments

Comments
 (0)