diff --git a/crates/dav/Cargo.toml b/crates/dav/Cargo.toml index 41f5056..2e2194b 100644 --- a/crates/dav/Cargo.toml +++ b/crates/dav/Cargo.toml @@ -22,3 +22,4 @@ serde = { version = "1.0.188", features = ["serde_derive", "derive"] } serde_json = "1.0.105" tokio = { version = "1.32.0", features = ["sync", "full"] } async-trait = "0.1.73" +thiserror = "1.0.48" diff --git a/crates/dav/src/error.rs b/crates/dav/src/error.rs index 07526cc..5ef1f87 100644 --- a/crates/dav/src/error.rs +++ b/crates/dav/src/error.rs @@ -1,24 +1,33 @@ use actix_web::{http::StatusCode, HttpResponse}; -use derive_more::{Display, Error}; +use thiserror::Error; -#[derive(Debug, Display, Error)] +use crate::routes::propfind; + +#[derive(Debug, Error)] pub enum Error { - #[display(fmt = "Internal server error")] - InternalError, - #[display(fmt = "Not found")] + #[error("Not found")] NotFound, - #[display(fmt = "Bad request")] + #[error("Bad request")] BadRequest, + #[error("Unauthorized")] Unauthorized, + #[error("Internal server error :(")] + InternalError, + #[error(transparent)] + PropfindError(#[from] propfind::Error), + #[error("Internal server error")] + Other(#[from] anyhow::Error), } impl actix_web::error::ResponseError for Error { fn status_code(&self) -> StatusCode { - match *self { + match self { Self::InternalError => StatusCode::INTERNAL_SERVER_ERROR, + Self::Other(_) => StatusCode::INTERNAL_SERVER_ERROR, Self::NotFound => StatusCode::NOT_FOUND, Self::BadRequest => StatusCode::BAD_REQUEST, Self::Unauthorized => StatusCode::UNAUTHORIZED, + Self::PropfindError(e) => e.status_code(), } } diff --git a/crates/dav/src/routes/event.rs b/crates/dav/src/routes/event.rs index 31c2544..d03c174 100644 --- a/crates/dav/src/routes/event.rs +++ b/crates/dav/src/routes/event.rs @@ -1,8 +1,27 @@ -use crate::{CalDavContext, Error}; +use crate::CalDavContext; +use actix_web::http::StatusCode; use actix_web::web::{Data, Path}; -use actix_web::HttpResponse; +use actix_web::{HttpResponse, ResponseError}; use rustical_auth::{AuthInfoExtractor, CheckAuthentication}; use rustical_store::calendar::CalendarStore; +use thiserror::Error; + +#[derive(Debug, Error)] +pub enum Error { + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +impl ResponseError for Error { + fn status_code(&self) -> actix_web::http::StatusCode { + match self { + Self::Other(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } + fn error_response(&self) -> HttpResponse { + HttpResponse::build(self.status_code()).body(self.to_string()) + } +} pub async fn delete_event( context: Data>, @@ -15,13 +34,7 @@ pub async fn delete_event( if cid.ends_with(".ics") { cid.truncate(cid.len() - 4); } - context - .store - .write() - .await - .delete_event(&cid, &uid) - .await - .map_err(|_e| Error::InternalError)?; + context.store.write().await.delete_event(&cid, &uid).await?; Ok(HttpResponse::Ok().body("")) } @@ -36,13 +49,7 @@ pub async fn get_event( if uid.ends_with(".ics") { uid.truncate(uid.len() - 4); } - let event = context - .store - .read() - .await - .get_event(&cid, &uid) - .await - .map_err(|_e| Error::NotFound)?; + let event = context.store.read().await.get_event(&cid, &uid).await?; Ok(HttpResponse::Ok() .insert_header(("ETag", event.get_etag())) @@ -66,8 +73,7 @@ pub async fn put_event( .write() .await .upsert_event(cid, uid, body) - .await - .map_err(|_e| Error::InternalError)?; + .await?; Ok(HttpResponse::Ok().body("")) } diff --git a/crates/dav/src/routes/propfind.rs b/crates/dav/src/routes/propfind.rs index a8a5c8c..baf0e4b 100644 --- a/crates/dav/src/routes/propfind.rs +++ b/crates/dav/src/routes/propfind.rs @@ -1,18 +1,43 @@ use crate::depth_extractor::Depth; -use crate::error::Error; use crate::namespace::Namespace; use crate::resource::{HandlePropfind, Resource}; use crate::xml_snippets::generate_multistatus; use crate::CalDavContext; use actix_web::http::header::ContentType; +use actix_web::http::StatusCode; use actix_web::web::{Data, Path}; use actix_web::{HttpRequest, HttpResponse}; -use anyhow::{anyhow, Result}; +use anyhow::Result; use quick_xml::events::BytesText; use rustical_auth::{AuthInfoExtractor, CheckAuthentication}; use rustical_store::calendar::CalendarStore; +use thiserror::Error; -fn parse_propfind(body: &str) -> Result> { +#[derive(Debug, Error)] +pub enum Error { + #[error("invalid propfind request: {0}")] + InvalidPropfind(&'static str), + #[error("input is not valid xml")] + ParsingError(#[from] roxmltree::Error), + #[error(transparent)] + Other(#[from] anyhow::Error), +} + +impl actix_web::error::ResponseError for Error { + fn status_code(&self) -> actix_web::http::StatusCode { + match self { + Self::InvalidPropfind(_) => StatusCode::BAD_REQUEST, + Self::ParsingError(_) => StatusCode::BAD_REQUEST, + Self::Other(_) => StatusCode::INTERNAL_SERVER_ERROR, + } + } + + fn error_response(&self) -> HttpResponse { + HttpResponse::build(self.status_code()).body(self.to_string()) + } +} + +fn parse_propfind(body: &str) -> Result, Error> { if body.is_empty() { // if body is empty, allprops must be returned (RFC 4918) return Ok(vec!["allprops"]); @@ -21,7 +46,7 @@ fn parse_propfind(body: &str) -> Result> { let propfind_node = doc.root_element(); if propfind_node.tag_name().name() != "propfind" { - return Err(anyhow!("invalid tag")); + return Err(Error::InvalidPropfind("root tag is not ")); } let prop_node = if let Some(el) = propfind_node.first_element_child() { @@ -30,15 +55,15 @@ fn parse_propfind(body: &str) -> Result> { return Ok(Vec::new()); }; - let props = match prop_node.tag_name().name() { + match prop_node.tag_name().name() { "prop" => Ok(prop_node .children() .map(|node| node.tag_name().name()) .collect()), - _ => Err(anyhow!("invalid prop tag")), - }; - dbg!(body, &props); - props + _ => Err(Error::InvalidPropfind( + "invalid tag in , expected ", + )), + } } pub async fn route_propfind( @@ -49,7 +74,7 @@ pub async fn route_propfind, depth: Depth, ) -> Result { - let props = parse_propfind(&body).map_err(|_e| Error::BadRequest)?; + let props = parse_propfind(&body)?; // let req_path = req.path().to_string(); let auth_info = auth.inner; @@ -59,24 +84,13 @@ pub async fn route_propfind