1use std::{borrow::Cow, io, str};
2
3use encoding_rs::Encoding;
4
5#[macro_export]
6macro_rules! io_error {
7 ($kind:ident, $($arg:tt)+) => {
8 std::io::Error::new(std::io::ErrorKind::$kind, format!($($arg)+))
9 };
10}
11
12pub fn buffer_to_zstring<'a>(
13 buf: &'a [u8],
14 encoding: &'static Encoding,
15) -> io::Result<Cow<'a, str>> {
16 let Some(null_byte_position) = buf.iter().position(|&x| x == 0) else {
17 return Err(io_error!(UnexpectedEof, "should be a null-terminated string"));
18 };
19
20 let (cow, _, had_error) = encoding.decode(&buf[..null_byte_position]);
21
22 if had_error {
23 return Err(io_error!(
24 InvalidData,
25 "should be a correct sequence of characters"
26 ));
27 }
28
29 Ok(cow)
30}
31
32#[cfg(test)]
33mod tests {
34 mod buffer_to_zstring {
35 use encoding_rs::UTF_8;
36
37 use super::super::buffer_to_zstring;
38 use std::io::ErrorKind;
39
40 #[test]
41 fn correct() {
42 let res = buffer_to_zstring(b"text\0\0\0\0\0", UTF_8);
43 assert_eq!(res.unwrap(), "text");
44 }
45
46 #[test]
47 fn not_null_terminated() {
48 let res = buffer_to_zstring(b"text", UTF_8);
49 assert_eq!(res.unwrap_err().kind(), ErrorKind::UnexpectedEof);
50 }
51
52 #[test]
53 fn invalid_utf8_sequence() {
54 let res = buffer_to_zstring(b"text\xc3\x28\0", UTF_8);
55 assert_eq!(res.unwrap_err().kind(), ErrorKind::InvalidData);
56 }
57 }
58}