use crate::data::{CalendarOps, CalendarReminder, MotdRemindEnum, ReminderTarget};
use crate::entities::calendar;
use crate::holidays::{self, CalendarDef, GROUPS};
use crate::PplError;
use chrono::{Datelike, Local};
use clap::{Args, Subcommand};
use sea_orm::DatabaseConnection;
use std::io;
#[derive(Args, Debug)]
pub struct CalendarArgs {
#[command(subcommand)]
pub action: Option<CalendarAction>,
}
#[derive(Subcommand, Debug)]
pub enum CalendarAction {
/// List the built-in calendars, and which are on
List {
/// Also list the states and provinces of countries that are off
#[arg(long, short)]
all: bool,
},
/// Show a calendar's holidays in the calendar tab, e.g. `enable us us-ny jewish`
Enable {
#[arg(required = true)]
keys: Vec<String>,
},
/// Stop showing a calendar's holidays
Disable {
#[arg(required = true)]
keys: Vec<String>,
},
/// Mention a calendar's holidays in the motd DAYS days ahead (0 is on the day).
/// TARGET is a calendar key, or a group's default: countries, religious, other
Set { target: String, days: u32 },
/// How often the motd mentions them: daily, hourly, random:N, always, never
Remind {
target: String,
mode: MotdRemindEnum,
},
/// Go back to the defaults (a state's country, then the group, then on the day, daily)
Clear { target: String },
/// Print a year's holidays from the calendars that are on, or the ones named
Show {
keys: Vec<String>,
/// Defaults to this year
#[arg(long, short)]
year: Option<i32>,
},
}
pub async fn do_calendars(args: &CalendarArgs, db: &DatabaseConnection) -> Result<(), PplError> {
let enabled = CalendarOps::enabled(db).await?;
let rows = CalendarOps::rows(db).await?;
match &args.action {
None => list(&enabled, &rows, false),
Some(CalendarAction::List { all }) => list(&enabled, &rows, *all),
Some(CalendarAction::Set { target, days }) => {
let t = ReminderTarget::parse(target)?;
let r = t.reminder(&rows);
CalendarOps::set_reminder(db, t, Some(*days), r.remind).await?;
println!("{}: {}", t.label(), window(*days));
}
Some(CalendarAction::Remind { target, mode }) => {
let t = ReminderTarget::parse(target)?;
let r = t.reminder(&rows);
CalendarOps::set_reminder(db, t, r.delta, Some(*mode)).await?;
println!("{}: remind {}", t.label(), mode.label());
}
Some(CalendarAction::Clear { target }) => {
let t = ReminderTarget::parse(target)?;
CalendarOps::set_reminder(db, t, None, None).await?;
let r = t.reminder(&CalendarOps::rows(db).await?);
println!("{}: back to the defaults, {}", t.label(), describe(&r));
}
Some(CalendarAction::Enable { keys }) => toggle(db, keys, true).await?,
Some(CalendarAction::Disable { keys }) => toggle(db, keys, false).await?,
Some(CalendarAction::Show { keys, year }) => {
let year = year.unwrap_or_else(|| Local::now().year());
let keys = match keys.is_empty() {
true => enabled,
false => {
for k in keys {
lookup(k)?;
}
keys.clone()
}
};
if keys.is_empty() {
println!("no calendars are on; try `ppl calendars enable us`");
}
for h in holidays::holidays_in(year, &keys) {
println!(
"{} {:<9} {}",
h.date.format("%a %Y-%m-%d"),
h.calendar.key,
h.name
);
}
}
}
Ok(())
}
fn lookup(key: &str) -> Result<&'static CalendarDef, PplError> {
holidays::find(key).ok_or_else(|| {
io::Error::new(
io::ErrorKind::NotFound,
format!("no calendar '{}', see `ppl calendars list --all`", key),
)
.into()
})
}
async fn toggle(db: &DatabaseConnection, keys: &[String], on: bool) -> Result<(), PplError> {
// Check them all first, so a typo doesn't leave half of them changed
for k in keys {
lookup(k)?;
}
for k in keys {
let changed = CalendarOps::set(db, k, on).await?;
if changed.is_empty() {
println!("{} is already {}", k, if on { "on" } else { "off" });
}
for c in changed {
println!(
"{} {} ({})",
if on { "on: " } else { "off:" },
c.name,
c.key
);
}
}
Ok(())
}
fn window(days: u32) -> String {
match days {
0 => "on the day".to_string(),
1 => "1 day ahead".to_string(),
n => format!("{} days ahead", n),
}
}
/// "7 days ahead · daily (default)"
fn describe(r: &CalendarReminder) -> String {
let mark = |own: bool| if own { "" } else { " (default)" };
format!(
"{}{} · {}{}",
window(r.window()),
mark(r.delta.is_some()),
r.mode().label(),
mark(r.remind.is_some())
)
}
fn list(enabled: &[String], rows: &[calendar::Model], all: bool) {
let is_on = |c: &CalendarDef| enabled.iter().any(|k| k == c.key);
let line = |c: &'static CalendarDef, indent: &str| {
let motd = match is_on(c) {
true => format!(
" motd: {}",
describe(&ReminderTarget::Calendar(c).reminder(rows))
),
false => String::new(),
};
println!(
"{}[{}] {:<16} {}{}{}",
indent,
if is_on(c) { "x" } else { " " },
c.key,
c.name,
c.note.map(|n| format!(" ({})", n)).unwrap_or_default(),
motd
);
};
for g in &GROUPS {
let r = ReminderTarget::Group(g).reminder(rows);
println!("{} (`{}`, motd: {})", g.name, g.key, describe(&r));
for c in holidays::in_group(g) {
line(c, " ");
let regions = holidays::regions(c.key).collect::<Vec<_>>();
if all || is_on(c) {
for r in regions {
line(r, " ");
}
} else if !regions.is_empty() {
println!(" ({} more, see --all)", regions.len());
}
}
}
}
⭐
Log in to mark this repository.
6.4 KiB · text
History
d9460a8