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; use std::error::Error;
/// Defines a day within the month. Negative numbers count from the end. /// Defines a day within the month. Negative numbers count from the end.
#[derive(Debug, Clone, Copy)]
pub enum DaySpec { pub enum DaySpec {
/// nth day, no matter the weekday /// nth day, no matter the weekday
DayOfMonth(i32), DayOfMonth(i32),

View file

@ -141,20 +141,67 @@ macro_rules! verboseln {
fn is_trace() -> bool { fn is_trace() -> bool {
env::var("TRACE").map(|v| !v.is_empty()).unwrap_or(false) 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 { macro_rules! traceln {
($($arg:tt)*) => { ($($arg:tt)*) => {
if is_trace() { 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 { fn today() -> NaiveDate {
env::var("TODAY") env::var("TODAY")
.map(|v| NaiveDate::parse_from_str(&v, "%F").expect("'TODAY' hat nicht format YYYY-MM-DD")) .map(|v| NaiveDate::parse_from_str(&v, "%F").expect("'TODAY' hat nicht format YYYY-MM-DD"))
.unwrap_or(Local::now().date_naive()) .unwrap_or(Local::now().date_naive())
} }
#[derive(Debug)]
struct Args { struct Args {
check_mode: bool, check_mode: bool,
config_file: String, config_file: String,
@ -190,8 +237,9 @@ fn parse_args() -> Args {
fn main() -> Result<(), Box<dyn Error>> { fn main() -> Result<(), Box<dyn Error>> {
// set up config file access // set up config file access
let args = parse_args(); let args = parse_args();
trace_var!(args);
let config_file = args.config_file.as_str(); 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(); let config = KV::new(config_file).unwrap();
config_spec::populate_defaults(&CONFIG_SPEC, &config); config_spec::populate_defaults(&CONFIG_SPEC, &config);
if args.check_mode { if args.check_mode {
@ -200,7 +248,7 @@ fn main() -> Result<(), Box<dyn Error>> {
} }
// get config // get config
let hedgedoc = HedgeDoc::new(&config["hedgedoc-server-url"], is_dry_run()); let hedgedoc = HedgeDoc::new(&config["hedgedoc-server-url"], is_dry_run());
traceln!("Hedgedoc: {:?}", hedgedoc); trace_var!(hedgedoc);
let email_ = Email::new( let email_ = Email::new(
&config["email-server"], &config["email-server"],
&config["email-user"], &config["email-user"],
@ -213,18 +261,21 @@ fn main() -> Result<(), Box<dyn Error>> {
&config["email-to"], &config["email-to"],
config.get("email-in-reply-to").ok(), config.get("email-in-reply-to").ok(),
); );
traceln!("Email: {:?}", email); trace_var_!(email);
let wiki = Mediawiki::new( let wiki = Mediawiki::new(
&config["wiki-server-url"], &config["wiki-server-url"],
&config["wiki-http-user"], &config["wiki-http-user"],
&config["wiki-http-password"], &config["wiki-http-password"],
is_dry_run(), is_dry_run(),
); );
traceln!("Wiki: {:?}", wiki); trace_var_!(wiki);
// get next plenum days // get next plenum days
let today = today(); let today = today();
verboseln!("Heute ist {}", today.to_string().cyan());
let plenum_spec = date::parse_spec(&config["date-spec"])?; 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); let nearest_plenum_days = date::get_matching_dates_around(today, plenum_spec);
trace_var!(nearest_plenum_days);
// figure out where we are // figure out where we are
let mut last_state = ProgramState::parse(&config["state-name"]); let mut last_state = ProgramState::parse(&config["state-name"]);
let last_run = config.get("state-last-run").unwrap_or_default(); 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 .unwrap(); // always has at least 2 elems
let plenum_day = today.checked_add_signed(chrono::TimeDelta::days(delta)).unwrap(); 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 { let intended_state = if delta > 3 {
ProgramState::Normal // nothing to do 3+ days in advance ProgramState::Normal // nothing to do 3+ days in advance
} else if delta > 1 { } else if delta > 1 {
ProgramState::Announced // 2+ days in advance we want to have it announced ProgramState::Announced // 2+ days in advance we want to have it announced
} else if delta >= 0 { } else if delta >= 0 {
ProgramState::Reminded // up to the day of, we want to send a reminder (or cancel) 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 ProgramState::Waiting // we will wait a day for the protocol to be cleaned up
} else { } else {
ProgramState::Logged // after that, we want to log it to the list & the wiki 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]; let action: &ST = &TRANSITION_LUT[last_state as usize][intended_state as usize];
action(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?; trace_var!(action);
action.get()(delta, &plenum_day, &config, &hedgedoc, &email, &wiki)?;
// TODO: cleanup / write new state // TODO: cleanup / write new state
@ -638,15 +697,41 @@ type TransitionFunction = fn(
) -> Result<(), Box<dyn Error>>; ) -> Result<(), Box<dyn Error>>;
#[rustfmt::skip] #[rustfmt::skip]
const TRANSITION_LUT: [[TransitionFunction; 5]; 5] = [ const TRANSITION_LUT: [[ST; 5]; 5] = [
/* NORMAL ANNOUNCED REMINDED WAITING LOGGED */ /* NORMAL ANNOUNCED REMINDED WAITING LOGGED */
/* NORMAL */ [nop, do_announcement, do_reminder, nop, nop], /* NORMAL */ [ST::Nop, ST::DoAnnouncement, ST::DoReminder, ST::Nop, ST::Nop],
/* ANNOUNCED */ [do_cleanup, nop, do_reminder, nop, do_protocol], /* ANNOUNCED */ [ST::DoCleanup, ST::Nop, ST::DoReminder, ST::Nop, ST::DoProtocol],
/* REMINDED */ [do_cleanup, do_clean_announcement, nop, nop, do_protocol], /* REMINDED */ [ST::DoCleanup, ST::DoCleanupThenAnnouncement, ST::Nop, ST::Nop, ST::DoProtocol],
/* WAITING */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_protocol], /* WAITING */ [ST::DoCleanup, ST::DoCleanupThenAnnouncement, ST::DoCleanupThenReminder, ST::Nop, ST::DoProtocol],
/* LOGGED */ [do_cleanup, do_clean_announcement, do_clean_reminder, nop, do_cleanup], /* 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( fn nop(
_: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki, _: i64, _: &NaiveDate, _: &KV, _: &HedgeDoc, _: &SimpleEmail, _: &Mediawiki,
) -> Result<(), Box<dyn Error>> { ) -> Result<(), Box<dyn Error>> {

View file

@ -35,7 +35,6 @@ pub const CONFIG: CfgGroup<'static> = CfgGroup {
], ],
}; };
#[derive(Debug)]
pub struct Mediawiki { pub struct Mediawiki {
server_url: String, server_url: String,
http_user: String, http_user: String,
@ -44,6 +43,18 @@ pub struct Mediawiki {
client: Client, 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 { impl Mediawiki {
pub fn new( pub fn new(
server_url: &str, http_auth_user: &str, http_auth_password: &str, is_dry_run: bool, server_url: &str, http_auth_user: &str, http_auth_password: &str, is_dry_run: bool,