1use std::ffi::CStr;
4use std::{error, fmt, slice};
5
6type LogCallback = Box<dyn Fn(Level, &'static CStr, &'static CStr, u32, &[u8])>;
7
8static mut LOG_CB: Option<LogCallback> = None;
9
10pub unsafe fn set_log_cb(
17 cb: impl Fn(Level, &'static CStr, &'static CStr, u32, &[u8]) + 'static,
18 min_level: Level,
19) {
20 unsafe { LOG_CB = Some(Box::new(cb)) };
21 unsafe { libddwaf_sys::ddwaf_set_log_cb(Some(bridge_log_cb), min_level.as_raw()) };
22}
23
24pub unsafe fn reset_log_cb() {
31 unsafe { libddwaf_sys::ddwaf_set_log_cb(None, Level::Off.as_raw()) };
32 unsafe { LOG_CB = None };
33}
34
35#[non_exhaustive]
37#[derive(Debug, Copy, Clone, PartialEq, Eq)]
38pub enum Level {
39 Trace,
41 Debug,
43 Info,
45 Warn,
47 Error,
49 Off,
51}
52impl Level {
53 const fn as_raw(self) -> libddwaf_sys::DDWAF_LOG_LEVEL {
54 match self {
55 Self::Trace => libddwaf_sys::DDWAF_LOG_TRACE,
56 Self::Debug => libddwaf_sys::DDWAF_LOG_DEBUG,
57 Self::Info => libddwaf_sys::DDWAF_LOG_INFO,
58 Self::Warn => libddwaf_sys::DDWAF_LOG_WARN,
59 Self::Error => libddwaf_sys::DDWAF_LOG_ERROR,
60 Self::Off => libddwaf_sys::DDWAF_LOG_OFF,
61 }
62 }
63}
64impl fmt::Display for Level {
65 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
66 match self {
67 Self::Trace => write!(f, "TRACE"),
68 Self::Debug => write!(f, "DEBUG"),
69 Self::Info => write!(f, "INFO"),
70 Self::Warn => write!(f, "WARN"),
71 Self::Error => write!(f, "ERROR"),
72 Self::Off => write!(f, "OFF"),
73 }
74 }
75}
76impl TryFrom<libddwaf_sys::DDWAF_LOG_LEVEL> for Level {
77 type Error = UnknownLogLevelError;
78
79 fn try_from(value: libddwaf_sys::DDWAF_LOG_LEVEL) -> Result<Self, UnknownLogLevelError> {
80 match value {
81 libddwaf_sys::DDWAF_LOG_TRACE => Ok(Level::Trace),
82 libddwaf_sys::DDWAF_LOG_DEBUG => Ok(Level::Debug),
83 libddwaf_sys::DDWAF_LOG_INFO => Ok(Level::Info),
84 libddwaf_sys::DDWAF_LOG_WARN => Ok(Level::Warn),
85 libddwaf_sys::DDWAF_LOG_ERROR => Ok(Level::Error),
86 libddwaf_sys::DDWAF_LOG_OFF => Ok(Level::Off),
87 unknown => Err(UnknownLogLevelError { raw: unknown }),
88 }
89 }
90}
91
92#[derive(Debug)]
94pub struct UnknownLogLevelError {
95 raw: libddwaf_sys::DDWAF_LOG_LEVEL,
96}
97impl fmt::Display for UnknownLogLevelError {
98 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
99 write!(f, "Unknown log level: 0x{:02X}", self.raw)
100 }
101}
102impl error::Error for UnknownLogLevelError {}
103
104extern "C" fn bridge_log_cb(
107 level: libddwaf_sys::DDWAF_LOG_LEVEL,
108 file: *const std::os::raw::c_char,
109 function: *const std::os::raw::c_char,
110 line: u32,
111 message: *const std::os::raw::c_char,
112 message_len: u64,
113) {
114 unsafe {
115 #[allow(static_mut_refs)]
116 if let Some(cb) = &LOG_CB {
117 let file = CStr::from_ptr(file);
118 let function = CStr::from_ptr(function);
119 let message = if message.is_null() {
120 b"<null>\0"
121 } else {
122 slice::from_raw_parts(message.cast(), message_len.try_into().unwrap_or(usize::MAX))
123 };
124 cb(
125 Level::try_from(level).unwrap_or(Level::Error),
126 file,
127 function,
128 line,
129 message,
130 );
131 }
132 }
133}