Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions src/ffi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,17 @@ const VALUE_NUMBER: c_int = 2;
/// `LCH_VALUE_BOOLEAN` from `leech2.h`. Cell kind tag.
const VALUE_BOOLEAN: c_int = 3;

/// `LCH_LOG_ERROR` from `leech2.h`. Log level passed to `lch_log_callback_t`.
pub const LOG_ERROR: i32 = 1;
/// `LCH_LOG_WARN` from `leech2.h`. Log level passed to `lch_log_callback_t`.
pub const LOG_WARN: i32 = 2;
/// `LCH_LOG_INFO` from `leech2.h`. Log level passed to `lch_log_callback_t`.
pub const LOG_INFO: i32 = 3;
/// `LCH_LOG_DEBUG` from `leech2.h`. Log level passed to `lch_log_callback_t`.
pub const LOG_DEBUG: i32 = 4;
/// `LCH_LOG_TRACE` from `leech2.h`. Log level passed to `lch_log_callback_t`.
pub const LOG_TRACE: i32 = 5;

/// Run an FFI body inside `catch_unwind`, returning `default` if a panic is caught.
/// Panicking across an `extern "C"` boundary is undefined behavior, so every FFI
/// entry point routes its body through this guard as a last line of defense.
Expand Down
15 changes: 13 additions & 2 deletions src/logger.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
use std::ffi::{CString, c_char, c_void};
use std::sync::{Once, RwLock};

use log::{LevelFilter, Log, Metadata, Record};
use log::{Level, LevelFilter, Log, Metadata, Record};

use crate::ffi::{LOG_DEBUG, LOG_ERROR, LOG_INFO, LOG_TRACE, LOG_WARN};

type LogCallback = unsafe extern "C" fn(i32, *const c_char, *mut c_void);

Expand Down Expand Up @@ -32,7 +34,16 @@ impl Log for CallbackLogger {
if let Some(ref state) = *guard {
let message = format!("{}", record.args());
if let Ok(cstr) = CString::new(message) {
let level = record.level() as i32;
// Map explicitly to the LCH_LOG_* values rather than casting
// the log crate's Level discriminant, which is not a stable
// ABI contract.
let level = match record.level() {
Level::Error => LOG_ERROR,
Level::Warn => LOG_WARN,
Level::Info => LOG_INFO,
Level::Debug => LOG_DEBUG,
Level::Trace => LOG_TRACE,
};
unsafe {
(state.callback)(level, cstr.as_ptr(), state.user_data);
}
Expand Down
Loading