#![ allow( warnings ) ] use std::env; use clap::{ArgAction, ArgMatches}; use clap::{Arg, crate_version, Command}; use log::Level; use log::LevelFilter; use env_logger::Builder; use std::io::Write; use colored::Colorize; use log::{trace, debug, info, warn, error}; use std::ffi::{c_char, CStr}; mod error; mod utils; mod victor; mod eval; mod commands { pub mod info; pub mod stat; pub mod run; pub mod build; pub mod list; pub mod gui; pub mod env; pub mod snix_testing; } static APPNAME: &str = "vic"; static DEFAULT_LOG_LEVEL: LevelFilter = LevelFilter::Warn; fn main() { let cli_matches = cli_matches(); //init_logger(&cli_matches); // log a message of every level, to test loging // test_logger(); // match the subcommands let result = match cli_matches.subcommand() { Some(("info", sub_matches)) => commands::info::main(&sub_matches), Some(("stat", sub_matches)) => commands::stat::main(&sub_matches), Some(("run", sub_matches)) => commands::run::main(&sub_matches), Some(("build", sub_matches)) => commands::build::main(&sub_matches), Some(("list", sub_matches)) => commands::list::main(&sub_matches), Some(("gui", sub_matches)) => commands::gui::main(&sub_matches), Some(("env", sub_matches)) => commands::env::main(&sub_matches), Some(("env-get", sub_matches)) => commands::env::get(&sub_matches), Some(("snix-mount", sub_matches)) => commands::snix_testing::run_mount_blocking(&sub_matches), Some(("snix-test", sub_matches)) => commands::snix_testing::run_snix_test(&sub_matches), Some(("snix-build", sub_matches)) => commands::snix_testing::run_build(&sub_matches), None => { println!("Hi, this is victorinix (also known as victor or vic). \nrun 'vic help' for a list of subcommands."); Ok(())}, Some((unknown_subcommand, _)) => { println!("The subcommand '{}' is not known to victorinix", unknown_subcommand); Ok(()) }, }; if let Err(vic_err) = result { vic_err.log(); } } fn test() { let c_buf: *const c_char = unsafe { libelf::raw::elf_errmsg(-1) }; let c_str: &CStr = unsafe { CStr::from_ptr(c_buf) }; let str_slice: &str = c_str.to_str().unwrap(); println!("ERROR: {}", str_slice); } fn init_logger(cli_matches: &ArgMatches) { // to save log messages, before the logger is setup let mut log_messages: Vec<(Level, String)> = Vec::new(); log_messages.push((Level::Trace, "Starting victorinix".to_owned())); // builder with DEFAULT_LOG_LEVEL let mut builder = Builder::new(); builder.format_indent(Some(8)); // set loglevel let mut log_level = DEFAULT_LOG_LEVEL; log_messages.push((Level::Trace, format!("set log level to {} as specified by DEFAULT_LOG_LEVEL in the src/main.rs", DEFAULT_LOG_LEVEL))); // check env RUST_LOG let mut val_rust_log = level_from_env("RUST_LOG", log_level); log_level = if let Some(level_rust_log) = val_rust_log.0 { log_messages.push((Level::Trace, format!("set log level from the Variable RUST_LOG to {}", level_rust_log))); level_rust_log } else {log_level}; log_messages.append(&mut val_rust_log.1); // check env VICTORINIX_LOG let mut val_victorinix_log = level_from_env("VICTORINIX_LOG", log_level); log_level = if let Some(level_victorinix_log) = val_victorinix_log.0 { log_messages.push((Level::Trace, format!("set log level from the Variable VICTORINIX_LOG to {}", level_victorinix_log))); level_victorinix_log } else {log_level}; log_messages.append(&mut val_victorinix_log.1); // check -vvvvv flags let v_flag_count = cli_matches.get_count("verbose"); let old_log_level_as_num = match log_level { LevelFilter::Off => 0, LevelFilter::Error => 1, LevelFilter::Warn => 2, LevelFilter::Info => 3, LevelFilter::Debug => 4, LevelFilter::Trace => 5, }; let new_log_level_num = old_log_level_as_num + v_flag_count; log_level = match new_log_level_num { 0 => LevelFilter::Off, 1 => LevelFilter::Error, 2 => LevelFilter::Warn, 3 => LevelFilter::Info, 4 => LevelFilter::Debug, 5 => LevelFilter::Trace, _ => { log_messages.push((Level::Trace, format!("the number of verbose (-v) flags passed ({}) exeeds the maximumg log_level", v_flag_count))); LevelFilter::Trace }, }; log_messages.push((Level::Trace, format!("set log level from the verbose flags to {}", log_level))); // check --log-level arg if let Some(log_level_arg_string) = cli_matches.get_one::("log-level") { let log_level_from_arg = match log_level_arg_string.to_lowercase().as_str() { "none" => LevelFilter::Off, "trace" => LevelFilter::Trace, "all" => LevelFilter::Trace, "debug" => LevelFilter::Debug, "info" => LevelFilter::Info, "warn" => LevelFilter::Warn, "error" => LevelFilter::Error, _ => { log_messages.push((Level::Warn, format!( "The argument to --log-level \"{}\" is not a valid log-level. loglevel stays at: {}" , log_level_arg_string , log_level ))); log_level } }; log_level = log_level_from_arg; log_messages.push((Level::Trace, format!("set log level from the --log-level arg to {}", log_level_from_arg))); } // check --silent flag if cli_matches.get_flag("silent") { if log_level != DEFAULT_LOG_LEVEL { log_messages.push((Level::Warn, format!( "The loglevel was set by some method (it's not the DEFAULT_LOG_LEVEL) and is now overwritten by the --siltne to be OFF" ))) } log_level = LevelFilter::Off; } // actually set the log_level on the builder builder.filter_level(log_level); log_messages.push((Level::Info, format!("loglevel is now {}", log_level))); // set format builder.format(|buf, record| { match record.level() { Level::Trace => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).yellow(), record.args()), Level::Debug => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).green(), record.args()), Level::Info => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).blue(), record.args()), Level::Warn => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).truecolor(245, 164, 66), record.args()), Level::Error => writeln!(buf, "[ {} ] {}", format!("{}", record.level()).red(), record.args()), } }); builder.init(); // write out all log messages from log_messages for (level, msg) in log_messages { match level { Level::Error => error!("{}", msg), Level::Warn => warn!("{}", msg), Level::Info => info!("{}", msg), Level::Debug => debug!("{}", msg), Level::Trace => trace!("{}", msg), } } } fn test_logger() { error!("hi from error"); warn!("hi from warn"); info!("hi from info"); debug!("hi from debug"); trace!("hi from trace"); } fn level_from_env(var_name: &str, log_level: LevelFilter) -> (Option, Vec<(Level, String)>) { let mut log_messages: Vec<(Level, String)> = Vec::new(); let var_value = env::var(var_name); match var_value { Ok(val) => { // set loglevel acording to RUST_LOG let level = match val.to_lowercase().as_str() { "none" => LevelFilter::Off, "trace" => LevelFilter::Trace, "debug" => LevelFilter::Debug, "info" => LevelFilter::Info, "warn" => LevelFilter::Warn, "error" => LevelFilter::Error, _ => { log_messages.push((Level::Warn, format!( "The value of Environment Variable {}: \"{}\" is not a valid log-level. loglevel stays at: {}" , var_name , val , log_level ))); log_level } }; return (Some(level), log_messages) }, Err(env::VarError::NotPresent) => {}, Err(env::VarError::NotUnicode(os_string)) => { let os_string_as_hex : String = os_string.clone().into_encoded_bytes().iter() .map(|b| format!("{:02x}", b).to_string()) .collect::>() .join(""); log_messages.push((Level::Debug, format!( "value of Environment Variable {} was not a unicode valid string. Therefore ignoring it. \n value with replacement char U+FFFD: {} \n the WTF-8 raw bytes as hex: {}" , var_name , os_string.to_string_lossy(), os_string_as_hex ))); }, }; return (None, log_messages); } fn cli_matches() -> clap::ArgMatches { let main = Command::new(APPNAME) .version(crate_version!()) .author("Sebastian Moser (c2vi)") .about("Victorinix: Friendly and usefull box of portable computer tools. Powered by Nix and MiZe") .arg_required_else_help(true) .arg(Arg::new("verbose") .long("verbose") .short('v') .action(ArgAction::Count) ) .arg(Arg::new("log-level") .long("log-level") .value_name("LOGLEVEL") .help("set the log-level to one of OFF, ERROR, WARN, INFO, DEBUG, TRACE") ) .arg(Arg::new("silent") .long("silent") .action(ArgAction::SetTrue) .help("set the log-level to OFF") ) .subcommand(Command::new("info") ) .subcommand(Command::new("stat") ) .subcommand(Command::new("run") .arg(Arg::new("runnable") .help("The item to be run") .action(clap::ArgAction::Append) .trailing_var_arg(true) ) //.arg(Arg::new("rest") //.help("args to pass down to the runnable command") //) ) .subcommand(Command::new("build") ) .subcommand(Command::new("list") ) .subcommand(Command::new("gui") ) .subcommand(Command::new("env") ) .subcommand(Command::new("env-get") .alias("eg") ) .subcommand(Command::new("snix-mount") .alias("sm") ) .subcommand(Command::new("snix-test") .alias("st") ) .subcommand(Command::new("snix-build") .alias("sb") ) ; return main.get_matches(); }