Skip to main content

io_webdav/rfc4791/calendar/
list.rs

1//! `list-calendars` coroutine: PROPFIND Depth:1 against the calendar
2//! home-set URL, collecting every child collection whose resourcetype
3//! is `<C:calendar/>`.
4//!
5//! # Example
6//!
7//! ```rust,no_run
8//! use std::{
9//!     io::{Read, Write},
10//!     net::TcpStream,
11//! };
12//!
13//! use io_webdav::{
14//!     coroutine::{WebdavCoroutine, WebdavCoroutineState, WebdavYield},
15//!     rfc4791::calendar::list::ListCalendars,
16//!     rfc4918::WebdavAuth,
17//! };
18//! use url::Url;
19//!
20//! // Ready stream needed (TCP-connected, TLS-negociated)
21//! let mut stream = TcpStream::connect("dav.example.org:443").unwrap();
22//! let mut buf = [0u8; 4096];
23//!
24//! let base_url: Url = "https://dav.example.org/".parse().unwrap();
25//! let auth = WebdavAuth::None;
26//! let mut coroutine = ListCalendars::new(&base_url, &auth, "io-webdav", "/dav/calendars/");
27//! let mut arg = None;
28//!
29//! let calendars = loop {
30//!     match coroutine.resume(arg.take()) {
31//!         WebdavCoroutineState::Yielded(WebdavYield::WantsWrite(bytes)) => {
32//!             stream.write_all(&bytes).unwrap();
33//!         }
34//!         WebdavCoroutineState::Yielded(WebdavYield::WantsRead) => {
35//!             let n = stream.read(&mut buf).unwrap();
36//!             arg = Some(&buf[..n]);
37//!         }
38//!         WebdavCoroutineState::Complete(Ok(calendars)) => break calendars,
39//!         WebdavCoroutineState::Complete(Err(err)) => panic!("{err}"),
40//!     }
41//! };
42//!
43//! println!("{} calendars", calendars.len());
44//! ```
45
46use alloc::{collections::BTreeSet, string::ToString};
47
48use log::trace;
49use url::Url;
50
51use crate::{
52    coroutine::*,
53    rfc4791::calendar::{
54        CALENDAR, CALENDAR_COLOR, CALENDAR_DESCRIPTION, CALENDAR_TIMEZONE, Calendar, LIST_PROPS,
55    },
56    rfc4918::{
57        DISPLAYNAME, GETCTAG, RESOURCETYPE, ResponseEntry, WebdavAuth, propfind::Propfind,
58        send::SendError, trace_unrecognized,
59    },
60    webdav_try,
61};
62
63/// Coroutine that lists calendars under `home_set_path`.
64#[derive(Debug)]
65pub struct ListCalendars {
66    state: State,
67}
68
69impl ListCalendars {
70    /// Builds a new `list-calendars` coroutine.
71    pub fn new(base_url: &Url, auth: &WebdavAuth, user_agent: &str, home_set_path: &str) -> Self {
72        let propfind = Propfind::new(base_url, auth, user_agent, home_set_path, 1, LIST_PROPS);
73        Self {
74            state: State::Propfind(propfind),
75        }
76    }
77}
78
79impl WebdavCoroutine for ListCalendars {
80    type Yield = WebdavYield;
81    type Return = Result<BTreeSet<Calendar>, SendError>;
82
83    fn resume(&mut self, arg: Option<&[u8]>) -> WebdavCoroutineState<Self::Yield, Self::Return> {
84        trace!("sending request");
85        match &mut self.state {
86            State::Propfind(propfind) => {
87                let multistatus = webdav_try!(propfind, arg);
88                let calendars = multistatus
89                    .responses
90                    .iter()
91                    .filter_map(from_entry)
92                    .collect();
93                WebdavCoroutineState::Complete(Ok(calendars))
94            }
95        }
96    }
97}
98
99fn from_entry(entry: &ResponseEntry) -> Option<Calendar> {
100    if !entry.has_resource_type(RESOURCETYPE, CALENDAR) {
101        trace!("skip non-calendar response {}", entry.href);
102        return None;
103    }
104
105    let id = entry.id();
106    if id.is_empty() {
107        return None;
108    }
109
110    trace_unrecognized(entry, LIST_PROPS);
111
112    Some(Calendar {
113        id: id.to_string(),
114        display_name: entry.text(DISPLAYNAME).map(ToString::to_string),
115        description: entry.text(CALENDAR_DESCRIPTION).map(ToString::to_string),
116        color: entry.text(CALENDAR_COLOR).map(ToString::to_string),
117        ctag: entry.text(GETCTAG).map(ToString::to_string),
118        tz: entry.text(CALENDAR_TIMEZONE).map(ToString::to_string),
119    })
120}
121
122#[derive(Debug)]
123enum State {
124    Propfind(Propfind),
125}