From 8d68526cc2f8e3edaede4c00a51e7bc2324e4341 Mon Sep 17 00:00:00 2001 From: John Eckersberg Date: Fri, 12 Sep 2025 15:42:48 -0400 Subject: [PATCH] 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`. 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 --- crates/kernel_cmdline/src/bytes.rs | 35 ++++++++++++++++++--------- crates/kernel_cmdline/src/utf8.rs | 39 ++++++++++++++++++++---------- 2 files changed, 49 insertions(+), 25 deletions(-) diff --git a/crates/kernel_cmdline/src/bytes.rs b/crates/kernel_cmdline/src/bytes.rs index de7ed0a1..8ff518e8 100644 --- a/crates/kernel_cmdline/src/bytes.rs +++ b/crates/kernel_cmdline/src/bytes.rs @@ -43,7 +43,7 @@ impl<'a> Iterator for CmdlineIter<'a> { type Item = Parameter<'a>; fn next(&mut self) -> Option { - 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 + ?Sized>(input: &'a T) -> Option { + 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 + ?Sized>(input: &'a T) -> (Option, &'a [u8]) { + pub fn parse_one + ?Sized>(input: &'a T) -> (Option, &'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())); } diff --git a/crates/kernel_cmdline/src/utf8.rs b/crates/kernel_cmdline/src/utf8.rs index d6a4d35e..9d51592f 100644 --- a/crates/kernel_cmdline/src/utf8.rs +++ b/crates/kernel_cmdline/src/utf8.rs @@ -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 + ?Sized>(input: &'a T) -> Option { + 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 + ?Sized>(input: &'a T) -> (Option, &'a str) { - let (bytes, rest) = bytes::Parameter::parse(input.as_ref().as_bytes()); + pub fn parse_one + ?Sized>(input: &'a T) -> (Option, &'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(),