slog2_parse/
lib.rs

1pub mod ffi;
2
3use std::{
4    ffi::{CStr, CString, c_char},
5    os::unix::ffi::OsStrExt,
6    path::Path,
7};
8
9pub use slog2_types::RegisterFlags;
10pub use slog2_types::Verbosity;
11
12use bitflags::bitflags;
13
14bitflags! {
15    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
16    pub struct ParseFlags: u32 {
17        const DYNAMIC = ffi::SLOG2_PARSE_FLAGS_DYNAMIC;
18        const CURRENT = ffi::SLOG2_PARSE_FLAGS_CURRENT;
19    }
20}
21
22bitflags! {
23    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
24    pub struct PacketFlags: u32 {
25        const FIRSTPACKET = ffi::SLOG2_PACKET_FLAGS_FIRSTPACKET;
26        const MONOTONIC = ffi::SLOG2_PACKET_FLAGS_MONOTONIC;
27    }
28}
29
30#[derive(Clone, Copy)]
31pub struct PacketInfo<'a> {
32    info: &'a ffi::slog2_packet_info_t,
33    payload: &'a CStr,
34}
35
36impl<'a> PacketInfo<'a> {
37    pub(crate) fn from_callback_data(
38        info: &'a ffi::slog2_packet_info_t,
39        payload: &'a CStr,
40    ) -> PacketInfo<'a> {
41        Self { info, payload }
42    }
43
44    pub fn sequence_number(&self) -> u16 {
45        self.info.sequence_number
46    }
47
48    pub fn size(&self) -> u16 {
49        self.info.data_size
50    }
51
52    pub fn timestamp_raw(&self) -> u64 {
53        self.info.timestamp
54    }
55
56    pub fn timestamp(&self) -> chrono::NaiveDateTime {
57        chrono::DateTime::from_timestamp_millis(self.timestamp_raw() as i64)
58            .unwrap_or_default()
59            .naive_local()
60    }
61
62    pub fn thread_id(&self) -> u16 {
63        self.info.thread_id
64    }
65
66    pub fn severity(&self) -> Verbosity {
67        Verbosity::from_u8(self.info.severity)
68    }
69
70    pub fn file_name(&self) -> Result<&str, std::str::Utf8Error> {
71        unsafe { CStr::from_ptr(self.info.file_name.as_ptr()).to_str() }
72    }
73
74    pub fn buffer_name(&self) -> Result<&str, std::str::Utf8Error> {
75        unsafe { CStr::from_ptr(self.info.buffer_name.as_ptr()).to_str() }
76    }
77
78    pub fn owner_pid(&self) -> u32 {
79        self.info.owner_pid
80    }
81
82    pub fn flags(&self) -> Option<PacketFlags> {
83        PacketFlags::from_bits(self.info.flags)
84    }
85
86    pub fn register_flags(&self) -> Option<RegisterFlags> {
87        RegisterFlags::from_bits(self.info.register_flags)
88    }
89
90    pub fn message(&self) -> Result<&str, std::str::Utf8Error> {
91        self.payload.to_str()
92    }
93}
94
95impl<'a> std::fmt::Debug for PacketInfo<'a> {
96    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
97        f.debug_struct("PacketInfo")
98            .field("sequence_number", &self.sequence_number())
99            .field("size", &self.size())
100            .field("timestamp", &self.timestamp())
101            .field("thread_id", &self.thread_id())
102            .field("severity", &self.severity())
103            .field("file_name", &self.file_name().ok())
104            .field("buffer_name", &self.buffer_name().ok())
105            .field("owner_pid", &self.owner_pid())
106            .field("flags", &self.flags())
107            .field("register_flags", &self.register_flags())
108            .field("message", &self.message().ok())
109            .finish()
110    }
111}
112
113pub struct LogInfo(ffi::slog2_log_info_t);
114
115impl LogInfo {
116    pub fn num_buffers(&self) -> usize {
117        self.0.num_buffers as usize
118    }
119
120    pub fn owner_pid(&self) -> u32 {
121        self.0.owner_pid
122    }
123
124    pub fn buffer_name(&self) -> Result<&str, std::str::Utf8Error> {
125        unsafe { CStr::from_ptr(self.0.buffer_set_name).to_str() }
126    }
127
128    pub fn verbosity(&self) -> Verbosity {
129        Verbosity::from_u8(self.0.verbosity_level)
130    }
131}
132
133impl std::fmt::Debug for LogInfo {
134    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
135        f.debug_struct("LogInfo")
136            .field("num_buffers", &self.num_buffers())
137            .field("owner_pid", &self.owner_pid())
138            .field("buffer_name", &self.buffer_name().ok())
139            .field("verbosity", &self.verbosity())
140            .finish()
141    }
142}
143
144pub struct BufferInfo(ffi::slog2_buffer_info_t);
145
146impl BufferInfo {
147    pub fn buffer_size(&self) -> u32 {
148        self.0.buffer_size
149    }
150
151    pub fn buffer_name(&self) -> Result<&str, std::str::Utf8Error> {
152        unsafe { CStr::from_ptr(self.0.buffer_name).to_str() }
153    }
154}
155
156impl std::fmt::Debug for BufferInfo {
157    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
158        f.debug_struct("BufferInfo")
159            .field("buffer_size", &self.buffer_size())
160            .field("buffer_name", &self.buffer_name().ok())
161            .finish()
162    }
163}
164
165pub struct BufferInfoIterator<'a> {
166    log_file: &'a LogFile,
167    num_buffers: usize,
168    current_index: usize,
169}
170
171impl<'a> BufferInfoIterator<'a> {
172    pub fn new(log_file: &'a LogFile, num_buffers: usize) -> BufferInfoIterator<'a> {
173        BufferInfoIterator {
174            num_buffers,
175            current_index: 0,
176            log_file: log_file,
177        }
178    }
179}
180
181impl<'a> Iterator for BufferInfoIterator<'a> {
182    type Item = Option<BufferInfo>;
183
184    fn next(&mut self) -> Option<Self::Item> {
185        if self.current_index >= self.num_buffers {
186            None
187        } else {
188            let buffer = self.log_file.buffer_info(self.current_index as i32).ok();
189            self.current_index += 1;
190            Some(buffer)
191        }
192    }
193}
194
195pub struct LogFile(ffi::slog2_log_t);
196
197impl LogFile {
198    pub fn open(filename: &str) -> Option<Self> {
199        let filename = CString::new(filename).ok()?;
200        let result = unsafe { ffi::slog2_open_log(filename.as_ptr()) };
201        if result.is_null() {
202            None
203        } else {
204            Some(LogFile(result))
205        }
206    }
207
208    pub fn info(&self) -> Result<LogInfo, i32> {
209        let mut info = ffi::SLOG2_LOG_INFO_INIT;
210        let result = unsafe { ffi::slog2_get_log_info(self.0, &mut info) };
211        if result == 0 {
212            Ok(LogInfo(info))
213        } else {
214            Err(result)
215        }
216    }
217
218    pub fn buffer_info(&self, buffer_index: i32) -> Result<BufferInfo, i32> {
219        let mut info = ffi::SLOG2_BUFFER_INFO_INIT;
220        let result = unsafe { ffi::slog2_get_buffer_info(self.0, buffer_index, &mut info) };
221        if result == 0 {
222            Ok(BufferInfo(info))
223        } else {
224            Err(result)
225        }
226    }
227
228    pub fn parse_static<F>(&self, buffer_index: i32, callback: F) -> Result<(), i32>
229    where
230        F: FnMut(PacketInfo) -> Result<(), i32>,
231    {
232        let mut callback_box = Box::new(callback);
233        let param = &mut *callback_box as *mut _ as *mut core::ffi::c_void;
234        let mut packet_info = ffi::SLOG2_PACKET_INFO_INIT;
235        let result = unsafe {
236            ffi::slog2_parse_dynamic_buffer(
237                self.0,
238                buffer_index,
239                &mut packet_info,
240                Some(parse_trampoline::<F>),
241                param,
242            )
243        };
244        if result == 0 { Ok(()) } else { Err(result) }
245    }
246
247    pub fn parse_dynamic<F>(&self, buffer_index: i32, callback: F) -> Result<(), i32>
248    where
249        F: FnMut(PacketInfo) -> Result<(), i32>,
250    {
251        let mut callback_box = Box::new(callback);
252        let param = &mut *callback_box as *mut _ as *mut core::ffi::c_void;
253        let mut packet_info = ffi::SLOG2_PACKET_INFO_INIT;
254        let result = unsafe {
255            ffi::slog2_parse_dynamic_buffer(
256                self.0,
257                buffer_index,
258                &mut packet_info,
259                Some(parse_trampoline::<F>),
260                param,
261            )
262        };
263        if result == 0 { Ok(()) } else { Err(result) }
264    }
265
266    pub(crate) fn buffer_info_iter(&self) -> Result<BufferInfoIterator, i32> {
267        Ok(BufferInfoIterator::new(self, self.info()?.num_buffers()))
268    }
269}
270
271impl<'a> IntoIterator for &'a LogFile {
272    type Item = Option<BufferInfo>;
273    type IntoIter = BufferInfoIterator<'a>;
274
275    fn into_iter(self) -> Self::IntoIter {
276        // Since we can't return an error we return an iterator with zero elements
277        self.buffer_info_iter().unwrap_or(BufferInfoIterator {
278            log_file: self,
279            num_buffers: 0,
280            current_index: 0,
281        })
282    }
283}
284
285impl Drop for LogFile {
286    fn drop(&mut self) {
287        unsafe { ffi::slog2_close_log(self.0) };
288    }
289}
290
291pub fn parse_all<F>(
292    flags: Option<ParseFlags>,
293    directory: Option<&Path>,
294    match_list: Option<&str>,
295    callback: F,
296) -> Result<(), i32>
297where
298    F: FnMut(PacketInfo) -> Result<(), i32>,
299{
300    let directory = directory
301        .map(|p| CString::new(p.as_os_str().as_bytes()).expect("Path contains interior NUL"));
302    let directory_ptr = directory
303        .as_ref()
304        .map_or(std::ptr::null_mut(), |cstr| cstr.as_ptr() as *mut c_char);
305
306    let match_list = match_list.map(|s| CString::new(s).expect("Path contains interior NUL"));
307    let match_list_ptr = match_list
308        .as_ref()
309        .map_or(std::ptr::null_mut(), |cstr| cstr.as_ptr() as *mut c_char);
310
311    let mut callback_box = Box::new(callback);
312    let param = &mut *callback_box as *mut _ as *mut core::ffi::c_void;
313    let mut packet_info = ffi::SLOG2_PACKET_INFO_INIT;
314    let result = unsafe {
315        ffi::slog2_parse_all(
316            flags.map(|flags| flags.bits()).unwrap_or_default(),
317            directory_ptr,
318            match_list_ptr,
319            &mut packet_info,
320            Some(parse_trampoline::<F>),
321            param,
322        )
323    };
324    if result == 0 { Ok(()) } else { Err(result) }
325}
326
327extern "C" fn parse_trampoline<F>(
328    info: *mut ffi::slog2_packet_info_t,
329    payload: *mut core::ffi::c_void,
330    param: *mut core::ffi::c_void,
331) -> i32
332where
333    F: FnMut(PacketInfo) -> Result<(), i32>,
334{
335    let closure: &mut F = unsafe { &mut *(param as *mut F) };
336    let payload = unsafe { CStr::from_ptr(payload as *mut c_char) };
337    let info = PacketInfo::from_callback_data(unsafe { &*info }, payload);
338    if let Err(res) = closure(info) { res } else { 0 }
339}