use crate::common::{CWString, NullByteError};
pub use log::LevelFilter;
pub type Formatter = dyn Fn(&log::Record) -> String + Send + Sync + 'static;
#[must_use]
pub struct LogBuilder {
filter: env_filter::Builder,
formatter: Option<Box<Formatter>>,
}
impl LogBuilder {
pub fn new() -> Self {
LogBuilder {
filter: env_filter::Builder::new(),
formatter: None,
}
}
pub fn filter_level(mut self, level: log::LevelFilter) -> Self {
self.filter.filter_level(level);
self
}
pub fn filter_module(mut self, module: &str, level: log::LevelFilter) -> Self {
self.filter.filter_module(module, level);
self
}
pub fn formatter<F>(mut self, formatter: F) -> Self
where
F: Fn(&log::Record) -> String + Send + Sync + 'static,
{
self.formatter = Some(Box::new(formatter));
self
}
pub fn default_formatter(mut self) -> Self {
self.formatter = None;
self
}
pub fn init(self) {
self.try_init().expect("Failed to initialize logger")
}
pub fn try_init(self) -> Result<(), log::SetLoggerError> {
let LogBuilder {
mut filter,
formatter,
} = self;
let filter = filter.build();
let logger = InternalLogger::new(formatter.unwrap_or_else(|| {
Box::new(|record: &log::Record| format!("[{}] {}", record.target(), record.args()))
}));
log::set_max_level(filter.filter());
let logger = env_filter::FilteredLog::new(logger, filter);
log::set_boxed_logger(Box::new(logger))?;
Ok(())
}
}
impl Default for LogBuilder {
fn default() -> Self {
Self::new()
}
}
struct InternalLogger {
formatter: Box<Formatter>,
}
impl InternalLogger {
fn new(formatter: Box<Formatter>) -> Self {
InternalLogger { formatter }
}
}
impl log::Log for InternalLogger {
fn enabled(&self, _metadata: &log::Metadata) -> bool {
true
}
fn log(&self, record: &log::Record) {
let message = (self.formatter)(record);
send_record(record.level(), message);
}
fn flush(&self) {
}
}
#[macro_export]
macro_rules! ldbg {
() => {
$crate::lprintln!(verbose, "[{}:{}:{}]", ::std::file!(), ::std::line!(), ::std::column!());
};
($val:expr $(,)?) => {
match $val {
tmp => {
$crate::lprintln!(verbose, "[{}:{}:{}] {} = {:#?}",
::std::file!(),
::std::line!(),
::std::column!(),
::std::stringify!($val),
&&tmp as &dyn ::std::fmt::Debug,
);
tmp
}
}
};
($($val:expr),+ $(,)?) => {
($($crate::ldbg!($val)),+,)
};
}
#[macro_export]
macro_rules! lprintln {
(plugin, $($arg:tt)*) => {
let _ = $crate::logger::write_plugin_log(&format!($($arg)*));
};
(info, $($arg:tt)*) => {
let _ = $crate::logger::write_info_log(&format!($($arg)*));
};
(warn, $($arg:tt)*) => {
let _ = $crate::logger::write_warn_log(&format!($($arg)*));
};
(error, $($arg:tt)*) => {
let _ = $crate::logger::write_error_log(&format!($($arg)*));
};
(verbose, $($arg:tt)*) => {
let _ = $crate::logger::write_verbose_log(&format!($($arg)*));
};
($($arg:tt)*) => {
$crate::lprintln!(plugin, $($arg)*);
};
}
pub fn write_plugin_log(message: &str) -> Result<(), NullByteError> {
with_logger_handle(|handle| unsafe {
let wide_message = CWString::new(message)?;
((*handle).log)(handle, wide_message.as_ptr());
Ok(())
})
.unwrap_or(Ok(()))
}
#[duplicate::duplicate_item(
level function_name log_method;
["ERROR"] [write_error_log] [error];
["WARN"] [write_warn_log] [warn];
["INFO"] [write_info_log] [info];
["VERBOSE"] [write_verbose_log] [verbose];
)]
#[doc = concat!("ログに", level, "レベルのメッセージを書き込みます。")]
pub fn function_name(message: &str) -> Result<(), NullByteError> {
with_logger_handle(|handle| unsafe {
let wide_message = CWString::new(message)?;
((*handle).log_method)(handle, wide_message.as_ptr());
Ok(())
})
.unwrap_or(Ok(()))
}
struct InternalLoggerHandle(*mut aviutl2_sys::logger2::LOG_HANDLE);
unsafe impl Send for InternalLoggerHandle {}
static LOGGER_HANDLE: std::sync::OnceLock<std::sync::Mutex<InternalLoggerHandle>> =
std::sync::OnceLock::new();
#[doc(hidden)]
pub fn __initialize_logger(handle: *mut aviutl2_sys::logger2::LOG_HANDLE) {
let internal_handle = InternalLoggerHandle(handle);
LOGGER_HANDLE
.set(std::sync::Mutex::new(internal_handle))
.unwrap_or_else(|_| {
panic!("Logger has already been initialized");
});
}
impl InternalLoggerHandle {
fn ptr(&self) -> *mut aviutl2_sys::logger2::LOG_HANDLE {
self.0
}
}
fn with_logger_handle<F, T>(f: F) -> Option<T>
where
F: FnOnce(*mut aviutl2_sys::logger2::LOG_HANDLE) -> T,
{
let handle = LOGGER_HANDLE.get()?;
let handle = handle.lock().unwrap();
let handle_ptr = handle.ptr();
Some(f(handle_ptr))
}
fn send_record(level: log::Level, message: String) {
match level {
log::Level::Error => {
let _ = write_error_log(&message);
}
log::Level::Warn => {
let _ = write_warn_log(&message);
}
log::Level::Info => {
let _ = write_info_log(&message);
}
log::Level::Debug | log::Level::Trace => {
let _ = write_verbose_log(&message);
}
}
}
#[cfg(test)]
mod tests {
#[test]
fn test_can_compile_ldbg() {
let x = 42;
ldbg!();
ldbg!(x);
ldbg!(x + 1, x * 2);
}
}