Move properties into separate files

This commit is contained in:
Lennart
2025-06-09 21:09:46 +02:00
parent 0595920809
commit 71c2f8c019
35 changed files with 1023 additions and 948 deletions

View File

@@ -1,6 +1,6 @@
use crate::Error;
use crate::calendar::CalendarResourceService;
use crate::calendar::prop::SupportedCalendarComponentSet;
use crate::calendar::resource::CalendarResourceService;
use axum::extract::{Path, State};
use axum::response::{IntoResponse, Response};
use http::StatusCode;

View File

@@ -1,4 +1,4 @@
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
use rustical_dav::xml::PropfindType;
use rustical_ical::CalendarObject;
use rustical_store::CalendarStore;

View File

@@ -1,4 +1,4 @@
use crate::{Error, calendar_object::resource::CalendarObjectPropWrapperName};
use crate::{Error, calendar_object::CalendarObjectPropWrapperName};
use rustical_dav::xml::PropfindType;
use rustical_ical::{CalendarObject, UtcDateTime};
use rustical_store::{CalendarStore, calendar_store::CalendarQuery};

View File

@@ -1,8 +1,8 @@
use crate::{
CalDavPrincipalUri, Error,
calendar::resource::CalendarResourceService,
calendar_object::resource::{
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
calendar::CalendarResourceService,
calendar_object::{
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, resource::CalendarObjectResource,
},
};
use axum::{
@@ -147,7 +147,7 @@ pub async fn route_report_calendar<C: CalendarStore, S: SubscriptionStore>(
#[cfg(test)]
mod tests {
use super::*;
use crate::calendar_object::resource::{CalendarData, CalendarObjectPropName, ExpandElement};
use crate::calendar_object::{CalendarData, CalendarObjectPropName, ExpandElement};
use calendar_query::{CompFilterElement, FilterElement, TimeRangeElement};
use rustical_dav::xml::PropElement;
use rustical_ical::UtcDateTime;

View File

@@ -1,7 +1,7 @@
use crate::{
Error,
calendar_object::resource::{
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, CalendarObjectResource,
calendar_object::{
CalendarObjectPropWrapper, CalendarObjectPropWrapperName, resource::CalendarObjectResource,
},
};
use http::StatusCode;

View File

@@ -1,3 +1,6 @@
pub mod methods;
pub mod prop;
pub mod resource;
mod service;
pub use service::CalendarResourceService;

View File

@@ -1,32 +1,20 @@
use super::prop::{SupportedCalendarComponentSet, SupportedCalendarData, SupportedReportSet};
use crate::calendar::methods::mkcalendar::route_mkcalendar;
use crate::calendar::methods::report::route_report_calendar;
use crate::calendar_object::resource::{CalendarObjectResource, CalendarObjectResourceService};
use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use axum::extract::Request;
use axum::handler::Handler;
use axum::response::Response;
use crate::Error;
use chrono::{DateTime, Utc};
use derive_more::derive::{From, Into};
use futures_util::future::BoxFuture;
use rustical_dav::extensions::{
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
};
use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_dav_push::DavPushExtension;
use rustical_ical::CalDateTime;
use rustical_store::Calendar;
use rustical_store::auth::User;
use rustical_store::{Calendar, CalendarStore, SubscriptionStore};
use rustical_xml::{EnumVariants, PropName};
use rustical_xml::{XmlDeserialize, XmlSerialize};
use std::convert::Infallible;
use std::str::FromStr;
use std::sync::Arc;
use tower::Service;
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarPropName")]
@@ -317,113 +305,3 @@ impl Resource for CalendarResource {
))
}
}
pub struct CalendarResourceService<C: CalendarStore, S: SubscriptionStore> {
pub(crate) cal_store: Arc<C>,
pub(crate) sub_store: Arc<S>,
}
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarResourceService<C, S> {
fn clone(&self) -> Self {
Self {
cal_store: self.cal_store.clone(),
sub_store: self.sub_store.clone(),
}
}
}
impl<C: CalendarStore, S: SubscriptionStore> CalendarResourceService<C, S> {
pub fn new(cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
Self {
cal_store,
sub_store,
}
}
}
#[async_trait]
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourceService<C, S> {
type MemberType = CalendarObjectResource;
type PathComponents = (String, String); // principal, calendar_id
type Resource = CalendarResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
async fn get_resource(
&self,
(principal, cal_id): &Self::PathComponents,
) -> Result<Self::Resource, Error> {
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
Ok(CalendarResource {
cal: calendar,
read_only: self.cal_store.is_read_only(),
})
}
async fn get_members(
&self,
(principal, cal_id): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
Ok(self
.cal_store
.get_objects(principal, cal_id)
.await?
.into_iter()
.map(|object| CalendarObjectResource {
object,
principal: principal.to_owned(),
})
.collect())
}
async fn save_resource(
&self,
(principal, cal_id): &Self::PathComponents,
file: Self::Resource,
) -> Result<(), Self::Error> {
self.cal_store
.update_calendar(principal.to_owned(), cal_id.to_owned(), file.into())
.await?;
Ok(())
}
async fn delete_resource(
&self,
(principal, cal_id): &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.cal_store
.delete_calendar(principal, cal_id, use_trashbin)
.await?;
Ok(())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/{object_id}",
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(route_report_calendar::<C, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
{
Some(|state, req| {
let mut service = Handler::with_state(route_mkcalendar::<C, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
}

View File

@@ -0,0 +1,128 @@
use crate::calendar::methods::mkcalendar::route_mkcalendar;
use crate::calendar::methods::report::route_report_calendar;
use crate::calendar::resource::CalendarResource;
use crate::calendar_object::CalendarObjectResourceService;
use crate::calendar_object::resource::CalendarObjectResource;
use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use axum::extract::Request;
use axum::handler::Handler;
use axum::response::Response;
use futures_util::future::BoxFuture;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::auth::User;
use rustical_store::{CalendarStore, SubscriptionStore};
use std::convert::Infallible;
use std::sync::Arc;
use tower::Service;
pub struct CalendarResourceService<C: CalendarStore, S: SubscriptionStore> {
pub(crate) cal_store: Arc<C>,
pub(crate) sub_store: Arc<S>,
}
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarResourceService<C, S> {
fn clone(&self) -> Self {
Self {
cal_store: self.cal_store.clone(),
sub_store: self.sub_store.clone(),
}
}
}
impl<C: CalendarStore, S: SubscriptionStore> CalendarResourceService<C, S> {
pub fn new(cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
Self {
cal_store,
sub_store,
}
}
}
#[async_trait]
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarResourceService<C, S> {
type MemberType = CalendarObjectResource;
type PathComponents = (String, String); // principal, calendar_id
type Resource = CalendarResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
async fn get_resource(
&self,
(principal, cal_id): &Self::PathComponents,
) -> Result<Self::Resource, Error> {
let calendar = self.cal_store.get_calendar(principal, cal_id).await?;
Ok(CalendarResource {
cal: calendar,
read_only: self.cal_store.is_read_only(),
})
}
async fn get_members(
&self,
(principal, cal_id): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
Ok(self
.cal_store
.get_objects(principal, cal_id)
.await?
.into_iter()
.map(|object| CalendarObjectResource {
object,
principal: principal.to_owned(),
})
.collect())
}
async fn save_resource(
&self,
(principal, cal_id): &Self::PathComponents,
file: Self::Resource,
) -> Result<(), Self::Error> {
self.cal_store
.update_calendar(principal.to_owned(), cal_id.to_owned(), file.into())
.await?;
Ok(())
}
async fn delete_resource(
&self,
(principal, cal_id): &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.cal_store
.delete_calendar(principal, cal_id, use_trashbin)
.await?;
Ok(())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/{object_id}",
CalendarObjectResourceService::new(self.cal_store.clone()).axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarResourceService<C, S> {
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(route_report_calendar::<C, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn mkcalendar() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>>
{
Some(|state, req| {
let mut service = Handler::with_state(route_mkcalendar::<C, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
}

View File

@@ -1,6 +1,5 @@
use super::resource::CalendarObjectPathComponents;
use crate::Error;
use crate::calendar_object::resource::CalendarObjectResourceService;
use crate::calendar_object::{CalendarObjectPathComponents, CalendarObjectResourceService};
use crate::error::Precondition;
use axum::body::Body;
use axum::extract::{Path, State};

View File

@@ -1,2 +1,6 @@
pub mod methods;
pub mod resource;
mod service;
pub use service::*;
mod prop;
pub use prop::*;

View File

@@ -0,0 +1,45 @@
use rustical_dav::extensions::CommonPropertiesProp;
use rustical_ical::UtcDateTime;
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarObjectPropName")]
pub enum CalendarObjectProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Getetag(String),
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
Getcontenttype(&'static str),
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
#[xml(prop = "CalendarData")]
CalendarData(String),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
pub enum CalendarObjectPropWrapper {
CalendarObject(CalendarObjectProp),
Common(CommonPropertiesProp),
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ExpandElement {
#[xml(ty = "attr")]
pub(crate) start: UtcDateTime,
#[xml(ty = "attr")]
pub(crate) end: UtcDateTime,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
pub struct CalendarData {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) comp: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) expand: Option<ExpandElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) limit_recurrence_set: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) limit_freebusy_set: Option<()>,
}

View File

@@ -1,66 +1,14 @@
// use super::methods::{get_event, put_event};
use crate::{
CalDavPrincipalUri, Error,
calendar_object::methods::{get_event, put_event},
};
use async_trait::async_trait;
use axum::{extract::Request, handler::Handler, response::Response};
use super::prop::*;
use crate::Error;
use derive_more::derive::{From, Into};
use futures_util::future::BoxFuture;
use rustical_dav::{
extensions::{CommonPropertiesExtension, CommonPropertiesProp},
extensions::CommonPropertiesExtension,
privileges::UserPrivilegeSet,
resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService},
resource::{PrincipalUri, Resource, ResourceName},
xml::Resourcetype,
};
use rustical_ical::{CalendarObject, UtcDateTime};
use rustical_store::{CalendarStore, auth::User};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use serde::{Deserialize, Deserializer};
use std::{convert::Infallible, sync::Arc};
use tower::Service;
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Eq, Hash)]
pub(crate) struct ExpandElement {
#[xml(ty = "attr")]
pub(crate) start: UtcDateTime,
#[xml(ty = "attr")]
pub(crate) end: UtcDateTime,
}
#[derive(XmlDeserialize, Clone, Debug, PartialEq, Default, Eq, Hash)]
pub struct CalendarData {
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) comp: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) expand: Option<ExpandElement>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) limit_recurrence_set: Option<()>,
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
pub(crate) limit_freebusy_set: Option<()>,
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarObjectPropName")]
pub enum CalendarObjectProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Getetag(String),
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
Getcontenttype(&'static str),
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
#[xml(prop = "CalendarData")]
CalendarData(String),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "CalendarObjectPropWrapperName", untagged)]
pub enum CalendarObjectPropWrapper {
CalendarObject(CalendarObjectProp),
Common(CommonPropertiesProp),
}
use rustical_ical::CalendarObject;
use rustical_store::auth::User;
#[derive(Clone, From, Into)]
pub struct CalendarObjectResource {
@@ -130,101 +78,3 @@ impl Resource for CalendarObjectResource {
))
}
}
fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let name: String = Deserialize::deserialize(deserializer)?;
if let Some(object_id) = name.strip_suffix(".ics") {
Ok(object_id.to_owned())
} else {
Err(serde::de::Error::custom("Missing .ics extension"))
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct CalendarObjectPathComponents {
pub principal: String,
pub calendar_id: String,
#[serde(deserialize_with = "deserialize_ics_name")]
pub object_id: String,
}
pub struct CalendarObjectResourceService<C: CalendarStore> {
pub(crate) cal_store: Arc<C>,
}
impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
fn clone(&self) -> Self {
Self {
cal_store: self.cal_store.clone(),
}
}
}
impl<C: CalendarStore> CalendarObjectResourceService<C> {
pub fn new(cal_store: Arc<C>) -> Self {
Self { cal_store }
}
}
#[async_trait]
impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
type PathComponents = CalendarObjectPathComponents;
type Resource = CalendarObjectResource;
type MemberType = CalendarObjectResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
async fn get_resource(
&self,
CalendarObjectPathComponents {
principal,
calendar_id,
object_id,
}: &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let object = self
.cal_store
.get_object(principal, calendar_id, object_id)
.await?;
Ok(CalendarObjectResource {
object,
principal: principal.to_owned(),
})
}
async fn delete_resource(
&self,
CalendarObjectPathComponents {
principal,
calendar_id,
object_id,
}: &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.cal_store
.delete_object(principal, calendar_id, object_id, use_trashbin)
.await?;
Ok(())
}
}
impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(get_event::<C>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(put_event::<C>, state);
Box::pin(Service::call(&mut service, req))
})
}
}

View File

@@ -0,0 +1,113 @@
use crate::{
CalDavPrincipalUri, Error,
calendar_object::{
methods::{get_event, put_event},
resource::CalendarObjectResource,
},
};
use async_trait::async_trait;
use axum::{extract::Request, handler::Handler, response::Response};
use futures_util::future::BoxFuture;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::{CalendarStore, auth::User};
use serde::{Deserialize, Deserializer};
use std::{convert::Infallible, sync::Arc};
use tower::Service;
#[derive(Debug, Clone, Deserialize)]
pub struct CalendarObjectPathComponents {
pub principal: String,
pub calendar_id: String,
#[serde(deserialize_with = "deserialize_ics_name")]
pub object_id: String,
}
pub struct CalendarObjectResourceService<C: CalendarStore> {
pub(crate) cal_store: Arc<C>,
}
impl<C: CalendarStore> Clone for CalendarObjectResourceService<C> {
fn clone(&self) -> Self {
Self {
cal_store: self.cal_store.clone(),
}
}
}
impl<C: CalendarStore> CalendarObjectResourceService<C> {
pub fn new(cal_store: Arc<C>) -> Self {
Self { cal_store }
}
}
#[async_trait]
impl<C: CalendarStore> ResourceService for CalendarObjectResourceService<C> {
type PathComponents = CalendarObjectPathComponents;
type Resource = CalendarObjectResource;
type MemberType = CalendarObjectResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
async fn get_resource(
&self,
CalendarObjectPathComponents {
principal,
calendar_id,
object_id,
}: &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let object = self
.cal_store
.get_object(principal, calendar_id, object_id)
.await?;
Ok(CalendarObjectResource {
object,
principal: principal.to_owned(),
})
}
async fn delete_resource(
&self,
CalendarObjectPathComponents {
principal,
calendar_id,
object_id,
}: &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.cal_store
.delete_object(principal, calendar_id, object_id, use_trashbin)
.await?;
Ok(())
}
}
impl<C: CalendarStore> AxumMethods for CalendarObjectResourceService<C> {
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(get_event::<C>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(put_event::<C>, state);
Box::pin(Service::call(&mut service, req))
})
}
}
fn deserialize_ics_name<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let name: String = Deserialize::deserialize(deserializer)?;
if let Some(object_id) = name.strip_suffix(".ics") {
Ok(object_id.to_owned())
} else {
Err(serde::de::Error::custom("Missing .ics extension"))
}
}

View File

@@ -1,15 +1,14 @@
use crate::calendar::resource::{CalendarResource, CalendarResourceService};
use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use crate::Error;
use rustical_dav::extensions::CommonPropertiesExtension;
use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
use rustical_store::auth::User;
use rustical_store::{CalendarStore, SubscriptionStore};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
mod service;
pub use service::*;
mod prop;
pub use prop::*;
#[derive(Clone)]
pub struct CalendarSetResource {
@@ -24,12 +23,6 @@ impl ResourceName for CalendarSetResource {
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Common(CommonPropertiesProp),
}
impl Resource for CalendarSetResource {
type Prop = PrincipalPropWrapper;
type Error = Error;
@@ -67,77 +60,3 @@ impl Resource for CalendarSetResource {
})
}
}
pub struct CalendarSetResourceService<C: CalendarStore, S: SubscriptionStore> {
name: &'static str,
cal_store: Arc<C>,
sub_store: Arc<S>,
}
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarSetResourceService<C, S> {
fn clone(&self) -> Self {
Self {
name: self.name,
cal_store: self.cal_store.clone(),
sub_store: self.sub_store.clone(),
}
}
}
impl<C: CalendarStore, S: SubscriptionStore> CalendarSetResourceService<C, S> {
pub fn new(name: &'static str, cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
Self {
name,
cal_store,
sub_store,
}
}
}
#[async_trait]
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetResourceService<C, S> {
type PathComponents = (String,);
type MemberType = CalendarResource;
type Resource = CalendarSetResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
async fn get_resource(
&self,
(principal,): &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
Ok(CalendarSetResource {
principal: principal.to_owned(),
read_only: self.cal_store.is_read_only(),
name: self.name,
})
}
async fn get_members(
&self,
(principal,): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
let calendars = self.cal_store.get_calendars(principal).await?;
Ok(calendars
.into_iter()
.map(|cal| CalendarResource {
cal,
read_only: self.cal_store.is_read_only(),
})
.collect())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/{calendar_id}",
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
.axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}

View File

@@ -0,0 +1,8 @@
use rustical_dav::extensions::CommonPropertiesProp;
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Common(CommonPropertiesProp),
}

View File

@@ -0,0 +1,84 @@
use crate::calendar::CalendarResourceService;
use crate::calendar::resource::CalendarResource;
use crate::calendar_set::CalendarSetResource;
use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::auth::User;
use rustical_store::{CalendarStore, SubscriptionStore};
use std::sync::Arc;
pub struct CalendarSetResourceService<C: CalendarStore, S: SubscriptionStore> {
name: &'static str,
cal_store: Arc<C>,
sub_store: Arc<S>,
}
impl<C: CalendarStore, S: SubscriptionStore> Clone for CalendarSetResourceService<C, S> {
fn clone(&self) -> Self {
Self {
name: self.name,
cal_store: self.cal_store.clone(),
sub_store: self.sub_store.clone(),
}
}
}
impl<C: CalendarStore, S: SubscriptionStore> CalendarSetResourceService<C, S> {
pub fn new(name: &'static str, cal_store: Arc<C>, sub_store: Arc<S>) -> Self {
Self {
name,
cal_store,
sub_store,
}
}
}
#[async_trait]
impl<C: CalendarStore, S: SubscriptionStore> ResourceService for CalendarSetResourceService<C, S> {
type PathComponents = (String,);
type MemberType = CalendarResource;
type Resource = CalendarSetResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, extended-mkcol, calendar-access";
async fn get_resource(
&self,
(principal,): &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
Ok(CalendarSetResource {
principal: principal.to_owned(),
read_only: self.cal_store.is_read_only(),
name: self.name,
})
}
async fn get_members(
&self,
(principal,): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
let calendars = self.cal_store.get_calendars(principal).await?;
Ok(calendars
.into_iter()
.map(|cal| CalendarResource {
cal,
read_only: self.cal_store.is_read_only(),
})
.collect())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/{calendar_id}",
CalendarResourceService::new(self.cal_store.clone(), self.sub_store.clone())
.axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<C: CalendarStore, S: SubscriptionStore> AxumMethods for CalendarSetResourceService<C, S> {}

View File

@@ -1,16 +1,14 @@
use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use crate::Error;
use rustical_dav::extensions::CommonPropertiesExtension;
use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::user::PrincipalType;
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_store::{CalendarStore, SubscriptionStore};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
use rustical_store::auth::User;
mod service;
pub use service::*;
mod prop;
pub use prop::*;
#[derive(Clone)]
pub struct PrincipalResource {
@@ -24,37 +22,6 @@ impl ResourceName for PrincipalResource {
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(String),
// Scheduling Extensions to CalDAV (RFC 6638)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)]
CalendarUserType(PrincipalType),
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarUserAddressSet(HrefElement),
// WebDAV Access Control (RFC 3744)
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
PrincipalUrl(HrefElement),
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarHomeSet(CalendarHomeSet),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Principal(PrincipalProp),
Common(CommonPropertiesProp),
}
impl Resource for PrincipalResource {
type Prop = PrincipalPropWrapper;
type Error = Error;
@@ -124,104 +91,3 @@ impl Resource for PrincipalResource {
))
}
}
#[derive(Debug)]
pub struct PrincipalResourceService<
AP: AuthenticationProvider,
S: SubscriptionStore,
CS: CalendarStore,
BS: CalendarStore,
> {
pub(crate) auth_provider: Arc<AP>,
pub(crate) sub_store: Arc<S>,
pub(crate) cal_store: Arc<CS>,
pub(crate) birthday_store: Arc<BS>,
}
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> Clone
for PrincipalResourceService<AP, S, CS, BS>
{
fn clone(&self) -> Self {
Self {
auth_provider: self.auth_provider.clone(),
sub_store: self.sub_store.clone(),
cal_store: self.cal_store.clone(),
birthday_store: self.birthday_store.clone(),
}
}
}
#[async_trait]
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
ResourceService for PrincipalResourceService<AP, S, CS, BS>
{
type PathComponents = (String,);
type MemberType = CalendarSetResource;
type Resource = PrincipalResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
async fn get_resource(
&self,
(principal,): &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let user = self
.auth_provider
.get_principal(principal)
.await?
.ok_or(crate::Error::NotFound)?;
Ok(PrincipalResource {
principal: user,
home_set: &["calendar", "birthdays"],
})
}
async fn get_members(
&self,
(principal,): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
Ok(vec![
CalendarSetResource {
name: "calendar",
principal: principal.to_owned(),
read_only: false,
},
CalendarSetResource {
name: "birthdays",
principal: principal.to_owned(),
read_only: true,
},
])
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/calendar",
CalendarSetResourceService::new(
"calendar",
self.cal_store.clone(),
self.sub_store.clone(),
)
.axum_router(),
)
.nest(
"/birthdays",
CalendarSetResourceService::new(
"birthdays",
self.birthday_store.clone(),
self.sub_store.clone(),
)
.axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
AxumMethods for PrincipalResourceService<AP, S, CS, BS>
{
}

View File

@@ -0,0 +1,34 @@
use rustical_dav::{extensions::CommonPropertiesProp, xml::HrefElement};
use rustical_store::auth::user::PrincipalType;
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(String),
// Scheduling Extensions to CalDAV (RFC 6638)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV", skip_deserializing)]
CalendarUserType(PrincipalType),
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarUserAddressSet(HrefElement),
// WebDAV Access Control (RFC 3744)
#[xml(ns = "rustical_dav::namespace::NS_DAV", rename = b"principal-URL")]
PrincipalUrl(HrefElement),
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CALDAV")]
CalendarHomeSet(CalendarHomeSet),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Principal(PrincipalProp),
Common(CommonPropertiesProp),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
pub struct CalendarHomeSet(#[xml(ty = "untagged", flatten)] pub(super) Vec<HrefElement>);

View File

@@ -0,0 +1,110 @@
use crate::calendar_set::{CalendarSetResource, CalendarSetResourceService};
use crate::principal::PrincipalResource;
use crate::{CalDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_store::{CalendarStore, SubscriptionStore};
use std::sync::Arc;
#[derive(Debug)]
pub struct PrincipalResourceService<
AP: AuthenticationProvider,
S: SubscriptionStore,
CS: CalendarStore,
BS: CalendarStore,
> {
pub(crate) auth_provider: Arc<AP>,
pub(crate) sub_store: Arc<S>,
pub(crate) cal_store: Arc<CS>,
pub(crate) birthday_store: Arc<BS>,
}
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore> Clone
for PrincipalResourceService<AP, S, CS, BS>
{
fn clone(&self) -> Self {
Self {
auth_provider: self.auth_provider.clone(),
sub_store: self.sub_store.clone(),
cal_store: self.cal_store.clone(),
birthday_store: self.birthday_store.clone(),
}
}
}
#[async_trait]
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
ResourceService for PrincipalResourceService<AP, S, CS, BS>
{
type PathComponents = (String,);
type MemberType = CalendarSetResource;
type Resource = PrincipalResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CalDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, calendar-access";
async fn get_resource(
&self,
(principal,): &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let user = self
.auth_provider
.get_principal(principal)
.await?
.ok_or(crate::Error::NotFound)?;
Ok(PrincipalResource {
principal: user,
home_set: &["calendar", "birthdays"],
})
}
async fn get_members(
&self,
(principal,): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
Ok(vec![
CalendarSetResource {
name: "calendar",
principal: principal.to_owned(),
read_only: false,
},
CalendarSetResource {
name: "birthdays",
principal: principal.to_owned(),
read_only: true,
},
])
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> axum::Router<State> {
Router::new()
.nest(
"/calendar",
CalendarSetResourceService::new(
"calendar",
self.cal_store.clone(),
self.sub_store.clone(),
)
.axum_router(),
)
.nest(
"/birthdays",
CalendarSetResourceService::new(
"birthdays",
self.birthday_store.clone(),
self.sub_store.clone(),
)
.axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<AP: AuthenticationProvider, S: SubscriptionStore, CS: CalendarStore, BS: CalendarStore>
AxumMethods for PrincipalResourceService<AP, S, CS, BS>
{
}

View File

@@ -1,8 +1,6 @@
use std::str::FromStr;
use super::resource::AddressObjectPathComponents;
use super::AddressObjectPathComponents;
use super::AddressObjectResourceService;
use crate::Error;
use crate::address_object::resource::AddressObjectResourceService;
use crate::addressbook::resource::AddressbookResource;
use axum::body::Body;
use axum::extract::{Path, State};
@@ -15,6 +13,7 @@ use rustical_dav::resource::Resource;
use rustical_ical::AddressObject;
use rustical_store::AddressbookStore;
use rustical_store::auth::User;
use std::str::FromStr;
use tracing::instrument;
#[instrument(skip(addr_store))]

View File

@@ -1,2 +1,6 @@
pub mod methods;
pub mod resource;
mod service;
pub use service::*;
mod prop;
pub use prop::*;

View File

@@ -0,0 +1,23 @@
use rustical_dav::extensions::CommonPropertiesProp;
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressObjectPropName")]
pub enum AddressObjectProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Getetag(String),
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
Getcontenttype(&'static str),
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
AddressData(String),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
pub enum AddressObjectPropWrapper {
AddressObject(AddressObjectProp),
Common(CommonPropertiesProp),
}

View File

@@ -1,56 +1,19 @@
use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::{extract::Request, handler::Handler, response::Response};
use derive_more::derive::{Constructor, From, Into};
use futures_util::future::BoxFuture;
use crate::{
Error,
address_object::{
AddressObjectProp, AddressObjectPropName, AddressObjectPropWrapper,
AddressObjectPropWrapperName,
},
};
use derive_more::derive::{From, Into};
use rustical_dav::{
extensions::{CommonPropertiesExtension, CommonPropertiesProp},
extensions::CommonPropertiesExtension,
privileges::UserPrivilegeSet,
resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService},
resource::{PrincipalUri, Resource, ResourceName},
xml::Resourcetype,
};
use rustical_ical::AddressObject;
use rustical_store::{AddressbookStore, auth::User};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use serde::{Deserialize, Deserializer};
use std::{convert::Infallible, sync::Arc};
use tower::Service;
use super::methods::{get_object, put_object};
#[derive(Constructor)]
pub struct AddressObjectResourceService<AS: AddressbookStore> {
pub(crate) addr_store: Arc<AS>,
}
impl<AS: AddressbookStore> Clone for AddressObjectResourceService<AS> {
fn clone(&self) -> Self {
Self {
addr_store: self.addr_store.clone(),
}
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressObjectPropName")]
pub enum AddressObjectProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Getetag(String),
#[xml(ns = "rustical_dav::namespace::NS_DAV", skip_deserializing)]
Getcontenttype(&'static str),
// CalDAV (RFC 4791)
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
AddressData(String),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressObjectPropWrapperName", untagged)]
pub enum AddressObjectPropWrapper {
AddressObject(AddressObjectProp),
Common(CommonPropertiesProp),
}
use rustical_store::auth::User;
#[derive(Clone, From, Into)]
pub struct AddressObjectResource {
@@ -113,84 +76,3 @@ impl Resource for AddressObjectResource {
))
}
}
fn deserialize_vcf_name<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let name: String = Deserialize::deserialize(deserializer)?;
if let Some(object_id) = name.strip_suffix(".vcf") {
Ok(object_id.to_owned())
} else {
Err(serde::de::Error::custom("Missing .vcf extension"))
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct AddressObjectPathComponents {
pub principal: String,
pub addressbook_id: String,
#[serde(deserialize_with = "deserialize_vcf_name")]
pub object_id: String,
}
#[async_trait]
impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS> {
type PathComponents = AddressObjectPathComponents;
type Resource = AddressObjectResource;
type MemberType = AddressObjectResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CardDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
async fn get_resource(
&self,
AddressObjectPathComponents {
principal,
addressbook_id,
object_id,
}: &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let object = self
.addr_store
.get_object(principal, addressbook_id, object_id, false)
.await?;
Ok(AddressObjectResource {
object,
principal: principal.to_owned(),
})
}
async fn delete_resource(
&self,
AddressObjectPathComponents {
principal,
addressbook_id,
object_id,
}: &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.addr_store
.delete_object(principal, addressbook_id, object_id, use_trashbin)
.await?;
Ok(())
}
}
impl<AS: AddressbookStore> AxumMethods for AddressObjectResourceService<AS> {
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(get_object::<AS>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(put_object::<AS>, state);
Box::pin(Service::call(&mut service, req))
})
}
}

View File

@@ -0,0 +1,105 @@
use super::methods::{get_object, put_object};
use crate::{CardDavPrincipalUri, Error, address_object::resource::AddressObjectResource};
use async_trait::async_trait;
use axum::{extract::Request, handler::Handler, response::Response};
use derive_more::derive::Constructor;
use futures_util::future::BoxFuture;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::{AddressbookStore, auth::User};
use serde::{Deserialize, Deserializer};
use std::{convert::Infallible, sync::Arc};
use tower::Service;
#[derive(Constructor)]
pub struct AddressObjectResourceService<AS: AddressbookStore> {
pub(crate) addr_store: Arc<AS>,
}
impl<AS: AddressbookStore> Clone for AddressObjectResourceService<AS> {
fn clone(&self) -> Self {
Self {
addr_store: self.addr_store.clone(),
}
}
}
#[derive(Debug, Clone, Deserialize)]
pub struct AddressObjectPathComponents {
pub principal: String,
pub addressbook_id: String,
#[serde(deserialize_with = "deserialize_vcf_name")]
pub object_id: String,
}
#[async_trait]
impl<AS: AddressbookStore> ResourceService for AddressObjectResourceService<AS> {
type PathComponents = AddressObjectPathComponents;
type Resource = AddressObjectResource;
type MemberType = AddressObjectResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CardDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
async fn get_resource(
&self,
AddressObjectPathComponents {
principal,
addressbook_id,
object_id,
}: &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let object = self
.addr_store
.get_object(principal, addressbook_id, object_id, false)
.await?;
Ok(AddressObjectResource {
object,
principal: principal.to_owned(),
})
}
async fn delete_resource(
&self,
AddressObjectPathComponents {
principal,
addressbook_id,
object_id,
}: &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.addr_store
.delete_object(principal, addressbook_id, object_id, use_trashbin)
.await?;
Ok(())
}
}
impl<AS: AddressbookStore> AxumMethods for AddressObjectResourceService<AS> {
fn get() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(get_object::<AS>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn put() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(put_object::<AS>, state);
Box::pin(Service::call(&mut service, req))
})
}
}
fn deserialize_vcf_name<'de, D>(deserializer: D) -> Result<String, D::Error>
where
D: Deserializer<'de>,
{
let name: String = Deserialize::deserialize(deserializer)?;
if let Some(object_id) = name.strip_suffix(".vcf") {
Ok(object_id.to_owned())
} else {
Err(serde::de::Error::custom("Missing .vcf extension"))
}
}

View File

@@ -1,4 +1,4 @@
use crate::{Error, addressbook::resource::AddressbookResourceService};
use crate::{Error, addressbook::AddressbookResourceService};
use axum::{
extract::{Path, State},
response::{IntoResponse, Response},

View File

@@ -1,7 +1,7 @@
use crate::{
Error,
address_object::resource::{
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
address_object::{
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
},
};
use http::StatusCode;

View File

@@ -1,6 +1,6 @@
use crate::{
CardDavPrincipalUri, Error, address_object::resource::AddressObjectPropWrapperName,
addressbook::resource::AddressbookResourceService,
CardDavPrincipalUri, Error, address_object::AddressObjectPropWrapperName,
addressbook::AddressbookResourceService,
};
use addressbook_multiget::{AddressbookMultigetRequest, handle_addressbook_multiget};
use axum::{
@@ -81,7 +81,7 @@ pub async fn route_report_addressbook<AS: AddressbookStore, S: SubscriptionStore
#[cfg(test)]
mod tests {
use super::*;
use crate::address_object::resource::AddressObjectPropName;
use crate::address_object::AddressObjectPropName;
use rustical_dav::xml::{PropElement, sync_collection::SyncLevel};
#[test]

View File

@@ -1,7 +1,7 @@
use crate::{
Error,
address_object::resource::{
AddressObjectPropWrapper, AddressObjectPropWrapperName, AddressObjectResource,
address_object::{
AddressObjectPropWrapper, AddressObjectPropWrapperName, resource::AddressObjectResource,
},
};
use http::StatusCode;

View File

@@ -1,3 +1,5 @@
pub mod methods;
pub mod prop;
pub mod resource;
mod service;
pub use service::*;

View File

@@ -1,4 +1,33 @@
use rustical_xml::XmlSerialize;
use rustical_dav::extensions::{CommonPropertiesProp, SyncTokenExtensionProp};
use rustical_dav_push::DavPushExtensionProp;
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressbookPropName")]
pub enum AddressbookProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(Option<String>),
// CardDAV (RFC 6352)
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
AddressbookDescription(Option<String>),
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
SupportedAddressData(SupportedAddressData),
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
SupportedReportSet(SupportedReportSet),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
MaxResourceSize(i64),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
pub enum AddressbookPropWrapper {
Addressbook(AddressbookProp),
SyncToken(SyncTokenExtensionProp),
DavPush(DavPushExtensionProp),
Common(CommonPropertiesProp),
}
#[derive(Debug, Clone, XmlSerialize, PartialEq)]
pub struct AddressDataType {

View File

@@ -1,78 +1,16 @@
use super::methods::mkcol::route_mkcol;
use super::methods::report::route_report_addressbook;
use super::prop::{SupportedAddressData, SupportedReportSet};
use crate::address_object::resource::{AddressObjectResource, AddressObjectResourceService};
use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use axum::extract::Request;
use axum::handler::Handler;
use axum::response::Response;
use derive_more::derive::{From, Into};
use futures_util::future::BoxFuture;
use rustical_dav::extensions::{
CommonPropertiesExtension, CommonPropertiesProp, SyncTokenExtension, SyncTokenExtensionProp,
use crate::Error;
use crate::addressbook::prop::{
AddressbookProp, AddressbookPropName, AddressbookPropWrapper, AddressbookPropWrapperName,
};
use derive_more::derive::{From, Into};
use rustical_dav::extensions::{CommonPropertiesExtension, SyncTokenExtension};
use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
use rustical_dav::xml::{Resourcetype, ResourcetypeInner};
use rustical_dav_push::{DavPushExtension, DavPushExtensionProp};
use rustical_dav_push::DavPushExtension;
use rustical_store::Addressbook;
use rustical_store::auth::User;
use rustical_store::{Addressbook, AddressbookStore, SubscriptionStore};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use std::convert::Infallible;
use std::sync::Arc;
use tower::Service;
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
pub(crate) addr_store: Arc<AS>,
pub(crate) sub_store: Arc<S>,
}
impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S> {
pub fn new(addr_store: Arc<A>, sub_store: Arc<S>) -> Self {
Self {
addr_store,
sub_store,
}
}
}
impl<A: AddressbookStore, S: SubscriptionStore> Clone for AddressbookResourceService<A, S> {
fn clone(&self) -> Self {
Self {
addr_store: self.addr_store.clone(),
sub_store: self.sub_store.clone(),
}
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressbookPropName")]
pub enum AddressbookProp {
// WebDAV (RFC 2518)
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(Option<String>),
// CardDAV (RFC 6352)
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
AddressbookDescription(Option<String>),
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
SupportedAddressData(SupportedAddressData),
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV", skip_deserializing)]
SupportedReportSet(SupportedReportSet),
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
MaxResourceSize(i64),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "AddressbookPropWrapperName", untagged)]
pub enum AddressbookPropWrapper {
Addressbook(AddressbookProp),
SyncToken(SyncTokenExtensionProp),
DavPush(DavPushExtensionProp),
Common(CommonPropertiesProp),
}
#[derive(Clone, Debug, From, Into)]
pub struct AddressbookResource(pub(crate) Addressbook);
@@ -205,92 +143,3 @@ impl Resource for AddressbookResource {
))
}
}
#[async_trait]
impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
for AddressbookResourceService<AS, S>
{
type MemberType = AddressObjectResource;
type PathComponents = (String, String); // principal, addressbook_id
type Resource = AddressbookResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CardDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
async fn get_resource(
&self,
(principal, addressbook_id): &Self::PathComponents,
) -> Result<Self::Resource, Error> {
let addressbook = self
.addr_store
.get_addressbook(principal, addressbook_id, false)
.await
.map_err(|_e| Error::NotFound)?;
Ok(addressbook.into())
}
async fn get_members(
&self,
(principal, addressbook_id): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
Ok(self
.addr_store
.get_objects(principal, addressbook_id)
.await?
.into_iter()
.map(|object| AddressObjectResource {
object,
principal: principal.to_owned(),
})
.collect())
}
async fn save_resource(
&self,
(principal, addressbook_id): &Self::PathComponents,
file: Self::Resource,
) -> Result<(), Self::Error> {
self.addr_store
.update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into())
.await?;
Ok(())
}
async fn delete_resource(
&self,
(principal, addressbook_id): &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.addr_store
.delete_addressbook(principal, addressbook_id, use_trashbin)
.await?;
Ok(())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
Router::new()
.nest(
"/{object_id}",
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(route_report_addressbook::<AS, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn mkcol() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(route_mkcol::<AS, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
}

View File

@@ -0,0 +1,130 @@
use super::methods::mkcol::route_mkcol;
use super::methods::report::route_report_addressbook;
use crate::address_object::AddressObjectResourceService;
use crate::address_object::resource::AddressObjectResource;
use crate::addressbook::resource::AddressbookResource;
use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use axum::extract::Request;
use axum::handler::Handler;
use axum::response::Response;
use futures_util::future::BoxFuture;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::auth::User;
use rustical_store::{AddressbookStore, SubscriptionStore};
use std::convert::Infallible;
use std::sync::Arc;
use tower::Service;
pub struct AddressbookResourceService<AS: AddressbookStore, S: SubscriptionStore> {
pub(crate) addr_store: Arc<AS>,
pub(crate) sub_store: Arc<S>,
}
impl<A: AddressbookStore, S: SubscriptionStore> AddressbookResourceService<A, S> {
pub fn new(addr_store: Arc<A>, sub_store: Arc<S>) -> Self {
Self {
addr_store,
sub_store,
}
}
}
impl<A: AddressbookStore, S: SubscriptionStore> Clone for AddressbookResourceService<A, S> {
fn clone(&self) -> Self {
Self {
addr_store: self.addr_store.clone(),
sub_store: self.sub_store.clone(),
}
}
}
#[async_trait]
impl<AS: AddressbookStore, S: SubscriptionStore> ResourceService
for AddressbookResourceService<AS, S>
{
type MemberType = AddressObjectResource;
type PathComponents = (String, String); // principal, addressbook_id
type Resource = AddressbookResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CardDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
async fn get_resource(
&self,
(principal, addressbook_id): &Self::PathComponents,
) -> Result<Self::Resource, Error> {
let addressbook = self
.addr_store
.get_addressbook(principal, addressbook_id, false)
.await
.map_err(|_e| Error::NotFound)?;
Ok(addressbook.into())
}
async fn get_members(
&self,
(principal, addressbook_id): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
Ok(self
.addr_store
.get_objects(principal, addressbook_id)
.await?
.into_iter()
.map(|object| AddressObjectResource {
object,
principal: principal.to_owned(),
})
.collect())
}
async fn save_resource(
&self,
(principal, addressbook_id): &Self::PathComponents,
file: Self::Resource,
) -> Result<(), Self::Error> {
self.addr_store
.update_addressbook(principal.to_owned(), addressbook_id.to_owned(), file.into())
.await?;
Ok(())
}
async fn delete_resource(
&self,
(principal, addressbook_id): &Self::PathComponents,
use_trashbin: bool,
) -> Result<(), Self::Error> {
self.addr_store
.delete_addressbook(principal, addressbook_id, use_trashbin)
.await?;
Ok(())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
Router::new()
.nest(
"/{object_id}",
AddressObjectResourceService::new(self.addr_store.clone()).axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<AS: AddressbookStore, S: SubscriptionStore> AxumMethods for AddressbookResourceService<AS, S> {
fn report() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(route_report_addressbook::<AS, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
fn mkcol() -> Option<fn(Self, Request) -> BoxFuture<'static, Result<Response, Infallible>>> {
Some(|state, req| {
let mut service = Handler::with_state(route_mkcol::<AS, S>, state);
Box::pin(Service::call(&mut service, req))
})
}
}

View File

@@ -1,49 +1,14 @@
use crate::addressbook::resource::{AddressbookResource, AddressbookResourceService};
use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
use crate::Error;
use rustical_dav::extensions::CommonPropertiesExtension;
use rustical_dav::privileges::UserPrivilegeSet;
use rustical_dav::resource::{AxumMethods, PrincipalUri, Resource, ResourceName, ResourceService};
use rustical_dav::resource::{PrincipalUri, Resource, ResourceName};
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_store::{AddressbookStore, SubscriptionStore};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
use std::sync::Arc;
use rustical_store::auth::User;
pub struct PrincipalResourceService<
A: AddressbookStore,
AP: AuthenticationProvider,
S: SubscriptionStore,
> {
addr_store: Arc<A>,
auth_provider: Arc<AP>,
sub_store: Arc<S>,
}
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Clone
for PrincipalResourceService<A, AP, S>
{
fn clone(&self) -> Self {
Self {
addr_store: self.addr_store.clone(),
auth_provider: self.auth_provider.clone(),
sub_store: self.sub_store.clone(),
}
}
}
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore>
PrincipalResourceService<A, AP, S>
{
pub fn new(addr_store: Arc<A>, auth_provider: Arc<AP>, sub_store: Arc<S>) -> Self {
Self {
addr_store,
auth_provider,
sub_store,
}
}
}
mod service;
pub use service::*;
mod prop;
pub use prop::*;
#[derive(Debug, Clone)]
pub struct PrincipalResource {
@@ -56,34 +21,6 @@ impl ResourceName for PrincipalResource {
}
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] Vec<HrefElement>);
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(String),
// WebDAV Access Control (RFC 3744)
#[xml(rename = b"principal-URL")]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
PrincipalUrl(HrefElement),
// CardDAV (RFC 6352)
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
AddressbookHomeSet(AddressbookHomeSet),
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
PrincipalAddress(Option<HrefElement>),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Principal(PrincipalProp),
Common(CommonPropertiesProp),
}
impl Resource for PrincipalResource {
type Prop = PrincipalPropWrapper;
type Error = Error;
@@ -145,55 +82,3 @@ impl Resource for PrincipalResource {
))
}
}
#[async_trait]
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> ResourceService
for PrincipalResourceService<A, AP, S>
{
type PathComponents = (String,);
type MemberType = AddressbookResource;
type Resource = PrincipalResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CardDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
async fn get_resource(
&self,
(principal,): &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let user = self
.auth_provider
.get_principal(principal)
.await?
.ok_or(crate::Error::NotFound)?;
Ok(PrincipalResource { principal: user })
}
async fn get_members(
&self,
(principal,): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
let addressbooks = self.addr_store.get_addressbooks(principal).await?;
Ok(addressbooks
.into_iter()
.map(AddressbookResource::from)
.collect())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
Router::new()
.nest(
"/{addressbook_id}",
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
.axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods
for PrincipalResourceService<A, AP, S>
{
}

View File

@@ -0,0 +1,30 @@
use rustical_dav::{extensions::CommonPropertiesProp, xml::HrefElement};
use rustical_xml::{EnumVariants, PropName, XmlDeserialize, XmlSerialize};
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
pub struct AddressbookHomeSet(#[xml(ty = "untagged", flatten)] pub(super) Vec<HrefElement>);
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropName")]
pub enum PrincipalProp {
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
Displayname(String),
// WebDAV Access Control (RFC 3744)
#[xml(rename = b"principal-URL")]
#[xml(ns = "rustical_dav::namespace::NS_DAV")]
PrincipalUrl(HrefElement),
// CardDAV (RFC 6352)
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
AddressbookHomeSet(AddressbookHomeSet),
#[xml(ns = "rustical_dav::namespace::NS_CARDDAV")]
PrincipalAddress(Option<HrefElement>),
}
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone, EnumVariants, PropName)]
#[xml(unit_variants_ident = "PrincipalPropWrapperName", untagged)]
pub enum PrincipalPropWrapper {
Principal(PrincipalProp),
Common(CommonPropertiesProp),
}

View File

@@ -0,0 +1,96 @@
use crate::addressbook::AddressbookResourceService;
use crate::addressbook::resource::AddressbookResource;
use crate::principal::PrincipalResource;
use crate::{CardDavPrincipalUri, Error};
use async_trait::async_trait;
use axum::Router;
use rustical_dav::resource::{AxumMethods, ResourceService};
use rustical_store::auth::{AuthenticationProvider, User};
use rustical_store::{AddressbookStore, SubscriptionStore};
use std::sync::Arc;
pub struct PrincipalResourceService<
A: AddressbookStore,
AP: AuthenticationProvider,
S: SubscriptionStore,
> {
addr_store: Arc<A>,
auth_provider: Arc<AP>,
sub_store: Arc<S>,
}
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> Clone
for PrincipalResourceService<A, AP, S>
{
fn clone(&self) -> Self {
Self {
addr_store: self.addr_store.clone(),
auth_provider: self.auth_provider.clone(),
sub_store: self.sub_store.clone(),
}
}
}
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore>
PrincipalResourceService<A, AP, S>
{
pub fn new(addr_store: Arc<A>, auth_provider: Arc<AP>, sub_store: Arc<S>) -> Self {
Self {
addr_store,
auth_provider,
sub_store,
}
}
}
#[async_trait]
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> ResourceService
for PrincipalResourceService<A, AP, S>
{
type PathComponents = (String,);
type MemberType = AddressbookResource;
type Resource = PrincipalResource;
type Error = Error;
type Principal = User;
type PrincipalUri = CardDavPrincipalUri;
const DAV_HEADER: &str = "1, 3, access-control, addressbook";
async fn get_resource(
&self,
(principal,): &Self::PathComponents,
) -> Result<Self::Resource, Self::Error> {
let user = self
.auth_provider
.get_principal(principal)
.await?
.ok_or(crate::Error::NotFound)?;
Ok(PrincipalResource { principal: user })
}
async fn get_members(
&self,
(principal,): &Self::PathComponents,
) -> Result<Vec<Self::MemberType>, Self::Error> {
let addressbooks = self.addr_store.get_addressbooks(principal).await?;
Ok(addressbooks
.into_iter()
.map(AddressbookResource::from)
.collect())
}
fn axum_router<State: Send + Sync + Clone + 'static>(self) -> Router<State> {
Router::new()
.nest(
"/{addressbook_id}",
AddressbookResourceService::new(self.addr_store.clone(), self.sub_store.clone())
.axum_router(),
)
.route_service("/", self.axum_service())
}
}
impl<A: AddressbookStore, AP: AuthenticationProvider, S: SubscriptionStore> AxumMethods
for PrincipalResourceService<A, AP, S>
{
}