use std::fmt::Display;
use crate::utils::{csv::CSVFile, fs::FileMode, github::is_valid_token_file};
use super::{error::*, fs::write_csv};
use console::Style;
use console::Term;
use polars::frame::DataFrame;
#[derive(Debug)]
pub enum TaskStatus {
InProgress,
Success,
Failure,
}
impl Display for TaskStatus {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}",
match self {
TaskStatus::InProgress => "...",
TaskStatus::Success => " - SUCCESS",
TaskStatus::Failure => " - FAILED",
}
)
}
}
pub struct Logger {
term: Term,
last_line: Option<String>,
}
impl Default for Logger {
fn default() -> Self {
Logger::new()
}
}
impl Logger {
pub fn new() -> Logger {
Logger {
term: Term::stdout(),
last_line: None,
}
}
pub fn change_status(&self, status: TaskStatus) -> Result<(), Error> {
map_err(
self.term
.clear_line()
.and_then(|_| self.term.move_cursor_up(1)),
"Could not log in the terminal",
)?;
let last_line: &str = ok_or_else(
self.last_line.as_ref(),
"Could not change the status of a non previously logged task",
)?;
map_err(
self.term.write_line(&format!("{}{}", last_line, status)),
"Could not log in the terminal",
)
}
pub fn replace_last_line(&mut self, msg: &str) -> Result<(), Error> {
map_err(
self.term
.clear_line()
.and_then(|_| self.term.move_cursor_up(1))
.and_then(|_| self.term.write_line(msg)),
"Could not log in the terminal",
)
}
pub fn log(&mut self, msg: &str) -> Result<(), Error> {
self.last_line = Some(msg.to_string());
map_err(self.term.write_line(msg), "Could not log in the terminal")
}
pub fn log_warning(&mut self, msg: &str) -> Result<(), Error> {
let yellow = Style::new().yellow();
self.log(&yellow.apply_to(format!("WARNING: {}", msg)).to_string())
}
pub fn log_seed(&mut self, seed: u64) -> Result<(), Error> {
self.log(&format!("Your random seed is {}, don't forget it!", seed))
}
pub fn log_tokens(&mut self, tokens_file: &str) -> Result<Vec<String>, Error> {
self.log_completion("Loading tokens", || {
is_valid_token_file(tokens_file)
.and_then(|_| CSVFile::new(tokens_file, FileMode::Read)?.column(0))
})
}
pub fn start_task(&mut self, msg: &str) -> Result<(), Error> {
self.log(msg)?;
self.change_status(TaskStatus::InProgress)
}
pub fn log_completion<T, F>(&mut self, msg: &str, task: F) -> Result<T, Error>
where
F: FnOnce() -> Result<T, Error>,
{
self.start_task(msg)?;
let res: Result<T, Error> = task();
if res.is_ok() {
self.change_status(TaskStatus::Success)
} else {
self.change_status(TaskStatus::Failure)
}?;
res
}
}
pub fn log_output_file(
logger: &mut Logger,
output_path: &str,
no_output: bool,
force: bool,
) -> Result<(), Error> {
if no_output {
logger.log("No output file will be generated.")
} else {
match crate::utils::fs::check_path(output_path) {
Ok(_) => {
if force {
logger.log(&format!("Overriding existing file: {}", output_path))
} else {
Error::new(&format!(
"File {} already exists. Use --force to override it.",
output_path
))
.to_res()
}
}
Err(_) => logger.log(&format!("Creating new file: {}", output_path)),
}
}
}
pub fn log_write_output(
logger: &mut Logger,
output_path: &str,
data: &mut DataFrame,
no_output: bool,
) -> Result<(), Error> {
if !no_output {
logger.log_completion(&format!("Writing to {}", output_path), || {
write_csv(output_path, data)
})
} else {
Ok(())
}
}