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

Compare commits

..

2 Commits

Author SHA1 Message Date
8d4b8aa1e0 fix: clippy 2021-05-30 01:28:16 +08:00
c8a0f22425 feat: add runtime 2021-05-30 01:01:56 +08:00
13 changed files with 137 additions and 75 deletions

View File

@@ -1,6 +1,6 @@
[package] [package]
name = "rust_util" name = "rust_util"
version = "0.6.35" version = "0.6.37"
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

@@ -20,6 +20,7 @@ pub mod util_git;
#[cfg(feature = "use_clap")] #[cfg(feature = "use_clap")]
pub mod util_clap; pub mod util_clap;
pub mod util_tlv; pub mod util_tlv;
pub mod util_runtime;
/// iff!(condition, result_when_true, result_when_false) /// iff!(condition, result_when_true, result_when_false)
#[macro_export] macro_rules! iff { #[macro_export] macro_rules! iff {
@@ -47,6 +48,7 @@ pub mod util_tlv;
#[macro_export] macro_rules! failure_and_exit { #[macro_export] macro_rules! failure_and_exit {
($($arg:tt)+) => ( { ($($arg:tt)+) => ( {
rust_util::util_msg::print_error(&format!($($arg)+)); rust_util::util_msg::print_error(&format!($($arg)+));
rsut_util::util_runtime::register_callback();
std::process::exit(-1); std::process::exit(-1);
} ) } )
} }

View File

@@ -16,6 +16,7 @@ pub trait DefaultCommand {
} }
pub struct DefaultCommandImpl; pub struct DefaultCommandImpl;
impl DefaultCommand for DefaultCommandImpl { impl DefaultCommand for DefaultCommandImpl {
fn process_command<'a>(&self, app: App<'a, 'a>) -> App<'a, 'a> { fn process_command<'a>(&self, app: App<'a, 'a>) -> App<'a, 'a> {
app.arg(Arg::with_name("verbose").long("verbose").short("v").multiple(true).help("Show verbose info")) app.arg(Arg::with_name("verbose").long("verbose").short("v").multiple(true).help("Show verbose info"))
@@ -40,7 +41,7 @@ impl CommandExecutor {
} }
pub fn new(default_cmd: Option<Box<dyn DefaultCommand>>) -> Self { pub fn new(default_cmd: Option<Box<dyn DefaultCommand>>) -> Self {
CommandExecutor{ CommandExecutor {
default_cmd, default_cmd,
commands: Vec::new(), commands: Vec::new(),
} }
@@ -77,18 +78,25 @@ impl CommandExecutor {
if let Some(sub_cmd_matches) = matches.subcommand_matches(command.name()) { if let Some(sub_cmd_matches) = matches.subcommand_matches(command.name()) {
match command.run(&matches, sub_cmd_matches)? { match command.run(&matches, sub_cmd_matches)? {
None => return Ok(()), None => return Ok(()),
Some(code) => process::exit(code), Some(code) => {
crate::util_runtime::invoke_callbacks();
process::exit(code);
}
} }
} }
} }
match &self.default_cmd { match &self.default_cmd {
None => { None => {
util_msg::print_error("No default command, please try help (--help)"); util_msg::print_error("No default command, please try help (--help)");
crate::util_runtime::invoke_callbacks();
process::exit(1); process::exit(1);
}, }
Some(default_cmd) => match default_cmd.run(&matches)? { Some(default_cmd) => match default_cmd.run(&matches)? {
None => return Ok(()), None => return Ok(()),
Some(code) => process::exit(code), Some(code) => {
crate::util_runtime::invoke_callbacks();
process::exit(code);
}
}, },
} }
} }

View File

@@ -1,6 +1,6 @@
use std::io::{self, Error, ErrorKind}; use std::io::{self, Error, ErrorKind};
use std::process::{Command, ExitStatus, Output}; use std::process::{Command, ExitStatus, Output};
use crate::util_msg::{print_debug, print_error, MessageType}; use crate::util_msg::{print_debug, print_error, MessageType, print_message};
pub fn run_command_or_exit(cmd: &str, args: &[&str]) -> Output { pub fn run_command_or_exit(cmd: &str, args: &[&str]) -> Output {
let mut c = Command::new(cmd); let mut c = Command::new(cmd);
@@ -12,11 +12,21 @@ pub fn run_command_or_exit(cmd: &str, args: &[&str]) -> Output {
match output { match output {
Err(e) => { Err(e) => {
print_error(&format!("Run command: {:?}, failed: {}", c, e)); print_error(&format!("Run command: {:?}, failed: {}", c, e));
crate::util_runtime::invoke_callbacks();
std::process::exit(-1); std::process::exit(-1);
}, }
Ok(output) => { Ok(output) => {
if !output.status.success() { if !output.status.success() {
print_error(&format!(r##"Run command failed, code: {:?} print_output(MessageType::ERROR, &output);
}
output
}
}
}
pub fn print_output(message_type: MessageType, output: &Output) {
crate::util_msg::when(message_type, || {
print_message(message_type, &format!(r##"Run command failed, code: {:?}
-----std out--------------------------------------------------------------- -----std out---------------------------------------------------------------
{} {}
-----std err--------------------------------------------------------------- -----std err---------------------------------------------------------------
@@ -25,10 +35,7 @@ pub fn run_command_or_exit(cmd: &str, args: &[&str]) -> Output {
output.status.code(), output.status.code(),
String::from_utf8_lossy(&output.stdout), String::from_utf8_lossy(&output.stdout),
String::from_utf8_lossy(&output.stderr))); String::from_utf8_lossy(&output.stderr)));
} });
output
}
}
} }
pub fn run_command_and_wait(cmd: &mut Command) -> io::Result<ExitStatus> { pub fn run_command_and_wait(cmd: &mut Command) -> io::Result<ExitStatus> {

View File

@@ -4,7 +4,16 @@ pub fn is_env_on(var: &str) -> bool {
env::var(var).ok().map(|val| is_on(&val)).unwrap_or(false) env::var(var).ok().map(|val| is_on(&val)).unwrap_or(false)
} }
pub fn is_env_off(var: &str) -> bool {
env::var(var).ok().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) vec!["true", "yes", "1"].iter().any(|x| *x == lower_val)
} }
pub fn is_off(val: &str) -> bool {
let lower_val = val.to_lowercase();
vec!["false", "no", "0"].iter().any(|x| *x == lower_val)
}

View File

@@ -1,8 +1,7 @@
use std::env;
use std::fs::{self, File}; use std::fs::{self, File};
use std::io::{Lines, BufReader}; use std::io::{Lines, BufReader};
use std::path::{Path, PathBuf}; use std::path::{Path, PathBuf};
use crate::{iff, util_os, util_io, util_msg, new_box_ioerror, XResult}; use crate::{util_os, util_io, util_msg, new_box_ioerror, XResult};
pub struct JoinFilesReader { pub struct JoinFilesReader {
files: Vec<String>, files: Vec<String>,
@@ -18,7 +17,6 @@ fn open_file_as_lines(f: &str) -> XResult<Lines<BufReader<File>>> {
} }
impl JoinFilesReader { impl JoinFilesReader {
pub fn new(fns: &[&str]) -> XResult<Self> { pub fn new(fns: &[&str]) -> XResult<Self> {
let mut files: Vec<String> = vec![]; let mut files: Vec<String> = vec![];
for f in fns { for f in fns {
@@ -49,10 +47,11 @@ impl Iterator for JoinFilesReader {
} else { } else {
// if open file failed, will not continue read files // if open file failed, will not continue read files
self.file_lines = Some(Box::new(match open_file_as_lines(&self.files[self.file_ptr]) { self.file_lines = Some(Box::new(match open_file_as_lines(&self.files[self.file_ptr]) {
Ok(ln) => ln, Err(e) => return Some(Err(e)), Ok(ln) => ln,
Err(e) => return Some(Err(e)),
})); }));
} }
}, }
}, },
None => return None, None => return None,
} }
@@ -129,13 +128,15 @@ pub fn locate_file(files: &[String]) -> Option<PathBuf> {
None None
} }
#[deprecated]
pub fn get_home_str() -> Option<String> { pub fn get_home_str() -> Option<String> {
iff!(util_os::is_macos_or_linux(), env::var("HOME").ok(), None) util_os::get_user_home()
} }
pub fn resolve_file_path(path: &str) -> String { pub fn resolve_file_path(path: &str) -> String {
let home_path = match get_home_str() { let home_path = match util_os::get_user_home() {
Some(p) => p, None => return path.to_owned(), Some(p) => p,
None => return path.to_owned(),
}; };
match path { match path {
"~" => home_path, "~" => home_path,
@@ -145,13 +146,13 @@ pub fn resolve_file_path(path: &str) -> String {
} }
pub fn get_home_path() -> Option<PathBuf> { pub fn get_home_path() -> Option<PathBuf> {
Some(PathBuf::from(get_home_str()?)) Some(PathBuf::from(util_os::get_user_home()?))
} }
pub fn get_absolute_path(path: &str) -> Option<PathBuf> { pub fn get_absolute_path(path: &str) -> Option<PathBuf> {
match path { match path {
"~" => Some(PathBuf::from(get_home_str()?)), "~" => Some(PathBuf::from(util_os::get_user_home()?)),
path if path.starts_with("~/") => Some(PathBuf::from(&format!("{}/{}", get_home_str()?, &path[2..]))), path if path.starts_with("~/") => Some(PathBuf::from(&format!("{}/{}", util_os::get_user_home()?, &path[2..]))),
path => fs::canonicalize(path).ok(), path => fs::canonicalize(path).ok(),
} }
} }
@@ -188,17 +189,19 @@ fn walk_dir_with_depth_check<FError, FProcess, FFilter>(depth: &mut u32, dir: &P
return Err(new_box_ioerror(&format!("Depth exceed, depth: {}, path: {:?}", *depth, dir))); return Err(new_box_ioerror(&format!("Depth exceed, depth: {}, path: {:?}", *depth, dir)));
} }
let read_dir = match dir.read_dir() { let read_dir = match dir.read_dir() {
Ok(rd) => rd, Err(err) => { Ok(rd) => rd,
Err(err) => {
func_walk_error(&dir, Box::new(err)); func_walk_error(&dir, Box::new(err));
return Ok(()); return Ok(());
}, }
}; };
for dir_entry_item in read_dir { for dir_entry_item in read_dir {
let dir_entry = match dir_entry_item { let dir_entry = match dir_entry_item {
Ok(item) => item, Err(err) => { Ok(item) => item,
Err(err) => {
func_walk_error(&dir, Box::new(err)); func_walk_error(&dir, Box::new(err));
continue; // Ok? continue; // Ok?
}, }
}; };
let path_buf = dir_entry.path(); let path_buf = dir_entry.path();

View File

@@ -58,7 +58,7 @@ pub fn git_branch(working_dir: Option<&str>) -> XResult<Option<String>> {
util_msg::print_info(&format!("Exec: {:?}", cmd)); util_msg::print_info(&format!("Exec: {:?}", cmd));
let output = cmd.output()?; let output = cmd.output()?;
let git_branch = String::from_utf8(output.stdout)?; let git_branch = String::from_utf8(output.stdout)?;
let current_branch = git_branch.lines().find(|ln| ln.trim().starts_with("*")); let current_branch = git_branch.lines().find(|ln| ln.trim().starts_with('*'));
Ok(current_branch.map(|ln| ln.trim()[1..].trim().into())) Ok(current_branch.map(|ln| ln.trim()[1..].trim().into()))
} }

View File

@@ -13,15 +13,13 @@ pub fn stdout_or_file_write(file: Option<&str>, overwrite: bool) -> XResult<Box<
match file { match file {
None => Ok(Box::new(io::stdout())), None => Ok(Box::new(io::stdout())),
Some(output) => { Some(output) => {
if let Ok(_) = File::open(output) { if File::open(output).is_ok() && !overwrite {
if !overwrite {
return Err(SimpleError::new(format!("File exists: {}", output)).into()); return Err(SimpleError::new(format!("File exists: {}", output)).into());
} }
}
Ok(Box::new(File::create(output).map_err(|e| { Ok(Box::new(File::create(output).map_err(|e| {
SimpleError::new(format!("Create file: {}, failed: {}", output, e)) SimpleError::new(format!("Create file: {}, failed: {}", output, e))
})?)) })?))
}, }
} }
} }
@@ -59,10 +57,7 @@ impl PrintStatusContext {
if written > self.total_written_bytes && (written - self.total_written_bytes > self.print_interval_bytes) { if written > self.total_written_bytes && (written - self.total_written_bytes > self.print_interval_bytes) {
return true; return true;
} }
match last_print_cost.as_millis() { last_print_cost.as_millis() > self.print_interval_time.as_millis()
m if m > self.print_interval_time.as_millis() => true,
_ => false,
}
}; };
if should_update_status_line() { if should_update_status_line() {
self.last_print_time = now; self.last_print_time = now;

View File

@@ -8,6 +8,7 @@ lazy_static! {
static ref PRINT_MESSAGE_LOCK: Arc<Mutex<()>> = Arc::new(Mutex::new(())); static ref PRINT_MESSAGE_LOCK: Arc<Mutex<()>> = Arc::new(Mutex::new(()));
} }
#[allow(clippy::upper_case_acronyms)]
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
pub enum MessageType { DEBUG, INFO, OK, WARN, ERROR } pub enum MessageType { DEBUG, INFO, OK, WARN, ERROR }
@@ -92,7 +93,7 @@ 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<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();
} }

View File

@@ -79,7 +79,7 @@ impl IpAddress {
pub fn to_u32(&self) -> u32 { pub fn to_u32(&self) -> u32 {
match self { match self {
IpAddress::Ipv4(ipv4) => { IpAddress::Ipv4(ipv4) => {
u32::from_be_bytes(ipv4.clone()) u32::from_be_bytes(*ipv4)
} }
} }
} }

View File

@@ -1,3 +1,6 @@
use std::env;
use std::path::PathBuf;
use crate::iff;
pub fn is_macos() -> bool { pub fn is_macos() -> bool {
cfg!(target_os = "macos") cfg!(target_os = "macos")
@@ -10,3 +13,13 @@ pub fn is_linux() -> bool {
pub fn is_macos_or_linux() -> bool { pub fn is_macos_or_linux() -> bool {
is_macos() || is_linux() is_macos() || is_linux()
} }
pub fn get_user_home() -> Option<String> {
iff!(is_macos_or_linux(), env::var("HOME").ok(), None)
}
pub fn get_full_work_dir() -> Option<String> {
PathBuf::from(".").canonicalize().ok().map(|p| {
p.to_str().map(ToString::to_string)
}).flatten()
}

24
src/util_runtime.rs Normal file
View File

@@ -0,0 +1,24 @@
use std::sync::Mutex;
use crate::util_msg::MessageType;
lazy_static! {
static ref EXIT_CALLBACK: Mutex<Vec<Box<dyn Fn() + Send + 'static>>> = Mutex::new(vec![]);
}
pub fn register_callback<F>(f: F) where F: Fn() + Send + 'static {
let mut exit_callbacks = EXIT_CALLBACK.lock().unwrap();
exit_callbacks.push(Box::new(f));
}
pub fn invoke_callbacks() {
let mut exit_callbacks = EXIT_CALLBACK.lock().unwrap();
let total = exit_callbacks.len();
let mut index = 0;
while exit_callbacks.len() > 0 {
crate::util_msg::when(MessageType::DEBUG, || {
crate::util_msg::print_debug(&format!("Running exit callbacks: {} of {}", index, total));
});
exit_callbacks.remove(0)();
index += 1;
}
}

View File

@@ -1,11 +1,11 @@
use std::time::{SystemTime, Duration}; use std::time::{SystemTime, Duration};
pub fn get_current_secs() -> u64 { pub fn get_current_secs() -> u64 {
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0 /* SHOULD NOT HAPPEN */ ) SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map(|d| d.as_secs()).unwrap_or(0 /* SHOULD NOT HAPPEN */)
} }
pub fn get_current_millis() -> u128 { pub fn get_current_millis() -> u128 {
SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map(|d| d.as_millis()).unwrap_or(0 /* SHOULD NOT HAPPEN */ ) SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).map(|d| d.as_millis()).unwrap_or(0 /* SHOULD NOT HAPPEN */)
} }
pub fn parse_duration(t: &str) -> Option<Duration> { pub fn parse_duration(t: &str) -> Option<Duration> {
@@ -20,7 +20,7 @@ pub fn parse_duration(t: &str) -> Option<Duration> {
Some('m') => parse_and_process_time(60 * 1000), Some('m') => parse_and_process_time(60 * 1000),
Some('h') => parse_and_process_time(60 * 60 * 1000), Some('h') => parse_and_process_time(60 * 60 * 1000),
Some('d') => parse_and_process_time(24 * 60 * 60 * 1000), Some('d') => parse_and_process_time(24 * 60 * 60 * 1000),
_ => t.parse::<u64>().map(|t| Duration::from_millis(t)).ok(), _ => t.parse::<u64>().map(Duration::from_millis).ok(),
} }
} }
@@ -47,5 +47,5 @@ fn test_parse_duration() {
assert_eq!(Duration::from_millis(60000), parse_duration("1m").unwrap()); assert_eq!(Duration::from_millis(60000), parse_duration("1m").unwrap());
assert_eq!(Duration::from_millis(3600000), parse_duration("1h").unwrap()); assert_eq!(Duration::from_millis(3600000), parse_duration("1h").unwrap());
assert_eq!(Duration::from_millis(1800000), parse_duration("0.5h").unwrap()); assert_eq!(Duration::from_millis(1800000), parse_duration("0.5h").unwrap());
assert_eq!(Duration::from_millis(24*3600000), parse_duration("1d").unwrap()); assert_eq!(Duration::from_millis(24 * 3600000), parse_duration("1d").unwrap());
} }