falco_plugin/strings/
cstring_writer.rs1use memchr::memchr;
2use std::ffi::CString;
3use std::fmt::{Debug, Formatter};
4use std::io::Write;
5
6#[derive(Default)]
28pub struct CStringWriter(Vec<u8>);
29
30impl Debug for CStringWriter {
31 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
32 f.debug_tuple("CStringWriter")
33 .field(&String::from_utf8_lossy(self.0.as_slice()))
34 .finish()
35 }
36}
37
38impl Write for CStringWriter {
39 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
40 if memchr(0, buf).is_some() {
41 Err(std::io::Error::new(
42 std::io::ErrorKind::InvalidData,
43 "NUL in data",
44 ))
45 } else {
46 self.0.write(buf)
47 }
48 }
49
50 fn flush(&mut self) -> std::io::Result<()> {
51 self.0.flush()
52 }
53}
54
55impl CStringWriter {
56 pub fn into_cstring(mut self) -> CString {
61 self.0.push(0);
62
63 unsafe { CString::from_vec_with_nul_unchecked(self.0) }
66 }
67
68 pub fn store(self, target: &mut CString) {
74 let mut s = self.into_cstring();
75 std::mem::swap(&mut s, target)
76 }
77}
78
79pub trait WriteIntoCString {
99 fn write_into<F>(&mut self, func: F) -> std::io::Result<()>
101 where
102 F: FnOnce(&mut CStringWriter) -> std::io::Result<()>;
103}
104
105impl WriteIntoCString for CString {
106 fn write_into<F>(&mut self, func: F) -> std::io::Result<()>
107 where
108 F: FnOnce(&mut CStringWriter) -> std::io::Result<()>,
109 {
110 let mut w = CStringWriter::default();
111 func(&mut w)?;
112 w.store(self);
113 Ok(())
114 }
115}
116
117#[cfg(test)]
118mod tests {
119 use super::*;
120
121 #[test]
122 fn test_valid_store() {
123 let mut buf = CString::default();
124
125 let mut writer = CStringWriter::default();
126 write!(writer, "hello").unwrap();
127 #[allow(clippy::write_literal)]
128 write!(writer, ", {}", "world").unwrap();
129 writer.flush().unwrap();
130
131 writer.store(&mut buf);
132
133 assert_eq!(buf.as_c_str(), c"hello, world");
134 }
135
136 #[test]
137 fn test_invalid_store() {
138 let mut writer = CStringWriter::default();
139 write!(writer, "hell\0o").unwrap_err();
140 }
141
142 #[test]
143 fn test_valid_write_into() {
144 let mut buf = CString::default();
145
146 buf.write_into(|w| write!(w, "hello")).unwrap();
147
148 assert_eq!(buf.as_c_str(), c"hello");
149 }
150
151 #[test]
152 fn test_invalid_write_into() {
153 let mut buf = CString::default();
154
155 buf.write_into(|w| write!(w, "hell\0o")).unwrap_err();
156 }
157}