use crate::consts::TraceLogLevel;
use crate::ffi;
use std::ffi::CString;
#[inline]
pub fn set_trace_log(types: TraceLogLevel) {
unsafe {
ffi::SetTraceLogLevel((types as u32) as i32);
}
}
#[inline]
pub fn trace_log(msg_type: TraceLogLevel, text: &str) {
unsafe {
let text = CString::new(text).unwrap();
ffi::TraceLog((msg_type as u32) as i32, text.as_ptr());
}
}
#[cfg(feature = "log")]
#[inline]
fn log_bridge(level: TraceLogLevel, text: &str) {
use log::Level;
let level = match level {
TraceLogLevel::LOG_TRACE => Level::Trace,
TraceLogLevel::LOG_DEBUG => Level::Debug,
TraceLogLevel::LOG_INFO => Level::Info,
TraceLogLevel::LOG_WARNING => Level::Warn,
TraceLogLevel::LOG_ERROR | TraceLogLevel::LOG_FATAL => Level::Error,
TraceLogLevel::LOG_NONE | TraceLogLevel::LOG_ALL => return,
};
log::log!(target: "raylib", level, "{text}");
}
#[cfg(feature = "log")]
pub(crate) fn install_log_bridge() {
let _ = crate::core::callbacks::set_trace_log_callback(log_bridge);
set_trace_log(TraceLogLevel::LOG_ALL);
}
#[cfg(all(test, feature = "log"))]
mod tests {
use super::*;
use std::sync::Mutex;
struct CaptureLogger {
records: Mutex<Vec<(log::Level, String, String)>>,
}
impl log::Log for CaptureLogger {
fn enabled(&self, _: &log::Metadata<'_>) -> bool {
true
}
fn log(&self, record: &log::Record<'_>) {
self.records.lock().unwrap().push((
record.level(),
record.target().to_string(),
record.args().to_string(),
));
}
fn flush(&self) {}
}
static LOGGER: CaptureLogger = CaptureLogger {
records: Mutex::new(Vec::new()),
};
#[test]
fn bridge_forwards_trace_log_to_log_facade() {
log::set_logger(&LOGGER)
.expect("another logger is already installed in this process — run via cargo nextest (per-test process isolation)");
log::set_max_level(log::LevelFilter::Trace);
install_log_bridge();
trace_log(TraceLogLevel::LOG_WARNING, "bridge-ffi-probe");
log_bridge(TraceLogLevel::LOG_TRACE, "map-trace");
log_bridge(TraceLogLevel::LOG_DEBUG, "map-debug");
log_bridge(TraceLogLevel::LOG_INFO, "map-info");
log_bridge(TraceLogLevel::LOG_WARNING, "map-warn");
log_bridge(TraceLogLevel::LOG_ERROR, "map-error");
log_bridge(TraceLogLevel::LOG_FATAL, "map-fatal");
log_bridge(TraceLogLevel::LOG_NONE, "map-none");
log_bridge(TraceLogLevel::LOG_ALL, "map-all");
let records = LOGGER.records.lock().unwrap();
let find = |needle: &str| {
records
.iter()
.find(|(_, _, msg)| msg.contains(needle))
.cloned()
};
let (lvl, target, _) = find("bridge-ffi-probe").expect("ffi probe forwarded");
assert_eq!(lvl, log::Level::Warn);
assert_eq!(target, "raylib");
assert_eq!(find("map-trace").unwrap().0, log::Level::Trace);
assert_eq!(find("map-debug").unwrap().0, log::Level::Debug);
assert_eq!(find("map-info").unwrap().0, log::Level::Info);
assert_eq!(find("map-warn").unwrap().0, log::Level::Warn);
assert_eq!(find("map-error").unwrap().0, log::Level::Error);
assert_eq!(find("map-fatal").unwrap().0, log::Level::Error);
assert!(find("map-none").is_none(), "LOG_NONE must not emit");
assert!(find("map-all").is_none(), "LOG_ALL must not emit");
assert!(records.iter().all(|(_, t, _)| t == "raylib"));
}
}