149 lines
4.2 KiB
Rust
149 lines
4.2 KiB
Rust
use super::{CalendarUi, EventInstance, Parameters, SimpleDownload};
|
|
use crate::prelude::*;
|
|
use chrono::DateTime;
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
struct SillyDownloadable {
|
|
/// URL to scrape to download the file from
|
|
download_url: Url,
|
|
|
|
/// Disk location to cache the file for debugging
|
|
file_path: Utf8PathBuf,
|
|
}
|
|
|
|
#[derive(Clone, Deserialize)]
|
|
pub(crate) struct Config {
|
|
#[serde(flatten)]
|
|
dl: SillyDownloadable,
|
|
|
|
#[serde(flatten)]
|
|
pub(crate) ui: CalendarUi,
|
|
}
|
|
|
|
impl Config {
|
|
pub(crate) fn simple_download(&self, now: DateTime<chrono_tz::Tz>) -> SimpleDownload {
|
|
let date = now.format("%Y-%m-%d").to_string();
|
|
let mut url = self.dl.download_url.clone();
|
|
url.query_pairs_mut().append_pair("date", &date);
|
|
|
|
SimpleDownload {
|
|
download_url: Some(url),
|
|
file_path: self.dl.file_path.clone(),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct Event {
|
|
#[serde(alias = "durationInMinutes")]
|
|
duration_in_minutes: u32,
|
|
timestamp: i64,
|
|
title: String,
|
|
description: String,
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct Data {
|
|
items: Vec<Event>,
|
|
}
|
|
|
|
/// The bit that we deserialize directly from JSON
|
|
#[derive(Deserialize)]
|
|
struct CalendarInner {
|
|
data: Data,
|
|
}
|
|
|
|
pub(crate) struct Calendar {
|
|
config: Config,
|
|
inner: CalendarInner,
|
|
}
|
|
|
|
impl Calendar {
|
|
fn to_event_instance(&self, params: &Parameters, ev: &Event) -> Result<Option<EventInstance>> {
|
|
let dt = DateTime::from_timestamp_millis(ev.timestamp)
|
|
.context("cannot represent timestamp as a date")?
|
|
.with_timezone(¶ms.tz);
|
|
let dtstart = crate::DatePerhapsTime { dt, all_day: false };
|
|
if dtstart.dt < params.output_start || dtstart.dt > params.output_stop {
|
|
return Ok(None);
|
|
}
|
|
|
|
Ok(Some(EventInstance {
|
|
calendar_ui: self.config.ui.clone(),
|
|
dtstart,
|
|
location: None,
|
|
recurrence_id: None,
|
|
summary: Some(ev.title.clone()),
|
|
uid: None,
|
|
url: None,
|
|
}))
|
|
}
|
|
|
|
pub(crate) fn event_instances(&self, params: &Parameters) -> Result<Vec<EventInstance>> {
|
|
self.inner
|
|
.data
|
|
.items
|
|
.iter()
|
|
.filter_map(|ev| self.to_event_instance(params, ev).transpose())
|
|
.collect()
|
|
}
|
|
|
|
pub(crate) fn read_from_str(config: Config, s: &str) -> Result<Self> {
|
|
let inner = serde_json::from_str(s)?;
|
|
Ok(Self { config, inner })
|
|
}
|
|
|
|
pub(crate) fn read_from_config(config: Config) -> Result<Self> {
|
|
let s = std::fs::read_to_string(&config.dl.file_path)?;
|
|
Self::read_from_str(config, &s)
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
|
|
fn example_config() -> Config {
|
|
Config {
|
|
dl: SillyDownloadable {
|
|
download_url: Url::parse("https://www.commoninja.com/api/apps/calendar/get-monthly-events?widgetId=00000000-0000-0000-000000000000").unwrap(),
|
|
file_path: ".".into(),
|
|
},
|
|
ui: CalendarUi {
|
|
html_url: None,
|
|
short_name: "asdf".into(),
|
|
},
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn end_to_end() {
|
|
let s = r#"
|
|
{"data":{"items":[{"timestamp":1748989800000,"durationInMinutes":90,"title":"Foo Bar","description":""},{"timestamp":1749999600000,"durationInMinutes":30,"title":"Snaf Oo","description":""}]},"success":true,"message":""}
|
|
"#;
|
|
|
|
let cfg = example_config();
|
|
let cal = Calendar::read_from_str(cfg, s).unwrap();
|
|
let params = Parameters::new(
|
|
DateTime::from_timestamp(1748989700, 0)
|
|
.unwrap()
|
|
.with_timezone(&chrono_tz::America::Chicago),
|
|
)
|
|
.unwrap();
|
|
let instances = cal.event_instances(¶ms).unwrap();
|
|
|
|
assert_eq!(instances.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn unsillify() {
|
|
let dt = DateTime::from_timestamp(1756190298, 0)
|
|
.unwrap()
|
|
.with_timezone(&chrono_tz::America::Chicago);
|
|
let cfg = example_config();
|
|
assert_eq!(
|
|
cfg.simple_download(dt).download_url.unwrap().to_string(),
|
|
"https://www.commoninja.com/api/apps/calendar/get-monthly-events?widgetId=00000000-0000-0000-000000000000&date=2025-08-26"
|
|
);
|
|
}
|
|
}
|