caldir-cli 0.12.1

CLI for interacting with your local caldir directory and syncing with external calendar providers
use anyhow::Result;
use caldir_core::{Caldir, CaldirError, Calendar};

use crate::output::calendars::{CalendarView, CalendarsView};

pub fn run(caldir: &Caldir) -> Result<CalendarsView> {
    build_view(caldir.calendars())
}

fn build_view(calendars: Vec<Result<Calendar, CaldirError>>) -> Result<CalendarsView> {
    let mut calendars = calendars.into_iter().collect::<Result<Vec<_>, _>>()?;
    calendars.sort_by(|a, b| a.slug().cmp(&b.slug()));

    Ok(CalendarsView(
        calendars.into_iter().map(CalendarView::from).collect(),
    ))
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::output::{Output, TextRender};
    use caldir_core::{CalendarConfig, ProviderSlug, RemoteConfig, RemoteConfigParams};
    use indoc::indoc;
    use pretty_assertions::assert_eq;

    #[test]
    fn renders_connected_and_local_calendars_ordered_by_slug() {
        let tmp = tempfile::tempdir().unwrap();
        let remote = RemoteConfig::new(ProviderSlug::from("google"), RemoteConfigParams::new());
        let config = CalendarConfig::new(
            Some("Personal".to_string()),
            Some("#4285f4".to_string()),
            Some(true),
            Some(remote),
        );
        let connected = Calendar::create(&tmp.path().join("personal"), Some(config)).unwrap();
        let local = Calendar::create(&tmp.path().join("local-notes"), None).unwrap();

        let view = build_view(vec![Ok(connected), Ok(local)]).unwrap();

        assert_eq!(
            view.to_text(),
            indoc! {r#"
                local-notes
                  name: -
                  color: -
                  read only: no
                  provider: -

                personal
                  name: Personal
                  color: #4285f4
                  read only: yes
                  provider: google"#}
        );

        let json = view.to_json();
        assert_eq!(json[0]["slug"], "local-notes");
        assert_eq!(json[0]["name"], serde_json::Value::Null);
        assert_eq!(json[0]["color"], serde_json::Value::Null);
        assert_eq!(json[0]["read_only"], false);
        assert_eq!(json[0]["provider"], serde_json::Value::Null);
        assert_eq!(json[1]["slug"], "personal");
        assert_eq!(json[1]["name"], "Personal");
        assert_eq!(json[1]["color"], "#4285f4");
        assert_eq!(json[1]["read_only"], true);
        assert_eq!(json[1]["provider"], "google");
    }

    #[test]
    fn invalid_calendar_config_returns_an_error() {
        let tmp = tempfile::tempdir().unwrap();
        let calendar_path = tmp.path().join("broken");
        std::fs::create_dir_all(calendar_path.join(".caldir")).unwrap();
        std::fs::write(calendar_path.join(".caldir/config.toml"), "not valid toml").unwrap();

        let calendar = Calendar::load(&calendar_path).map_err(CaldirError::from);

        assert!(build_view(vec![calendar]).is_err());
    }
}