falco_event/types/string/
cstr.rs

1use crate::fields::{FromBytes, FromBytesError, ToBytes};
2use crate::types::ByteBufFormatter;
3use std::ffi::CStr;
4use std::fmt::{Debug, Formatter};
5use std::io::Write;
6
7impl<'a> FromBytes<'a> for &'a CStr {
8    #[inline]
9    fn from_bytes(buf: &mut &'a [u8]) -> Result<Self, FromBytesError> {
10        let cstr = CStr::from_bytes_until_nul(buf).map_err(|_| FromBytesError::MissingNul)?;
11        let len = cstr.to_bytes().len();
12        *buf = &buf[len + 1..];
13        Ok(cstr)
14    }
15}
16
17impl ToBytes for &CStr {
18    #[inline]
19    fn binary_size(&self) -> usize {
20        self.to_bytes().len() + 1
21    }
22
23    #[inline]
24    fn write<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
25        writer.write_all(self.to_bytes_with_nul())
26    }
27
28    #[inline]
29    fn default_repr() -> impl ToBytes {
30        0u8
31    }
32}
33
34/// Falco-style CStr formatter
35///
36/// Formats the string like a byte buffer (replacing non-ASCII characters with `.`).
37/// See [`ByteBufFormatter`] for the implementation.
38pub struct CStrFormatter<'a>(pub &'a CStr);
39
40impl Debug for CStrFormatter<'_> {
41    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
42        Debug::fmt(&ByteBufFormatter(self.0.to_bytes()), f)
43    }
44}
45
46#[cfg(test)]
47mod tests {
48    use crate::fields::{FromBytes, ToBytes};
49    use std::ffi::CStr;
50
51    #[test]
52    fn test_cstr() {
53        let s = CStr::from_bytes_until_nul(b"foo\0").unwrap();
54
55        let mut binary = Vec::new();
56        s.write(&mut binary).unwrap();
57
58        hexdump::hexdump(binary.as_slice());
59
60        assert_eq!(binary.as_slice(), b"foo\0".as_slice());
61
62        let mut buf = binary.as_slice();
63        let s2 = <&CStr>::from_bytes(&mut buf).unwrap();
64
65        assert_eq!(s2.to_bytes_with_nul(), b"foo\0".as_slice());
66        assert_eq!(buf.len(), 0);
67    }
68}