1use icalendar::Calendar;
2use std::ffi::OsString;
3use std::path::Path;
4
5#[derive(Debug)]
6struct IcsReadError {
7 path: OsString,
8 }
10
11fn list_ics_from_dir(
12 path: &Path,
13) -> Result<impl Iterator<Item = OsString>, Box<dyn std::error::Error>> {
14 match std::fs::read_dir(path) {
15 Ok(dirs) => Ok(dirs.filter_map(|res| match res {
16 Ok(t) => {
17 if let Some(x) = t.path().extension() {
18 if x.eq_ignore_ascii_case("ics") {
19 Some(t.path().into_os_string())
20 } else {
21 None
22 }
23 } else {
24 None
25 }
26 }
27 Err(_) => None,
28 })),
29 Err(error) => Err(Box::new(error)),
30 }
31}
32
33fn read_ics_from_dir(
34 path: &Path,
35) -> Result<impl Iterator<Item = Result<Calendar, IcsReadError>>, Box<dyn std::error::Error>> {
36 match list_ics_from_dir(path) {
37 Ok(ics_it) => Ok(ics_it.map(|ipath| {
38 let ics_path = ipath.to_os_string();
40 match &mut std::fs::File::open(ipath) {
41 Ok(f) => {
42 let readable: &mut dyn std::io::Read = f;
43 let mut output = String::new();
44 match readable.read_to_string(&mut output) {
45 Ok(_) => {
46 match output.parse::<Calendar>() {
48 Ok(read) => Ok(read),
49 Err(_error) => Err(IcsReadError {
50 path: ics_path,
51 }),
53 }
54 }
55 Err(_error) => Err(IcsReadError {
56 path: ics_path,
57 }),
59 }
60 }
61 Err(_error) => Err(IcsReadError {
62 path: ics_path,
63 }),
65 }
66 })),
67 Err(error) => Err(error),
68 }
69}
70
71pub fn read_vdir_cal(path: &Path) -> Result<Calendar, Box<dyn std::error::Error>> {
72 match read_ics_from_dir(Path::new(path)) {
77 Ok(entries_it) => {
78 let mut cal = Calendar::new();
79 cal.components.extend(
80 entries_it
81 .filter_map(|entry| match entry {
82 Ok(p) => Some(p.components),
83 Err(error) => {
84 eprintln!("Issue reading {:#?}\n\t{:#?}", error.path, error);
85 None
86 }
87 })
88 .flatten(),
89 );
90 Ok(cal)
91 }
92 Err(error) => Err(error),
93 }
94}