caltemps 0.3.1

A tool to query and report on your iCalendar data from vDirs.
Documentation
use icalendar::Calendar;
use std::ffi::OsString;
use std::path::Path;

#[derive(Debug)]
struct IcsReadError {
    path: OsString,
    //error: Box<dyn std::error::Error>,
}

fn list_ics_from_dir(
    path: &Path,
) -> Result<impl Iterator<Item = OsString>, Box<dyn std::error::Error>> {
    match std::fs::read_dir(path) {
        Ok(dirs) => Ok(dirs.filter_map(|res| match res {
            Ok(t) => {
                if let Some(x) = t.path().extension() {
                    if x.eq_ignore_ascii_case("ics") {
                        Some(t.path().into_os_string())
                    } else {
                        None
                    }
                } else {
                    None
                }
            }
            Err(_) => None,
        })),
        Err(error) => Err(Box::new(error)),
    }
}

fn read_ics_from_dir(
    path: &Path,
) -> Result<impl Iterator<Item = Result<Calendar, IcsReadError>>, Box<dyn std::error::Error>> {
    match list_ics_from_dir(path) {
        Ok(ics_it) => Ok(ics_it.map(|ipath| {
            // Read file to output
            let ics_path = ipath.to_os_string();
            match &mut std::fs::File::open(ipath) {
                Ok(f) => {
                    let readable: &mut dyn std::io::Read = f;
                    let mut output = String::new();
                    match readable.read_to_string(&mut output) {
                        Ok(_) => {
                            //icalendar::parser::read_calendar(&output)
                            match output.parse::<Calendar>() {
                                Ok(read) => Ok(read),
                                Err(_error) => Err(IcsReadError {
                                    path: ics_path,
                                    //error: Box::new(error.into()),
                                }),
                            }
                        }
                        Err(_error) => Err(IcsReadError {
                            path: ics_path,
                            //error: Box::new(error.to_string()),
                        }),
                    }
                }
                Err(_error) => Err(IcsReadError {
                    path: ics_path,
                    //error: Box::new(error.to_string()),
                }),
            }
        })),
        Err(error) => Err(error),
    }
}

pub fn read_vdir_cal(path: &Path) -> Result<Calendar, Box<dyn std::error::Error>> {
    // TODO: we can read some metadata like displayname and color
    /*if path.join("displayname").is_file() {
        cal.
    }*/
    match read_ics_from_dir(Path::new(path)) {
        Ok(entries_it) => {
            let mut cal = Calendar::new();
            cal.components.extend(
                entries_it
                    .filter_map(|entry| match entry {
                        Ok(p) => Some(p.components),
                        Err(error) => {
                            eprintln!("Issue reading {:#?}\n\t{:#?}", error.path, error);
                            None
                        }
                    })
                    .flatten(),
            );
            Ok(cal)
        }
        Err(error) => Err(error),
    }
}