fixes
This commit is contained in:
160
fedora/khal-calendar/src/main.rs
Normal file
160
fedora/khal-calendar/src/main.rs
Normal file
@@ -0,0 +1,160 @@
|
||||
use std::{
|
||||
collections::BTreeSet,
|
||||
process::{Command, Stdio},
|
||||
thread::sleep,
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use chrono::{Datelike, Local, NaiveDate, Timelike};
|
||||
use serde_json::json;
|
||||
|
||||
const COMMAND_TIMEOUT: Duration = Duration::from_secs(5);
|
||||
const MONTH_NAMES: [&str; 13] = [
|
||||
"",
|
||||
"January",
|
||||
"February",
|
||||
"March",
|
||||
"April",
|
||||
"May",
|
||||
"June",
|
||||
"July",
|
||||
"August",
|
||||
"September",
|
||||
"October",
|
||||
"November",
|
||||
"December",
|
||||
];
|
||||
|
||||
fn run_khal(args: &[&str]) -> Option<String> {
|
||||
let mut child = Command::new("khal")
|
||||
.args(args)
|
||||
.stdout(Stdio::piped())
|
||||
.stderr(Stdio::null())
|
||||
.spawn()
|
||||
.ok()?;
|
||||
|
||||
let deadline = Instant::now() + COMMAND_TIMEOUT;
|
||||
loop {
|
||||
match child.try_wait() {
|
||||
Ok(Some(_status)) => {
|
||||
let output = child.wait_with_output().ok()?;
|
||||
return Some(String::from_utf8_lossy(&output.stdout).trim().to_owned());
|
||||
}
|
||||
Ok(None) if Instant::now() >= deadline => {
|
||||
let _ = child.kill();
|
||||
let _ = child.wait();
|
||||
return None;
|
||||
}
|
||||
Ok(None) => sleep(Duration::from_millis(25)),
|
||||
Err(_) => return None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn days_in_month(year: i32, month: u32) -> u32 {
|
||||
let (next_year, next_month) = if month == 12 {
|
||||
(year + 1, 1)
|
||||
} else {
|
||||
(year, month + 1)
|
||||
};
|
||||
|
||||
NaiveDate::from_ymd_opt(next_year, next_month, 1)
|
||||
.and_then(|date| date.pred_opt())
|
||||
.map_or(31, |date| date.day())
|
||||
}
|
||||
|
||||
fn get_event_days(year: i32, month: u32) -> BTreeSet<u32> {
|
||||
let Some(first) = NaiveDate::from_ymd_opt(year, month, 1) else {
|
||||
return BTreeSet::new();
|
||||
};
|
||||
let Some(last) = NaiveDate::from_ymd_opt(year, month, days_in_month(year, month)) else {
|
||||
return BTreeSet::new();
|
||||
};
|
||||
|
||||
let start = first.format("%d.%m.%Y").to_string();
|
||||
let end = last.format("%d.%m.%Y").to_string();
|
||||
let Some(stdout) = run_khal(&[
|
||||
"list",
|
||||
&start,
|
||||
&end,
|
||||
"--format",
|
||||
"{start-date}",
|
||||
"--day-format",
|
||||
"",
|
||||
]) else {
|
||||
return BTreeSet::new();
|
||||
};
|
||||
|
||||
stdout
|
||||
.lines()
|
||||
.filter_map(|line| NaiveDate::parse_from_str(line.trim(), "%d.%m.%Y").ok())
|
||||
.filter(|date| date.month() == month)
|
||||
.map(|date| date.day())
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn build_calendar(year: i32, month: u32, event_days: &BTreeSet<u32>, today: NaiveDate) -> String {
|
||||
let Some(first) = NaiveDate::from_ymd_opt(year, month, 1) else {
|
||||
return String::new();
|
||||
};
|
||||
|
||||
let month_name = MONTH_NAMES
|
||||
.get(month as usize)
|
||||
.copied()
|
||||
.unwrap_or("Unknown");
|
||||
let mut lines = vec![
|
||||
format!(r##"<span color="#1e66f5"><b>{month_name} {year}</b></span>"##),
|
||||
r##"<span color="#8839ef"><b>Mo Tu We Th Fr Sa Su</b></span>"##.to_owned(),
|
||||
];
|
||||
|
||||
let mut week = vec![" ".to_owned(); (first.weekday().number_from_monday() - 1) as usize];
|
||||
for day in 1..=days_in_month(year, month) {
|
||||
let day_string = if day == today.day() && month == today.month() && year == today.year() {
|
||||
format!(r##"<span color="#d20f39"><b><u>{day:2}</u></b></span>"##)
|
||||
} else if event_days.contains(&day) {
|
||||
format!(r##"<span color="#40a02b"><b>{day:2}</b></span>"##)
|
||||
} else {
|
||||
format!(r##"<span color="#4c4f69">{day:2}</span>"##)
|
||||
};
|
||||
week.push(day_string);
|
||||
|
||||
if week.len() == 7 {
|
||||
lines.push(week.join(" "));
|
||||
week.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if !week.is_empty() {
|
||||
week.resize(7, " ".to_owned());
|
||||
lines.push(week.join(" "));
|
||||
}
|
||||
|
||||
lines.join("\n")
|
||||
}
|
||||
|
||||
fn get_upcoming_events() -> String {
|
||||
run_khal(&["list", "today", "7d"]).unwrap_or_default()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let now = Local::now();
|
||||
let today = now.date_naive();
|
||||
let event_days = get_event_days(today.year(), today.month());
|
||||
let calendar = build_calendar(today.year(), today.month(), &event_days, today);
|
||||
let events = get_upcoming_events();
|
||||
|
||||
let tooltip = if events.is_empty() {
|
||||
calendar
|
||||
} else {
|
||||
format!(r##"{calendar}\n\n<span color="#1e66f5"><b>Upcoming</b></span>\n{events}"##)
|
||||
};
|
||||
|
||||
println!(
|
||||
"{}",
|
||||
json!({
|
||||
"text": format!("{:02}:{:02}", now.hour(), now.minute()),
|
||||
"tooltip": tooltip,
|
||||
"alt": today.format("%Y-%m-%d").to_string(),
|
||||
})
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user