multiboot2/
util.rs

1//! Various utilities.
2
3use core::str::Utf8Error;
4use thiserror::Error;
5
6/// Error type describing failures when parsing the string from a tag.
7#[derive(Debug, PartialEq, Eq, Clone, Error)]
8pub enum StringError {
9    /// There is no terminating NUL character, although the specification
10    /// requires one.
11    #[error("string is not null terminated")]
12    MissingNul(#[source] core::ffi::FromBytesUntilNulError),
13    /// The sequence until the first NUL character is not valid UTF-8.
14    #[error("string is not valid UTF-8")]
15    Utf8(#[source] Utf8Error),
16}
17
18/// Parses the provided byte sequence as Multiboot string, which maps to a
19/// [`str`].
20pub fn parse_slice_as_string(bytes: &[u8]) -> Result<&str, StringError> {
21    let cstr = core::ffi::CStr::from_bytes_until_nul(bytes).map_err(StringError::MissingNul)?;
22    cstr.to_str().map_err(StringError::Utf8)
23}
24
25#[cfg(test)]
26mod tests {
27    use super::*;
28
29    #[test]
30    fn test_parse_slice_as_string() {
31        // empty slice is invalid
32        assert!(matches!(
33            parse_slice_as_string(&[]),
34            Err(StringError::MissingNul(_))
35        ));
36        // empty string is fine
37        assert_eq!(parse_slice_as_string(&[0x00]), Ok(""));
38        // reject invalid utf8
39        assert!(matches!(
40            parse_slice_as_string(&[0xff, 0x00]),
41            Err(StringError::Utf8(_))
42        ));
43        // reject missing null
44        assert!(matches!(
45            parse_slice_as_string(b"hello"),
46            Err(StringError::MissingNul(_))
47        ));
48        // must not include final null
49        assert_eq!(parse_slice_as_string(b"hello\0"), Ok("hello"));
50        assert_eq!(parse_slice_as_string(b"hello\0\0"), Ok("hello"));
51        // must skip everything after first null
52        assert_eq!(parse_slice_as_string(b"hello\0foo"), Ok("hello"));
53    }
54}