caldir-cli 0.12.1

CLI for interacting with your local caldir directory and syncing with external calendar providers
use caldir_core::Calendar;
use serde::Serialize;

use crate::output::TextRender;

#[derive(Serialize)]
pub struct CalendarView {
    pub slug: String,
    pub name: Option<String>,
    pub color: Option<String>,
    pub read_only: bool,
    pub provider: Option<String>,
}

impl From<Calendar> for CalendarView {
    fn from(calendar: Calendar) -> Self {
        Self {
            slug: calendar.slug().unwrap_or_default().to_string(),
            name: calendar.name().map(str::to_string),
            color: calendar.color().map(str::to_string),
            read_only: calendar.is_read_only(),
            provider: calendar
                .remote_config()
                .map(|remote| remote.provider_slug().to_string()),
        }
    }
}

#[derive(Serialize)]
#[serde(transparent)]
pub struct CalendarsView(pub Vec<CalendarView>);

impl TextRender for CalendarsView {
    fn to_text(&self) -> String {
        if self.0.is_empty() {
            return "No calendars found.".to_string();
        }

        self.0
            .iter()
            .map(|calendar| {
                format!(
                    "{}\n  name: {}\n  color: {}\n  read only: {}\n  provider: {}",
                    calendar.slug,
                    calendar.name.as_deref().unwrap_or("-"),
                    calendar.color.as_deref().unwrap_or("-"),
                    if calendar.read_only { "yes" } else { "no" },
                    calendar.provider.as_deref().unwrap_or("-")
                )
            })
            .collect::<Vec<_>>()
            .join("\n\n")
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::output::{Output, TextRender};
    use pretty_assertions::assert_eq;

    #[test]
    fn empty_view_renders_text_and_json() {
        let view = CalendarsView(vec![]);

        assert_eq!(view.to_text(), "No calendars found.");
        assert_eq!(view.to_json(), serde_json::json!([]));
    }
}