// icaltool - a tool to get information out of ical/ics files. // Copyright (C) 2018 Harald Eilertsen // // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program. If not, see . use ical; use chrono::{DateTime, TimeZone, Utc}; use regex::Regex; use std::fs::File; use std::io::BufReader; fn parse_datetime(datetime: Option) -> Option> { if let Some(mut dt) = datetime { if dt.is_empty() { return None; } if !dt.contains('T') { dt += "T000000"; } Utc.datetime_from_str(&dt, "%Y%m%dT%H%M%S").ok() } else { None } } fn print_event(event: &ical::parser::ical::component::IcalEvent) { let mut start: Option> = None; let mut end: Option> = None; let mut summary = String::new(); for p in &event.properties { match p.name.as_ref() { "DTSTART" => start = parse_datetime(p.value.clone()), "DTEND" => end = parse_datetime(p.value.clone()), "SUMMARY" => summary = p.value.clone().unwrap_or_default(), _ => (), } } println!("---> {} - {}: {}", start.unwrap_or_else(Utc::now), end.unwrap_or_else(Utc::now), summary); } fn read_icalendar_from_file(filename: &str) -> std::io::Result> { let buf = BufReader::new(File::open(filename)?); Ok(ical::IcalParser::new(buf) .filter_map(|c| c.ok()) .collect()) } fn match_event(event: &ical::parser::ical::component::IcalEvent, pattern: &str) -> bool { if pattern == "*" { true } else { let parts: Vec<&str> = pattern.split(':').collect(); let (key, pat) = (parts[0].to_uppercase(), parts[1]); let re = Regex::new(&pat).unwrap(); event.properties.iter() .filter(|ref p| p.name == key && re.is_match(&p.value.clone().unwrap())) .count() > 0 } } #[cfg(test)] fn test_match_event_helper(pattern: &str) -> Vec { let cal = read_icalendar_from_file("test/fixtures/events.ics").unwrap(); cal[0].events.iter() .filter_map(|event| { if match_event(&event, pattern) { Some(event.clone()) } else { None } }) .collect() } #[test] fn match_all_events() { let events:Vec<_> = test_match_event_helper("*"); assert_eq!(3, events.len()); } #[test] fn match_events_by_summary() { let events:Vec<_> = test_match_event_helper("summary:All"); assert_eq!(2, events.len()); } #[test] fn match_events_by_regex() { let events:Vec<_> = test_match_event_helper("summary:^All.+1$"); assert_eq!(1, events.len()); } fn main() { let mut args = std::env::args().skip(1); let filename = args.next().unwrap(); let pattern = args.next().unwrap(); let calendars = read_icalendar_from_file(&filename).unwrap(); println!("Found {} calendard in file...", calendars.len()); for c in calendars { for e in c.events.iter().filter(|e| match_event(&e, &pattern)) { print_event(&e); } } }