Skip to main content

io_webdav/rfc4791/item/
create.rs

1//! `create-item` coroutine: PUT raw iCalendar bytes against
2//! `<calendar>/<id>.ics`.
3//!
4//! Uses `If-None-Match: *` so the server rejects the PUT when a
5//! resource with the same id already exists (RFC 4791 ยง5.3.2).
6//!
7//! # Example
8//!
9//! ```rust,no_run
10//! use std::{
11//!     io::{Read, Write},
12//!     net::TcpStream,
13//! };
14//!
15//! use io_webdav::{
16//!     coroutine::{WebdavCoroutine, WebdavCoroutineState, WebdavYield},
17//!     rfc4791::item::create::CreateItem,
18//!     rfc4918::WebdavAuth,
19//! };
20//! use url::Url;
21//!
22//! // Ready stream needed (TCP-connected, TLS-negociated)
23//! let mut stream = TcpStream::connect("dav.example.org:443").unwrap();
24//! let mut buf = [0u8; 4096];
25//!
26//! let base_url: Url = "https://dav.example.org/".parse().unwrap();
27//! let auth = WebdavAuth::None;
28//! let ical = b"BEGIN:VCALENDAR\r\n...\r\nEND:VCALENDAR\r\n".to_vec();
29//! let mut coroutine =
30//!     CreateItem::new(&base_url, &auth, "io-webdav", "/dav/calendars/personal/", "event-1", ical);
31//! let mut arg = None;
32//!
33//! let created = loop {
34//!     match coroutine.resume(arg.take()) {
35//!         WebdavCoroutineState::Yielded(WebdavYield::WantsWrite(bytes)) => {
36//!             stream.write_all(&bytes).unwrap();
37//!         }
38//!         WebdavCoroutineState::Yielded(WebdavYield::WantsRead) => {
39//!             let n = stream.read(&mut buf).unwrap();
40//!             arg = Some(&buf[..n]);
41//!         }
42//!         WebdavCoroutineState::Complete(Ok(created)) => break created,
43//!         WebdavCoroutineState::Complete(Err(err)) => panic!("{err}"),
44//!     }
45//! };
46//!
47//! println!("created {} (etag {:?})", created.id, created.etag);
48//! ```
49
50use core::mem;
51
52use alloc::{
53    string::{String, ToString},
54    vec::Vec,
55};
56
57use log::trace;
58use url::Url;
59
60use crate::{
61    coroutine::*,
62    rfc4791::item::join_path,
63    rfc4918::{
64        WebdavAuth,
65        put::{Put, PutArgs},
66        read_etag,
67        send::{SendError, SendOk},
68    },
69    webdav_try,
70};
71
72/// Coroutine that creates a calendar item.
73#[derive(Debug)]
74pub struct CreateItem {
75    id: String,
76    state: State,
77}
78
79impl CreateItem {
80    /// Builds a new `create-item` coroutine targeting
81    /// `<calendar_path>/<id>.ics`.
82    pub fn new(
83        base_url: &Url,
84        auth: &WebdavAuth,
85        user_agent: &str,
86        calendar_path: &str,
87        id: &str,
88        ical: Vec<u8>,
89    ) -> Self {
90        let path = join_path(calendar_path, id);
91        let put = Put::new(PutArgs {
92            base_url,
93            auth,
94            user_agent,
95            path: &path,
96            content_type: "text/calendar; charset=utf-8",
97            body: ical,
98            if_match: None,
99            if_none_match: Some("*"),
100        });
101        Self {
102            id: id.to_string(),
103            state: State::Put(put),
104        }
105    }
106}
107
108impl WebdavCoroutine for CreateItem {
109    type Yield = WebdavYield;
110    type Return = Result<CreateItemOk, SendError>;
111
112    fn resume(&mut self, arg: Option<&[u8]>) -> WebdavCoroutineState<Self::Yield, Self::Return> {
113        trace!("sending request");
114        match &mut self.state {
115            State::Put(put) => {
116                let SendOk { response, .. } = webdav_try!(put, arg);
117                let etag = read_etag(&response);
118                let id = mem::take(&mut self.id);
119                WebdavCoroutineState::Complete(Ok(CreateItemOk { id, etag }))
120            }
121        }
122    }
123}
124
125#[derive(Debug)]
126enum State {
127    Put(Put),
128}
129
130/// Outcome of a successful
131/// [`CreateItem`] resume.
132#[derive(Clone, Debug)]
133pub struct CreateItemOk {
134    /// Item identifier (as supplied by the caller).
135    pub id: String,
136    /// Entity tag returned by the server, when present.
137    pub etag: Option<String>,
138}