summaryrefslogtreecommitdiffstats
path: root/calendar/src/main.rs
diff options
context:
space:
mode:
authorLeonard Kugis <leonard@kug.is>2025-04-12 12:46:52 +0200
committerLeonard Kugis <leonard@kug.is>2025-04-12 12:46:52 +0200
commit8bf0adac445465f2ecd3121f688171895cd61dd1 (patch)
tree25e2cb4a82cdf097e63a62fccfd51dc69d3db20f /calendar/src/main.rs
parent5575ed931b95bb1f4a041c9fc41790ad02911b88 (diff)
downloadscripts-8bf0adac445465f2ecd3121f688171895cd61dd1.tar.gz
Implemented calendar in Rust
Diffstat (limited to 'calendar/src/main.rs')
-rw-r--r--calendar/src/main.rs118
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()
+}