io-calendar 0.0.1

Set of I/O-free coroutines to manage calendars
use std::path::{Path, PathBuf};

use io_fs::io::FsIo;
use io_vdir::{
    constants::ICS,
    coroutines::read_item::{
        ReadItem as ReadVdirItem, ReadItemError as ReadVdirItemError,
        ReadItemResult as ReadVdirItemResult,
    },
    item::ItemKind,
};
use thiserror::Error;

use crate::item::CalendarItem;

#[derive(Clone, Debug, Error)]
pub enum ReadItemError {
    #[error("Read calendar vdir item error")]
    ReadVdirItem(#[from] ReadVdirItemError),
    #[error("Invalid calendar item path {0}")]
    InvalidCalendarItemPath(PathBuf),
    #[error("Invalid calendar id at {0}")]
    InvalidCalendarId(PathBuf),
    #[error("Invalid calendar item id at {0}")]
    InvalidCalendarItemId(PathBuf),
    #[error("Invalid calendar item at {0}")]
    InvalidCalendarItem(PathBuf),
}

#[derive(Clone, Debug)]
pub enum ReadCalendarItemResult {
    Ok(CalendarItem),
    Err(ReadItemError),
    Io(FsIo),
}

#[derive(Debug)]
pub struct ReadCalendarItem(ReadVdirItem);

impl ReadCalendarItem {
    pub fn new(root: impl AsRef<Path>, calendar_id: impl AsRef<str>, id: impl AsRef<str>) -> Self {
        let path = root
            .as_ref()
            .join(calendar_id.as_ref())
            .join(id.as_ref())
            .with_extension(ICS);

        Self(ReadVdirItem::new(path))
    }

    pub fn resume(&mut self, input: Option<FsIo>) -> ReadCalendarItemResult {
        let item = loop {
            match self.0.resume(input) {
                ReadVdirItemResult::Ok(item) => break item,
                ReadVdirItemResult::Err(err) => return ReadCalendarItemResult::Err(err.into()),
                ReadVdirItemResult::Io(io) => return ReadCalendarItemResult::Io(io),
            }
        };

        let p = &item.path;

        let Some(parent) = p.parent() else {
            return ReadCalendarItemResult::Err(ReadItemError::InvalidCalendarItemPath(
                p.to_owned(),
            ));
        };

        let Some(calendar_id) = parent.file_stem() else {
            return ReadCalendarItemResult::Err(ReadItemError::InvalidCalendarId(p.to_owned()));
        };

        let Some(id) = p.file_stem() else {
            return ReadCalendarItemResult::Err(ReadItemError::InvalidCalendarItemId(p.to_owned()));
        };

        let ItemKind::Ical(ical) = item.kind else {
            return ReadCalendarItemResult::Err(ReadItemError::InvalidCalendarItem(p.to_owned()));
        };

        let item = CalendarItem {
            id: id.to_string_lossy().to_string(),
            calendar_id: calendar_id.to_string_lossy().to_string(),
            ical,
        };

        ReadCalendarItemResult::Ok(item)
    }
}