1
0
mirror of https://github.com/jht5945/rust_util.git synced 2026-01-14 08:10:04 +08:00

Compare commits

..

7 Commits

Author SHA1 Message Date
254585bb90 feat: v0.6.51 2026-01-04 23:16:31 +08:00
e13b2a3db4 feat: v0.6.50 2025-08-24 22:57:45 +08:00
e2b258ca09 feat: add env_var 2025-08-24 16:11:03 +08:00
4b596db8de feat: update cname 2025-07-26 10:45:54 +08:00
af4b91d4e9 feat: v0.5.48 2025-07-26 08:52:31 +08:00
c1ef4c4b53 feat: v0.6.47 2024-01-20 14:51:02 +08:00
ca597cf0d6 feat: v0.6.46 2023-10-28 16:30:32 +08:00
13 changed files with 148 additions and 82 deletions

2
CNAME
View File

@@ -1 +1 @@
rust-util.hatter.cc rust-util.ruststack.org

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rust_util" name = "rust_util"
version = "0.6.45" version = "0.6.51"
authors = ["Hatter Jiang <jht5945@gmail.com>"] authors = ["Hatter Jiang <jht5945@gmail.com>"]
edition = "2018" edition = "2018"
description = "Hatter's Rust Util" description = "Hatter's Rust Util"

View File

@@ -1,10 +1,22 @@
#[macro_use] extern crate rust_util; #[macro_use] extern crate rust_util;
use std::sync::mpsc::channel;
use std::thread;
use rust_util::{XResult, SimpleError}; use rust_util::{XResult, SimpleError};
use rust_util::util_msg::set_logger_std_out; use rust_util::util_msg::{set_logger_sender, set_logger_std_out};
// cargo run --example log // cargo run --example log
fn main() -> XResult<()> { fn main() -> XResult<()> {
let (sender, receiver) = channel::<String>();
set_logger_sender(sender);
let _t = thread::spawn(move || {
loop {
let msg = receiver.recv();
println!("[RECV]: {:?}", msg)
}
});
std::env::set_var("LOGGER_LEVEL", "*"); std::env::set_var("LOGGER_LEVEL", "*");
println!(r##"env LOGGER_LEVEL set to: println!(r##"env LOGGER_LEVEL set to:
debug or * debug or *

View File

@@ -1,19 +1,31 @@
use std::env; use std::env;
pub fn is_env_on(var: &str) -> bool { pub fn is_env_on(var: &str) -> bool {
env::var(var).ok().map(|val| is_on(&val)).unwrap_or(false) env_var(var).map(|val| is_on(&val)).unwrap_or(false)
} }
pub fn is_env_off(var: &str) -> bool { pub fn is_env_off(var: &str) -> bool {
env::var(var).ok().map(|val| is_off(&val)).unwrap_or(false) env_var(var).map(|val| is_off(&val)).unwrap_or(false)
} }
pub fn is_on(val: &str) -> bool { pub fn is_on(val: &str) -> bool {
let lower_val = val.to_lowercase(); let lower_val = val.to_lowercase();
vec!["true", "yes", "1"].iter().any(|x| *x == lower_val) ["true", "yes", "1"].iter().any(|x| *x == lower_val)
} }
pub fn is_off(val: &str) -> bool { pub fn is_off(val: &str) -> bool {
let lower_val = val.to_lowercase(); let lower_val = val.to_lowercase();
vec!["false", "no", "0"].iter().any(|x| *x == lower_val) ["false", "no", "0"].iter().any(|x| *x == lower_val)
}
pub fn env_var(var: &str) -> Option<String> {
let var_from_env = env::var(var).ok();
if var_from_env.is_some() {
return var_from_env;
}
let var_content = crate::util_file::read_file_content(&format!("~/.config/envs/{}", var));
if let Ok(var_content) = var_content {
return Some(var_content.trim().to_string());
}
None
} }

View File

@@ -10,7 +10,7 @@ pub struct JoinFilesReader {
} }
fn open_file_as_lines(f: &str) -> XResult<Lines<BufReader<File>>> { fn open_file_as_lines(f: &str) -> XResult<Lines<BufReader<File>>> {
let f = File::open(&f)?; let f = File::open(f)?;
let br = BufReader::new(f); let br = BufReader::new(f);
use std::io::BufRead; use std::io::BufRead;
Ok(br.lines()) Ok(br.lines())
@@ -191,7 +191,7 @@ fn walk_dir_with_depth_check<FError, FProcess, FFilter>(depth: &mut u32, dir: &P
let read_dir = match dir.read_dir() { let read_dir = match dir.read_dir() {
Ok(rd) => rd, Ok(rd) => rd,
Err(err) => { Err(err) => {
func_walk_error(&dir, Box::new(err)); func_walk_error(dir, Box::new(err));
return Ok(()); return Ok(());
} }
}; };
@@ -199,7 +199,7 @@ fn walk_dir_with_depth_check<FError, FProcess, FFilter>(depth: &mut u32, dir: &P
let dir_entry = match dir_entry_item { let dir_entry = match dir_entry_item {
Ok(item) => item, Ok(item) => item,
Err(err) => { Err(err) => {
func_walk_error(&dir, Box::new(err)); func_walk_error(dir, Box::new(err));
continue; // Ok? continue; // Ok?
} }
}; };
@@ -207,11 +207,11 @@ fn walk_dir_with_depth_check<FError, FProcess, FFilter>(depth: &mut u32, dir: &P
let path_buf = dir_entry.path(); let path_buf = dir_entry.path();
let sub_dir = path_buf.as_path(); let sub_dir = path_buf.as_path();
if sub_dir.is_file() { if sub_dir.is_file() {
func_process_file(&sub_dir); func_process_file(sub_dir);
} else if sub_dir.is_dir() && func_filter_dir(&sub_dir) { } else if sub_dir.is_dir() && func_filter_dir(sub_dir) {
*depth += 1; *depth += 1;
if let Err(err) = walk_dir_with_depth_check(depth, &sub_dir, func_walk_error, func_process_file, func_filter_dir) { if let Err(err) = walk_dir_with_depth_check(depth, sub_dir, func_walk_error, func_process_file, func_filter_dir) {
func_walk_error(&sub_dir, err); func_walk_error(sub_dir, err);
} }
*depth -= 1; *depth -= 1;
} // should process else ? not file, dir } // should process else ? not file, dir

View File

@@ -1,5 +1,6 @@
use std::process::Command; use std::process::Command;
use crate::{XResult, util_msg, util_cmd};
use crate::{util_cmd, util_msg, XResult};
const LANG: &str = "LANG"; const LANG: &str = "LANG";
const EN_US: &str = "en_US"; const EN_US: &str = "en_US";
@@ -75,7 +76,7 @@ pub fn git_add(working_dir: Option<&str>, files: &[String]) {
let mut cmd = new_git_command(working_dir); let mut cmd = new_git_command(working_dir);
cmd.arg("add"); cmd.arg("add");
for f in files { for f in files {
cmd.arg(&f); cmd.arg(f);
} }
util_msg::print_info(&format!("Exec: {:?}", cmd)); util_msg::print_info(&format!("Exec: {:?}", cmd));
if let Err(e) = util_cmd::run_command_and_wait(&mut cmd) { if let Err(e) = util_cmd::run_command_and_wait(&mut cmd) {
@@ -89,7 +90,7 @@ pub fn git_commit(working_dir: Option<&str>, message: &str, files: &[String]) {
cmd.arg("-m"); cmd.arg("-m");
cmd.arg(message); cmd.arg(message);
for f in files { for f in files {
cmd.arg(&f); cmd.arg(f);
} }
util_msg::print_info(&format!("Exec: {:?}", cmd)); util_msg::print_info(&format!("Exec: {:?}", cmd));
if let Err(e) = util_cmd::run_command_and_wait(&mut cmd) { if let Err(e) = util_cmd::run_command_and_wait(&mut cmd) {
@@ -100,19 +101,19 @@ pub fn git_commit(working_dir: Option<&str>, message: &str, files: &[String]) {
fn parse_git_status_change(git_status: &str) -> XResult<GitStatusChange> { fn parse_git_status_change(git_status: &str) -> XResult<GitStatusChange> {
let mut git_status_change: GitStatusChange = Default::default(); let mut git_status_change: GitStatusChange = Default::default();
for ln in git_status.lines() { for ln in git_status.lines() {
if ln.starts_with("\t") { if ln.starts_with('\t') {
let ln = ln.trim(); let ln = ln.trim();
if ln.starts_with("new file:") { if let Some(new_file) = ln.strip_prefix("new file:") {
let f = ln["new file:".len()..].trim(); let f = new_file.trim();
git_status_change.added.push(f.to_owned()); git_status_change.added.push(f.to_owned());
} else if ln.starts_with("deleted:") { } else if let Some(deleted) = ln.strip_prefix("deleted:") {
let f = ln["deleted:".len()..].trim(); let f = deleted.trim();
git_status_change.deleted.push(f.to_owned()); git_status_change.deleted.push(f.to_owned());
} else if ln.starts_with("modified:") { } else if let Some(modified) = ln.strip_prefix("modified:") {
let f = ln["modified:".len()..].trim(); let f = modified.trim();
git_status_change.modified.push(f.to_owned()); git_status_change.modified.push(f.to_owned());
} else if ln.starts_with("renamed:") { } else if let Some(renamed) = ln.strip_prefix("renamed:") {
let f = ln["renamed:".len()..].trim(); let f = renamed.trim();
let mut fs = f.split("->"); let mut fs = f.split("->");
let fa = fs.next(); let fa = fs.next();
let fb = fs.next(); let fb = fs.next();

View File

@@ -1,11 +1,11 @@
use std::io::{self, Write, ErrorKind, prelude::*};
use std::fs::File; use std::fs::File;
use std::time::{SystemTime, Duration}; use std::io::{self, ErrorKind, prelude::*, Write};
use std::time::{Duration, SystemTime};
use crate::{SimpleError, XResult}; use crate::{SimpleError, XResult};
use crate::util_size;
use crate::util_msg;
use crate::util_file; use crate::util_file;
use crate::util_msg;
use crate::util_size;
pub const DEFAULT_BUF_SIZE: usize = 8 * 1024; pub const DEFAULT_BUF_SIZE: usize = 8 * 1024;
@@ -79,7 +79,7 @@ pub fn get_read_stdin_or_file(file: &str) -> XResult<Box<dyn Read>> {
if file.is_empty() { if file.is_empty() {
Ok(Box::new(io::stdin())) Ok(Box::new(io::stdin()))
} else { } else {
match File::open(&util_file::resolve_file_path(file)) { match File::open(util_file::resolve_file_path(file)) {
Ok(f) => Ok(Box::new(f)), Ok(f) => Ok(Box::new(f)),
Err(err) => Err(SimpleError::new(format!("Open file {}, erorr: {}", file, err)).into()), Err(err) => Err(SimpleError::new(format!("Open file {}, erorr: {}", file, err)).into()),
} }
@@ -145,7 +145,7 @@ pub fn print_status_last_line(head: &str, total: i64, written: i64, print_status
} }
let cost_as_secs = cost.as_secs(); let cost_as_secs = cost.as_secs();
if cost_as_secs > 0 { if cost_as_secs > 0 {
download_speed = format!("{}/s", util_size::get_display_size((written / (cost_as_secs as i64)) as i64)); download_speed = format!("{}/s", util_size::get_display_size(written / (cost_as_secs as i64)));
} }
if total > 0 { if total > 0 {
util_msg::print_lastline(&format!("{}, Total: {}, Finished: {}, Speed: {}", util_msg::print_lastline(&format!("{}, Total: {}, Finished: {}, Speed: {}",

View File

@@ -1,14 +1,21 @@
use std::env; use std::env;
use std::io::{self, Write}; use std::io::{self, Write};
use std::sync::{Arc, Mutex, RwLock}; use std::sync::{Arc, Mutex, RwLock};
use std::sync::mpsc::Sender;
lazy_static! { lazy_static! {
pub static ref IS_ATTY: bool = is_atty(); pub static ref IS_ATTY: bool = is_atty();
static ref LOGGER_LEVEL: MessageType = get_logger_level(); 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 LOGGER_TO_STDOUT: Arc<RwLock<bool>> = Arc::new(RwLock::new(true));
static ref PRINT_MESSAGE_LOCK: Arc<Mutex<()>> = Arc::new(Mutex::new(())); 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) { pub fn set_logger_std_out(is_std_out: bool) {
let mut std_out = LOGGER_TO_STDOUT.write().unwrap(); let mut std_out = LOGGER_TO_STDOUT.write().unwrap();
*std_out = is_std_out; *std_out = is_std_out;
@@ -55,7 +62,7 @@ pub fn get_logger_level() -> MessageType {
} }
pub fn is_atty() -> bool { pub fn is_atty() -> bool {
let stdout_fileno = unsafe { libc::isatty(libc::STDOUT_FILENO as i32) }; let stdout_fileno = unsafe { libc::isatty(libc::STDOUT_FILENO) };
stdout_fileno != 0 stdout_fileno != 0
} }
@@ -105,6 +112,13 @@ pub fn print_color_and_flush(color: Option<term::color::Color>, is_bold: bool, m
} }
pub fn print_message_ex(color: Option<term::color::Color>, h: &str, message: &str) { 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 is_std_out = get_logger_std_out();
let mut lock = PRINT_MESSAGE_LOCK.lock().unwrap(); let mut lock = PRINT_MESSAGE_LOCK.lock().unwrap();
print_color(is_std_out, color, true, h); print_color(is_std_out, color, true, h);
@@ -124,6 +138,7 @@ pub fn print_ex(message: &str, new_line: bool) {
print!("{}", message) print!("{}", message)
} }
} else { } else {
#[allow(clippy::collapsible_else_if)]
if new_line { if new_line {
eprintln!("{}", message) eprintln!("{}", message)
} else { } else {
@@ -148,6 +163,10 @@ pub fn is_logger_level_enabled(mt: MessageType) -> bool {
mt.get_u8_value() >= logger_level.get_u8_value() 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() { pub fn when<F>(mt: MessageType, f: F) where F: Fn() {
if is_logger_level_enabled(mt) { if is_logger_level_enabled(mt) {
f(); f();
@@ -206,9 +225,9 @@ pub fn get_term_width_message(message: &str, left: usize) -> String {
return message.to_string(); return message.to_string();
} }
let mut s = String::new(); let mut s = String::new();
s.push_str(&message[0..find_char_boundary(&message, w - 10 - 5 - left)]); s.push_str(&message[0..find_char_boundary(message, w - 10 - 5 - left)]);
s.push_str("[...]"); s.push_str("[...]");
s.push_str(&message[find_char_boundary(&message, len - 10)..]); s.push_str(&message[find_char_boundary(message, len - 10)..]);
s s
} }
} }

View File

@@ -11,6 +11,12 @@ pub struct IpAndIpMaskMatcher {
ip_and_ip_mask_set: HashSet<u64>, ip_and_ip_mask_set: HashSet<u64>,
} }
impl Default for IpAndIpMaskMatcher {
fn default() -> Self {
Self::new()
}
}
impl IpAndIpMaskMatcher { impl IpAndIpMaskMatcher {
pub fn new() -> Self { pub fn new() -> Self {
IpAndIpMaskMatcher { IpAndIpMaskMatcher {
@@ -133,7 +139,7 @@ impl IpAddressMask {
let socket_addr_v4_octets = socket_addr_v4.ip().octets(); let socket_addr_v4_octets = socket_addr_v4.ip().octets();
match self { match self {
IpAddressMask::Ipv4(self_ipv4_octets, mask) => { IpAddressMask::Ipv4(self_ipv4_octets, mask) => {
let self_ipv4_u32 = ipv4_to_u32(&self_ipv4_octets); let self_ipv4_u32 = ipv4_to_u32(self_ipv4_octets);
let addr_ipv4_u32 = ipv4_to_u32(&socket_addr_v4_octets); let addr_ipv4_u32 = ipv4_to_u32(&socket_addr_v4_octets);
let mask_u32 = ipv4_mask(*mask); let mask_u32 = ipv4_mask(*mask);
self_ipv4_u32 & mask_u32 == addr_ipv4_u32 & mask_u32 self_ipv4_u32 & mask_u32 == addr_ipv4_u32 & mask_u32

View File

@@ -1,5 +1,6 @@
use std::env; use std::env;
use std::path::PathBuf; use std::path::PathBuf;
use crate::iff; use crate::iff;
pub fn is_macos() -> bool { pub fn is_macos() -> bool {
@@ -19,7 +20,7 @@ pub fn get_user_home() -> Option<String> {
} }
pub fn get_full_work_dir() -> Option<String> { pub fn get_full_work_dir() -> Option<String> {
PathBuf::from(".").canonicalize().ok().map(|p| { PathBuf::from(".").canonicalize().ok().and_then(|p| {
p.to_str().map(ToString::to_string) p.to_str().map(ToString::to_string)
}).flatten() })
} }

View File

@@ -13,7 +13,7 @@ pub fn parse_size(size: &str) -> XResult<i64> {
lower_size.ends_with('b'), &lower_size[0..lower_size.len()-1], &lower_size lower_size.ends_with('b'), &lower_size[0..lower_size.len()-1], &lower_size
); );
let parse_and_process_size = |mul: i64| -> XResult<i64> { let parse_and_process_size = |mul: i64| -> XResult<i64> {
Ok((mul as f64 * no_last_b_size[0..no_last_b_size.len()-1].parse::<f64>()?) as i64) Ok((mul as f64 * no_last_b_size[0..no_last_b_size.len() - 1].parse::<f64>()?) as i64)
}; };
match no_last_b_size.chars().last() { match no_last_b_size.chars().last() {
Some('k') => parse_and_process_size(SIZE_KB), Some('k') => parse_and_process_size(SIZE_KB),
@@ -27,17 +27,17 @@ pub fn parse_size(size: &str) -> XResult<i64> {
pub fn get_display_size(size: i64) -> String { pub fn get_display_size(size: i64) -> String {
if size < SIZE_KB { if size < SIZE_KB {
size.to_string() format!("{} {}", size, crate::iff!(size == 1, "byte", "bytes" ))
} else if size < SIZE_MB { } else if size < SIZE_MB {
format!("{:.*}KB", 2, (size as f64) / SIZE_KB as f64) format!("{:.*}KiB", 2, (size as f64) / SIZE_KB as f64)
} else if size < SIZE_GB { } else if size < SIZE_GB {
format!("{:.*}MB", 2, (size as f64) / SIZE_MB as f64) format!("{:.*}MiB", 2, (size as f64) / SIZE_MB as f64)
} else if size < SIZE_TB { } else if size < SIZE_TB {
format!("{:.*}GB", 2, (size as f64) / SIZE_GB as f64) format!("{:.*}GiB", 2, (size as f64) / SIZE_GB as f64)
} else if size < SIZE_PB { } else if size < SIZE_PB {
format!("{:.*}TB", 2, (size as f64) / SIZE_TB as f64) format!("{:.*}TiB", 2, (size as f64) / SIZE_TB as f64)
} else { } else {
format!("{:.*}PB", 2, (size as f64) / SIZE_PB as f64) format!("{:.*}PiB", 2, (size as f64) / SIZE_PB as f64)
} }
} }
@@ -54,11 +54,11 @@ fn test_parse_size() {
#[test] #[test]
fn test_get_display_size() { fn test_get_display_size() {
assert_eq!(get_display_size(0), "0"); assert_eq!(get_display_size(0), "0 bytes");
assert_eq!(get_display_size(111), "111"); assert_eq!(get_display_size(111), "111 bytes");
assert_eq!(get_display_size(1024), "1.00KB"); assert_eq!(get_display_size(1024), "1.00KiB");
assert_eq!(get_display_size(1024 * 1024), "1.00MB"); assert_eq!(get_display_size(1024 * 1024), "1.00MiB");
assert_eq!(get_display_size(1024 * 1024 * 1024), "1.00GB"); assert_eq!(get_display_size(1024 * 1024 * 1024), "1.00GiB");
assert_eq!(get_display_size(1024 * 1024 * 1024 * 1024), "1.00TB"); assert_eq!(get_display_size(1024 * 1024 * 1024 * 1024), "1.00TiB");
assert_eq!(get_display_size(1024 * 1024 * 1024 * 1024 * 1024), "1.00PB"); assert_eq!(get_display_size(1024 * 1024 * 1024 * 1024 * 1024), "1.00PiB");
} }

View File

@@ -1,29 +1,6 @@
/// Split string to lines, splited by '\r', '\n' or "\r\n" /// Split string to lines, splited by '\r', '\n' or "\r\n"
pub fn read_str_to_lines(s: &str) -> Vec<String> { pub fn read_str_to_lines(s: &str) -> Vec<String> {
s.lines().map(|ln| ln.to_owned()).collect() s.lines().map(|ln| ln.to_owned()).collect()
// let mut r = vec![];
// let mut line = String::new();
// let mut cs = s.chars().peekable();
// while let Some(c) = cs.next() {
// if c == '\n' || c == '\r' {
// r.push(line.clone());
// line.clear();
// if c == '\r' {
// if let Some(nc) = cs.peek() {
// if *nc == '\n' {
// cs.next();
// }
// }
// }
// } else {
// line.push(c);
// }
// }
// if !line.is_empty() {
// r.push(line);
// }
// r
} }
pub fn split_kv(s: &str, split: char) -> (String, String) { pub fn split_kv(s: &str, split: char) -> (String, String) {
@@ -61,25 +38,30 @@ fn test_read_str_to_lines() {
let s = ""; let s = "";
let lines = read_str_to_lines(s); let lines = read_str_to_lines(s);
assert_eq!(lines.len(), 0); assert_eq!(lines.len(), 0);
} { }
{
let s = "\n"; let s = "\n";
let lines = read_str_to_lines(s); let lines = read_str_to_lines(s);
assert_eq!(lines.len(), 1); assert_eq!(lines.len(), 1);
} { }
{
let s = "\r"; let s = "\r";
let lines = read_str_to_lines(s); let lines = read_str_to_lines(s);
assert_eq!(lines.len(), 1); assert_eq!(lines.len(), 1);
} { }
{
let s = "\r\n"; let s = "\r\n";
let lines = read_str_to_lines(s); let lines = read_str_to_lines(s);
assert_eq!(lines.len(), 1); assert_eq!(lines.len(), 1);
} { }
{
let s = "aa\r\nbb"; let s = "aa\r\nbb";
let lines = read_str_to_lines(s); let lines = read_str_to_lines(s);
assert_eq!(lines.len(), 2); assert_eq!(lines.len(), 2);
assert_eq!(lines[0], "aa"); assert_eq!(lines[0], "aa");
assert_eq!(lines[1], "bb"); assert_eq!(lines[1], "bb");
} { }
{
let s = "aa\r\nbb\ncc"; let s = "aa\r\nbb\ncc";
let lines = read_str_to_lines(s); let lines = read_str_to_lines(s);
assert_eq!(lines.len(), 3); assert_eq!(lines.len(), 3);

View File

@@ -2,9 +2,42 @@ use std::io::{self, Write};
use crate::util_msg; use crate::util_msg;
pub const STD_BLACK: &str = "\x1B[30m";
pub const STD_RED: &str = "\x1B[31m";
pub const STD_GREEN: &str = "\x1B[32m";
pub const STD_YELLOW: &str = "\x1B[33m";
pub const STD_BLUE: &str = "\x1B[34m";
pub const STD_MAGENTA: &str = "\x1B[35m"; // 品红色/洋红
pub const STD_CYAN: &str = "\x1B[36m"; // 青色
pub const STD_WHITE: &str = "\x1B[37m";
pub const BLACK: &str = "\x1B[90m";
pub const RED: &str = "\x1B[91m"; pub const RED: &str = "\x1B[91m";
pub const GREEN: &str = "\x1B[92m"; pub const GREEN: &str = "\x1B[92m";
pub const YELLOW: &str = "\x1B[93m"; pub const YELLOW: &str = "\x1B[93m";
pub const BLUE: &str = "\x1B[94m";
pub const MAGENTA: &str = "\x1B[95m";
pub const CYAN: &str = "\x1B[96m";
pub const WHITE: &str = "\x1B[97m";
pub const BG_STD_BLACK: &str = "\x1B[40m";
pub const BG_STD_RED: &str = "\x1B[41m";
pub const BG_STD_GREEN: &str = "\x1B[42m";
pub const BG_STD_YELLOW: &str = "\x1B[43m";
pub const BG_STD_BLUE: &str = "\x1B[44m";
pub const BG_STD_MAGENTA: &str = "\x1B[45m";
pub const BG_STD_CYAN: &str = "\x1B[46m";
pub const BG_STD_WHITE: &str = "\x1B[47m";
pub const BG_BLACK: &str = "\x1B[100m";
pub const BG_RED: &str = "\x1B[101m";
pub const BG_GREEN: &str = "\x1B[102m";
pub const BG_YELLOW: &str = "\x1B[103m";
pub const BG_BLUE: &str = "\x1B[104m";
pub const BG_MAGENTA: &str = "\x1B[105m";
pub const BG_CYAN: &str = "\x1B[106m";
pub const BG_WHITE: &str = "\x1B[107m";
pub const BOLD: &str = "\x1B[1m"; pub const BOLD: &str = "\x1B[1m";
pub const UNDER: &str = "\x1B[4m"; pub const UNDER: &str = "\x1B[4m";
pub const END: &str = "\x1B[0m"; pub const END: &str = "\x1B[0m";
@@ -16,10 +49,10 @@ pub fn read_yes_no(hint: &str) -> bool {
let mut buff = String::new(); let mut buff = String::new();
let _ = io::stdin().read_line(&mut buff).expect("Read line from stdin"); let _ = io::stdin().read_line(&mut buff).expect("Read line from stdin");
let buff = buff.trim().to_lowercase(); let buff = buff.trim().to_lowercase();
if vec!["y", "yes"].contains(&buff.as_str()) { if ["y", "yes"].contains(&buff.as_str()) {
return true; return true;
} }
if vec!["n", "no"].contains(&buff.as_str()) { if ["n", "no"].contains(&buff.as_str()) {
return false; return false;
} }
} }