mirror of
https://github.com/jht5945/rust_util.git
synced 2026-01-13 15:50:05 +08:00
Compare commits
7 Commits
807bf490fb
...
59705779d1
| Author | SHA1 | Date | |
|---|---|---|---|
| 59705779d1 | |||
| 0001f67f0d | |||
| c61c8d37e4 | |||
| 0c38427bcb | |||
| 6881cebf72 | |||
| a55ad86546 | |||
| 56f84533aa |
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rust_util"
|
name = "rust_util"
|
||||||
version = "0.6.17"
|
version = "0.6.22"
|
||||||
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"
|
||||||
|
|||||||
18
README.md
18
README.md
@@ -3,6 +3,11 @@
|
|||||||
|
|
||||||
Config `Cargo.toml`:
|
Config `Cargo.toml`:
|
||||||
```
|
```
|
||||||
|
[dependencies]
|
||||||
|
rust_util = "0.6"
|
||||||
|
|
||||||
|
--OR--
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
rust_util = { git = "https://github.com/jht5945/rust_util" }
|
rust_util = { git = "https://github.com/jht5945/rust_util" }
|
||||||
```
|
```
|
||||||
@@ -27,9 +32,22 @@ Run example:
|
|||||||
$ cargo run --example log
|
$ cargo run --example log
|
||||||
```
|
```
|
||||||
|
|
||||||
|
<br>
|
||||||
|
|
||||||
|
ENV `LOGGER_LEVEL` can be:
|
||||||
|
- `debug` or `*`
|
||||||
|
- `info` or `?` -- default
|
||||||
|
- `ok` or `#`
|
||||||
|
- `warn` or `!`
|
||||||
|
- `error` or `^`
|
||||||
|
|
||||||
|
|
||||||
## Update Log
|
## Update Log
|
||||||
|
|
||||||
|
* Nov 28, 2020 v0.6.19
|
||||||
|
* add util_git
|
||||||
|
* Nov 28, 2020 v0.6.18
|
||||||
|
* add util_term
|
||||||
* Jun 21, 2020 v0.3.0
|
* Jun 21, 2020 v0.3.0
|
||||||
* add struct `JoinFilesReader`
|
* add struct `JoinFilesReader`
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,17 @@
|
|||||||
|
|
||||||
// cargo run --example log
|
// cargo run --example log
|
||||||
fn main() {
|
fn main() {
|
||||||
|
std::env::set_var("LOGGER_LEVEL", "*");
|
||||||
|
println!(r##"env LOGGER_LEVEL set to:
|
||||||
|
debug or *
|
||||||
|
info or ? -- default
|
||||||
|
ok or #
|
||||||
|
warn or !
|
||||||
|
error or ^"##);
|
||||||
|
|
||||||
|
debugging!("Hello {}", "world!");
|
||||||
information!("Hello {}", "world!");
|
information!("Hello {}", "world!");
|
||||||
|
success!("Hello {}", "world!");
|
||||||
|
warning!("Hello {}", "world!");
|
||||||
|
failure!("Hello {}", "world!");
|
||||||
}
|
}
|
||||||
9
justfile
Normal file
9
justfile
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# help
|
||||||
|
aa:
|
||||||
|
@just --list
|
||||||
|
|
||||||
|
# example log
|
||||||
|
log:
|
||||||
|
cargo run --example log
|
||||||
|
|
||||||
|
|
||||||
@@ -14,6 +14,8 @@ pub mod util_size;
|
|||||||
pub mod util_file;
|
pub mod util_file;
|
||||||
pub mod util_time;
|
pub mod util_time;
|
||||||
pub mod util_net;
|
pub mod util_net;
|
||||||
|
pub mod util_term;
|
||||||
|
pub mod util_git;
|
||||||
|
|
||||||
/// 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 {
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
use std::{ io::{ self, Error, ErrorKind }, process::Command };
|
use std::io::{self, Error, ErrorKind};
|
||||||
|
use std::process::{Command, ExitStatus};
|
||||||
|
|
||||||
pub fn run_command_and_wait(cmd: &mut Command) -> io::Result<()> {
|
pub fn run_command_and_wait(cmd: &mut Command) -> io::Result<ExitStatus> {
|
||||||
cmd.spawn()?.wait()?;
|
cmd.spawn()?.wait()
|
||||||
Ok(())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn extract_package_and_wait(dir: &str, file_name: &str) -> io::Result<()> {
|
pub fn extract_package_and_wait(dir: &str, file_name: &str) -> io::Result<ExitStatus> {
|
||||||
let mut cmd: Command;
|
let mut cmd: Command;
|
||||||
if file_name.ends_with(".zip") {
|
if file_name.ends_with(".zip") {
|
||||||
cmd = Command::new("unzip");
|
cmd = Command::new("unzip");
|
||||||
|
|||||||
159
src/util_git.rs
Normal file
159
src/util_git.rs
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
use std::process::Command;
|
||||||
|
use crate::XResult;
|
||||||
|
use crate::util_msg;
|
||||||
|
use crate::util_cmd::run_command_and_wait;
|
||||||
|
|
||||||
|
const LANG: &str = "LANG";
|
||||||
|
const EN_US: &str = "en_US";
|
||||||
|
|
||||||
|
#[derive(Default, Debug, Clone)]
|
||||||
|
pub struct GitStatusChange {
|
||||||
|
pub added: Vec<String>,
|
||||||
|
pub modified: Vec<String>,
|
||||||
|
pub renamed: Vec<(String, String)>,
|
||||||
|
pub deleted: Vec<String>,
|
||||||
|
pub untracked: Vec<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GitStatusChange {
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.added.is_empty() && self.modified.is_empty()
|
||||||
|
&& self.deleted.is_empty() && self.untracked.is_empty()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_status_change(working_dir: Option<&str>) -> XResult<GitStatusChange> {
|
||||||
|
let git_status = git_status(working_dir)?;
|
||||||
|
parse_git_status_change(&git_status)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_fetch_dry_run(working_dir: Option<&str>) -> XResult<bool> {
|
||||||
|
let mut cmd = new_git_command(working_dir);
|
||||||
|
cmd.args(vec!["fetch", "--dry-run"]);
|
||||||
|
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||||
|
let output = cmd.output()?;
|
||||||
|
let fetch_dry_run = String::from_utf8(output.stdout)?;
|
||||||
|
Ok(fetch_dry_run.trim().is_empty())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_status(working_dir: Option<&str>) -> XResult<String> {
|
||||||
|
let mut cmd = new_git_command(working_dir);
|
||||||
|
cmd.arg("status");
|
||||||
|
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||||
|
let output = cmd.output()?;
|
||||||
|
let git_status = String::from_utf8(output.stdout)?;
|
||||||
|
Ok(git_status)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_push(working_dir: Option<&str>) {
|
||||||
|
let mut cmd = new_git_command(working_dir);
|
||||||
|
cmd.arg("push");
|
||||||
|
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||||
|
if let Err(e) = run_command_and_wait(&mut cmd) {
|
||||||
|
util_msg::print_error(&format!("Run git push failed: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_add(working_dir: Option<&str>, files: &[String]) {
|
||||||
|
let mut cmd = new_git_command(working_dir);
|
||||||
|
cmd.arg("add");
|
||||||
|
for f in files {
|
||||||
|
cmd.arg(&f);
|
||||||
|
}
|
||||||
|
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||||
|
if let Err(e) = run_command_and_wait(&mut cmd) {
|
||||||
|
util_msg::print_error(&format!("Run git add failed: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn git_commit(working_dir: Option<&str>, message: &str, files: &[String]) {
|
||||||
|
let mut cmd = new_git_command(working_dir);
|
||||||
|
cmd.arg("commit");
|
||||||
|
cmd.arg("-m");
|
||||||
|
cmd.arg(message);
|
||||||
|
for f in files {
|
||||||
|
cmd.arg(&f);
|
||||||
|
}
|
||||||
|
util_msg::print_info(&format!("Exec: {:?}", cmd));
|
||||||
|
if let Err(e) = run_command_and_wait(&mut cmd) {
|
||||||
|
util_msg::print_error(&format!("Run git commit failed: {}", e));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_git_status_change(git_status: &str) -> XResult<GitStatusChange> {
|
||||||
|
let mut git_status_change: GitStatusChange = Default::default();
|
||||||
|
for ln in git_status.lines() {
|
||||||
|
if ln.starts_with("\t") {
|
||||||
|
let ln = ln.trim();
|
||||||
|
if ln.starts_with("new file:") {
|
||||||
|
let f = ln["new file:".len()..].trim();
|
||||||
|
git_status_change.added.push(f.to_owned());
|
||||||
|
} else if ln.starts_with("deleted:") {
|
||||||
|
let f = ln["deleted:".len()..].trim();
|
||||||
|
git_status_change.deleted.push(f.to_owned());
|
||||||
|
} else if ln.starts_with("modified:") {
|
||||||
|
let f = ln["modified:".len()..].trim();
|
||||||
|
git_status_change.modified.push(f.to_owned());
|
||||||
|
} else if ln.starts_with("renamed:") {
|
||||||
|
let f = ln["renamed:".len()..].trim();
|
||||||
|
let mut fs = f.split("->");
|
||||||
|
let fa = fs.next();
|
||||||
|
let fb = fs.next();
|
||||||
|
if let (Some(fa), Some(fb)) = (fa, fb) {
|
||||||
|
git_status_change.renamed.push((fa.trim().to_owned(), fb.trim().to_owned()));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
git_status_change.untracked.push(ln.to_owned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(git_status_change)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_git_command(working_dir: Option<&str>) -> Command {
|
||||||
|
let mut cmd = Command::new("git");
|
||||||
|
cmd.env(LANG, EN_US);
|
||||||
|
if let Some(working_dir) = working_dir {
|
||||||
|
cmd.current_dir(working_dir);
|
||||||
|
}
|
||||||
|
cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_git_status() {
|
||||||
|
let git_status = r#"On branch master
|
||||||
|
Your branch is up to date with 'origin/master'.
|
||||||
|
|
||||||
|
Changes to be committed:
|
||||||
|
(use "git reset HEAD <file>..." to unstage)
|
||||||
|
|
||||||
|
new file: src/util_git.rs
|
||||||
|
renamed: src/template_regex.rs -> src/chk_regex.rs
|
||||||
|
|
||||||
|
Changes not staged for commit:
|
||||||
|
(use "git add/rm <file>..." to update what will be committed)
|
||||||
|
(use "git checkout -- <file>..." to discard changes in working directory)
|
||||||
|
|
||||||
|
deleted: README.md
|
||||||
|
modified: src/lib.rs
|
||||||
|
|
||||||
|
Untracked files:
|
||||||
|
(use "git add <file>..." to include in what will be committed)
|
||||||
|
|
||||||
|
Test
|
||||||
|
|
||||||
|
H"#;
|
||||||
|
let gsc = parse_git_status_change(git_status).unwrap();
|
||||||
|
println!("{:#?}", gsc);
|
||||||
|
assert_eq!(1, gsc.added.len());
|
||||||
|
assert_eq!("src/util_git.rs", gsc.added[0]);
|
||||||
|
assert_eq!(1, gsc.modified.len());
|
||||||
|
assert_eq!("src/lib.rs", gsc.modified[0]);
|
||||||
|
assert_eq!(1, gsc.renamed.len());
|
||||||
|
assert_eq!(("src/template_regex.rs".into(), "src/chk_regex.rs".into()), gsc.renamed[0]);
|
||||||
|
assert_eq!(1, gsc.deleted.len());
|
||||||
|
assert_eq!("README.md", gsc.deleted[0]);
|
||||||
|
assert_eq!(1, gsc.untracked.len());
|
||||||
|
assert_eq!("Test", gsc.untracked[0]);
|
||||||
|
}
|
||||||
25
src/util_term.rs
Normal file
25
src/util_term.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use std::io;
|
||||||
|
use std::io::Write;
|
||||||
|
|
||||||
|
pub const RED: &str = "\x1B[91m";
|
||||||
|
pub const GREEN: &str = "\x1B[92m";
|
||||||
|
pub const YELLOW: &str = "\x1B[93m";
|
||||||
|
pub const BOLD: &str = "\x1B[1m";
|
||||||
|
pub const UNDER: &str = "\x1B[4m";
|
||||||
|
pub const END: &str = "\x1B[0m";
|
||||||
|
|
||||||
|
pub fn read_yes_no(hint: &str) -> bool {
|
||||||
|
loop {
|
||||||
|
print!("{} (Yes/No): ", hint);
|
||||||
|
io::stdout().flush().ok();
|
||||||
|
let mut buff = String::new();
|
||||||
|
let _ = io::stdin().read_line(&mut buff).expect("Read line from stdin");
|
||||||
|
let buff = buff.trim().to_lowercase();
|
||||||
|
if vec!["y", "yes"].contains(&buff.as_str()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if vec!["n", "no"].contains(&buff.as_str()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user