use std::collections::HashSet;
use calcard::icalendar::ICalendar;
use io_stream::io::StreamIo;
use io_vdir::constants::ICS;
use log::{debug, trace};
use serde::Deserialize;
use crate::{
caldav::{
config::CaldavConfig,
request::Request,
response::{Multistatus, Value},
},
item::CalendarItem,
};
use super::send::{Send, SendOk, SendResult};
#[derive(Debug)]
pub struct ListCalendarItems {
calendar_id: String,
send: Send<Multistatus<Prop>>,
}
impl ListCalendarItems {
const BODY: &'static str = include_str!("./list-items.xml");
pub fn new(config: &CaldavConfig, calendar_id: impl AsRef<str>) -> Self {
let calendar_id = calendar_id.as_ref().to_owned();
let request = Request::report(config, &calendar_id)
.content_type_xml()
.depth(1);
Self {
calendar_id,
send: Send::new(request, Self::BODY.as_bytes().to_vec()),
}
}
pub fn resume(&mut self, arg: Option<StreamIo>) -> SendResult<HashSet<CalendarItem>> {
let ok = match self.send.resume(arg) {
SendResult::Ok(ok) => ok,
SendResult::Err(err) => return SendResult::Err(err),
SendResult::Io(io) => return SendResult::Io(io),
};
let mut items = HashSet::new();
if let Some(responses) = ok.body.responses {
for response in responses {
trace!("process multistatus");
if let Some(status) = response.status {
if !status.is_success() {
debug!("multistatus response error");
continue;
}
};
let Some(propstats) = response.propstats else {
continue;
};
let id = response
.href
.value
.trim_end_matches('/')
.rsplit('/')
.next()
.unwrap() .trim_end_matches(ICS)
.trim_end_matches('.')
.to_owned();
let mut item = None;
for propstat in propstats {
if !propstat.status.is_success() {
debug!("multistatus propstat error");
continue;
}
let Some(content) = propstat.prop.calendar_data else {
continue;
};
let Ok(ical) = ICalendar::parse(content.value) else {
continue;
};
item.replace(CalendarItem {
id: id.to_string(),
calendar_id: self.calendar_id.clone(),
ical,
});
break;
}
let Some(item) = item else {
continue;
};
items.insert(item);
}
};
SendResult::Ok(SendOk {
request: ok.request,
response: ok.response,
keep_alive: ok.keep_alive,
body: items,
})
}
}
#[derive(Clone, Debug, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Prop {
pub calendar_data: Option<Value>,
}