Skip to main content

libcdio_rs/
logging.rs

1// Copyright (C) 2026 Shiva Kiran Koninty <shiva@skran.xyz>
2//
3// This file is part of libcdio-rs.
4//
5// libcdio-rs is free software: you can redistribute it and/or
6// modify it under the terms of the GNU General Public License as
7// published by the Free Software Foundation, either version 3 of the
8// License, or (at your option) any later version.
9//
10// libcdio-rs is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13// General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with libcdio-rs. If not, see <https://www.gnu.org/licenses/>.
17
18use std::{
19    ffi::{CStr, c_char},
20    sync::Once,
21};
22
23use libcdio_sys::{
24    cdio_log_level_t, cdio_log_level_t_CDIO_LOG_ASSERT, cdio_log_level_t_CDIO_LOG_DEBUG,
25    cdio_log_level_t_CDIO_LOG_ERROR, cdio_log_level_t_CDIO_LOG_INFO,
26    cdio_log_level_t_CDIO_LOG_WARN,
27};
28
29/// Configures libcdio to emit tracing (or "log" crate) logs.
30/// This should be called before using any other methods.
31pub(crate) fn init_logger() {
32    LOG_HANDLER.call_once(|| unsafe {
33        libcdio_sys::cdio_log_set_handler(Some(log_handler));
34
35        // libcdio doesn't call our log handler unless its log level is
36        // appropriate. Since the current log level is chosen by log
37        // subscribers, hack around by setting libcdio's log level to debug.
38        libcdio_sys::cdio_loglevel_default = cdio_log_level_t_CDIO_LOG_DEBUG;
39    });
40}
41
42static LOG_HANDLER: Once = Once::new();
43
44/// Handle logs via tracing.
45extern "C" fn log_handler(level: cdio_log_level_t, message: *const c_char) {
46    if message.is_null() {
47        return;
48    }
49    // SAFETY: message is not null, and is backed by an array on the
50    // caller's stack.
51    let message = unsafe { CStr::from_ptr(message) };
52    let Ok(message) = message.to_str() else {
53        return;
54    };
55    #[allow(non_upper_case_globals)]
56    match level {
57        cdio_log_level_t_CDIO_LOG_ASSERT | cdio_log_level_t_CDIO_LOG_ERROR => {
58            tracing::error!(message)
59        }
60        cdio_log_level_t_CDIO_LOG_WARN => tracing::warn!(message),
61        cdio_log_level_t_CDIO_LOG_INFO => tracing::info!(message),
62        _ => tracing::debug!(message),
63    };
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69
70    #[test_log::test(test)]
71    fn log_test() {
72        init_logger();
73        unsafe {
74            libcdio_sys::cdio_log(cdio_log_level_t_CDIO_LOG_ASSERT, c"assert msg".as_ptr());
75            libcdio_sys::cdio_log(cdio_log_level_t_CDIO_LOG_ERROR, c"error msg".as_ptr());
76            libcdio_sys::cdio_log(cdio_log_level_t_CDIO_LOG_WARN, c"warn msg".as_ptr());
77            libcdio_sys::cdio_log(cdio_log_level_t_CDIO_LOG_INFO, c"info msg".as_ptr());
78            libcdio_sys::cdio_log(cdio_log_level_t_CDIO_LOG_DEBUG, c"debug msg".as_ptr());
79        }
80    }
81}