mirror of
https://github.com/crunchy-labs/crunchy-cli.git
synced 2026-01-21 12:12:00 -06:00
Rewrite it in Rust
This commit is contained in:
parent
d4bef511cb
commit
039d7cfb81
51 changed files with 4018 additions and 3208 deletions
197
crunchy-cli-core/src/cli/log.rs
Normal file
197
crunchy-cli-core/src/cli/log.rs
Normal file
|
|
@ -0,0 +1,197 @@
|
|||
use log::{
|
||||
set_boxed_logger, set_max_level, Level, LevelFilter, Log, Metadata, Record, SetLoggerError,
|
||||
};
|
||||
use std::io::{stdout, Write};
|
||||
use std::sync::{mpsc, Mutex};
|
||||
use std::thread;
|
||||
use std::thread::JoinHandle;
|
||||
use std::time::Duration;
|
||||
|
||||
struct CliProgress {
|
||||
handler: JoinHandle<()>,
|
||||
sender: mpsc::SyncSender<(String, Level)>,
|
||||
}
|
||||
|
||||
impl CliProgress {
|
||||
fn new(record: &Record) -> Self {
|
||||
let (tx, rx) = mpsc::sync_channel(1);
|
||||
|
||||
let init_message = format!("{}", record.args());
|
||||
let init_level = record.level();
|
||||
let handler = thread::spawn(move || {
|
||||
let states = ["-", "\\", "|", "/"];
|
||||
|
||||
let mut old_message = init_message.clone();
|
||||
let mut latest_info_message = init_message;
|
||||
let mut old_level = init_level;
|
||||
for i in 0.. {
|
||||
let (msg, level) = match rx.try_recv() {
|
||||
Ok(payload) => payload,
|
||||
Err(e) => match e {
|
||||
mpsc::TryRecvError::Empty => (old_message.clone(), old_level),
|
||||
mpsc::TryRecvError::Disconnected => break,
|
||||
},
|
||||
};
|
||||
|
||||
// clear last line
|
||||
// prefix (2), space (1), state (1), space (1), message(n)
|
||||
let _ = write!(stdout(), "\r {}", " ".repeat(old_message.len()));
|
||||
|
||||
if old_level != level || old_message != msg {
|
||||
if old_level <= Level::Warn {
|
||||
let _ = writeln!(stdout(), "\r:: • {}", old_message);
|
||||
} else if old_level == Level::Info && level <= Level::Warn {
|
||||
let _ = writeln!(stdout(), "\r:: → {}", old_message);
|
||||
} else if level == Level::Info {
|
||||
latest_info_message = msg.clone();
|
||||
}
|
||||
}
|
||||
|
||||
let _ = write!(
|
||||
stdout(),
|
||||
"\r:: {} {}",
|
||||
states[i / 2 % states.len()],
|
||||
if level == Level::Info {
|
||||
&msg
|
||||
} else {
|
||||
&latest_info_message
|
||||
}
|
||||
);
|
||||
let _ = stdout().flush();
|
||||
|
||||
old_message = msg;
|
||||
old_level = level;
|
||||
|
||||
thread::sleep(Duration::from_millis(100));
|
||||
}
|
||||
|
||||
// clear last line
|
||||
// prefix (2), space (1), state (1), space (1), message(n)
|
||||
let _ = write!(stdout(), "\r {}", " ".repeat(old_message.len()));
|
||||
let _ = writeln!(stdout(), "\r:: ✓ {}", old_message);
|
||||
let _ = stdout().flush();
|
||||
});
|
||||
|
||||
Self {
|
||||
handler,
|
||||
sender: tx,
|
||||
}
|
||||
}
|
||||
|
||||
fn send(&self, record: &Record) {
|
||||
let _ = self
|
||||
.sender
|
||||
.send((format!("{}", record.args()), record.level()));
|
||||
}
|
||||
|
||||
fn stop(self) {
|
||||
drop(self.sender);
|
||||
let _ = self.handler.join();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
pub struct CliLogger {
|
||||
level: LevelFilter,
|
||||
progress: Mutex<Option<CliProgress>>,
|
||||
}
|
||||
|
||||
impl Log for CliLogger {
|
||||
fn enabled(&self, metadata: &Metadata) -> bool {
|
||||
metadata.level() <= self.level
|
||||
}
|
||||
|
||||
fn log(&self, record: &Record) {
|
||||
if !self.enabled(record.metadata())
|
||||
|| (record.target() != "progress"
|
||||
&& record.target() != "progress_end"
|
||||
&& !record.target().starts_with("crunchy_cli"))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if self.level >= LevelFilter::Debug {
|
||||
self.extended(record);
|
||||
return;
|
||||
}
|
||||
|
||||
match record.target() {
|
||||
"progress" => self.progress(record, false),
|
||||
"progress_end" => self.progress(record, true),
|
||||
_ => {
|
||||
if self.progress.lock().unwrap().is_some() {
|
||||
self.progress(record, false);
|
||||
} else if record.level() > Level::Warn {
|
||||
self.normal(record)
|
||||
} else {
|
||||
self.error(record)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn flush(&self) {
|
||||
let _ = stdout().flush();
|
||||
}
|
||||
}
|
||||
|
||||
impl CliLogger {
|
||||
pub fn new(level: LevelFilter) -> Self {
|
||||
Self {
|
||||
level,
|
||||
progress: Mutex::new(None),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init(level: LevelFilter) -> Result<(), SetLoggerError> {
|
||||
set_max_level(level);
|
||||
set_boxed_logger(Box::new(CliLogger::new(level)))
|
||||
}
|
||||
|
||||
fn extended(&self, record: &Record) {
|
||||
println!(
|
||||
"[{}] {} {} ({}) {}",
|
||||
chrono::Utc::now().format("%Y-%m-%d %H:%M:%S"),
|
||||
record.level(),
|
||||
// replace the 'progress' prefix if this function is invoked via 'progress!'
|
||||
record
|
||||
.target()
|
||||
.replacen("progress", "crunchy_cli", 1)
|
||||
.replacen("progress_end", "crunchy_cli", 1),
|
||||
format!("{:?}", thread::current().id())
|
||||
.replace("ThreadId(", "")
|
||||
.replace(')', ""),
|
||||
record.args()
|
||||
)
|
||||
}
|
||||
|
||||
fn normal(&self, record: &Record) {
|
||||
println!(":: {}", record.args())
|
||||
}
|
||||
|
||||
fn error(&self, record: &Record) {
|
||||
eprintln!(":: {}", record.args())
|
||||
}
|
||||
|
||||
fn progress(&self, record: &Record, stop: bool) {
|
||||
let mut progress_option = self.progress.lock().unwrap();
|
||||
if stop && progress_option.is_some() {
|
||||
progress_option.take().unwrap().stop()
|
||||
} else if let Some(p) = &*progress_option {
|
||||
p.send(record);
|
||||
} else {
|
||||
*progress_option = Some(CliProgress::new(record))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! tab_info {
|
||||
($($arg:tt)+) => {
|
||||
if log::max_level() == log::LevelFilter::Debug {
|
||||
info!($($arg)+)
|
||||
} else {
|
||||
info!("\t{}", format!($($arg)+))
|
||||
}
|
||||
}
|
||||
}
|
||||
pub(crate) use tab_info;
|
||||
Loading…
Add table
Add a link
Reference in a new issue