···11// Safe Rust abstractions over Evolution Data Server C APIs
2233-use crate::ffi;
44-use chrono::{DateTime, Local, NaiveDate, TimeZone};
33+use super::event::Event;
44+use super::ffi;
55use std::ffi::{CStr, CString};
66use std::fmt;
77use std::os::raw::c_char;
···241241 }
242242 }
243243}
244244-245245-// Event time representation
246246-#[derive(Debug, Clone)]
247247-pub enum EventTime {
248248- DateTime(DateTime<Local>),
249249- Date(NaiveDate),
250250-}
251251-252252-impl EventTime {
253253- pub fn format(&self, fmt: &str) -> String {
254254- match self {
255255- EventTime::DateTime(dt) => dt.format(fmt).to_string(),
256256- EventTime::Date(date) => date.format(fmt).to_string(),
257257- }
258258- }
259259-}
260260-261261-// Event
262262-pub struct Event {
263263- pub summary: String,
264264- pub description: Option<String>,
265265- pub location: Option<String>,
266266- pub start: EventTime,
267267- pub end: EventTime,
268268- pub uid: String,
269269-}
270270-271271-impl Event {
272272- fn from_ical_component(comp: *mut ffi::ICalComponent) -> Option<Self> {
273273- unsafe {
274274- let summary_ptr = ffi::i_cal_component_get_summary(comp);
275275- let summary = if !summary_ptr.is_null() {
276276- CStr::from_ptr(summary_ptr).to_string_lossy().into_owned()
277277- } else {
278278- "(No title)".to_string()
279279- };
280280-281281- let description_ptr = ffi::i_cal_component_get_description(comp);
282282- let description = if !description_ptr.is_null() {
283283- let desc = CStr::from_ptr(description_ptr)
284284- .to_string_lossy()
285285- .into_owned();
286286- if desc.is_empty() {
287287- None
288288- } else {
289289- Some(desc)
290290- }
291291- } else {
292292- None
293293- };
294294-295295- let location_ptr = ffi::i_cal_component_get_location(comp);
296296- let location = if !location_ptr.is_null() {
297297- let loc = CStr::from_ptr(location_ptr).to_string_lossy().into_owned();
298298- if loc.is_empty() {
299299- None
300300- } else {
301301- Some(loc)
302302- }
303303- } else {
304304- None
305305- };
306306-307307- let uid_ptr = ffi::i_cal_component_get_uid(comp);
308308- let uid = if !uid_ptr.is_null() {
309309- CStr::from_ptr(uid_ptr).to_string_lossy().into_owned()
310310- } else {
311311- String::new()
312312- };
313313-314314- let start = ffi::i_cal_component_get_dtstart(comp);
315315- let end = ffi::i_cal_component_get_dtend(comp);
316316-317317- let is_all_day = ffi::i_cal_time_is_date(start) != 0;
318318-319319- let start_time_t = ffi::i_cal_time_as_timet(start);
320320- let end_time_t = ffi::i_cal_time_as_timet(end);
321321-322322- let (start_event_time, end_event_time) = if is_all_day {
323323- // For all-day events, use just the date
324324- let start_dt = Local.timestamp_opt(start_time_t, 0).unwrap();
325325- let end_dt = Local.timestamp_opt(end_time_t, 0).unwrap();
326326- (
327327- EventTime::Date(start_dt.date_naive()),
328328- EventTime::Date(end_dt.date_naive()),
329329- )
330330- } else {
331331- // For timed events, use full datetime
332332- (
333333- EventTime::DateTime(Local.timestamp_opt(start_time_t, 0).unwrap()),
334334- EventTime::DateTime(Local.timestamp_opt(end_time_t, 0).unwrap()),
335335- )
336336- };
337337-338338- ffi::g_object_unref(start as *mut std::os::raw::c_void);
339339- ffi::g_object_unref(end as *mut std::os::raw::c_void);
340340-341341- Some(Event {
342342- summary,
343343- description,
344344- location,
345345- start: start_event_time,
346346- end: end_event_time,
347347- uid,
348348- })
349349- }
350350- }
351351-}
+113
src/eds/event.rs
···11+// Event types and implementations
22+33+use super::ffi;
44+use chrono::{DateTime, Local, NaiveDate, TimeZone};
55+use std::ffi::CStr;
66+77+// Event time representation
88+#[derive(Debug, Clone)]
99+pub enum EventTime {
1010+ DateTime(DateTime<Local>),
1111+ Date(NaiveDate),
1212+}
1313+1414+impl EventTime {
1515+ pub fn format(&self, fmt: &str) -> String {
1616+ match self {
1717+ EventTime::DateTime(dt) => dt.format(fmt).to_string(),
1818+ EventTime::Date(date) => date.format(fmt).to_string(),
1919+ }
2020+ }
2121+}
2222+2323+// Event
2424+pub struct Event {
2525+ pub summary: String,
2626+ pub description: Option<String>,
2727+ pub location: Option<String>,
2828+ pub start: EventTime,
2929+ pub end: EventTime,
3030+ pub uid: String,
3131+}
3232+3333+impl Event {
3434+ pub(crate) fn from_ical_component(comp: *mut ffi::ICalComponent) -> Option<Self> {
3535+ unsafe {
3636+ let summary_ptr = ffi::i_cal_component_get_summary(comp);
3737+ let summary = if !summary_ptr.is_null() {
3838+ CStr::from_ptr(summary_ptr).to_string_lossy().into_owned()
3939+ } else {
4040+ "(No title)".to_string()
4141+ };
4242+4343+ let description_ptr = ffi::i_cal_component_get_description(comp);
4444+ let description = if !description_ptr.is_null() {
4545+ let desc = CStr::from_ptr(description_ptr)
4646+ .to_string_lossy()
4747+ .into_owned();
4848+ if desc.is_empty() {
4949+ None
5050+ } else {
5151+ Some(desc)
5252+ }
5353+ } else {
5454+ None
5555+ };
5656+5757+ let location_ptr = ffi::i_cal_component_get_location(comp);
5858+ let location = if !location_ptr.is_null() {
5959+ let loc = CStr::from_ptr(location_ptr).to_string_lossy().into_owned();
6060+ if loc.is_empty() {
6161+ None
6262+ } else {
6363+ Some(loc)
6464+ }
6565+ } else {
6666+ None
6767+ };
6868+6969+ let uid_ptr = ffi::i_cal_component_get_uid(comp);
7070+ let uid = if !uid_ptr.is_null() {
7171+ CStr::from_ptr(uid_ptr).to_string_lossy().into_owned()
7272+ } else {
7373+ String::new()
7474+ };
7575+7676+ let start = ffi::i_cal_component_get_dtstart(comp);
7777+ let end = ffi::i_cal_component_get_dtend(comp);
7878+7979+ let is_all_day = ffi::i_cal_time_is_date(start) != 0;
8080+8181+ let start_time_t = ffi::i_cal_time_as_timet(start);
8282+ let end_time_t = ffi::i_cal_time_as_timet(end);
8383+8484+ let (start_event_time, end_event_time) = if is_all_day {
8585+ // For all-day events, use just the date
8686+ let start_dt = Local.timestamp_opt(start_time_t, 0).unwrap();
8787+ let end_dt = Local.timestamp_opt(end_time_t, 0).unwrap();
8888+ (
8989+ EventTime::Date(start_dt.date_naive()),
9090+ EventTime::Date(end_dt.date_naive()),
9191+ )
9292+ } else {
9393+ // For timed events, use full datetime
9494+ (
9595+ EventTime::DateTime(Local.timestamp_opt(start_time_t, 0).unwrap()),
9696+ EventTime::DateTime(Local.timestamp_opt(end_time_t, 0).unwrap()),
9797+ )
9898+ };
9999+100100+ ffi::g_object_unref(start as *mut std::os::raw::c_void);
101101+ ffi::g_object_unref(end as *mut std::os::raw::c_void);
102102+103103+ Some(Event {
104104+ summary,
105105+ description,
106106+ location,
107107+ start: start_event_time,
108108+ end: end_event_time,
109109+ uid,
110110+ })
111111+ }
112112+ }
113113+}
+9
src/eds/mod.rs
···11+// Evolution Data Server module
22+33+mod core;
44+mod event;
55+mod ffi;
66+77+// Re-export public API
88+pub use core::{CalendarClient, EdsError, GLibVersion, Result, Source, SourceRegistry};
99+pub use event::{Event, EventTime};