rust_util 0.6.51

Hatter's Rust Util
Documentation
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
        }
    }
}