1
0
mirror of https://github.com/jht5945/rust_util.git synced 2025-12-27 07:30:05 +08:00
Files
rust_util/src/util_msg.rs
2025-07-26 08:52:31 +08:00

235 lines
6.8 KiB
Rust

use std::env;
use std::io::{self, Write};
use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::Sender;
lazy_static! {
pub static ref IS_ATTY: bool = is_atty();
static ref LOGGER_LEVEL: MessageType = get_logger_level();
static ref LOGGER_SENDER: Arc<RwLock<Option<Sender<String>>>> = Arc::new(RwLock::new(None));
static ref LOGGER_TO_STDOUT: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
static ref PRINT_MESSAGE_LOCK: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
}
pub fn set_logger_sender(sender: Sender<String>) {
let mut logger_sender_opt = LOGGER_SENDER.write().unwrap();
logger_sender_opt.replace(sender);
}
pub fn set_logger_std_out(is_std_out: bool) {
let mut std_out = LOGGER_TO_STDOUT.write().unwrap();
*std_out = is_std_out;
}
pub fn get_logger_std_out() -> bool {
*LOGGER_TO_STDOUT.read().unwrap()
}
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Copy)]
pub enum MessageType { DEBUG, INFO, OK, WARN, ERROR }
impl MessageType {
pub fn get_u8_value(&self) -> u8 {
match self {
MessageType::DEBUG => 0,
MessageType::INFO => 1,
MessageType::OK => 2,
MessageType::WARN => 3,
MessageType::ERROR => 4,
}
}
}
pub fn get_logger_level() -> MessageType {
if let Some(logger_level) = env::var("LOGGER_LEVEL").ok()
.or_else(|| env::var("LOGGER").ok())
.or_else(|| env::var("LEVEL").ok()) {
match logger_level.trim().to_lowercase().as_str() {
"debug" | "*" => MessageType::DEBUG,
"info" | "?" => MessageType::INFO,
"ok" | "#" => MessageType::OK,
"warn" | "!" => MessageType::WARN,
"error" | "^" => MessageType::ERROR,
_ => {
print_message_ex(Some(term::color::YELLOW), "[WARN ]", &format!("Unknown logger level: {}, set to default INFO", logger_level));
MessageType::INFO
}
}
} else {
MessageType::INFO
}
}
pub fn is_atty() -> bool {
let stdout_fileno = unsafe { libc::isatty(libc::STDOUT_FILENO) };
stdout_fileno != 0
}
pub fn print_color(is_std_out: bool, color: Option<term::color::Color>, is_bold: bool, m: &str) {
if is_std_out {
match term::stdout() {
Some(mut t) => {
if *IS_ATTY {
if let Some(c) = color {
t.fg(c).ok();
}
if is_bold {
t.attr(term::Attr::Bold).ok();
}
write!(t, "{}", m).ok();
t.reset().ok();
} else {
write!(t, "{}", m).ok();
}
}
None => print!("{}", m),
}
} else {
match term::stderr() {
Some(mut t) => {
if *IS_ATTY {
if let Some(c) = color {
t.fg(c).ok();
}
if is_bold {
t.attr(term::Attr::Bold).ok();
}
write!(t, "{}", m).ok();
t.reset().ok();
} else {
write!(t, "{}", m).ok();
}
}
None => eprint!("{}", m),
}
}
}
pub fn print_color_and_flush(color: Option<term::color::Color>, is_bold: bool, m: &str) {
print_color(true, color, is_bold, m);
flush_stdout();
}
pub fn print_message_ex(color: Option<term::color::Color>, h: &str, message: &str) {
{
let logger_sender_opt = LOGGER_SENDER.read().unwrap();
if let Some(logger_sender) = &*logger_sender_opt {
logger_sender.send(format!("{} {}", h, message)).ok();
return;
}
}
let is_std_out = get_logger_std_out();
let mut lock = PRINT_MESSAGE_LOCK.lock().unwrap();
print_color(is_std_out, color, true, h);
if is_std_out {
println!(" {}", message);
} else {
eprintln!(" {}", message)
}
*lock = ();
}
pub fn print_ex(message: &str, new_line: bool) {
if get_logger_std_out() {
if new_line {
println!("{}", message)
} else {
print!("{}", message)
}
} else {
#[allow(clippy::collapsible_else_if)]
if new_line {
eprintln!("{}", message)
} else {
eprint!("{}", message)
}
}
}
pub fn print_ok(message: &str) { print_message(MessageType::OK, message); }
pub fn print_warn(message: &str) { print_message(MessageType::WARN, message); }
pub fn print_error(message: &str) { print_message(MessageType::ERROR, message); }
pub fn print_info(message: &str) { print_message(MessageType::INFO, message); }
pub fn print_debug(message: &str) { print_message(MessageType::DEBUG, message); }
#[inline]
pub fn is_logger_level_enabled(mt: MessageType) -> bool {
let logger_level = *LOGGER_LEVEL;
mt.get_u8_value() >= logger_level.get_u8_value()
}
pub fn when_debug<F>(f: F) where F: Fn() {
when(MessageType::DEBUG, f)
}
pub fn when<F>(mt: MessageType, f: F) where F: Fn() {
if is_logger_level_enabled(mt) {
f();
}
}
pub fn print_message(mt: MessageType, message: &str) {
if is_logger_level_enabled(mt) {
match mt {
MessageType::OK => print_message_ex(Some(term::color::GREEN), "[OK ]", message),
MessageType::WARN => print_message_ex(Some(term::color::YELLOW), "[WARN ]", message),
MessageType::ERROR => print_message_ex(Some(term::color::RED), "[ERROR]", message),
MessageType::INFO => print_message_ex(None, "[INFO ]", message),
MessageType::DEBUG => print_message_ex(Some(term::color::MAGENTA), "[DEBUG]", message),
}
}
}
impl MessageType {
pub fn print(&self, message: &str) {
print_message(*self, message);
}
}
pub fn flush_stdout() {
io::stdout().flush().ok();
}
pub fn clear_lastline() {
print_lastline("");
}
pub fn print_lastline(line: &str) {
print!("\x1b[1000D{}\x1b[K", line);
flush_stdout();
}
// thanks https://blog.csdn.net/star_xiong/article/details/89401149
pub fn find_char_boundary(s: &str, index: usize) -> usize {
if s.len() <= index {
return index;
}
let mut new_index = index;
while !s.is_char_boundary(new_index) {
new_index += 1;
}
new_index
}
pub fn get_term_width_message(message: &str, left: usize) -> String {
match term_size::dimensions() {
None => message.to_string(),
Some((w, _h)) => {
let len = message.len();
if w > len {
return message.to_string();
}
let mut s = String::new();
s.push_str(&message[0..find_char_boundary(message, w - 10 - 5 - left)]);
s.push_str("[...]");
s.push_str(&message[find_char_boundary(message, len - 10)..]);
s
}
}
}