improve debuggability

more Debug-derives, trace/verbose, …
This commit is contained in:
nobody 2024-08-18 02:30:39 +02:00 committed by murmeldin
parent 4686eff0d4
commit 12501c8c73
3 changed files with 113 additions and 16 deletions

View file

@ -10,6 +10,7 @@ use std::collections::BTreeSet;
use std::error::Error;
/// Defines a day within the month. Negative numbers count from the end.
#[derive(Debug, Clone, Copy)]
pub enum DaySpec {
/// nth day, no matter the weekday
DayOfMonth(i32),

View file

@ -141,20 +141,67 @@ macro_rules! verboseln {
fn is_trace() -> bool {
env::var("TRACE").map(|v| !v.is_empty()).unwrap_or(false)
}
/// Like `println!`, but only if `is_trace` is true (due to the environment
/// variable `TRACE` being set.)
macro_rules! traceln {
($($arg:tt)*) => {
if is_trace() {
println!($($arg)*);
println!( "{}", format!($($arg)*).yellow() );
}
};
}
/// `trace_var!( [msg,] var )` prints either `varname = value` or `msg: value`
/// *if TRACE is set* (else is silent.)
///
/// There's an alternative form of `trace_var!( [msg,] var[, true] )` or the
/// preferred form of `trace_var_!( [msg,] var )` (i.e. just add an underscore
/// to the name), which will use the "pretty" form.
macro_rules! trace_var {
($var:expr, $pretty:expr) => {
if is_trace() {
if $pretty {
println!("{} = {}", stringify!($var).green(), format!("{:#?}", $var).cyan());
} else {
println!("{} = {}", stringify!($var).green(), format!("{:?}", $var).cyan());
}
}
};
($msg:expr, $var:expr, $pretty:expr) => {
if is_trace() {
if $pretty {
println!("{}: {}", $msg.green(), format!("{:#?}", $var).cyan());
} else {
println!("{}: {}", $msg.green(), format!("{:?}", $var).cyan());
}
}
};
($var:expr) => {
trace_var!($var, false);
};
($msg:expr, $var:expr) => {
trace_var!($msg, $var, false);
};
}
/// Pretty form of `trace_var!`
macro_rules! trace_var_ {
($var:expr) => {
trace_var!($var, true);
};
($msg:expr, $var:expr) => {
trace_var!($msg, $var, true);
};
}
/// Gets either today or the date from the environment variable `TODAY` (for
/// testing purposes.)
fn today() -> NaiveDate {
env::var("TODAY")
.map(|v| NaiveDate::parse_from_str(&v, "%F").expect("'TODAY' hat nicht format YYYY-MM-DD"))
.unwrap_or(Local::now().date_naive())
}
#[derive(Debug)]
struct Args {
check_mode: bool,
config_file: String,
@ -190,8 +237,9 @@ fn parse_args() -> Args {
fn main() -> Result<(), Box<dyn Error>> {
// set up config file access
let args = parse_args();
trace_var!(args);
let config_file = args.config_file.as_str();
verboseln!("Using config file {config_file}.");
verboseln!("Using config file {}.", config_file.cyan());
let config = KV::new(config_file).unwrap();
config_spec::populate_defaults(&CONFIG_SPEC, &config);
if args.check_mode {
@ -200,7 +248,7 @@ fn main() -> Result<(), Box<dyn Error>> {
}
// get config
let hedgedoc = HedgeDoc::new(&config["hedgedoc-server-url"], is_dry_run());
traceln!("Hedgedoc: {:?}", hedgedoc);
trace_var!(hedgedoc);
let email_ = Email::new(
&config["email-server"],
&config["email-user"],
@ -213,18 +261,21 @@ fn main() -> Result<(), Box<dyn Error>> {
&config["email-to"],
config.get("email-in-reply-to").ok(),
);
traceln!("Email: {:?}", email);
trace_var_!(email);
let wiki = Mediawiki::new(
&config["wiki-server-url"],
&config["wiki-http-user"],
&config["wiki-http-password"],
is_dry_run(),
);
traceln!("Wiki: {:?}", wiki);
trace_var_!(wiki);
// get next plenum days
let today = today();
verboseln!("Heute ist {}", today.to_string().cyan());
let plenum_spec = date::parse_spec(&config["date-spec"])?;
trace_var!(plenum_spec);
let nearest_plenum_days = date::get_matching_dates_around(today, plenum_spec);
trace_var!(nearest_plenum_days);
// figure out where we are
let mut last_state = ProgramState::parse(&config["state-name"]);
let last_run = config.get("state-last-run").unwrap_or_default();
@ -256,20 +307,28 @@ fn main() -> Result<(), Box<dyn Error>> {
}
.unwrap(); // always has at least 2 elems
let plenum_day = today.checked_add_signed(chrono::TimeDelta::days(delta)).unwrap();
verboseln!(
"Relevantes Plenum ist am {} ({})",
plenum_day.to_string().cyan(),
relative_date(delta).cyan()
);
let intended_state = if delta > 3 {
ProgramState::Normal // nothing to do 3+ days in advance
} else if delta > 1 {
ProgramState::Announced // 2+ days in advance we want to have it announced
} else if delta >= 0 {
ProgramState::Reminded // up to the day of, we want to send a reminder (or cancel)
} else if delta > -2 {
} else if delta >= -1 {
ProgramState::Waiting // we will wait a day for the protocol to be cleaned up
} else {
ProgramState::Logged // after that, we want to log it to the list & the wiki
};
verboseln!("Aktueller Zustand: {}", last_state.to_string().cyan());
verboseln!("Soll-Zustand: {}", intended_state.to_string().cyan());
let action: TransitionFunction = TRANSITION_LUT[last_state as usize][intended_state as usize];
action(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?;
let action: &ST = &TRANSITION_LUT[last_state as usize][intended_state as usize];
trace_var!(action);
action.get()(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?;
// TODO: cleanup / write new state
@ -638,15 +697,41 @@ type TransitionFunction = fn(
) -> Result<(), Box<dyn Error>>;
#[rustfmt::skip]
const TRANSITION_LUT: [[TransitionFunction; 5]; 5] = [
/* NORMAL ANNOUNCED REMINDED WAITING LOGGED */
/* NORMAL */ [nop, do_announcement, do_reminder, nop, nop],
/* ANNOUNCED */ [do_cleanup, nop, do_reminder, nop, do_protocol],
/* REMINDED */ [do_cleanup, do_clean_announcement, nop, nop, do_protocol],
/* WAITING */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_protocol],
/* LOGGED */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_cleanup],
const TRANSITION_LUT: [[ST; 5]; 5] = [
/* NORMAL ANNOUNCED REMINDED WAITING LOGGED */
/* NORMAL */ [ST::Nop, ST::DoAnnouncement, ST::DoReminder, ST::Nop, ST::Nop],
/* ANNOUNCED */ [ST::DoCleanup, ST::Nop, ST::DoReminder, ST::Nop, ST::DoProtocol],
/* REMINDED */ [ST::DoCleanup, ST::DoCleanupThenAnnouncement, ST::Nop, ST::Nop, ST::DoProtocol],
/* WAITING */ [ST::DoCleanup, ST::DoCleanupThenAnnouncement, ST::DoCleanupThenReminder, ST::Nop, ST::DoProtocol],
/* LOGGED */ [ST::DoCleanup, ST::DoCleanupThenAnnouncement, ST::DoCleanupThenReminder, ST::Nop, ST::DoCleanup],
];
#[derive(Debug, Default, Clone, Copy)]
enum ST {
#[default]
Nop,
DoAnnouncement,
DoReminder,
DoProtocol,
DoCleanup,
DoCleanupThenAnnouncement,
DoCleanupThenReminder,
}
impl ST {
fn get(&self) -> TransitionFunction {
match self {
ST::Nop => nop,
ST::DoAnnouncement => do_announcement,
ST::DoReminder => do_reminder,
ST::DoProtocol => do_protocol,
ST::DoCleanup => do_cleanup,
ST::DoCleanupThenAnnouncement => do_clean_announcement,
ST::DoCleanupThenReminder => do_clean_reminder,
}
}
}
fn nop(
_: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki,
) -> Result<(), Box<dyn Error>> {

View file

@ -35,7 +35,6 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup {
],
};
#[derive(Debug)]
pub struct Mediawiki {
server_url: String,
http_user: String,
@ -44,6 +43,18 @@ pub struct Mediawiki {
client: Client,
}
impl std::fmt::Debug for Mediawiki {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Mediawiki")
.field("server_url", &self.server_url)
.field("http_user", &self.http_user)
.field("http_password", &"*****")
.field("is_dry_run", &self.is_dry_run)
.field("client", &self.client)
.finish()
}
}
impl Mediawiki {
pub fn new(
server_url: &str, http_auth_user: &str, http_auth_password: &str, is_dry_run: bool,