diff options
author | Leonard Kugis <leonard@kug.is> | 2025-04-12 12:46:52 +0200 |
---|---|---|
committer | Leonard Kugis <leonard@kug.is> | 2025-04-12 12:46:52 +0200 |
commit | 8bf0adac445465f2ecd3121f688171895cd61dd1 (patch) | |
tree | 25e2cb4a82cdf097e63a62fccfd51dc69d3db20f /calendar/src/main.rs | |
parent | 5575ed931b95bb1f4a041c9fc41790ad02911b88 (diff) | |
download | scripts-8bf0adac445465f2ecd3121f688171895cd61dd1.tar.gz |
Implemented calendar in Rust
Diffstat (limited to 'calendar/src/main.rs')
-rw-r--r-- | calendar/src/main.rs | 118 |
1 files changed, 118 insertions, 0 deletions
diff --git a/calendar/src/main.rs b/calendar/src/main.rs new file mode 100644 index 0000000..77271fd --- /dev/null +++ b/calendar/src/main.rs @@ -0,0 +1,118 @@ +use std::{fs, io::Write, path::PathBuf}; +use serde::Deserialize; +use regex::Regex; +use tempfile::tempdir; +use reqwest::blocking::Client; + +#[derive(Deserialize)] +struct Config { + calendar: CalendarConfig, +} + +#[derive(Deserialize)] +struct CalendarConfig { + caldav_base_url: String, + caldav_username: String, + caldav_password: String, + calendars: Vec<CalendarEntry>, +} + +#[derive(Deserialize)] +struct CalendarEntry { + name: String, + ics: String, +} + +fn main() -> anyhow::Result<()> { + let base_dir = "/home/leonard/Projects/scripts"; + let config_path = format!("{}/config.json", base_dir); + let config_data = fs::read_to_string(&config_path)?; + let config: Config = serde_json::from_str(&config_data)?; + + let tmp_dir = tempdir()?; + let client = Client::new(); + + for cal in &config.calendar.calendars { + println!("INFO: Lade ICS von: {}", cal.ics); + let ics_path = tmp_dir.path().join(format!("{}.ics", cal.name)); + let response = client.get(&cal.ics).send()?; + if !response.status().is_success() { + eprintln!("WARNING: Fehler beim Herunterladen von {}", cal.ics); + continue; + } + let ics_content = response.text()?; + fs::write(&ics_path, &ics_content)?; + + let caldav_url = format!("{}/{}", config.calendar.caldav_base_url.trim_end_matches('/'), cal.name); + println!("INFO: Zerlege Datei: {:?}", ics_path); + + let vevent_blocks = split_vevents(&ics_content); + for vevent in vevent_blocks { + if let Some(uid) = extract_uid(&vevent) { + let safe_uid = sanitize_filename(&uid); + let event_path = tmp_dir.path().join(format!("{}.ics", safe_uid)); + let mut event_file = fs::File::create(&event_path)?; + + writeln!(event_file, "BEGIN:VCALENDAR")?; + writeln!(event_file, "VERSION:2.0")?; + writeln!(event_file, "PRODID:-//calendar script//EN")?; + writeln!(event_file, "{}", vevent.trim())?; + writeln!(event_file, "END:VCALENDAR")?; + + println!("INFO: Lade UID={} → {}/{}.ics", safe_uid, caldav_url, safe_uid); + let put_url = format!("{}/{}.ics", caldav_url, safe_uid); + + let body = fs::read(&event_path)?; + let put_response = client + .put(&put_url) + .basic_auth(&config.calendar.caldav_username, Some(&config.calendar.caldav_password)) + .header("Content-Type", "text/calendar; charset=utf-8") + .body(body) + .send()?; + + println!("→ HTTP-Code: {}", put_response.status()); + } + } + } + + Ok(()) +} + +fn split_vevents(ics: &str) -> Vec<String> { + let mut vevents = Vec::new(); + let mut in_event = false; + let mut buffer = String::new(); + + for line in ics.lines() { + if line.trim() == "BEGIN:VEVENT" { + in_event = true; + buffer.clear(); + } + + if in_event { + buffer.push_str(line); + buffer.push('\n'); + } + + if line.trim() == "END:VEVENT" && in_event { + in_event = false; + vevents.push(buffer.clone()); + } + } + + vevents +} + +fn extract_uid(event: &str) -> Option<String> { + for line in event.lines() { + if line.starts_with("UID:") { + return Some(line.trim_start_matches("UID:").trim().to_string()); + } + } + None +} + +fn sanitize_filename(name: &str) -> String { + let re = Regex::new(r"[^a-zA-Z0-9._-]").unwrap(); + re.replace_all(name, "_").to_string() +} |