rustical_caldav/calendar_object/
resource.rs1use super::prop::{
2 CalendarData, CalendarObjectProp, CalendarObjectPropName, CalendarObjectPropWrapper,
3 CalendarObjectPropWrapperName,
4};
5use crate::Error;
6use caldata::generator::Emitter;
7use derive_more::derive::{From, Into};
8use rustical_dav::{
9 extensions::CommonPropertiesExtension,
10 privileges::UserPrivilegeSet,
11 resource::{PrincipalUri, Resource, ResourceName},
12 resourcetype,
13 xml::Resourcetype,
14};
15use rustical_ical::CalendarObject;
16use rustical_store::auth::Principal;
17use std::borrow::Cow;
18
19#[derive(Clone, From, Into)]
20pub struct CalendarObjectResource {
21 pub object: CalendarObject,
22 pub object_id: String,
23 pub principal: String,
24}
25
26impl ResourceName for CalendarObjectResource {
27 fn get_name(&self) -> Cow<'_, str> {
28 Cow::from(format!("{}.ics", self.object_id))
29 }
30}
31
32impl Resource for CalendarObjectResource {
33 type Prop = CalendarObjectPropWrapper;
34 type Error = Error;
35 type Principal = Principal;
36
37 fn is_collection(&self) -> bool {
38 false
39 }
40
41 fn get_resourcetype(&self) -> Resourcetype {
42 resourcetype!()
43 }
44
45 fn get_prop(
46 &self,
47 puri: &impl PrincipalUri,
48 user: &Principal,
49 prop: &CalendarObjectPropWrapperName,
50 ) -> Result<Self::Prop, Self::Error> {
51 Ok(match prop {
52 CalendarObjectPropWrapperName::CalendarObject(prop) => {
53 CalendarObjectPropWrapper::CalendarObject(match prop {
54 CalendarObjectPropName::Getetag => {
55 CalendarObjectProp::Getetag(self.object.get_etag())
56 }
57 CalendarObjectPropName::CalendarData(CalendarData { expand, .. }) => {
61 CalendarObjectProp::CalendarData(expand.as_ref().map_or_else(
62 || Ok::<_, Error>(self.object.get_ics().to_owned()),
63 |expand| {
64 Ok(self
65 .object
66 .get_inner()
67 .expand_recurrence(
68 Some(expand.start.to_utc()),
69 Some(expand.end.to_utc()),
70 )
71 .ok_or(Error::NotFound)?
72 .generate())
73 },
74 )?)
75 }
76 CalendarObjectPropName::Getcontenttype => {
77 CalendarObjectProp::Getcontenttype("text/calendar;charset=utf-8")
78 }
79 })
80 }
81 CalendarObjectPropWrapperName::Common(prop) => CalendarObjectPropWrapper::Common(
82 CommonPropertiesExtension::get_prop(self, puri, user, prop)?,
83 ),
84 })
85 }
86
87 fn get_displayname(&self) -> Option<&str> {
88 None
89 }
90
91 fn get_owner(&self) -> Option<&str> {
92 Some(&self.principal)
93 }
94
95 fn get_etag(&self) -> Option<String> {
96 Some(self.object.get_etag())
97 }
98
99 fn get_user_privileges(&self, user: &Principal) -> Result<UserPrivilegeSet, Self::Error> {
100 Ok(UserPrivilegeSet::owner_only(
101 user.is_principal(&self.principal),
102 ))
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use chrono::{TimeZone, Utc};
109 use rustical_dav::resource::Resource;
110 use rustical_ical::{CalendarObject, UtcDateTime};
111 use rustical_store::auth::Principal;
112
113 use crate::{
114 CalDavPrincipalUri, Error,
115 calendar_object::{
116 CalendarData, CalendarObjectPropName, CalendarObjectPropWrapperName, ExpandElement,
117 resource::CalendarObjectResource,
118 },
119 };
120
121 #[test]
122 fn test_expand_out_of_range() {
123 let resource = CalendarObjectResource {
124 principal: "user@rustical.dev".to_string(),
125 object_id: "asd".to_string(),
126 object: CalendarObject::from_ics(
127 r"BEGIN:VCALENDAR
128VERSION:2.0
129PRODID:-//Example Corp.//CalDAV Client//EN
130BEGIN:VEVENT
131UID:20010712T182145Z-123401@example.com
132DTSTAMP:20060712T182145Z
133DTSTART:20060714T170000Z
134RRULE:FREQ=YEARLY;COUNT=1
135DTEND:20060715T040000Z
136SUMMARY:Bastille Day Party
137END:VEVENT
138END:VCALENDAR"
139 .to_string(),
140 )
141 .unwrap(),
142 };
143 assert!(matches!(
144 resource.get_prop(
145 &CalDavPrincipalUri("/"),
146 &Principal {
147 id: "user@rustical.dev".to_string(),
148 displayname: None,
149 principal_type: rustical_store::auth::PrincipalType::Individual,
150 password: None,
151 memberships: vec![],
152 },
153 &CalendarObjectPropWrapperName::CalendarObject(
154 CalendarObjectPropName::CalendarData(CalendarData {
155 expand: Some(ExpandElement {
156 start: UtcDateTime(Utc.with_ymd_and_hms(1900, 1, 1, 0, 0, 0).unwrap()),
157 end: UtcDateTime(Utc.with_ymd_and_hms(1901, 1, 1, 0, 0, 0).unwrap()),
158 }),
159 ..Default::default()
160 },)
161 ),
162 ),
163 Err(Error::NotFound)
164 ));
165 }
166}