1
0
mirror of https://github.com/containers/bootc.git synced 2026-02-05 15:45:53 +01:00

kernel_cmdline: rename parse to parse_one and add new parse method

The (now-named) parse_one method is not particularly useful outside of
`CmdlineIter`.  Almost always end users don't care about extra
unparsed content, they just want the `Option<Parameter>`.

This greatly improves ergnomics for users so they can create
parameters like...

`Parameter::parse("foo=bar").unwrap()`

... knowing at the call-site that "foo=bar" is a valid parameter so
the `unwrap()` is safe.

Signed-off-by: John Eckersberg <jeckersb@redhat.com>
This commit is contained in:
John Eckersberg
2025-09-12 15:42:48 -04:00
parent 78b677bded
commit 8d68526cc2
2 changed files with 49 additions and 25 deletions

View File

@@ -43,7 +43,7 @@ impl<'a> Iterator for CmdlineIter<'a> {
type Item = Parameter<'a>;
fn next(&mut self) -> Option<Self::Item> {
let (param, rest) = Parameter::parse(self.0);
let (param, rest) = Parameter::parse_one(self.0);
self.0 = rest;
param
}
@@ -266,6 +266,18 @@ pub struct Parameter<'a> {
}
impl<'a> Parameter<'a> {
/// Attempt to parse a single command line parameter from a slice
/// of bytes.
///
/// Returns `Some(Parameter)`, or `None` if a Parameter could not
/// be constructed from the input. This occurs when the input is
/// either empty or contains only whitespace.
///
/// Any remaining bytes not consumed from the input are discarded.
pub fn parse<T: AsRef<[u8]> + ?Sized>(input: &'a T) -> Option<Self> {
Self::parse_one(input).0
}
/// Attempt to parse a single command line parameter from a slice
/// of bytes.
///
@@ -276,7 +288,7 @@ impl<'a> Parameter<'a> {
///
/// Any remaining bytes not consumed from the input are returned
/// as the second tuple item.
pub fn parse<T: AsRef<[u8]> + ?Sized>(input: &'a T) -> (Option<Self>, &'a [u8]) {
pub fn parse_one<T: AsRef<[u8]> + ?Sized>(input: &'a T) -> (Option<Self>, &'a [u8]) {
let input = input.as_ref().trim_ascii_start();
if input.is_empty() {
@@ -355,33 +367,33 @@ mod tests {
// convenience methods for tests
fn param(s: &str) -> Parameter<'_> {
Parameter::parse(s.as_bytes()).0.unwrap()
Parameter::parse(s.as_bytes()).unwrap()
}
fn param_utf8(s: &str) -> utf8::Parameter<'_> {
utf8::Parameter::parse(s).0.unwrap()
utf8::Parameter::parse(s).unwrap()
}
#[test]
fn test_parameter_parse() {
let (p, rest) = Parameter::parse(b"foo");
fn test_parameter_parse_one() {
let (p, rest) = Parameter::parse_one(b"foo");
let p = p.unwrap();
assert_eq!(p.key.0, b"foo");
assert_eq!(p.value, None);
assert_eq!(rest, "".as_bytes());
// should consume one parameter and return the rest of the input
let (p, rest) = Parameter::parse(b"foo=bar baz");
let (p, rest) = Parameter::parse_one(b"foo=bar baz");
let p = p.unwrap();
assert_eq!(p.key.0, b"foo");
assert_eq!(p.value, Some(b"bar".as_slice()));
assert_eq!(rest, " baz".as_bytes());
// should return None on empty or whitespace inputs
let (p, rest) = Parameter::parse(b"");
let (p, rest) = Parameter::parse_one(b"");
assert!(p.is_none());
assert_eq!(rest, b"".as_slice());
let (p, rest) = Parameter::parse(b" ");
let (p, rest) = Parameter::parse_one(b" ");
assert!(p.is_none());
assert_eq!(rest, b"".as_slice());
}
@@ -418,7 +430,7 @@ mod tests {
#[test]
fn test_parameter_internal_key_whitespace() {
let (p, rest) = Parameter::parse("foo bar=baz".as_bytes());
let (p, rest) = Parameter::parse_one("foo bar=baz".as_bytes());
let p = p.unwrap();
assert_eq!(p.key.0, b"foo");
assert_eq!(p.value, None);
@@ -444,8 +456,7 @@ mod tests {
assert!(failed_conversion.is_err());
let mut p = b"foo=".to_vec();
p.push(non_utf8_byte[0]);
let (p, _rest) = Parameter::parse(&p);
let p = p.unwrap();
let p = Parameter::parse(&p).unwrap();
assert_eq!(p.value, Some(non_utf8_byte.as_slice()));
}

View File

@@ -195,6 +195,19 @@ impl PartialEq for ParameterKey<'_> {
pub struct Parameter<'a>(bytes::Parameter<'a>);
impl<'a> Parameter<'a> {
/// Attempt to parse a single command line parameter from a UTF-8
/// string.
///
/// Returns `Some(Parameter)`, or `None` if a Parameter could not
/// be constructed from the input. This occurs when the input is
/// either empty or contains only whitespace.
///
/// Any remaining characters not consumed from the input are
/// discarded.
pub fn parse<T: AsRef<str> + ?Sized>(input: &'a T) -> Option<Self> {
Self::parse_one(input).0
}
/// Attempt to parse a single command line parameter from a UTF-8
/// string.
///
@@ -205,8 +218,8 @@ impl<'a> Parameter<'a> {
///
/// Any remaining characters not consumed from the input are
/// returned as the second tuple item.
pub fn parse<T: AsRef<str> + ?Sized>(input: &'a T) -> (Option<Self>, &'a str) {
let (bytes, rest) = bytes::Parameter::parse(input.as_ref().as_bytes());
pub fn parse_one<T: AsRef<str> + ?Sized>(input: &'a T) -> (Option<Self>, &'a str) {
let (bytes, rest) = bytes::Parameter::parse_one(input.as_ref().as_bytes());
// SAFETY: we know this is valid UTF-8 since input is &str,
// and `rest` is a subslice of that &str which was split on
@@ -288,29 +301,29 @@ mod tests {
// convenience method for tests
fn param(s: &str) -> Parameter<'_> {
Parameter::parse(s).0.unwrap()
Parameter::parse(s).unwrap()
}
#[test]
fn test_parameter_parse() {
let (p, rest) = Parameter::parse("foo");
fn test_parameter_parse_one() {
let (p, rest) = Parameter::parse_one("foo");
let p = p.unwrap();
assert_eq!(p.key(), "foo".into());
assert_eq!(p.value(), None);
assert_eq!(rest, "");
// should consume one parameter and return the rest of the input
let (p, rest) = Parameter::parse("foo=bar baz");
let (p, rest) = Parameter::parse_one("foo=bar baz");
let p = p.unwrap();
assert_eq!(p.key(), "foo".into());
assert_eq!(p.value(), Some("bar"));
assert_eq!(rest, " baz");
// should return None on empty or whitespace inputs
let (p, rest) = Parameter::parse("");
let (p, rest) = Parameter::parse_one("");
assert!(p.is_none());
assert_eq!(rest, "");
let (p, rest) = Parameter::parse(" ");
let (p, rest) = Parameter::parse_one(" ");
assert!(p.is_none());
assert_eq!(rest, "");
}
@@ -341,7 +354,7 @@ mod tests {
#[test]
fn test_parameter_internal_key_whitespace() {
let (p, rest) = Parameter::parse("foo bar=baz");
let (p, rest) = Parameter::parse_one("foo bar=baz");
let p = p.unwrap();
assert_eq!(p.key(), "foo".into());
assert_eq!(p.value(), None);
@@ -393,19 +406,19 @@ mod tests {
#[test]
fn test_parameter_tryfrom() {
// ok switch
let p = bytes::Parameter::parse(b"foo").0.unwrap();
let p = bytes::Parameter::parse(b"foo").unwrap();
let utf = Parameter::try_from(p).unwrap();
assert_eq!(utf.key(), "foo".into());
assert_eq!(utf.value(), None);
// ok key/value
let p = bytes::Parameter::parse(b"foo=bar").0.unwrap();
let p = bytes::Parameter::parse(b"foo=bar").unwrap();
let utf = Parameter::try_from(p).unwrap();
assert_eq!(utf.key(), "foo".into());
assert_eq!(utf.value(), Some("bar".into()));
// bad switch
let p = bytes::Parameter::parse(b"f\xffoo").0.unwrap();
let p = bytes::Parameter::parse(b"f\xffoo").unwrap();
let e = Parameter::try_from(p);
assert_eq!(
e.unwrap_err().to_string(),
@@ -413,7 +426,7 @@ mod tests {
);
// bad key/value
let p = bytes::Parameter::parse(b"foo=b\xffar").0.unwrap();
let p = bytes::Parameter::parse(b"foo=b\xffar").unwrap();
let e = Parameter::try_from(p);
assert_eq!(
e.unwrap_err().to_string(),