From 79166669c48e8abe85d980b925ddf4db4a732d0f Mon Sep 17 00:00:00 2001 From: theMackabu <theMackabu@gmail.com> Date: Wed, 20 Nov 2024 14:43:25 -0800 Subject: [PATCH] full migration to tracing --- Cargo.toml | 6 +- maid/client/cli/{butler.rs => dispatch.rs} | 39 ++++++----- maid/client/cli/mod.rs | 43 ++++++------- maid/client/cli/run.rs | 59 ++++++----------- maid/client/cli/tasks.rs | 28 ++++---- maid/client/helpers/logger.rs | 24 +++---- maid/client/helpers/maidfile.rs | 13 +--- maid/client/helpers/status.rs | 16 ++--- maid/client/helpers/string.rs | 11 +--- maid/client/main.rs | 64 +++++++++---------- maid/client/parse/file.rs | 40 ++++-------- maid/client/server/cli.rs | 25 +++----- maid/client/server/file.rs | 9 +-- maid/client/table.rs | 28 ++++---- maid/server/docker/run.rs | 52 +++++++-------- maid/server/helpers/file.rs | 9 +-- maid/server/main.rs | 5 +- maid/server/table.rs | 34 ++++------ maid/shared/colors.rs | 36 +++++++++++ maid/shared/lib.rs | 2 + maid/{client/cli/verbose.rs => shared/log.rs} | 50 ++++++++++++--- 21 files changed, 297 insertions(+), 296 deletions(-) rename maid/client/cli/{butler.rs => dispatch.rs} (75%) create mode 100644 maid/shared/colors.rs create mode 100644 maid/shared/lib.rs rename maid/{client/cli/verbose.rs => shared/log.rs} (80%) diff --git a/Cargo.toml b/Cargo.toml index 03696a3..5251675 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,6 +8,10 @@ build = "build/attribute.rs" repository = "https://github.com/exact-rs/maid" description = "๐จ A comprehensive build tool for all your needs." +[lib] +name = "maid" +path = "maid/shared/lib.rs" + [[bin]] name = "maid" path = "maid/client/main.rs" @@ -71,6 +75,7 @@ home = "0.5.9" colored = "2.1.0" flate2 = "1.0.35" anyhow = "1.0.93" +tracing = "0.1.40" termcolor = "1.4.1" macros-rs = "0.5.2" indicatif = "0.17.9" @@ -78,7 +83,6 @@ serde_json = "1.0.133" text_placeholder = "0.5.1" global_placeholders = "0.1.0" -log = { version = "0.1.40", package = "tracing" } clap = { version = "4.5.21", features = ["derive"] } serde = { version = "1.0.215", features = ["derive"] } uuid = { version = "1.11.0", features = ["v4", "fast-rng"] } diff --git a/maid/client/cli/butler.rs b/maid/client/cli/dispatch.rs similarity index 75% rename from maid/client/cli/butler.rs rename to maid/client/cli/dispatch.rs index c92f238..3e2710e 100644 --- a/maid/client/cli/butler.rs +++ b/maid/client/cli/dispatch.rs @@ -1,19 +1,24 @@ use crate::helpers; +use maid::log::prelude::*; -use colored::Colorize; use inquire::Text; use macros_rs::string; use notify::RecursiveMode; use notify_debouncer_mini::new_debouncer; use std::{fs::File, io::Write, path::Path, time::Duration}; -fn create_error(name: &str, path: &str) { - println!("An error happened when asking for {name}, try again later."); - std::fs::remove_file(path).unwrap(); - std::process::exit(1); +pub(crate) fn clean() { + if let Ok(_) = std::fs::remove_dir_all(".maid/temp") { + info!("{}", "Purged temp archives".green()) + } + + match std::fs::remove_dir_all(".maid/cache") { + Ok(_) => info!("{}", "Emptied build cache".green()), + Err(_) => warn!("{}", "Build cache does not exist, cannot remove".yellow()), + }; } -pub fn watch(path: &Path) { +pub(crate) fn watch(path: &Path) { let (tx, rx) = std::sync::mpsc::channel(); let mut debouncer = new_debouncer(Duration::from_secs(1), tx).unwrap(); @@ -25,11 +30,15 @@ pub fn watch(path: &Path) { } } -pub fn update() { println!("check and retrive updates") } +pub(crate) fn update() { println!("check and retrive updates") } + +pub(crate) fn init() { + fn create_error(name: &str, path: &str) { + std::fs::remove_file(path).unwrap(); + error!("An error happened when asking for {}, try again later.", name); + } -pub fn init() { let path = "maidfile"; - let example_maidfile = "[tasks.example]\ninfo = \"this is a comment\"\nscript = \"echo 'hello world'\""; if !helpers::Exists::file(path.to_owned()).unwrap() { @@ -60,15 +69,3 @@ pub fn init() { println!("{}", "maidfile already exists, aborting".yellow()) } } - -pub fn clean() { - match std::fs::remove_dir_all(".maid/temp") { - Ok(_) => println!("{}", "removed temp archives".green()), - Err(_) => {} - }; - - match std::fs::remove_dir_all(".maid/cache") { - Ok(_) => println!("{}", "cleaned maid cache".green()), - Err(_) => println!("{}", "maid cache does not exist, cannot remove".yellow()), - }; -} diff --git a/maid/client/cli/mod.rs b/maid/client/cli/mod.rs index 84d8a82..71b1f2a 100644 --- a/maid/client/cli/mod.rs +++ b/maid/client/cli/mod.rs @@ -1,5 +1,4 @@ -pub(crate) mod verbose; -pub(crate) mod butler; +pub(crate) mod dispatch; pub(crate) mod run; pub(crate) mod tasks; @@ -9,7 +8,8 @@ use crate::server; use crate::structs::{Cache, CacheConfig, Task}; use crate::task; -use colored::Colorize; +use maid::log::prelude::*; + use fs_extra::dir::get_size; use global_placeholders::global; use human_bytes::human_bytes; @@ -23,7 +23,7 @@ pub fn get_version(short: bool) -> String { }; } -pub fn info(path: &String) { +pub fn info(path: &String) { let values = helpers::maidfile::merge(path); let project_root = parse::file::find_maidfile_root(path); @@ -52,8 +52,8 @@ pub fn info(path: &String) { ); } -pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: bool, is_remote: bool, log_level: Option<log::Level>, force: bool) { - log::info!("Starting maid {}", env!("CARGO_PKG_VERSION")); +pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: bool, is_remote: bool, log_level: Option<tracing::Level>, force: bool) { + debug!("Starting maid {}", env!("CARGO_PKG_VERSION")); if task.is_empty() { if is_remote { @@ -101,7 +101,7 @@ pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: pb.suspend(|| { println!( "{} {} in {} {}\n", - helpers::string::check_icon(), + maid::colors::OK, format!("finished {} {}", deps.len(), ternary!(deps.len() > 1, "dependencies", "dependency")).bright_green(), format!("{:.2?}", start.elapsed()).yellow(), format!("[{}]", deps.join(", ")).white() @@ -127,7 +127,7 @@ pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: if !cache.path.trim().is_empty() && !cache.target.is_empty() && !is_remote { if !helpers::Exists::folder(global!("maid.cache_dir", task)).unwrap() { std::fs::create_dir_all(global!("maid.cache_dir", task)).unwrap(); - log::debug!("created maid cache dir"); + debug!("created maid cache dir"); } let hash = task::cache::create_hash(&cache.path); @@ -142,7 +142,7 @@ pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: }) .unwrap(), ) { - Ok(_) => log::debug!("created {task} cache config"), + Ok(_) => debug!("created {task} cache config"), Err(err) => crashln!("error {err} creating cache config"), }; } @@ -170,11 +170,8 @@ pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: ); match std::fs::copy(Path::new(&cache_file), target.clone()) { - Ok(_) => log::debug!("copied target file {}", target), - Err(err) => { - log::warn!("{err}"); - crashln!("Cannot copy target file."); - } + Ok(_) => debug!("copied target file {}", target), + Err(err) => error!(%err, "Cannot copy target file."), }; } @@ -188,23 +185,23 @@ pub fn exec(task: &str, args: &Vec<String>, path: &String, silent: bool, is_dep: }) .unwrap(), ) { - Ok(_) => log::debug!("added hash for {task} -> {hash}"), - Err(err) => crashln!("error {err} creating cache config"), + Ok(_) => debug!("added hash for {task} -> {hash}"), + Err(err) => error!(%err, "error creating cache config"), }; } }; - log::debug!("Is remote?: {is_remote}"); - log::debug!("Project dir: {:?}", project_root); - log::debug!("Task path: {task_path}"); - log::debug!("Working dir: {cwd}"); - log::debug!("Started task: {task}"); + debug!("Is remote?: {is_remote}"); + debug!("Project dir: {:?}", project_root); + debug!("Task path: {task_path}"); + debug!("Working dir: {cwd}"); + debug!("Started task: {task}"); if !silent && !is_remote { ternary!( task_path == helpers::string::path_to_str(project_root.as_path()) || task_path == "%{dir.current}" || task_path == "." || task_path == *cwd, - println!("{} {}", helpers::string::arrow_icon(), &values.tasks[task].script), - println!("{} {} {}", format!("({task_path})").bright_cyan(), helpers::string::arrow_icon(), &values.tasks[task].script) + println!("{} {}", maid::colors::ARROW, &values.tasks[task].script), + println!("{} {} {}", format!("({task_path})").bright_cyan(), maid::colors::ARROW, &values.tasks[task].script) ) } diff --git a/maid/client/cli/run.rs b/maid/client/cli/run.rs index e7b8433..59ded4e 100644 --- a/maid/client/cli/run.rs +++ b/maid/client/cli/run.rs @@ -1,14 +1,14 @@ +use maid::log::prelude::*; + use crate::cli; use crate::helpers; use crate::shell::IntoArgs; use crate::structs::{Cache, Runner}; use crate::table; -use colored::Colorize; use fs_extra::dir::get_size; use human_bytes::human_bytes; use macros_rs::{crashln, string}; -use serde_json::json; use std::env; use std::io::Error; use std::path::Path; @@ -32,42 +32,28 @@ fn run_script(runner: Runner) { args.remove(0); (result[0].clone(), args) } - Err(err) => { - log::warn!("{err}"); - crashln!("Script could not be parsed into args"); - } + Err(err) => error!(%err, "Script could not be parsed into args"), }; - log::debug!("Original Script: {}", string); - log::debug!("Parsed Script: {}", script); - log::trace!("{}", json!({"name": name, "args": args})); - log::info!("Execute Command: '{name} {}'", args.join(" ")); + debug!("Original Script: {}", string); + debug!("Parsed Script: {}", script); + debug!("Execute Command: '{name} {}'", args.join(" ")); let working_dir = runner.project.join(&Path::new(runner.path)); match env::set_current_dir(&working_dir) { - Ok(_) => { - log::info!("Working directory: {:?}", &working_dir); - } - Err(err) => { - crashln!("Failed to set working directory {:?}\nError: {:#?}", &working_dir, err); - } + Ok(_) => debug!("Working directory: {:?}", &working_dir), + Err(err) => error!(%err, "Failed to set working directory {:?}", &working_dir), }; if runner.is_dep { cmd = match Command::new(&name).stdout(Stdio::null()).stderr(Stdio::null()).stdin(Stdio::null()).args(args.clone()).spawn() { Ok(output) => output, - Err(err) => { - log::warn!("{err}"); - crashln!("Cannot start command {name}."); - } + Err(err) => error!(%err, "Cannot start command {name}."), }; } else { cmd = match Command::new(&name).args(args.clone()).stdout(Stdio::inherit()).stderr(Stdio::inherit()).stdin(Stdio::inherit()).spawn() { Ok(output) => output, - Err(err) => { - log::warn!("{err}"); - crashln!("Cannot start command {name}."); - } + Err(err) => error!(%err, "Cannot start command {name}."), }; } @@ -75,12 +61,12 @@ fn run_script(runner: Runner) { let exit_code = helpers::status::code(&status); status_array.push(status); - log::debug!("Finished cmd: '{name} {}' with exit code: {:?} in {:.2?}", args.join(" "), exit_code, start.elapsed()); + debug!("Finished cmd: '{name} {}' with exit code: {:?} in {:.2?}", args.join(" "), exit_code, start.elapsed()); } let status = match status_array.last() { Some(status) => status, - None => crashln!("Failed to fetch final status code."), + None => error!("Failed to fetch final status code."), }; let cache = match &runner.maidfile.tasks[runner.name].cache { @@ -93,7 +79,7 @@ fn run_script(runner: Runner) { if !runner.silent { if success { - println!("\n{} {}", helpers::string::check_icon(), "finished task successfully".bright_green()); + println!("\n{} {}", maid::colors::OK, "finished task successfully".bright_green()); if !cache.path.trim().is_empty() && !cache.target.is_empty() { for target in cache.target { let cache_file = format!(".maid/cache/{}/target/{}", runner.name, Path::new(&target).file_name().unwrap().to_str().unwrap()); @@ -104,19 +90,15 @@ fn run_script(runner: Runner) { format!("saved target '{}' to cache", target).bright_magenta(), format!("{}", human_bytes(get_size(cache_file.clone()).unwrap() as f64).white()) ); - log::debug!("saved target file {}", target) - } - Err(err) => { - log::warn!("{err}"); - log::debug!("path: {}", target); - crashln!("Cannot save target file."); + debug!("saved target file {}", target) } + Err(err) => error!(%err, target, "Cannot save target file"), }; } } println!("{} took {}", runner.name.white(), format!("{:.2?}", start.elapsed()).yellow()); } else { - println!("\n{} {} {}", helpers::string::cross_icon(), "exited with status code".bright_red(), format!("{}", exit_code).red()); + println!("\n{} {} {}", maid::colors::FAIL, "exited with status code".bright_red(), format!("{}", exit_code).red()); println!("{} took {}", runner.name.white(), format!("{:.2?}", start.elapsed()).yellow()); } } else { @@ -127,15 +109,12 @@ fn run_script(runner: Runner) { match std::fs::copy(Path::new(&target), cache_file.clone()) { Ok(_) => println!( "{} {}{}{}", - helpers::string::add_icon(), + maid::colors::ADD, format!("{}", target).bright_green(), - helpers::string::seperator(), + maid::colors::SEP, format!("{}", human_bytes(get_size(cache_file.clone()).unwrap() as f64).bright_cyan()) ), - Err(err) => { - log::error!("{err}"); - log::debug!("path: {}", target); - } + Err(err) => warn!(%err, %target, "Cache miss"), }; } } diff --git a/maid/client/cli/tasks.rs b/maid/client/cli/tasks.rs index 798a875..572d8f5 100644 --- a/maid/client/cli/tasks.rs +++ b/maid/client/cli/tasks.rs @@ -4,9 +4,9 @@ use crate::parse; use crate::structs; use crate::table; -use colored::Colorize; use inquire::Select; use macros_rs::{string, ternary}; +use maid::log::prelude::*; use text_placeholder::Template; pub fn json(path: &String, args: &Vec<String>, hydrate: bool) { @@ -21,19 +21,22 @@ pub fn json(path: &String, args: &Vec<String>, hydrate: bool) { pub struct List; impl List { - pub fn all(path: &String, silent: bool, log_level: Option<log::Level>, force: bool) { + pub fn all(path: &String, silent: bool, log_level: Option<tracing::Level>, force: bool) { let values = helpers::maidfile::merge(path); let mut options: Vec<_> = values .tasks .iter() .map(|(key, task)| { let info = match &task.info { - Some(info) => ternary!(info.trim().len() < 1, string!("(no description)").bright_red(), format!("({info})").white()), - None => string!("(no description)").bright_red(), + Some(info) => match info.trim().len() < 1 { + true => "(no description)".to_string().bright_red(), + false => format!("({info})").white(), + }, + None => "(no description)".to_string().bright_red(), }; let verbose = match log_level.unwrap() { - log::Level::ERROR => string!(), + tracing::Level::ERROR => string!(), _ => string!(task.script), }; @@ -59,7 +62,7 @@ impl List { options.retain(|key| key.hidden == false); match Select::new("Select a task to run:", options).prompt() { Ok(task) => { - log::debug!("Starting {}", task.name); + debug!("Starting {}", task.name); cli::exec(&String::from(task.name), &vec![String::from("")], &path, silent, false, false, log_level, force); } @@ -67,19 +70,22 @@ impl List { } } - pub fn remote(path: &String, silent: bool, log_level: Option<log::Level>) { + pub fn remote(path: &String, silent: bool, log_level: Option<tracing::Level>) { let values = helpers::maidfile::merge(path); let mut options: Vec<_> = values .tasks .iter() .map(|(key, task)| { let info = match &task.info { - Some(info) => ternary!(info.trim().len() < 1, string!("(no description)").bright_red(), format!("({info})").white()), - None => string!("(no description)").bright_red(), + Some(info) => match info.trim().len() < 1 { + true => "(no description)".to_string().bright_red(), + false => format!("({info})").white(), + }, + None => "(no description)".to_string().bright_red(), }; let verbose = match log_level.unwrap() { - log::Level::ERROR => string!(), + tracing::Level::ERROR => string!(), _ => string!(task.script), }; @@ -99,7 +105,7 @@ impl List { options.retain(|key| key.hidden == false); match Select::new("Select a remote task to run:", options).prompt() { Ok(task) => { - log::debug!("Starting {}", task.name); + debug!("Starting {}", task.name); cli::exec(&String::from(task.name), &vec![String::from("")], &path, silent, false, true, log_level, false); } diff --git a/maid/client/helpers/logger.rs b/maid/client/helpers/logger.rs index d91148f..08bc460 100644 --- a/maid/client/helpers/logger.rs +++ b/maid/client/helpers/logger.rs @@ -2,17 +2,17 @@ macro_rules! log { ($level:expr, $($arg:tt)*) => {{ lazy_static::lazy_static! { - static ref LEVEL_COLORS: std::collections::HashMap<Level, (&'static str, &'static str)> = { + static ref LEVEL_COLORS: std::collections::HashMap<Level, (&'static str, colored::Color)> = { let mut map = std::collections::HashMap::new(); - map.insert(Level::Fatal, ("FATAL", "bright red")); - map.insert(Level::Docker, ("DOCKER", "bright yellow")); - map.insert(Level::Info, ("INFO", "cyan")); - map.insert(Level::Build, ("BUILD", "bright green")); - map.insert(Level::Success, ("SUCCESS", "green")); - map.insert(Level::Debug, ("DEBUG", "magenta")); - map.insert(Level::Notice, ("NOTICE", "bright blue")); - map.insert(Level::Warning, ("WARN", "yellow")); - map.insert(Level::Error, ("ERROR", "red")); + map.insert(Level::Fatal, ("FATAL", colored::Color::BrightRed)); + map.insert(Level::Docker, ("DOCKER", colored::Color::BrightYellow)); + map.insert(Level::Info, ("INFO", colored::Color::Cyan)); + map.insert(Level::Build, ("BUILD", colored::Color::BrightGreen)); + map.insert(Level::Success, ("SUCCESS", colored::Color::Green)); + map.insert(Level::Debug, ("DEBUG", colored::Color::Magenta)); + map.insert(Level::Notice, ("NOTICE", colored::Color::BrightBlue)); + map.insert(Level::Warning, ("WARN", colored::Color::Yellow)); + map.insert(Level::Error, ("ERROR", colored::Color::Red)); return map; }; } @@ -21,8 +21,8 @@ macro_rules! log { print!("{}", format_args!($($arg)*).to_string()); } else { match LEVEL_COLORS.get(&$level) { - Some((level_text, color_func)) => { - let level_text = level_text.color(color_func.to_string()); + Some((level_text, color)) => { + let level_text = level_text.color(*color); println!("{} {}", level_text, format_args!($($arg)*).to_string()) } None => println!("Unknown log level: {:?}", $level), diff --git a/maid/client/helpers/maidfile.rs b/maid/client/helpers/maidfile.rs index 5354625..6c7aad8 100644 --- a/maid/client/helpers/maidfile.rs +++ b/maid/client/helpers/maidfile.rs @@ -1,7 +1,6 @@ use crate::parse; use crate::structs::{DisplayTask, Maidfile}; - -use macros_rs::crashln; +use maid::log::prelude::*; pub fn merge(path: &String) -> Maidfile { let mut values = parse::file::read_maidfile(path); @@ -10,10 +9,7 @@ pub fn merge(path: &String) -> Maidfile { for import in imported_values.iter() { values = match merge_struct::merge(&values, &import) { Ok(merge) => merge, - Err(err) => { - log::warn!("{err}"); - crashln!("Unable to import tasks."); - } + Err(err) => error!(%err, "Unable to import tasks"), }; } @@ -24,10 +20,7 @@ impl Maidfile { pub fn to_json(&self) -> String { match serde_json::to_string(&self) { Ok(contents) => contents, - Err(err) => { - log::warn!("{err}"); - crashln!("Cannot read maidfile."); - } + Err(err) => error!(%err, "Cannot read Maidfile"), } } } diff --git a/maid/client/helpers/status.rs b/maid/client/helpers/status.rs index bd4c29b..12bdb71 100644 --- a/maid/client/helpers/status.rs +++ b/maid/client/helpers/status.rs @@ -1,29 +1,23 @@ -use macros_rs::{crashln, errorln}; +use maid::log::prelude::*; use std::{io::Error, process::ExitStatus}; pub fn error(debug_err: &str) { - errorln!("Unable to parse maidfile. Contains unexpected {debug_err} values."); + error!("Unable to parse maidfile. Contains unexpected {debug_err} values."); } pub fn code(status: &Result<ExitStatus, Error>) -> i32 { match status.as_ref() { Ok(status) => match status.code() { Some(iter) => iter, - None => crashln!("Missing status value"), + None => error!("Missing status value"), }, - Err(err) => { - log::warn!("{err}"); - crashln!("Unknown error, check verbose."); - } + Err(err) => error!(%err, "Unknown error"), } } pub fn success(status: &Result<ExitStatus, Error>) -> bool { match status.as_ref() { Ok(status) => status.success(), - Err(err) => { - log::warn!("{err}"); - crashln!("Unknown error, check verbose."); - } + Err(err) => error!(%err, "Unknown error"), } } diff --git a/maid/client/helpers/string.rs b/maid/client/helpers/string.rs index e99f525..0a05c95 100644 --- a/maid/client/helpers/string.rs +++ b/maid/client/helpers/string.rs @@ -1,9 +1,8 @@ -use colored::{ColoredString, Colorize}; use std::path::Path; // cache use std::collections::HashMap; -use std::sync::{Mutex, LazyLock}; +use std::sync::{LazyLock, Mutex}; static STRING_CACHE: LazyLock<Mutex<HashMap<String, &'static str>>> = LazyLock::new(|| Mutex::new(HashMap::new())); @@ -12,7 +11,7 @@ pub(crate) fn path_to_str(path: &Path) -> &'static str { if let Some(cached) = STRING_CACHE.lock().unwrap().get(&string) { return cached; } - + let leaked = Box::leak(string.clone().into_boxed_str()); STRING_CACHE.lock().unwrap().insert(string, leaked); leaked @@ -24,9 +23,3 @@ pub(crate) fn trim_start_end(value: &str) -> &str { chars.next_back(); chars.as_str() } - -pub(crate) fn seperator() -> ColoredString { ":".white() } -pub(crate) fn arrow_icon() -> ColoredString { "ยป".white() } -pub(crate) fn add_icon() -> ColoredString { "+".green() } -pub(crate) fn cross_icon() -> ColoredString { "โ".red() } -pub(crate) fn check_icon() -> ColoredString { "โ".green() } \ No newline at end of file diff --git a/maid/client/main.rs b/maid/client/main.rs index d9dcf91..58eebc4 100644 --- a/maid/client/main.rs +++ b/maid/client/main.rs @@ -8,11 +8,12 @@ mod structs; mod table; mod task; +use maid::log::{InfoLevel, Verbosity}; + use clap::{Parser, ValueEnum}; -use cli::verbose::{NoneLevel, Verbosity}; -use tracing_subscriber::{fmt, prelude::*}; use macros_rs::str; use std::path::Path; +use tracing_subscriber::{fmt, prelude::*}; macro_rules! dispatch { ($cli:expr, { $($flag:ident => $func:expr),+ $(,)? }) => {$( @@ -29,50 +30,50 @@ struct Cli { /// Run a task defined in maidfile #[arg(default_value = "", hide_default_value = true)] task: Vec<String>, - + /// Base path for Maidfile #[arg(short, long, default_value = "maidfile")] path: String, - + /// Ignore cache on build #[arg(short, long)] force: bool, - + /// Switch Maid to server mode #[arg(short, long, visible_alias = "online")] remote: bool, - + /// Clear build cache #[arg(short = 'C', long, visible_alias = "purge", group = "commands")] clean_cache: bool, - + /// Create new Maid project #[arg(short, long, group = "commands")] init: bool, - + /// List all runnable tasks #[arg(short, long, visible_alias = "tasks", visible_alias = "ls", group = "commands")] list: bool, - - /// Watch for changes in specified path + + /// Watch for changes in specified path #[arg(short = 'W', long)] watch: Option<String>, - + /// View Maid health (server health if enabled) #[arg(short = 'H', long, group = "commands")] health: bool, - + /// Per project commands #[arg(short = 'w', long, group = "commands")] project: Option<Project>, - + /// Management Maid commands #[arg(short = 'g', long, group = "commands")] system: Option<System>, - + #[clap(flatten)] - verbose: Verbosity<NoneLevel>, - + verbose: Verbosity<InfoLevel>, + /// Shows this quick reference #[clap(short, long, action = clap::ArgAction::HelpLong)] help: Option<bool>, @@ -99,49 +100,46 @@ enum Project { fn main() { let cli = Cli::parse(); let fmt_layer = fmt::layer().without_time(); - - tracing_subscriber::registry() - .with(cli.verbose.log_level_filter()) - .with(fmt_layer) - .init(); - + + tracing_subscriber::registry().with(cli.verbose.log_level_filter()).with(fmt_layer).init(); + globals::init(); - + dispatch!(cli, { - init => cli::butler::init(), - health => server::cli::connect(&cli.path), + init => cli::dispatch::init(), + health => server::cli::connect(&cli.path), health => match cli.remote { true => server::cli::connect(&cli.path), false => server::cli::connect(&cli.path), // improve health command for later }, clean_cache => match cli.remote { true => server::cli::connect(&cli.path), - false => cli::butler::clean(), + false => cli::dispatch::clean(), }, list => match cli.remote { true => cli::tasks::List::remote(&cli.path, cli.verbose.is_silent(), cli.verbose.log_level()), false => cli::tasks::List::all(&cli.path, cli.verbose.is_silent(), cli.verbose.log_level(), cli.force), } }); - + if let Some(project) = cli.project { return match project { Project::Info => cli::info(&cli.path), // add more info - Project::Env => {}, // print env from maidfile + Project::Env => {} // print env from maidfile }; } - + if let Some(system) = cli.system { return match system { - System::Update => cli::butler::update(), // add real update checker + System::Update => cli::dispatch::update(), // add real update checker System::Json => cli::tasks::json(&cli.path, &cli.task, false), System::JsonHydrated => cli::tasks::json(&cli.path, &cli.task, true), }; } - + if let Some(path) = cli.watch { - return cli::butler::watch(Path::new(&path)); // migrate watch path into executer below + return cli::dispatch::watch(Path::new(&path)); // migrate watch path into executer below } - + cli::exec(cli.task[0].trim(), &cli.task, &cli.path, cli.verbose.is_silent(), false, cli.remote, cli.verbose.log_level(), cli.force) } diff --git a/maid/client/parse/file.rs b/maid/client/parse/file.rs index 18d6f83..8d9a9ea 100644 --- a/maid/client/parse/file.rs +++ b/maid/client/parse/file.rs @@ -1,5 +1,6 @@ use crate::structs::Maidfile; -use colored::Colorize; +use maid::log::prelude::*; + use macros_rs::{crashln, string, then}; use std::{env, fs, io::Result, path::Path, path::PathBuf}; @@ -80,7 +81,7 @@ fn read_file(path: PathBuf, kind: &str) -> Maidfile { let contents = match fs::read_to_string(&path) { Ok(contents) => contents, Err(err) => { - log::warn!("{}", err); + warn!("{}", err); crashln!("Cannot find maidfile. Does it exist?"); } }; @@ -90,17 +91,12 @@ fn read_file(path: PathBuf, kind: &str) -> Maidfile { "json" => serde_json::from_str(&contents).map_err(|err| string!(err)), "json5" => json5::from_str(&contents).map_err(|err| string!(err)), "yaml" | "yml" => serde_yaml::from_str(&contents).map_err(|err| string!(err)), - _ => { - log::warn!("Invalid format"); - crashln!("Cannot read maidfile."); - } + _ => error!("Invalid format, cannot read Maidfile"), }; match result { Ok(parsed) => parsed, - Err(err) => { - crashln!("Cannot read maidfile.\n{}", err.white()); - } + Err(err) => error!("Cannot read Maidfile.\n{}", err.white()), } } @@ -109,22 +105,16 @@ pub fn read_maidfile_with_error(filename: &String, error: &str) -> Maidfile { Ok(path) => match find_file(&path, &filename) { Some(path) => { let extension = path.extension().and_then(|s| s.to_str()); - log::debug!(path = path.display().to_string(), kind = extension, "Found tasks"); - + debug!(path = path.display().to_string(), kind = extension, "Found tasks"); + match extension { Some("yaml") | Some("yml") | Some("json") | Some("json5") => read_file(path.clone(), extension.unwrap()), _ => read_file(path, "toml"), } } - None => { - log::warn!("{error}"); - crashln!("{error}"); - } + None => error!("{error}"), }, - Err(err) => { - log::warn!("{err}"); - crashln!("Home directory could not found."); - } + Err(err) => error!(%err, "Home directory could not found"), } } @@ -133,18 +123,12 @@ pub fn find_maidfile_root(filename: &String) -> PathBuf { Ok(path) => match find_file(&path, &filename) { Some(mut path) => { path.pop(); - log::info!("Found project path: {}", path.display()); + debug!("Found project path: {}", path.display()); return path; } - None => { - log::warn!("Cannot find project root."); - crashln!("Cannot find project root."); - } + None => error!("Cannot find project root."), }, - Err(err) => { - log::warn!("{err}"); - crashln!("Home directory could not found."); - } + Err(err) => error!(%err, "Home directory could not found"), } } diff --git a/maid/client/server/cli.rs b/maid/client/server/cli.rs index ef776df..4f4f6aa 100644 --- a/maid/client/server/cli.rs +++ b/maid/client/server/cli.rs @@ -2,8 +2,8 @@ use crate::helpers; use crate::server; use crate::structs::{ConnectionData, ConnectionInfo, Kind, Level, Maidfile, Task, Websocket}; -use colored::Colorize; use macros_rs::{crashln, fmtstr}; +use maid::log::prelude::*; use reqwest::blocking::Client; use tungstenite::protocol::frame::{coding::CloseCode::Normal, CloseFrame}; use tungstenite::{client::connect_with_config, client::IntoClientRequest, protocol::WebSocketConfig, Message}; @@ -14,20 +14,13 @@ fn health(client: Client, values: Maidfile) -> server::api::health::Route { let response = match client.get(fmtstr!("{address}/api/health")).header("Authorization", fmtstr!("Bearer {token}")).send() { Ok(res) => res, - Err(err) => { - log::warn!("{err}"); - crashln!("Unable to connect to the maid server. Is it up?"); - } + Err(err) => error!(%err, "Unable to connect to the maid server. Is it up?"), }; - let body = - match response.json::<server::api::health::Route>() { - Ok(body) => body, - Err(err) => { - log::warn!("{err}"); - crashln!("Unable to connect to the maid server. Is the token correct?") - } - }; + let body = match response.json::<server::api::health::Route>() { + Ok(body) => body, + Err(err) => error!(%err, "Unable to connect to the maid server. Is the token correct?"), + }; return body; } @@ -102,7 +95,7 @@ pub fn remote(task: Task) { request.headers_mut().insert("Authorization", fmtstr!("Bearer {token}").parse().unwrap()); let (mut socket, response) = connect_with_config(request, Some(websocket_config), 3).expect("Can't connect"); - log::debug!("response code: {}", response.status()); + debug!("response code: {}", response.status()); let connection_data = ConnectionData { info: ConnectionInfo { @@ -121,7 +114,7 @@ pub fn remote(task: Task) { } }; - log::debug!("sending information"); + debug!("sending information"); socket.send(Message::Text(serde_json::to_string(&connection_data).unwrap())).unwrap(); loop { @@ -159,7 +152,7 @@ pub fn remote(task: Task) { server::file::remove_tar(&file_name); // run.rs:96 implement that later - println!("\n{} {}", helpers::string::check_icon(), "finished task successfully".bright_green()); + println!("\n{} {}", maid::colors::OK, "finished task successfully".bright_green()); println!("{}", "removed temporary archive".bright_magenta()); if let Err(err) = socket.close(Some(CloseFrame { diff --git a/maid/client/server/file.rs b/maid/client/server/file.rs index bc11ede..0e864a9 100644 --- a/maid/client/server/file.rs +++ b/maid/client/server/file.rs @@ -3,6 +3,7 @@ use crate::helpers; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use global_placeholders::global; use macros_rs::crashln; +use maid::log::prelude::*; use std::{fs::write, fs::File, path::PathBuf}; use tar::{Archive, Builder}; use uuid::Uuid; @@ -27,7 +28,7 @@ pub fn remove_tar(file: &String) { pub fn read_tar(archive: &Vec<u8>) -> Result<String, std::io::Error> { if !helpers::Exists::folder(global!("maid.temp_dir")).unwrap() { std::fs::create_dir_all(global!("maid.temp_dir")).unwrap(); - log::debug!("created maid temp dir"); + debug!("created maid temp dir"); } let file_name = format!("{}/{}.tgz", global!("maid.temp_dir"), Uuid::new_v4()); @@ -47,7 +48,7 @@ pub fn unpack_tar(path: &String) -> std::io::Result<()> { pub fn write_tar(files: &Vec<String>) -> Result<String, std::io::Error> { if !helpers::Exists::folder(global!("maid.temp_dir")).unwrap() { std::fs::create_dir_all(global!("maid.temp_dir")).unwrap(); - log::debug!("created maid temp dir"); + debug!("created maid temp dir"); } let file_name = format!("{}/{}.tgz", global!("maid.temp_dir"), Uuid::new_v4()); @@ -55,10 +56,10 @@ pub fn write_tar(files: &Vec<String>) -> Result<String, std::io::Error> { let enc = GzEncoder::new(archive, Compression::default()); let mut tar = Builder::new(enc); - log::debug!("compressing to {}", &file_name); + debug!("compressing to {}", &file_name); for path in files { append_to_tar(&mut tar, path)?; - log::debug!("{} {:?}", helpers::string::add_icon(), path); + debug!("{} {:?}", maid::colors::ADD, path); } Ok(file_name) diff --git a/maid/client/table.rs b/maid/client/table.rs index 70c05f8..ec03a8b 100644 --- a/maid/client/table.rs +++ b/maid/client/table.rs @@ -1,7 +1,8 @@ use crate::helpers; use crate::structs::Maidfile; +use maid::log::prelude::*; -use macros_rs::{errorln, str, ternary}; +use macros_rs::{str, ternary}; use std::path::PathBuf; use std::{collections::BTreeMap, collections::HashMap, env}; use text_placeholder::Template; @@ -14,35 +15,30 @@ pub fn create(values: Maidfile, args: &Vec<String>, project: PathBuf) -> HashMap table.insert("os.platform", env::consts::OS); table.insert("os.arch", env::consts::ARCH); - log::trace!(os_platform = env::consts::OS); - log::trace!(os_arch = env::consts::ARCH); + trace!(os_platform = env::consts::OS); + trace!(os_arch = env::consts::ARCH); match env::current_dir() { Ok(path) => { table.insert("dir.current", helpers::string::path_to_str(&path)); - log::trace!(dir_current = path.display().to_string()); - } - Err(err) => { - log::warn!("{err}"); - errorln!("Current directory could not be added as script variable."); + trace!(dir_current = path.display().to_string()); } + Err(err) => error!(%err, "Current directory could not be added as script variable."), } match home::home_dir() { Some(path) => { table.insert("dir.home", helpers::string::path_to_str(&path)); - log::trace!(dir_home = path.display().to_string()); - } - None => { - errorln!("Home directory could not be added as script variable."); + trace!(dir_home = path.display().to_string()); } + None => error!("Home directory could not be added as script variable."), } table.insert("dir.project", helpers::string::path_to_str(&project)); - log::trace!(dir_project = project.display().to_string()); + trace!(dir_project = project.display().to_string()); for (pos, arg) in args.iter().enumerate() { - log::trace!("arg_{pos} = \"{arg}\""); + trace!("arg_{pos} = \"{arg}\""); table.insert(str!(format!("arg.{pos}")), arg); } @@ -59,9 +55,9 @@ pub fn create(values: Maidfile, args: &Vec<String>, project: PathBuf) -> HashMap ); env::set_var(key, value_formatted.clone()); - log::trace!("env_{key} = \"{value_formatted}\""); + trace!("env_{key} = \"{value_formatted}\""); table.insert(str!(format!("env.{}", key.clone())), str!(value_formatted)); } - + return table; } diff --git a/maid/server/docker/run.rs b/maid/server/docker/run.rs index ec689c6..91f858c 100644 --- a/maid/server/docker/run.rs +++ b/maid/server/docker/run.rs @@ -2,14 +2,16 @@ macro_rules! Handle { ($id:ident, $socket:ident, $expr:expr $(, || $callback:expr)?) => { $( $callback; )? if let Err(err) = $expr { - log::error!("{err}"); + warn!("{err}"); $socket.remove_container(&$id, Some(RemoveContainerOptions { force: true, ..Default::default() })).await?; - log::warn!("removed old container"); + warn!("removed old container"); } }; } use crate::{structs::ConnectionData, table, Kind, Level, Response}; +use maid::log::prelude::*; + use bytes::Bytes; use flate2::{write::GzEncoder, Compression}; use futures_core::Stream; @@ -48,7 +50,7 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro Ok(value) => { parsed = Some(value); } - Err(err) => log::error!("Failed to deserialize JSON: {:?}", err), + Err(err) => error!("Failed to deserialize JSON: {:?}", err), } } } @@ -57,7 +59,7 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro let name = &parsed.info.name; let image = parsed.info.remote.image.clone(); - log::info!("creating container (task={name}, image={})", image); + info!("creating container (task={name}, image={})", image); let image_config = CreateImageOptions { from_image: str!(image.clone()), @@ -65,7 +67,7 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro }; let mut container = socket.create_image(Some(image_config), None, None); - log::info!("image created"); + info!("image created"); while let Some(message) = container.next().await { let message = message.as_ref().expect("Failed to get CreateImageInfo"); @@ -75,12 +77,11 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro message.progress.clone().unwrap_or_else(|| string!("")) ); - let docker_message = - Response { - level: Level::Docker, - kind: Kind::Message, - message: Some(formatted), - }; + let docker_message = Response { + level: Level::Docker, + kind: Kind::Message, + message: Some(formatted), + }; stream.send(docker_message.into()).await?; } @@ -92,9 +93,9 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro }; let id = socket.create_container::<&str, String>(None, config).await?.id; - log::info!("created container"); + info!("created container"); - Handle!(id, socket, socket.start_container::<String>(&id, None).await, || log::info!("started container")); + Handle!(id, socket, socket.start_container::<String>(&id, None).await, || info!("started container")); let binary_message = Response { level: Level::Success, @@ -105,14 +106,14 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro stream.send(binary_message.into()).await?; if let Some(result) = stream.next().await { - log::info!("received message: binary"); + info!("received message: binary"); let msg = result?; let bytes_to_body = |bytes: &[u8]| -> rocket::http::hyper::Body { rocket::http::hyper::Body::from(bytes.to_vec()) }; let upload_options = UploadToContainerOptions { path: "/opt", ..Default::default() }; Handle!(id, socket, socket.upload_to_container(&id, Some(upload_options), bytes_to_body(&msg.into_data())).await); - log::info!("wrote tarfile to container"); + info!("wrote tarfile to container"); } let dependencies = match &parsed.maidfile.tasks[&parsed.info.name].depends { @@ -177,18 +178,17 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro Handle!(id, socket, stream.send(output_message.into()).await); } else if let Err(err) = msg { - log::error!("{err}"); + warn!("{err}"); } } } - let res = - socket.download_from_container( - &id, - Some(DownloadFromContainerOptions { - path: fmtstr!("/opt/{}", parsed.info.remote.pull.clone()), - }), - ); + let res = socket.download_from_container( + &id, + Some(DownloadFromContainerOptions { + path: fmtstr!("/opt/{}", parsed.info.remote.pull.clone()), + }), + ); let bytes = concat_byte_stream(res).await?; let mut encoder = GzEncoder::new(Vec::new(), Compression::default()); @@ -197,7 +197,7 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro let compressed_data = encoder.finish()?; Handle!(id, socket, stream.send(Message::binary(compressed_data)).await); - log::info!("sent message: binary, from [{}]", parsed.info.remote.pull); + info!("sent message: binary, from [{}]", parsed.info.remote.pull); let done_message = Response { level: Level::Success, @@ -206,10 +206,10 @@ pub async fn exec(mut stream: DuplexStream, docker: &Result<Docker, anyhow::Erro }; stream.send(done_message.into()).await?; - log::info!("sent message: [done]"); + info!("sent message: [done]"); socket.remove_container(&id, Some(RemoveContainerOptions { force: true, ..Default::default() })).await?; - log::info!("removed old container"); + info!("removed old container"); Ok(()) } diff --git a/maid/server/helpers/file.rs b/maid/server/helpers/file.rs index 498eb20..4c1eb72 100644 --- a/maid/server/helpers/file.rs +++ b/maid/server/helpers/file.rs @@ -1,4 +1,5 @@ use crate::helpers; +use maid::log::prelude::*; use flate2::{read::GzDecoder, write::GzEncoder, Compression}; use global_placeholders::global; @@ -27,7 +28,7 @@ pub fn remove_tar(file: &String) { pub fn read_tar(archive: &Vec<u8>) -> Result<String, std::io::Error> { if !helpers::Exists::folder(global!("maid.temp_dir")).unwrap() { std::fs::create_dir_all(global!("maid.temp_dir")).unwrap(); - log::info!("created maid temp dir"); + info!("created maid temp dir"); } let file_name = format!("{}/{}.tgz", global!("maid.temp_dir"), Uuid::new_v4()); @@ -47,7 +48,7 @@ pub fn unpack_tar(path: &String) -> std::io::Result<()> { pub fn write_tar(files: &Vec<String>) -> Result<String, std::io::Error> { if !helpers::Exists::folder(global!("maid.temp_dir")).unwrap() { std::fs::create_dir_all(global!("maid.temp_dir")).unwrap(); - log::info!("created maid temp dir"); + info!("created maid temp dir"); } let file_name = format!("{}/{}.tgz", global!("maid.temp_dir"), Uuid::new_v4()); @@ -55,10 +56,10 @@ pub fn write_tar(files: &Vec<String>) -> Result<String, std::io::Error> { let enc = GzEncoder::new(archive, Compression::default()); let mut tar = Builder::new(enc); - log::info!("compressing to {}", &file_name); + info!("compressing to {}", &file_name); for path in files { append_to_tar(&mut tar, path)?; - log::info!("{} {:?}", helpers::string::add_icon(), path); + info!("{} {:?}", helpers::string::add_icon(), path); } Ok(file_name) diff --git a/maid/server/main.rs b/maid/server/main.rs index 832d027..9562196 100644 --- a/maid/server/main.rs +++ b/maid/server/main.rs @@ -7,6 +7,7 @@ mod table; use bollard::{Docker, API_DEFAULT_VERSION}; use docker::container; use macros_rs::{fmtstr, ternary}; +use maid::log::prelude::*; use rocket::futures::SinkExt; use rocket::{get, http::Status, launch, outcome::Outcome, routes, State}; use rocket_ws::{Channel, Message, WebSocket}; @@ -136,8 +137,8 @@ fn stream(ws: WebSocket, docker_state: &State<DockerState>, _token: Token) -> Ch stream.send(connect_success.into()).await?; match docker::run::exec(stream, &docker_state.docker).await { - Ok(_) => log::info!("build finished"), - Err(_) => log::error!("failed to build"), + Ok(_) => info!("build finished"), + Err(_) => warn!("failed to build"), }; Ok(()) diff --git a/maid/server/table.rs b/maid/server/table.rs index 8001057..120a1f1 100644 --- a/maid/server/table.rs +++ b/maid/server/table.rs @@ -1,9 +1,9 @@ use crate::helpers; use crate::structs::Maidfile; +use maid::log::prelude::*; -use colored::Colorize; -use macros_rs::{errorln, str, ternary}; -use serde_json::{json, Value}; +use macros_rs::{str, ternary}; +use serde_json::Value; use std::path::PathBuf; use std::{collections::BTreeMap, collections::HashMap, env}; use text_placeholder::Template; @@ -15,36 +15,30 @@ pub fn create(values: Maidfile, args: &Vec<String>, project: PathBuf) -> HashMap table.insert("os.platform", env::consts::OS); table.insert("os.arch", env::consts::ARCH); - log::info!("{} os.platform: '{}'", helpers::string::add_icon(), env::consts::OS.yellow()); - log::info!("{} os.arch: '{}'", helpers::string::add_icon(), env::consts::ARCH.yellow()); + trace!(os_platform = env::consts::OS); + trace!(os_arch = env::consts::ARCH); match env::current_dir() { Ok(path) => { table.insert("dir.current", helpers::string::path_to_str(&path)); - log::info!("{} dir.current: '{}'", helpers::string::add_icon(), helpers::string::path_to_str(&path).yellow()); - } - Err(err) => { - log::warn!("{err}"); - errorln!("Current directory could not be added as script variable."); + trace!(dir_current = path.display().to_string()); } + Err(err) => error!(%err, "Current directory could not be added as script variable."), } match home::home_dir() { Some(path) => { table.insert("dir.home", helpers::string::path_to_str(&path)); - log::info!("{} dir.home: '{}'", helpers::string::add_icon(), helpers::string::path_to_str(&path).yellow()); - } - None => { - errorln!("Home directory could not be added as script variable."); + trace!(dir_home = path.display().to_string()); } + None => error!("Home directory could not be added as script variable."), } - let project_root = helpers::string::path_to_str(&project); - table.insert("dir.project", project_root); - log::info!("{} dir.project: '{}'", helpers::string::add_icon(), project_root.yellow()); + table.insert("dir.project", helpers::string::path_to_str(&project)); + trace!(dir_project = project.display().to_string()); for (pos, arg) in args.iter().enumerate() { - log::info!("{} arg.{pos}: '{}'", helpers::string::add_icon(), arg.yellow()); + trace!("arg_{pos} = \"{arg}\""); table.insert(str!(format!("arg.{pos}")), arg); } @@ -61,11 +55,9 @@ pub fn create(values: Maidfile, args: &Vec<String>, project: PathBuf) -> HashMap ); env::set_var(key, value_formatted.clone()); - log::info!("{} env.{key}: '{}'", helpers::string::add_icon(), value_formatted.yellow()); + trace!("env_{key} = \"{value_formatted}\""); table.insert(str!(format!("env.{}", key.clone())), str!(value_formatted)); } - log::trace!("{}", json!({ "env": table })); - return table; } diff --git a/maid/shared/colors.rs b/maid/shared/colors.rs new file mode 100644 index 0000000..c1449a9 --- /dev/null +++ b/maid/shared/colors.rs @@ -0,0 +1,36 @@ +use colored::{ColoredString, Colorize}; +use std::{fmt, ops::Deref, sync::OnceLock}; + +pub struct LazyColoredString { + inner: OnceLock<ColoredString>, + initializer: fn() -> ColoredString, +} + +impl LazyColoredString { + const fn new(initializer: fn() -> ColoredString) -> Self { LazyColoredString { inner: OnceLock::new(), initializer } } + fn get(&self) -> &ColoredString { self.inner.get_or_init(self.initializer) } +} + +impl Deref for LazyColoredString { + type Target = ColoredString; + fn deref(&self) -> &Self::Target { self.get() } +} + +impl fmt::Display for LazyColoredString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fmt::Display::fmt(self.get(), f) } +} + +macro_rules! create_symbols { + ($($name:ident: $style:ident->$text:expr),* $(,)?) => {$( + pub static $name: LazyColoredString = LazyColoredString::new(|| $text.$style()); + )*}; +} + +create_symbols! { + SEP: white->":", + ARROW: white->"ยป", + ADD: green->"+", + WARN: yellow->"!", + FAIL: red->"โ", + OK: green->"โ", +} diff --git a/maid/shared/lib.rs b/maid/shared/lib.rs new file mode 100644 index 0000000..7746688 --- /dev/null +++ b/maid/shared/lib.rs @@ -0,0 +1,2 @@ +pub mod colors; +pub mod log; diff --git a/maid/client/cli/verbose.rs b/maid/shared/log.rs similarity index 80% rename from maid/client/cli/verbose.rs rename to maid/shared/log.rs index 3cbd6ff..0326213 100644 --- a/maid/client/cli/verbose.rs +++ b/maid/shared/log.rs @@ -1,5 +1,46 @@ -use log::{level_filters::LevelFilter, Level}; use std::fmt; +use tracing::{level_filters::LevelFilter, Level}; + +pub mod prelude { + pub use crate::{debug, error, info, trace, warn}; + pub use colored::{ColoredString, Colorize}; +} + +#[macro_export] +macro_rules! info { + ($($arg:tt)*) => { + tracing::info!($($arg)*) + } +} + +#[macro_export] +macro_rules! warn { + ($($arg:tt)*) => { + tracing::warn!($($arg)*) + } +} + +#[macro_export] +macro_rules! error { + ($($arg:tt)*) => {{ + tracing::error!($($arg)*); + std::process::exit(1); + }} +} + +#[macro_export] +macro_rules! debug { + ($($arg:tt)*) => { + tracing::debug!($($arg)*) + } +} + +#[macro_export] +macro_rules! trace { + ($($arg:tt)*) => { + tracing::trace!($($arg)*) + } +} #[derive(clap::Args, Debug, Clone, Default)] pub struct Verbosity<L: LogLevel = ErrorLevel> { @@ -107,10 +148,3 @@ pub struct InfoLevel; impl LogLevel for InfoLevel { fn default() -> Option<Level> { return Some(Level::INFO); } } - -#[derive(Copy, Clone, Debug, Default)] -pub struct NoneLevel; - -impl LogLevel for NoneLevel { - fn default() -> Option<Level> { None } -} -- GitLab