Trying very hard not to miss calendar events
0
fork

Configure Feed

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

Refactor, second pass

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

+465 -283
+296
src/eds.rs
··· 1 + // Safe Rust abstractions over Evolution Data Server C APIs 2 + 3 + use crate::ffi; 4 + use std::ffi::{CStr, CString}; 5 + use std::fmt; 6 + use std::os::raw::c_char; 7 + use std::ptr; 8 + 9 + // Error handling 10 + #[derive(Debug)] 11 + pub struct EdsError { 12 + message: String, 13 + } 14 + 15 + impl fmt::Display for EdsError { 16 + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 17 + write!(f, "{}", self.message) 18 + } 19 + } 20 + 21 + impl std::error::Error for EdsError {} 22 + 23 + impl EdsError { 24 + fn from_gerror(error: *mut ffi::GError) -> Self { 25 + unsafe { 26 + let message = if !error.is_null() && !(*error).message.is_null() { 27 + CStr::from_ptr((*error).message) 28 + .to_string_lossy() 29 + .into_owned() 30 + } else { 31 + "Unknown error".to_string() 32 + }; 33 + ffi::g_error_free(error); 34 + EdsError { message } 35 + } 36 + } 37 + } 38 + 39 + pub type Result<T> = std::result::Result<T, EdsError>; 40 + 41 + // GLib version info 42 + pub struct GLibVersion { 43 + pub major: u32, 44 + pub minor: u32, 45 + pub micro: u32, 46 + } 47 + 48 + impl GLibVersion { 49 + pub fn get() -> Self { 50 + unsafe { 51 + GLibVersion { 52 + major: ffi::glib_major_version, 53 + minor: ffi::glib_minor_version, 54 + micro: ffi::glib_micro_version, 55 + } 56 + } 57 + } 58 + } 59 + 60 + // Source Registry 61 + pub struct SourceRegistry { 62 + ptr: *mut ffi::ESourceRegistry, 63 + } 64 + 65 + impl SourceRegistry { 66 + pub fn new() -> Result<Self> { 67 + unsafe { 68 + let mut error: *mut ffi::GError = ptr::null_mut(); 69 + let registry = ffi::e_source_registry_new_sync(ptr::null_mut(), &mut error); 70 + 71 + if !error.is_null() { 72 + return Err(EdsError::from_gerror(error)); 73 + } 74 + 75 + if registry.is_null() { 76 + return Err(EdsError { 77 + message: "Failed to create source registry".to_string(), 78 + }); 79 + } 80 + 81 + Ok(SourceRegistry { ptr: registry }) 82 + } 83 + } 84 + 85 + pub fn list_sources(&self) -> Result<Vec<Source>> { 86 + unsafe { 87 + let sources_list = ffi::e_source_registry_list_sources( 88 + self.ptr, 89 + ffi::E_SOURCE_EXTENSION_CALENDAR.as_ptr() as *const c_char, 90 + ); 91 + 92 + let mut sources = Vec::new(); 93 + let mut current = sources_list; 94 + 95 + while !current.is_null() { 96 + let source_ptr = (*current).data as *mut ffi::ESource; 97 + sources.push(Source { 98 + ptr: source_ptr, 99 + _owned: false, 100 + }); 101 + current = (*current).next; 102 + } 103 + 104 + ffi::g_list_free_full(sources_list, Some(ffi::glib_object_unref_wrapper)); 105 + 106 + Ok(sources) 107 + } 108 + } 109 + } 110 + 111 + impl Drop for SourceRegistry { 112 + fn drop(&mut self) { 113 + unsafe { 114 + ffi::g_object_unref(self.ptr as *mut std::os::raw::c_void); 115 + } 116 + } 117 + } 118 + 119 + // Source 120 + pub struct Source { 121 + ptr: *mut ffi::ESource, 122 + _owned: bool, 123 + } 124 + 125 + impl Source { 126 + pub fn display_name(&self) -> &str { 127 + unsafe { 128 + let name_ptr = ffi::e_source_get_display_name(self.ptr); 129 + if name_ptr.is_null() { 130 + "" 131 + } else { 132 + CStr::from_ptr(name_ptr).to_str().unwrap_or("") 133 + } 134 + } 135 + } 136 + 137 + pub fn uid(&self) -> &str { 138 + unsafe { 139 + let uid_ptr = ffi::e_source_get_uid(self.ptr); 140 + if uid_ptr.is_null() { 141 + "" 142 + } else { 143 + CStr::from_ptr(uid_ptr).to_str().unwrap_or("") 144 + } 145 + } 146 + } 147 + 148 + pub(crate) fn as_ptr(&self) -> *mut ffi::ESource { 149 + self.ptr 150 + } 151 + } 152 + 153 + // Calendar Client 154 + pub struct CalendarClient { 155 + client_ptr: *mut ffi::EClient, 156 + } 157 + 158 + impl CalendarClient { 159 + pub fn connect(source: &Source) -> Result<Self> { 160 + unsafe { 161 + let mut error: *mut ffi::GError = ptr::null_mut(); 162 + let client = ffi::e_cal_client_connect_sync( 163 + source.as_ptr(), 164 + ffi::ECalClientSourceType::Events, 165 + 30, 166 + ptr::null_mut(), 167 + &mut error, 168 + ); 169 + 170 + if !error.is_null() { 171 + return Err(EdsError::from_gerror(error)); 172 + } 173 + 174 + if client.is_null() { 175 + return Err(EdsError { 176 + message: "Failed to connect to calendar".to_string(), 177 + }); 178 + } 179 + 180 + Ok(CalendarClient { client_ptr: client }) 181 + } 182 + } 183 + 184 + pub fn list_events(&self, start_time: i64, end_time: i64) -> Result<Vec<Event>> { 185 + unsafe { 186 + let iso_start = ffi::isodate_from_time_t(start_time); 187 + let iso_end = ffi::isodate_from_time_t(end_time); 188 + let query_fmt = 189 + CString::new("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))") 190 + .unwrap(); 191 + let query = ffi::g_strdup_printf(query_fmt.as_ptr(), iso_start, iso_end); 192 + 193 + let mut ical_components: *mut ffi::GSList = ptr::null_mut(); 194 + let mut error: *mut ffi::GError = ptr::null_mut(); 195 + 196 + let success = ffi::e_cal_client_get_object_list_sync( 197 + self.client_ptr as *mut ffi::ECalClient, 198 + query, 199 + &mut ical_components, 200 + ptr::null_mut(), 201 + &mut error, 202 + ); 203 + 204 + ffi::g_free(query as *mut std::os::raw::c_void); 205 + ffi::g_free(iso_start as *mut std::os::raw::c_void); 206 + ffi::g_free(iso_end as *mut std::os::raw::c_void); 207 + 208 + if !error.is_null() { 209 + return Err(EdsError::from_gerror(error)); 210 + } 211 + 212 + let mut events = Vec::new(); 213 + 214 + if success != 0 && !ical_components.is_null() { 215 + let mut current = ical_components; 216 + while !current.is_null() { 217 + let icalcomp = (*current).data as *mut ffi::ICalComponent; 218 + 219 + if !icalcomp.is_null() { 220 + if let Some(event) = Event::from_ical_component(icalcomp) { 221 + events.push(event); 222 + } 223 + } 224 + 225 + current = (*current).next; 226 + } 227 + 228 + ffi::g_slist_free_full(ical_components, Some(ffi::glib_object_unref_wrapper)); 229 + } 230 + 231 + Ok(events) 232 + } 233 + } 234 + } 235 + 236 + impl Drop for CalendarClient { 237 + fn drop(&mut self) { 238 + unsafe { 239 + ffi::g_object_unref(self.client_ptr as *mut std::os::raw::c_void); 240 + } 241 + } 242 + } 243 + 244 + // Event 245 + pub struct Event { 246 + pub summary: String, 247 + pub start_time: i64, 248 + pub end_time: i64, 249 + } 250 + 251 + impl Event { 252 + fn from_ical_component(comp: *mut ffi::ICalComponent) -> Option<Self> { 253 + unsafe { 254 + let summary_ptr = ffi::i_cal_component_get_summary(comp); 255 + let summary = if !summary_ptr.is_null() { 256 + CStr::from_ptr(summary_ptr) 257 + .to_string_lossy() 258 + .into_owned() 259 + } else { 260 + "(No title)".to_string() 261 + }; 262 + 263 + let start = ffi::i_cal_component_get_dtstart(comp); 264 + let end = ffi::i_cal_component_get_dtend(comp); 265 + 266 + let start_time = ffi::i_cal_time_as_timet(start); 267 + let end_time = ffi::i_cal_time_as_timet(end); 268 + 269 + ffi::g_object_unref(start as *mut std::os::raw::c_void); 270 + ffi::g_object_unref(end as *mut std::os::raw::c_void); 271 + 272 + Some(Event { 273 + summary, 274 + start_time, 275 + end_time, 276 + }) 277 + } 278 + } 279 + 280 + pub fn format_time(timestamp: i64) -> String { 281 + unsafe { 282 + let tm = libc::localtime(&timestamp); 283 + let mut buf = vec![0u8; 64]; 284 + let fmt = CString::new("%Y-%m-%d %H:%M").unwrap(); 285 + libc::strftime( 286 + buf.as_mut_ptr() as *mut c_char, 287 + buf.len(), 288 + fmt.as_ptr(), 289 + tm, 290 + ); 291 + CStr::from_ptr(buf.as_ptr() as *const c_char) 292 + .to_string_lossy() 293 + .into_owned() 294 + } 295 + } 296 + }
+109
src/ffi.rs
··· 1 + // Raw FFI bindings to C libraries 2 + // This module contains all unsafe extern "C" declarations 3 + 4 + #![allow(non_camel_case_types)] 5 + #![allow(dead_code)] 6 + 7 + use std::os::raw::{c_char, c_int, c_uint, c_void}; 8 + 9 + // Opaque C types 10 + #[repr(C)] 11 + pub struct GError { 12 + pub domain: u32, 13 + pub code: c_int, 14 + pub message: *mut c_char, 15 + } 16 + 17 + #[repr(C)] 18 + pub struct GList { 19 + pub data: *mut c_void, 20 + pub next: *mut GList, 21 + pub prev: *mut GList, 22 + } 23 + 24 + #[repr(C)] 25 + pub struct GSList { 26 + pub data: *mut c_void, 27 + pub next: *mut GSList, 28 + } 29 + 30 + pub enum ESourceRegistry {} 31 + pub enum ESource {} 32 + pub enum EClient {} 33 + pub enum ECalClient {} 34 + pub enum ICalComponent {} 35 + pub enum ICalTime {} 36 + 37 + #[repr(C)] 38 + pub enum ECalClientSourceType { 39 + Events = 0, 40 + } 41 + 42 + // GLib functions 43 + #[link(name = "glib-2.0")] 44 + extern "C" { 45 + pub fn g_list_length(list: *mut GList) -> u32; 46 + pub fn g_list_free_full(list: *mut GList, free_func: Option<extern "C" fn(*mut c_void)>); 47 + pub fn g_slist_free_full(list: *mut GSList, free_func: Option<extern "C" fn(*mut c_void)>); 48 + pub fn g_object_unref(object: *mut c_void); 49 + pub fn g_error_free(error: *mut GError); 50 + pub fn g_clear_error(error: *mut *mut GError); 51 + pub fn g_strdup_printf(format: *const c_char, ...) -> *mut c_char; 52 + pub fn g_free(mem: *mut c_void); 53 + 54 + pub static glib_major_version: c_uint; 55 + pub static glib_minor_version: c_uint; 56 + pub static glib_micro_version: c_uint; 57 + } 58 + 59 + // Evolution Data Server functions 60 + #[link(name = "edataserver-1.2")] 61 + extern "C" { 62 + pub fn e_source_registry_new_sync( 63 + cancellable: *mut c_void, 64 + error: *mut *mut GError, 65 + ) -> *mut ESourceRegistry; 66 + pub fn e_source_registry_list_sources( 67 + registry: *mut ESourceRegistry, 68 + extension_name: *const c_char, 69 + ) -> *mut GList; 70 + pub fn e_source_get_display_name(source: *mut ESource) -> *const c_char; 71 + pub fn e_source_get_uid(source: *mut ESource) -> *const c_char; 72 + } 73 + 74 + // Evolution Calendar functions 75 + #[link(name = "ecal-2.0")] 76 + extern "C" { 77 + pub fn e_cal_client_connect_sync( 78 + source: *mut ESource, 79 + source_type: ECalClientSourceType, 80 + wait_for_connected_seconds: u32, 81 + cancellable: *mut c_void, 82 + error: *mut *mut GError, 83 + ) -> *mut EClient; 84 + pub fn e_cal_client_get_object_list_sync( 85 + client: *mut ECalClient, 86 + sexp: *const c_char, 87 + out_icalcomps: *mut *mut GSList, 88 + cancellable: *mut c_void, 89 + error: *mut *mut GError, 90 + ) -> c_int; 91 + pub fn isodate_from_time_t(t: libc::time_t) -> *mut c_char; 92 + } 93 + 94 + // libical-glib functions 95 + #[link(name = "ical-glib")] 96 + extern "C" { 97 + pub fn i_cal_component_get_summary(comp: *mut ICalComponent) -> *const c_char; 98 + pub fn i_cal_component_get_dtstart(comp: *mut ICalComponent) -> *mut ICalTime; 99 + pub fn i_cal_component_get_dtend(comp: *mut ICalComponent) -> *mut ICalTime; 100 + pub fn i_cal_time_as_timet(tt: *mut ICalTime) -> libc::time_t; 101 + } 102 + 103 + // Helper for GObject unrefs 104 + pub extern "C" fn glib_object_unref_wrapper(ptr: *mut c_void) { 105 + unsafe { g_object_unref(ptr) } 106 + } 107 + 108 + // Constants 109 + pub const E_SOURCE_EXTENSION_CALENDAR: &[u8] = b"Calendar\0";
+60 -283
src/main.rs
··· 1 - use std::env; 2 - use std::ffi::{CStr, CString}; 3 - use std::os::raw::{c_char, c_int, c_void}; 4 - use std::ptr; 5 - 6 - // FFI bindings for GLib 7 - #[link(name = "glib-2.0")] 8 - extern "C" { 9 - fn g_list_length(list: *mut GList) -> u32; 10 - fn g_list_free_full(list: *mut GList, free_func: Option<extern "C" fn(*mut c_void)>); 11 - fn g_slist_free_full(list: *mut GSList, free_func: Option<extern "C" fn(*mut c_void)>); 12 - fn g_object_unref(object: *mut c_void); 13 - fn g_error_free(error: *mut GError); 14 - fn g_clear_error(error: *mut *mut GError); 15 - fn g_strdup_printf(format: *const c_char, ...) -> *mut c_char; 16 - fn g_free(mem: *mut c_void); 17 - 18 - static glib_major_version: u32; 19 - static glib_minor_version: u32; 20 - static glib_micro_version: u32; 21 - } 22 - 23 - // FFI bindings for Evolution Data Server 24 - #[link(name = "edataserver-1.2")] 25 - extern "C" { 26 - fn e_source_registry_new_sync( 27 - cancellable: *mut c_void, 28 - error: *mut *mut GError, 29 - ) -> *mut ESourceRegistry; 30 - fn e_source_registry_list_sources( 31 - registry: *mut ESourceRegistry, 32 - extension_name: *const c_char, 33 - ) -> *mut GList; 34 - fn e_source_get_display_name(source: *mut ESource) -> *const c_char; 35 - fn e_source_get_uid(source: *mut ESource) -> *const c_char; 36 - } 37 - 38 - const E_SOURCE_EXTENSION_CALENDAR: &[u8] = b"Calendar\0"; 39 - 40 - // FFI bindings for Evolution Calendar 41 - #[link(name = "ecal-2.0")] 42 - extern "C" { 43 - fn e_cal_client_connect_sync( 44 - source: *mut ESource, 45 - source_type: ECalClientSourceType, 46 - wait_for_connected_seconds: u32, 47 - cancellable: *mut c_void, 48 - error: *mut *mut GError, 49 - ) -> *mut EClient; 50 - fn e_cal_client_get_object_list_sync( 51 - client: *mut ECalClient, 52 - sexp: *const c_char, 53 - out_icalcomps: *mut *mut GSList, 54 - cancellable: *mut c_void, 55 - error: *mut *mut GError, 56 - ) -> c_int; 57 - } 58 - 59 - // FFI bindings for libical-glib 60 - #[link(name = "ical-glib")] 61 - extern "C" { 62 - fn i_cal_component_get_summary(comp: *mut ICalComponent) -> *const c_char; 63 - fn i_cal_component_get_dtstart(comp: *mut ICalComponent) -> *mut ICalTime; 64 - fn i_cal_component_get_dtend(comp: *mut ICalComponent) -> *mut ICalTime; 65 - fn i_cal_time_as_timet(tt: *mut ICalTime) -> libc::time_t; 66 - } 67 - 68 - // FFI bindings for e-cal-time-util 69 - #[link(name = "ecal-2.0")] 70 - extern "C" { 71 - fn isodate_from_time_t(t: libc::time_t) -> *mut c_char; 72 - } 73 - 74 - // Opaque types 75 - #[repr(C)] 76 - struct GError { 77 - domain: u32, 78 - code: c_int, 79 - message: *mut c_char, 80 - } 81 - 82 - #[repr(C)] 83 - struct GList { 84 - data: *mut c_void, 85 - next: *mut GList, 86 - prev: *mut GList, 87 - } 88 - 89 - #[repr(C)] 90 - struct GSList { 91 - data: *mut c_void, 92 - next: *mut GSList, 93 - } 94 - 95 - enum ESourceRegistry {} 96 - enum ESource {} 97 - enum EClient {} 98 - enum ECalClient {} 99 - enum ICalComponent {} 100 - enum ICalTime {} 1 + mod eds; 2 + mod ffi; 101 3 102 - #[repr(C)] 103 - enum ECalClientSourceType { 104 - Events = 0, 105 - } 106 - 107 - extern "C" fn glib_object_unref_wrapper(ptr: *mut c_void) { 108 - unsafe { g_object_unref(ptr) } 109 - } 4 + use std::env; 110 5 111 6 fn print_version() { 112 - unsafe { 113 - println!("Evolution Data Server test program"); 114 - println!("Library version info:"); 115 - println!(" GLib version: {}.{}.{}", 116 - glib_major_version, 117 - glib_minor_version, 118 - glib_micro_version 119 - ); 120 - } 7 + let version = eds::GLibVersion::get(); 8 + println!("Evolution Data Server test program"); 9 + println!("Library version info:"); 10 + println!(" GLib version: {}.{}.{}", version.major, version.minor, version.micro); 121 11 } 122 12 123 13 fn print_usage(program_name: &str) { ··· 129 19 } 130 20 131 21 fn list_sources() -> i32 { 132 - unsafe { 133 - let mut error: *mut GError = ptr::null_mut(); 134 - let registry = e_source_registry_new_sync(ptr::null_mut(), &mut error); 135 - 136 - if !error.is_null() { 137 - let err_msg = CStr::from_ptr((*error).message); 138 - eprintln!("Failed to create source registry: {}", err_msg.to_string_lossy()); 139 - g_error_free(error); 22 + let registry = match eds::SourceRegistry::new() { 23 + Ok(reg) => reg, 24 + Err(e) => { 25 + eprintln!("Failed to create source registry: {}", e); 140 26 return 1; 141 27 } 28 + }; 142 29 143 - if !registry.is_null() { 144 - println!("Successfully connected to Evolution Data Server"); 30 + println!("Successfully connected to Evolution Data Server"); 145 31 146 - let sources = e_source_registry_list_sources( 147 - registry, 148 - E_SOURCE_EXTENSION_CALENDAR.as_ptr() as *const c_char 149 - ); 150 - let count = g_list_length(sources); 151 - println!("Found {} calendar source(s)", count); 32 + let sources = match registry.list_sources() { 33 + Ok(srcs) => srcs, 34 + Err(e) => { 35 + eprintln!("Failed to list sources: {}", e); 36 + return 1; 37 + } 38 + }; 152 39 153 - let mut current = sources; 154 - while !current.is_null() { 155 - let source = (*current).data as *mut ESource; 156 - let display_name = CStr::from_ptr(e_source_get_display_name(source)); 157 - let uid = CStr::from_ptr(e_source_get_uid(source)); 158 - println!(" - {} (UID: {})", 159 - display_name.to_string_lossy(), 160 - uid.to_string_lossy() 161 - ); 162 - current = (*current).next; 163 - } 40 + println!("Found {} calendar source(s)", sources.len()); 164 41 165 - g_list_free_full(sources, Some(glib_object_unref_wrapper)); 166 - g_object_unref(registry as *mut c_void); 167 - } 42 + for source in sources { 43 + println!(" - {} (UID: {})", source.display_name(), source.uid()); 168 44 } 169 45 170 46 0 171 47 } 172 48 173 49 fn list_events() -> i32 { 174 - unsafe { 175 - let mut error: *mut GError = ptr::null_mut(); 176 - let registry = e_source_registry_new_sync(ptr::null_mut(), &mut error); 177 - 178 - if !error.is_null() { 179 - let err_msg = CStr::from_ptr((*error).message); 180 - eprintln!("Failed to create source registry: {}", err_msg.to_string_lossy()); 181 - g_error_free(error); 50 + let registry = match eds::SourceRegistry::new() { 51 + Ok(reg) => reg, 52 + Err(e) => { 53 + eprintln!("Failed to create source registry: {}", e); 182 54 return 1; 183 55 } 56 + }; 184 57 185 - let now = libc::time(ptr::null_mut()); 186 - let end_time = now + (60 * 24 * 60 * 60); 58 + let now = unsafe { libc::time(std::ptr::null_mut()) }; 59 + let end_time = now + (60 * 24 * 60 * 60); 187 60 188 - let sources = e_source_registry_list_sources( 189 - registry, 190 - E_SOURCE_EXTENSION_CALENDAR.as_ptr() as *const c_char 191 - ); 192 - let mut total_events = 0; 61 + let sources = match registry.list_sources() { 62 + Ok(srcs) => srcs, 63 + Err(e) => { 64 + eprintln!("Failed to list sources: {}", e); 65 + return 1; 66 + } 67 + }; 193 68 194 - let mut current_source = sources; 195 - while !current_source.is_null() { 196 - let source = (*current_source).data as *mut ESource; 197 - let display_name = CStr::from_ptr(e_source_get_display_name(source)); 69 + let mut total_events = 0; 198 70 199 - let client_base = e_cal_client_connect_sync( 200 - source, 201 - ECalClientSourceType::Events, 202 - 30, 203 - ptr::null_mut(), 204 - &mut error, 205 - ); 71 + for source in sources { 72 + let display_name = source.display_name(); 206 73 207 - if !error.is_null() { 208 - let err_msg = CStr::from_ptr((*error).message); 209 - eprintln!( 210 - "Failed to connect to calendar '{}': {}", 211 - display_name.to_string_lossy(), 212 - err_msg.to_string_lossy() 213 - ); 214 - g_clear_error(&mut error); 215 - current_source = (*current_source).next; 74 + let client = match eds::CalendarClient::connect(&source) { 75 + Ok(client) => client, 76 + Err(e) => { 77 + eprintln!("Failed to connect to calendar '{}': {}", display_name, e); 216 78 continue; 217 79 } 80 + }; 218 81 219 - let client = client_base as *mut ECalClient; 220 - 221 - let iso_start = isodate_from_time_t(now); 222 - let iso_end = isodate_from_time_t(end_time); 223 - let query_fmt = CString::new("(occur-in-time-range? (make-time \"%s\") (make-time \"%s\"))").unwrap(); 224 - let query = g_strdup_printf(query_fmt.as_ptr(), iso_start, iso_end); 225 - 226 - let mut ical_components: *mut GSList = ptr::null_mut(); 227 - let success = e_cal_client_get_object_list_sync( 228 - client, 229 - query, 230 - &mut ical_components, 231 - ptr::null_mut(), 232 - &mut error, 233 - ); 234 - 235 - if !error.is_null() { 236 - let err_msg = CStr::from_ptr((*error).message); 237 - eprintln!( 238 - "Failed to query events from '{}': {}", 239 - display_name.to_string_lossy(), 240 - err_msg.to_string_lossy() 241 - ); 242 - g_clear_error(&mut error); 243 - g_free(query as *mut c_void); 244 - g_free(iso_start as *mut c_void); 245 - g_free(iso_end as *mut c_void); 246 - g_object_unref(client_base as *mut c_void); 247 - current_source = (*current_source).next; 82 + let events = match client.list_events(now, end_time) { 83 + Ok(evts) => evts, 84 + Err(e) => { 85 + eprintln!("Failed to query events from '{}': {}", display_name, e); 248 86 continue; 249 87 } 88 + }; 250 89 251 - if success != 0 && !ical_components.is_null() { 252 - let mut current_comp = ical_components; 253 - while !current_comp.is_null() { 254 - let icalcomp = (*current_comp).data as *mut ICalComponent; 90 + for event in events { 91 + let start_str = eds::Event::format_time(event.start_time); 92 + let end_str = eds::Event::format_time(event.end_time); 255 93 256 - if !icalcomp.is_null() { 257 - let summary_ptr = i_cal_component_get_summary(icalcomp); 258 - let summary = if !summary_ptr.is_null() { 259 - CStr::from_ptr(summary_ptr).to_string_lossy() 260 - } else { 261 - "(No title)".into() 262 - }; 263 - 264 - let start = i_cal_component_get_dtstart(icalcomp); 265 - let end = i_cal_component_get_dtend(icalcomp); 266 - 267 - let start_t = i_cal_time_as_timet(start); 268 - let end_t = i_cal_time_as_timet(end); 269 - 270 - let mut start_buf = vec![0u8; 64]; 271 - let mut end_buf = vec![0u8; 64]; 272 - let fmt = CString::new("%Y-%m-%d %H:%M").unwrap(); 273 - 274 - let start_tm = libc::localtime(&start_t); 275 - libc::strftime( 276 - start_buf.as_mut_ptr() as *mut c_char, 277 - start_buf.len(), 278 - fmt.as_ptr(), 279 - start_tm, 280 - ); 281 - 282 - let end_tm = libc::localtime(&end_t); 283 - libc::strftime( 284 - end_buf.as_mut_ptr() as *mut c_char, 285 - end_buf.len(), 286 - fmt.as_ptr(), 287 - end_tm, 288 - ); 289 - 290 - let start_str = CStr::from_ptr(start_buf.as_ptr() as *const c_char) 291 - .to_string_lossy(); 292 - let end_str = CStr::from_ptr(end_buf.as_ptr() as *const c_char) 293 - .to_string_lossy(); 294 - 295 - println!( 296 - "[{}] {} | {} - {}", 297 - display_name.to_string_lossy(), 298 - summary, 299 - start_str, 300 - end_str 301 - ); 302 - total_events += 1; 303 - 304 - g_object_unref(start as *mut c_void); 305 - g_object_unref(end as *mut c_void); 306 - } 307 - 308 - current_comp = (*current_comp).next; 309 - } 310 - 311 - g_slist_free_full(ical_components, Some(glib_object_unref_wrapper)); 312 - } 313 - 314 - g_free(query as *mut c_void); 315 - g_free(iso_start as *mut c_void); 316 - g_free(iso_end as *mut c_void); 317 - g_object_unref(client_base as *mut c_void); 318 - 319 - current_source = (*current_source).next; 94 + println!( 95 + "[{}] {} | {} - {}", 96 + display_name, event.summary, start_str, end_str 97 + ); 98 + total_events += 1; 320 99 } 321 - 322 - println!("\nTotal events: {}", total_events); 323 - g_list_free_full(sources, Some(glib_object_unref_wrapper)); 324 - g_object_unref(registry as *mut c_void); 325 100 } 101 + 102 + println!("\nTotal events: {}", total_events); 326 103 327 104 0 328 105 }