1use std::ffi::{CStr, CString};
6use std::os::raw::c_char;
7
8pub type HostLogErrorFn = extern "C" fn(*const c_char);
10pub type HostLogWarnFn = extern "C" fn(*const c_char);
11pub type HostLogInfoFn = extern "C" fn(*const c_char);
12pub type HostLogDebugFn = extern "C" fn(*const c_char);
13pub type HostLogTraceFn = extern "C" fn(*const c_char);
14
15pub trait ModuleLogger {
17 fn error(&self, msg: &str);
19
20 fn warn(&self, msg: &str);
22
23 fn info(&self, msg: &str);
25
26 fn debug(&self, msg: &str);
28
29 fn trace(&self, msg: &str);
31
32 fn log(&self, msg: &str) {
34 self.info(msg);
35 }
36}
37
38pub struct DefaultModuleLogger {
40 host_log_error: Option<HostLogErrorFn>,
42 host_log_warn: Option<HostLogWarnFn>,
43 host_log_info: Option<HostLogInfoFn>,
44 host_log_debug: Option<HostLogDebugFn>,
45 host_log_trace: Option<HostLogTraceFn>,
46 module_name: String,
48}
49
50impl DefaultModuleLogger {
51 pub const fn new() -> Self {
53 Self {
54 host_log_error: None,
55 host_log_warn: None,
56 host_log_info: None,
57 host_log_debug: None,
58 host_log_trace: None,
59 module_name: String::new(),
60 }
61 }
62
63 pub fn with_name(name: &str) -> Self {
65 Self {
66 host_log_error: None,
67 host_log_warn: None,
68 host_log_info: None,
69 host_log_debug: None,
70 host_log_trace: None,
71 module_name: name.to_string(),
72 }
73 }
74
75 pub fn set_module_name(&mut self, name: &str) {
77 self.module_name = name.to_string();
78 }
79
80 pub fn module_name(&self) -> &str {
82 &self.module_name
83 }
84
85 pub fn set_host_callbacks(
87 &mut self,
88 error_fn: HostLogErrorFn,
89 warn_fn: HostLogWarnFn,
90 info_fn: HostLogInfoFn,
91 debug_fn: HostLogDebugFn,
92 trace_fn: HostLogTraceFn,
93 ) {
94 self.host_log_error = Some(error_fn);
95 self.host_log_warn = Some(warn_fn);
96 self.host_log_info = Some(info_fn);
97 self.host_log_debug = Some(debug_fn);
98 self.host_log_trace = Some(trace_fn);
99 }
100
101 pub fn global() -> *mut DefaultModuleLogger {
103 static mut GLOBAL_LOGGER: DefaultModuleLogger = DefaultModuleLogger::new();
104 unsafe { &raw mut GLOBAL_LOGGER }
105 }
106}
107
108impl Default for DefaultModuleLogger {
109 fn default() -> Self {
110 Self::new()
111 }
112}
113
114impl ModuleLogger for DefaultModuleLogger {
115 fn error(&self, msg: &str) {
116 let prefix = if self.module_name.is_empty() {
117 "[module]".to_string()
118 } else {
119 format!("[{}]", self.module_name)
120 };
121
122 if let Some(cb) = self.host_log_error {
123 let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
124 unsafe { cb(c.as_ptr()) };
125 } else {
126 tracing::error!("{} {}", prefix, msg);
128 }
129 }
130
131 fn warn(&self, msg: &str) {
132 let prefix = if self.module_name.is_empty() {
133 "[module]".to_string()
134 } else {
135 format!("[{}]", self.module_name)
136 };
137
138 if let Some(cb) = self.host_log_warn {
139 let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
140 unsafe { cb(c.as_ptr()) };
141 } else {
142 tracing::warn!("{} {}", prefix, msg);
143 }
144 }
145
146 fn info(&self, msg: &str) {
147 let prefix = if self.module_name.is_empty() {
148 "[module]".to_string()
149 } else {
150 format!("[{}]", self.module_name)
151 };
152
153 if let Some(cb) = self.host_log_info {
154 let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
155 unsafe { cb(c.as_ptr()) };
156 } else {
157 tracing::info!("{} {}", prefix, msg);
158 }
159 }
160
161 fn debug(&self, msg: &str) {
162 let prefix = if self.module_name.is_empty() {
163 "[module]".to_string()
164 } else {
165 format!("[{}]", self.module_name)
166 };
167
168 if let Some(cb) = self.host_log_debug {
169 let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
170 unsafe { cb(c.as_ptr()) };
171 } else {
172 tracing::debug!("{} {}", prefix, msg);
173 }
174 }
175
176 fn trace(&self, msg: &str) {
177 let prefix = if self.module_name.is_empty() {
178 "[module]".to_string()
179 } else {
180 format!("[{}]", self.module_name)
181 };
182
183 if let Some(cb) = self.host_log_trace {
184 let c = CString::new(format!("{} {}", prefix, msg)).unwrap();
185 unsafe { cb(c.as_ptr()) };
186 } else {
187 tracing::trace!("{} {}", prefix, msg);
188 }
189 }
190}
191
192pub mod host_functions {
194 use super::*;
195
196 #[unsafe(no_mangle)]
198 pub extern "C" fn host_log_error(ptr: *const c_char) {
199 let msg = unsafe { CStr::from_ptr(ptr) }
200 .to_string_lossy()
201 .into_owned();
202
203 tracing::error!(target: "module", "[MODULE] {}", msg);
204 }
205
206 #[unsafe(no_mangle)]
208 pub extern "C" fn host_log_warn(ptr: *const c_char) {
209 let msg = unsafe { CStr::from_ptr(ptr) }
210 .to_string_lossy()
211 .into_owned();
212
213 tracing::warn!(target: "module", "[MODULE] {}", msg);
214 }
215
216 #[unsafe(no_mangle)]
218 pub extern "C" fn host_log_info(ptr: *const c_char) {
219 let msg = unsafe { CStr::from_ptr(ptr) }
220 .to_string_lossy()
221 .into_owned();
222
223 tracing::info!(target: "module", "[MODULE] {}", msg);
224 }
225
226 #[unsafe(no_mangle)]
228 pub extern "C" fn host_log_debug(ptr: *const c_char) {
229 let msg = unsafe { CStr::from_ptr(ptr) }
230 .to_string_lossy()
231 .into_owned();
232
233 tracing::debug!(target: "module", "[MODULE] {}", msg);
234 }
235
236 #[unsafe(no_mangle)]
238 pub extern "C" fn host_log_trace(ptr: *const c_char) {
239 let msg = unsafe { CStr::from_ptr(ptr) }
240 .to_string_lossy()
241 .into_owned();
242
243 tracing::trace!(target: "module", "[MODULE] {}", msg);
244 }
245}
246
247pub mod module_setup {
249 use super::*;
250
251 pub type SetLoggerFn = unsafe extern "C" fn(
253 HostLogErrorFn,
254 HostLogWarnFn,
255 HostLogInfoFn,
256 HostLogDebugFn,
257 HostLogTraceFn,
258 );
259
260 pub unsafe fn setup_module_logger(
263 error_fn: HostLogErrorFn,
264 warn_fn: HostLogWarnFn,
265 info_fn: HostLogInfoFn,
266 debug_fn: HostLogDebugFn,
267 trace_fn: HostLogTraceFn,
268 ) {
269 let logger = DefaultModuleLogger::global();
270 unsafe {
271 (*logger).set_host_callbacks(error_fn, warn_fn, info_fn, debug_fn, trace_fn);
272 }
273 }
274
275 pub fn set_module_name(name: &str) {
277 let logger = DefaultModuleLogger::global();
278 unsafe {
279 (*logger).set_module_name(name);
280 }
281 }
282
283 pub unsafe fn setup_module_logger_with_name(
286 module_name: &str,
287 error_fn: HostLogErrorFn,
288 warn_fn: HostLogWarnFn,
289 info_fn: HostLogInfoFn,
290 debug_fn: HostLogDebugFn,
291 trace_fn: HostLogTraceFn,
292 ) {
293 let logger = DefaultModuleLogger::global();
294 unsafe {
295 (*logger).set_module_name(module_name);
296 (*logger).set_host_callbacks(error_fn, warn_fn, info_fn, debug_fn, trace_fn);
297 }
298 }
299
300 pub fn get_logger() -> &'static DefaultModuleLogger {
302 unsafe { &*DefaultModuleLogger::global() }
303 }
304}
305
306#[macro_export]
308macro_rules! module_log_error {
309 ($($arg:tt)*) => {
310 $crate::module_logging::module_setup::get_logger().error(&format!($($arg)*))
311 };
312}
313
314#[macro_export]
315macro_rules! module_log_warn {
316 ($($arg:tt)*) => {
317 $crate::module_logging::module_setup::get_logger().warn(&format!($($arg)*))
318 };
319}
320
321#[macro_export]
322macro_rules! module_log_info {
323 ($($arg:tt)*) => {
324 $crate::module_logging::module_setup::get_logger().info(&format!($($arg)*))
325 };
326}
327
328#[macro_export]
329macro_rules! module_log_debug {
330 ($($arg:tt)*) => {
331 $crate::module_logging::module_setup::get_logger().debug(&format!($($arg)*))
332 };
333}
334
335#[macro_export]
336macro_rules! module_log_trace {
337 ($($arg:tt)*) => {
338 $crate::module_logging::module_setup::get_logger().trace(&format!($($arg)*))
339 };
340}
341
342#[macro_export]
343macro_rules! module_log {
344 ($($arg:tt)*) => {
345 $crate::module_logging::module_setup::get_logger().log(&format!($($arg)*))
346 };
347}