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