rustical_caldav/calendar_object/
service.rs

1use crate::{
2    CalDavConfig, CalDavPrincipalUri, Error,
3    calendar_object::{
4        methods::{get_event, put_event},
5        resource::CalendarObjectResource,
6    },
7};
8use async_trait::async_trait;
9use axum::{extract::Request, handler::Handler, response::Response};
10use futures_util::future::BoxFuture;
11use rustical_dav::resource::{AxumMethods, ResourceService};
12use rustical_store::{CalendarStore, auth::Principal};
13use serde::{Deserialize, Deserializer};
14use std::{convert::Infallible, sync::Arc};
15use tower::Service;
16
17#[derive(Debug, Clone, Deserialize)]
18pub struct CalendarObjectPathComponents {
19    pub principal: String,
20    pub calendar_id: String,
21    #[serde(deserialize_with = "deserialize_ics_name")]
22    pub object_id: String,
23}
24
25pub struct CalendarObjectResourceService<C: CalendarStore> {
26    pub(crate) cal_store: Arc<C>,
27    pub(crate) config: Arc<CalDavConfig>,
28}
29
30impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
31    fn clone(&self) -> Self {
32        Self {
33            cal_store: self.cal_store.clone(),
34            config: self.config.clone(),
35        }
36    }
37}
38
39impl<C: CalendarStore> CalendarObjectResourceService<C> {
40    pub const fn new(cal_store: Arc<C>, config: Arc<CalDavConfig>) -> Self {
41        Self { cal_store, config }
42    }
43}
44
45#[async_trait]
46impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
47    type PathComponents = CalendarObjectPathComponents;
48    type Resource = CalendarObjectResource;
49    type MemberType = CalendarObjectResource;
50    type Error = Error;
51    type Principal = Principal;
52    type PrincipalUri = CalDavPrincipalUri;
53
54    const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
55
56    async fn get_resource(
57        &self,
58        CalendarObjectPathComponents {
59            principal,
60            calendar_id,
61            object_id,
62        }: &Self::PathComponents,
63        show_deleted: bool,
64    ) -> Result<Self::Resource, Self::Error> {
65        let object = self
66            .cal_store
67            .get_object(principal, calendar_id, object_id, show_deleted)
68            .await?;
69        Ok(CalendarObjectResource {
70            object,
71            object_id: object_id.to_owned(),
72            principal: principal.to_owned(),
73        })
74    }
75
76    async fn delete_resource(
77        &self,
78        CalendarObjectPathComponents {
79            principal,
80            calendar_id,
81            object_id,
82        }: &Self::PathComponents,
83        use_trashbin: bool,
84    ) -> Result<(), Self::Error> {
85        self.cal_store
86            .delete_object(principal, calendar_id, object_id, use_trashbin)
87            .await?;
88        Ok(())
89    }
90}
91
92impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
93    fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
94        Some(|state, req| {
95            let mut service = Handler::with_state(get_event::<C>, state);
96            Box::pin(Service::call(&mut service, req))
97        })
98    }
99    fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
100        Some(|state, req| {
101            let mut service = Handler::with_state(put_event::<C>, state);
102            Box::pin(Service::call(&mut service, req))
103        })
104    }
105}
106
107fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
108where
109    D: Deserializer<'de>,
110{
111    let name: String = Deserialize::deserialize(deserializer)?;
112    name.strip_suffix(".ics").map_or_else(
113        || Err(serde::de::Error::custom("Missing .ics extension")),
114        |object_id| Ok(object_id.to_owned()),
115    )
116}