Trying very hard not to miss calendar events
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Place EDS code in separate module

Co-authored-by: Claude <noreply@anthropic.com>

+124 -111
+2 -110
src/eds.rs src/eds/core.rs
··· 1 1 // Safe Rust abstractions over Evolution Data Server C APIs 2 2 3 - use crate::ffi; 4 - use chrono::{DateTime, Local, NaiveDate, TimeZone}; 3 + use super::event::Event; 4 + use super::ffi; 5 5 use std::ffi::{CStr, CString}; 6 6 use std::fmt; 7 7 use std::os::raw::c_char; ··· 241 241 } 242 242 } 243 243 } 244 - 245 - // Event time representation 246 - #[derive(Debug, Clone)] 247 - pub enum EventTime { 248 - DateTime(DateTime<Local>), 249 - Date(NaiveDate), 250 - } 251 - 252 - impl EventTime { 253 - pub fn format(&self, fmt: &str) -> String { 254 - match self { 255 - EventTime::DateTime(dt) => dt.format(fmt).to_string(), 256 - EventTime::Date(date) => date.format(fmt).to_string(), 257 - } 258 - } 259 - } 260 - 261 - // Event 262 - pub struct Event { 263 - pub summary: String, 264 - pub description: Option<String>, 265 - pub location: Option<String>, 266 - pub start: EventTime, 267 - pub end: EventTime, 268 - pub uid: String, 269 - } 270 - 271 - impl Event { 272 - fn from_ical_component(comp: *mut ffi::ICalComponent) -> Option<Self> { 273 - unsafe { 274 - let summary_ptr = ffi::i_cal_component_get_summary(comp); 275 - let summary = if !summary_ptr.is_null() { 276 - CStr::from_ptr(summary_ptr).to_string_lossy().into_owned() 277 - } else { 278 - "(No title)".to_string() 279 - }; 280 - 281 - let description_ptr = ffi::i_cal_component_get_description(comp); 282 - let description = if !description_ptr.is_null() { 283 - let desc = CStr::from_ptr(description_ptr) 284 - .to_string_lossy() 285 - .into_owned(); 286 - if desc.is_empty() { 287 - None 288 - } else { 289 - Some(desc) 290 - } 291 - } else { 292 - None 293 - }; 294 - 295 - let location_ptr = ffi::i_cal_component_get_location(comp); 296 - let location = if !location_ptr.is_null() { 297 - let loc = CStr::from_ptr(location_ptr).to_string_lossy().into_owned(); 298 - if loc.is_empty() { 299 - None 300 - } else { 301 - Some(loc) 302 - } 303 - } else { 304 - None 305 - }; 306 - 307 - let uid_ptr = ffi::i_cal_component_get_uid(comp); 308 - let uid = if !uid_ptr.is_null() { 309 - CStr::from_ptr(uid_ptr).to_string_lossy().into_owned() 310 - } else { 311 - String::new() 312 - }; 313 - 314 - let start = ffi::i_cal_component_get_dtstart(comp); 315 - let end = ffi::i_cal_component_get_dtend(comp); 316 - 317 - let is_all_day = ffi::i_cal_time_is_date(start) != 0; 318 - 319 - let start_time_t = ffi::i_cal_time_as_timet(start); 320 - let end_time_t = ffi::i_cal_time_as_timet(end); 321 - 322 - let (start_event_time, end_event_time) = if is_all_day { 323 - // For all-day events, use just the date 324 - let start_dt = Local.timestamp_opt(start_time_t, 0).unwrap(); 325 - let end_dt = Local.timestamp_opt(end_time_t, 0).unwrap(); 326 - ( 327 - EventTime::Date(start_dt.date_naive()), 328 - EventTime::Date(end_dt.date_naive()), 329 - ) 330 - } else { 331 - // For timed events, use full datetime 332 - ( 333 - EventTime::DateTime(Local.timestamp_opt(start_time_t, 0).unwrap()), 334 - EventTime::DateTime(Local.timestamp_opt(end_time_t, 0).unwrap()), 335 - ) 336 - }; 337 - 338 - ffi::g_object_unref(start as *mut std::os::raw::c_void); 339 - ffi::g_object_unref(end as *mut std::os::raw::c_void); 340 - 341 - Some(Event { 342 - summary, 343 - description, 344 - location, 345 - start: start_event_time, 346 - end: end_event_time, 347 - uid, 348 - }) 349 - } 350 - } 351 - }
+113
src/eds/event.rs
··· 1 + // Event types and implementations 2 + 3 + use super::ffi; 4 + use chrono::{DateTime, Local, NaiveDate, TimeZone}; 5 + use std::ffi::CStr; 6 + 7 + // Event time representation 8 + #[derive(Debug, Clone)] 9 + pub enum EventTime { 10 + DateTime(DateTime<Local>), 11 + Date(NaiveDate), 12 + } 13 + 14 + impl EventTime { 15 + pub fn format(&self, fmt: &str) -> String { 16 + match self { 17 + EventTime::DateTime(dt) => dt.format(fmt).to_string(), 18 + EventTime::Date(date) => date.format(fmt).to_string(), 19 + } 20 + } 21 + } 22 + 23 + // Event 24 + pub struct Event { 25 + pub summary: String, 26 + pub description: Option<String>, 27 + pub location: Option<String>, 28 + pub start: EventTime, 29 + pub end: EventTime, 30 + pub uid: String, 31 + } 32 + 33 + impl Event { 34 + pub(crate) fn from_ical_component(comp: *mut ffi::ICalComponent) -> Option<Self> { 35 + unsafe { 36 + let summary_ptr = ffi::i_cal_component_get_summary(comp); 37 + let summary = if !summary_ptr.is_null() { 38 + CStr::from_ptr(summary_ptr).to_string_lossy().into_owned() 39 + } else { 40 + "(No title)".to_string() 41 + }; 42 + 43 + let description_ptr = ffi::i_cal_component_get_description(comp); 44 + let description = if !description_ptr.is_null() { 45 + let desc = CStr::from_ptr(description_ptr) 46 + .to_string_lossy() 47 + .into_owned(); 48 + if desc.is_empty() { 49 + None 50 + } else { 51 + Some(desc) 52 + } 53 + } else { 54 + None 55 + }; 56 + 57 + let location_ptr = ffi::i_cal_component_get_location(comp); 58 + let location = if !location_ptr.is_null() { 59 + let loc = CStr::from_ptr(location_ptr).to_string_lossy().into_owned(); 60 + if loc.is_empty() { 61 + None 62 + } else { 63 + Some(loc) 64 + } 65 + } else { 66 + None 67 + }; 68 + 69 + let uid_ptr = ffi::i_cal_component_get_uid(comp); 70 + let uid = if !uid_ptr.is_null() { 71 + CStr::from_ptr(uid_ptr).to_string_lossy().into_owned() 72 + } else { 73 + String::new() 74 + }; 75 + 76 + let start = ffi::i_cal_component_get_dtstart(comp); 77 + let end = ffi::i_cal_component_get_dtend(comp); 78 + 79 + let is_all_day = ffi::i_cal_time_is_date(start) != 0; 80 + 81 + let start_time_t = ffi::i_cal_time_as_timet(start); 82 + let end_time_t = ffi::i_cal_time_as_timet(end); 83 + 84 + let (start_event_time, end_event_time) = if is_all_day { 85 + // For all-day events, use just the date 86 + let start_dt = Local.timestamp_opt(start_time_t, 0).unwrap(); 87 + let end_dt = Local.timestamp_opt(end_time_t, 0).unwrap(); 88 + ( 89 + EventTime::Date(start_dt.date_naive()), 90 + EventTime::Date(end_dt.date_naive()), 91 + ) 92 + } else { 93 + // For timed events, use full datetime 94 + ( 95 + EventTime::DateTime(Local.timestamp_opt(start_time_t, 0).unwrap()), 96 + EventTime::DateTime(Local.timestamp_opt(end_time_t, 0).unwrap()), 97 + ) 98 + }; 99 + 100 + ffi::g_object_unref(start as *mut std::os::raw::c_void); 101 + ffi::g_object_unref(end as *mut std::os::raw::c_void); 102 + 103 + Some(Event { 104 + summary, 105 + description, 106 + location, 107 + start: start_event_time, 108 + end: end_event_time, 109 + uid, 110 + }) 111 + } 112 + } 113 + }
+9
src/eds/mod.rs
··· 1 + // Evolution Data Server module 2 + 3 + mod core; 4 + mod event; 5 + mod ffi; 6 + 7 + // Re-export public API 8 + pub use core::{CalendarClient, EdsError, GLibVersion, Result, Source, SourceRegistry}; 9 + pub use event::{Event, EventTime};
src/ffi.rs src/eds/ffi.rs
-1
src/main.rs
··· 1 1 mod constants; 2 2 mod eds; 3 - mod ffi; 4 3 5 4 use std::env; 6 5