diff --git a/crates/kernel_cmdline/src/bytes.rs b/crates/kernel_cmdline/src/bytes.rs index ac6e70f66..4e4b8ff10 100644 --- a/crates/kernel_cmdline/src/bytes.rs +++ b/crates/kernel_cmdline/src/bytes.rs @@ -340,6 +340,38 @@ impl<'a> Cmdline<'a> { removed } + /// Returns the canonicalized version of the `Cmdline`. + /// + /// This: + /// + /// 1. Sorts the parameter list + /// 2. Canonicalizes each `Parameter` + /// 3. Joins each parameter together with a single space ' ' + /// + /// # Examples + /// + /// ``` + /// use bootc_kernel_cmdline::bytes::Cmdline; + /// + /// let cmdline = Cmdline::from(b"z a=\"b c\""); + /// assert_eq!(&cmdline.canonicalized(), b"\"a=b c\" z"); + /// ``` + pub fn canonicalized(&self) -> Vec { + let mut params = self.iter().collect::>(); + params.sort(); + + let mut res = Vec::new(); + + for (i, p) in params.iter().enumerate() { + if i > 0 { + res.push(b' '); + } + res.extend(p.canonicalized()); + } + + res + } + #[cfg(test)] pub(crate) fn is_owned(&self) -> bool { matches!(self.0, Cow::Owned(_)) @@ -426,6 +458,21 @@ impl ParameterKey<'_> { .iter() .map(|&c: &u8| if c == b'-' { b'_' } else { c }) } + + /// Returns the canonicalized version of the key. This replaces + /// all dashes '-' with underscores '_'. + /// + /// # Example + /// + /// ``` + /// use bootc_kernel_cmdline::bytes::ParameterKey; + /// + /// assert_eq!(&ParameterKey::from("key-with-dashes").canonicalized(), + /// "key_with_dashes".as_bytes()); + /// ``` + pub fn canonicalized(&self) -> Vec { + self.iter().collect() + } } impl PartialEq for ParameterKey<'_> { @@ -528,6 +575,57 @@ impl<'a> Parameter<'a> { pub fn value(&self) -> Option<&'a [u8]> { self.value } + + /// Returns the canonical representation of the parameter. + /// + /// The canonical representation: + /// + /// 1. Will use the canonicalized form of the key via + /// `ParameterKey::canonicalized` + /// + /// 2. Will be "externally" quoted if either the key or + /// (optional) value contains ascii whitespace. + /// + /// 3. Unnecessary quoting will be removed. + /// + /// # Examples + /// + /// ``` + /// use bootc_kernel_cmdline::bytes::Parameter; + /// + /// // key is canonicalized + /// assert_eq!(Parameter::parse("a-dashed-key").unwrap().canonicalized(), + /// "a_dashed_key".as_bytes()); + /// + /// // quotes are externally added if needed + /// assert_eq!(Parameter::parse("foo=\"has some spaces\"").unwrap().canonicalized(), + /// "\"foo=has some spaces\"".as_bytes()); + /// + /// // unnecessary quotes are removed + /// assert_eq!(Parameter::parse("foo=\"bar\"").unwrap().canonicalized(), + /// "foo=bar".as_bytes()); + /// ``` + pub fn canonicalized(&self) -> Vec { + let spaces = self.key.iter().any(|b| b.is_ascii_whitespace()) + || self + .value + .map_or(false, |val| val.iter().any(|b| b.is_ascii_whitespace())); + + let mut res = if spaces { vec![b'"'] } else { vec![] }; + + res.extend(self.key.iter()); + + if let Some(val) = self.value { + res.push(b'='); + res.extend(val); + } + + if spaces { + res.push(b'"'); + } + + res + } } impl PartialEq for Parameter<'_> { diff --git a/crates/kernel_cmdline/src/utf8.rs b/crates/kernel_cmdline/src/utf8.rs index 408ea2c41..60d303c4b 100644 --- a/crates/kernel_cmdline/src/utf8.rs +++ b/crates/kernel_cmdline/src/utf8.rs @@ -203,6 +203,29 @@ impl<'a> Cmdline<'a> { self.0.remove_exact(¶m.0) } + /// Returns the canonicalized version of the `Cmdline`. + /// + /// This: + /// + /// 1. Sorts the parameter list + /// 2. Canonicalizes each `Parameter` + /// 3. Joins each parameter together with a single space ' ' + /// + /// # Examples + /// + /// ``` + /// use bootc_kernel_cmdline::utf8::Cmdline; + /// + /// let cmdline = Cmdline::from("z a=\"b c\""); + /// assert_eq!(&cmdline.canonicalized(), "\"a=b c\" z"); + /// ``` + pub fn canonicalized(&self) -> String { + self.0 + .canonicalized() + .try_into() + .expect("We only construct the underlying bytes from valid UTF-8") + } + #[cfg(test)] pub(crate) fn is_owned(&self) -> bool { self.0.is_owned() @@ -298,6 +321,24 @@ impl<'a> ParameterKey<'a> { fn from_bytes(input: bytes::ParameterKey<'a>) -> Self { Self(input) } + + /// Returns the canonicalized version of the key. This replaces + /// all dashes '-' with underscores '_'. + /// + /// # Example + /// + /// ``` + /// use bootc_kernel_cmdline::utf8::ParameterKey; + /// + /// assert_eq!(ParameterKey::from("key-with-dashes").canonicalized(), + /// "key_with_dashes".to_string()); + /// ``` + pub fn canonicalized(&self) -> String { + self.0 + .canonicalized() + .try_into() + .expect("We only construct the underlying bytes from valid UTF-8") + } } impl<'a, T: AsRef + ?Sized> From<&'a T> for ParameterKey<'a> { @@ -358,6 +399,42 @@ impl<'a> Parameter<'a> { str::from_utf8(p).expect("We only construct the underlying bytes from valid UTF-8") }) } + + /// Returns the canonical representation of the parameter. + /// + /// The canonical representation: + /// + /// 1. Will use the canonicalized form of the key via + /// `ParameterKey::canonicalized` + /// + /// 2. Will be "externally" quoted if either the key or + /// (optional) value contains ascii whitespace. + /// + /// 3. Unnecessary quoting will be removed. + /// + /// # Examples + /// + /// ``` + /// use bootc_kernel_cmdline::utf8::Parameter; + /// + /// // key is canonicalized + /// assert_eq!(Parameter::parse("a-dashed-key").unwrap().canonicalized(), + /// "a_dashed_key".to_string()); + /// + /// // quotes are externally added if needed + /// assert_eq!(Parameter::parse("foo=\"has some spaces\"").unwrap().canonicalized(), + /// "\"foo=has some spaces\"".to_string()); + /// + /// // unnecessary quotes are removed + /// assert_eq!(Parameter::parse("foo=\"bar\"").unwrap().canonicalized(), + /// "foo=bar".to_string()); + /// ``` + pub fn canonicalized(&self) -> String { + self.0 + .canonicalized() + .try_into() + .expect("We only construct the underlying bytes from valid UTF-8") + } } impl<'a> TryFrom> for Parameter<'a> {