use std::convert::TryFrom;
use std::ffi::{CStr, CString};
use std::ptr;
use std::str::from_utf8;
use fern::Dispatch;
use libc::{c_char, c_int};
use log::{error, LevelFilter as LogLevelFilter};
use pact_matching::logging::fetch_buffer_contents;
use crate::error::set_error_msg;
use crate::log::level_filter::LevelFilter;
use crate::log::logger::{add_sink, apply_logger, set_logger};
use crate::log::sink::Sink;
use crate::log::status::Status;
use crate::util::string::to_c;
#[no_mangle]
pub extern "C" fn log_to_stdout(level_filter: LevelFilter) -> c_int {
logger_init();
let spec = match CString::new("stdout") {
Ok(spec) => spec,
Err(e) => {
set_error_msg(e.to_string());
return Status::CantConstructSink as c_int;
}
};
let status = logger_attach_sink(spec.as_ptr(), level_filter);
if status != 0 {
return status;
}
let status = logger_apply();
if status != 0 {
return status;
}
Status::Success as c_int
}
#[no_mangle]
pub extern "C" fn log_to_stderr(level_filter: LevelFilter) -> c_int {
logger_init();
let spec = match CString::new("stderr") {
Ok(spec) => spec,
Err(e) => {
set_error_msg(e.to_string());
return Status::CantConstructSink as c_int;
}
};
let status = logger_attach_sink(spec.as_ptr(), level_filter);
if status != 0 {
return status;
}
let status = logger_apply();
if status != 0 {
return status;
}
Status::Success as c_int
}
#[no_mangle]
pub unsafe extern "C" fn log_to_file(
file_name: *const c_char,
level_filter: LevelFilter,
) -> c_int {
logger_init();
let spec = {
if file_name.is_null() {
return Status::CantConstructSink as c_int;
}
let file_name =
match CStr::from_ptr(file_name).to_str() {
Ok(file_name) => file_name,
Err(e) => {
set_error_msg(e.to_string());
return Status::CantConstructSink as c_int;
}
};
let spec = format!("file {}", file_name);
match CString::new(spec) {
Ok(spec) => spec,
Err(e) => {
set_error_msg(e.to_string());
return Status::CantConstructSink as c_int;
}
}
};
let status = logger_attach_sink(spec.as_ptr(), level_filter);
if status != 0 {
return status;
}
let status = logger_apply();
if status != 0 {
return status;
}
Status::Success as c_int
}
#[no_mangle]
pub extern "C" fn log_to_buffer(level_filter: LevelFilter) -> c_int {
logger_init();
let spec = match CString::new("buffer") {
Ok(spec) => spec,
Err(e) => {
set_error_msg(e.to_string());
return Status::CantConstructSink as c_int;
}
};
let status = logger_attach_sink(spec.as_ptr(), level_filter);
if status != 0 {
return status;
}
let status = logger_apply();
if status != 0 {
return status;
}
Status::Success as c_int
}
#[no_mangle]
pub extern "C" fn logger_init() {
set_logger(Dispatch::new());
}
#[allow(clippy::missing_safety_doc)]
#[allow(clippy::not_unsafe_ptr_arg_deref)]
#[no_mangle]
pub extern "C" fn logger_attach_sink(
sink_specifier: *const c_char,
level_filter: LevelFilter,
) -> c_int {
let sink_specifier = unsafe { CStr::from_ptr(sink_specifier) };
let sink_specifier = match sink_specifier.to_str() {
Ok(sink_specifier) => sink_specifier,
Err(_) => return Status::SpecifierNotUtf8 as c_int,
};
let sink = match Sink::try_from(sink_specifier) {
Ok(sink) => sink,
Err(err) => return Status::from(err) as c_int,
};
let level_filter: LogLevelFilter = level_filter.into();
let dispatch = Into::<Dispatch>::into(sink)
.level(level_filter)
.format(|out, message, record| {
out.finish(format_args!(
"[{}][{}] {}",
record.level(),
record.target(),
message
))
});
let status = match add_sink(dispatch) {
Ok(_) => Status::Success,
Err(err) => Status::from(err),
};
status as c_int
}
#[no_mangle]
pub extern "C" fn logger_apply() -> c_int {
let status = match apply_logger() {
Ok(_) => Status::Success,
Err(err) => Status::from(err),
};
status as c_int
}
#[no_mangle]
pub unsafe extern "C" fn fetch_log_buffer(log_id: *const c_char,) -> *const c_char {
let id = if log_id.is_null() {
"global"
} else {
CStr::from_ptr(log_id).to_str().unwrap_or("global")
};
match from_utf8(&fetch_buffer_contents(&id.to_string())) {
Ok(contents) => match to_c(contents) {
Ok(c_str) => c_str,
Err(err) => {
error!("Failed to copy in-memory log buffer - {}", err);
ptr::null()
}
}
Err(err) => {
error!("Failed to convert in-memory log buffer to UTF-8 = {}", err);
ptr::null()
}
}
}