Simplify logging system

This commit is contained in:
southerntofu 2020-11-28 16:48:23 +01:00
parent bd6e1d67f9
commit 78b5ebcd64
3 changed files with 85 additions and 60 deletions

View File

@ -7,7 +7,7 @@ use lazy_static::lazy_static;
lazy_static! {
static ref LOGLEVEL: LogLevel = LogLevel::from_env();
static ref LANG: Lang = lang_from_env();
static ref LANG: Lang = Lang::from_env();
static ref TRANSLATIONS: HashMap<String, String> = load_translations();
}
@ -21,7 +21,19 @@ pub enum Lang {
Some(String)
}
pub type Context<'a> = &'a HashMap<&'a str, &'a str>;
impl Lang {
fn from_env() -> Lang {
let lang =
env::var("LANG").expect("$LANG not set in environment. Your machine is misconfigured!");
match lang.as_str() {
"NONE" => Lang::None,
"REPLACE" => Lang::Replace,
_ => Lang::Some(lang[0..2].to_string())
}
}
}
pub type Context = HashMap<String, String>;
/// Returns a PathBuf containing the translations. Does not mean
/// the translations are in there!
@ -96,15 +108,6 @@ fn trans(key: &str) -> String {
}
}
fn lang_from_env() -> Lang {
let lang =
env::var("LANG").expect("$LANG not set in environment. Your machine is misconfigured!");
match lang.as_str() {
"NONE" => Lang::None,
"REPLACE" => Lang::Replace,
_ => Lang::Some(lang[0..2].to_string())
}
}
struct LogLevel {
info: bool,
@ -142,39 +145,39 @@ impl LogLevel {
}
}
fn expand(msg: &str, vars: Option<Context>) -> String {
//let mut s = msg;
if vars.is_some() {
return vars
.unwrap()
.iter()
.fold(msg.to_string(), |prev, (key, val)| prev.replace(key, val));
/// Expands variables from the vars Context. If the
/// context is empty, the string is returned untouched
fn expand(msg: &str, vars: &Context) -> String {
if vars.is_empty() {
return msg.to_string();
}
return msg.to_string();
return vars
.iter()
.fold(msg.to_string(), |prev, (key, val)| prev.replace(key, val));
}
pub fn info(msg: &str, vars: Option<Context>) {
pub fn info(msg: &str, vars: &Context) {
if LOGLEVEL.info {
let t_msg = expand(&trans(msg), vars);
println!("[git-build] {}", t_msg);
}
}
pub fn error(msg: &str, vars: Option<Context>) {
pub fn error(msg: &str, vars: &Context) {
if LOGLEVEL.error {
let t_msg = expand(&trans(msg), vars);
eprintln!("{}{}", trans("error"), t_msg);
}
}
pub fn warn(msg: &str, vars: Option<Context>) {
pub fn warn(msg: &str, vars: &Context) {
if LOGLEVEL.error {
let t_msg = expand(&trans(msg), vars);
eprintln!("{}{}", trans("warning"), t_msg);
}
}
pub fn debug(msg: &str, vars: Option<Context>) {
pub fn debug(msg: &str, vars: &Context) {
if LOGLEVEL.debug {
let t_msg = expand(&trans(msg), vars);
println!("{}{}", trans("debug"), t_msg);

View File

@ -9,19 +9,25 @@ mod dvcs;
mod log;
mod task;
use log::Context;
fn main() -> Result<(), std::io::Error> {
// TODO: use StructOpt::from_iter_safe so we can hook on Cli error
// to display additional unknown_arg error message.
let cmd = cli::Cli::from_args();
let mut context = HashMap::new();
let mut context = Context::new();
let basedir = cmd.basedir();
context.insert("$i18n_basedir".to_string(), basedir.to_str().unwrap().to_string());
let basedir = match basedir.canonicalize() {
Ok(p) => p,
Ok(p) => {
context.insert("$i18n_basedir".to_string(), p.to_str().unwrap().to_string());
p
},
Err(_) => {
context.insert("$i18n_basedir", basedir.to_str().unwrap());
log::error("missing_basedir", Some(&context));
log::error("missing_basedir", &context);
std::process::exit(1);
}
};
@ -31,14 +37,16 @@ fn main() -> Result<(), std::io::Error> {
set_var("GITBUILDDIR", &basedir);
let mut tasks = if cmd.tasks.is_empty() {
log::info("no_task", Some(&context));
task::from_dir(&basedir).expect("Could not load DB")
log::info("no_task", &context);
task::from_dir(&basedir, &context).expect("Could not load DB")
} else {
match task::from_dir_and_list(&basedir, cmd.tasks) {
match task::from_dir_and_list(&basedir, cmd.tasks, &context) {
Ok(t) => t,
Err(task::MissingTask(t)) => {
context.insert("$i18n_task", &t);
log::error("unknown_arg", Some(&context));
// Temporarily override the global context
let mut context = context.clone();
context.insert("$i18n_task".to_string(), t);
log::error("unknown_arg", &context);
std::process::exit(1);
}
@ -46,15 +54,13 @@ fn main() -> Result<(), std::io::Error> {
};
for t in &tasks {
let mut context = context.clone();
context.insert("$i18n_task", t.name.as_str());
log::debug("found_task", Some(&context));
t.debug("found_task");
}
let (config_folder, ignored_tasks) = task::config(&basedir);
set_var("GITBUILDCONF", &config_folder);
context.insert("$i18n_config", config_folder.to_str().unwrap());
log::info("config", Some(&context));
context.insert("$i18n_config".to_string(), config_folder.to_str().unwrap().to_string());
log::info("config", &context);
// Reorder tasks alphanumerically
tasks.sort_unstable_by_key(|t| t.name.clone());
@ -62,26 +68,22 @@ fn main() -> Result<(), std::io::Error> {
// the corresponding source URL (so we'd be tempted to call the task twice)
tasks.dedup_by_key(|t| t.name.clone());
for task in &tasks {
let mut context = context.clone();
context.insert("$i18n_task", task.name.as_str());
log::debug("start_proc", Some(&context));
task.debug("start_proc");
if ignored_tasks.contains(&task.name) {
// Skip task which has CONFIG/task.ignore
continue;
}
log::info("process", Some(&context));
task.info("process");
// Maybe the task has a source we should clone?
if let Some(repo) = &task.repo {
context.insert("$source", &repo.source);
let source_dir = format!("{}/.{}", basedir_str, &task.name);
if task.cloned == false {
log::info("clone", Some(&context));
task.info("clone");
if !repo.clone() {
context.insert("$i18n_source", &repo.source);
log::error("clone_failed", Some(&context));
task.error("clone_failed");
// Skip further processing
continue;
}

View File

@ -17,6 +17,7 @@ pub struct Task {
pub hosts: Vec<String>,
pub cloned: bool,
pub subupdates: bool,
pub context: log::Context,
}
/// config returns an option of (settings directory, ignored tasks) as
@ -47,7 +48,7 @@ pub fn config(basedir: &Path) -> (PathBuf, Vec<String>) {
}
}
impl Task {
pub fn from_path(path: &Path) -> Option<Task> {
pub fn from_path(path: &Path, context: &log::Context) -> Option<Task> {
let name = path.file_name().unwrap().to_str().unwrap().to_string();
// We don't return a task if:
@ -64,6 +65,13 @@ impl Task {
let cloned = source.clone().map_or(false, |_| dest.is_dir());
let subupdates = read_extension(path, "subupdates").is_some();
// Copy the global context so we have a more local scope
let mut context = context.clone();
context.insert("$i18n_task".to_string(), name.clone());
if let Some(source_url) = &source {
context.insert("$i18n_source".to_string(), source_url.clone());
}
Some(Task {
name,
bin: path.to_path_buf(),
@ -84,6 +92,7 @@ impl Task {
}),
cloned,
subupdates: read_extension(path, "subupdates").is_some(),
context,
})
}
@ -110,17 +119,14 @@ impl Task {
}
pub fn update_and_run(&self, force: &bool) {
let mut context = HashMap::new();
context.insert("$i18n_task", self.name.as_str());
if let Some(repo) = &self.repo {
if repo.has_updates() {
self.run();
} else if *force {
log::debug("forcing", Some(&context));
self.debug("forcing");
self.run();
} else {
log::debug("no_update", Some(&context));
self.debug("no_update");
}
} else {
unreachable!("this function should never be called on a task whcih doesnt have a repo");
@ -133,9 +139,7 @@ impl Task {
return;
}
let mut context = HashMap::new();
context.insert("$i18n_task", self.name.as_str());
log::info("run", Some(&context));
self.info("run");
let cmd_out = Command::new("bash") // TODO: no need to call bash?
.arg(&self.bin)
@ -159,18 +163,34 @@ impl Task {
std::fs::write(&done_path, "").expect("Failed to register task as done");
}
}
pub fn debug(&self, message: &str) {
log::debug(message, &self.context);
}
pub fn info(&self, message: &str) {
log::info(message, &self.context);
}
pub fn warn(&self, message: &str) {
log::warn(message, &self.context);
}
pub fn error(&self, message: &str) {
log::error(message, &self.context);
}
}
/// Loads a task list from a given base directory. Fails if the directory
/// is not readable with std::io::Error.
pub fn from_dir(basedir: &Path) -> Result<Vec<Task>, std::io::Error> {
pub fn from_dir(basedir: &Path, context: &log::Context) -> Result<Vec<Task>, std::io::Error> {
Ok(
basedir.read_dir()?.filter_map(|f| {
if f.is_err() {
// Dismiss individual file errors
return None;
}
return Task::from_path(&f.unwrap().path());
return Task::from_path(&f.unwrap().path(), context);
}).collect()
)
}
@ -223,19 +243,19 @@ impl SourceSet {
/// Loads a task list from a given base directory, taking only tasks that are in requested list.
/// Given tasks can be either a task name or a task URL. This function will panic if the basedir
/// does not exist, or error if a requested task/source does not exist.
pub fn from_dir_and_list(basedir: &Path, list: Vec<String>) -> Result<Vec<Task>, MissingTask> {
pub fn from_dir_and_list(basedir: &Path, list: Vec<String>, context: &log::Context) -> Result<Vec<Task>, MissingTask> {
// Safe unwrap because we checked that basedir existed before
// or maybe can crash for permission problem? TODO: write tests
let sourceset = SourceSet::from(basedir).unwrap();
let mut tasks = Vec::new();
for t in list {
if let Some(task) = Task::from_path(&basedir.join(&t)) {
if let Some(task) = Task::from_path(&basedir.join(&t), context) {
tasks.push(task);
} else {
// Maybe it's not a task name, but a task URL?
if let Some(list) = sourceset.tasks_for(&t) {
// Hopefully safe unwrap (unless there's a source without a corresponding task?)
let task_list = list.iter().map(|t_name| Task::from_path(&basedir.join(&t_name)).unwrap());
let task_list = list.iter().map(|t_name| Task::from_path(&basedir.join(&t_name), context).unwrap());
tasks.extend(task_list);
} else {
return Err(MissingTask(t));