use super::methods::{axum_route_propfind, axum_route_proppatch}; use crate::resource::{ ResourceService, axum_methods::AxumMethods, methods::{axum_route_copy, axum_route_move}, }; use axum::{ body::Body, extract::FromRequestParts, handler::Handler, http::{Request, Response}, response::IntoResponse, }; use futures_util::future::BoxFuture; use headers::HeaderMapExt; use http::{HeaderValue, StatusCode}; use std::convert::Infallible; use tower::Service; #[derive(Clone)] pub struct AxumService { resource_service: RS, } impl AxumService { pub const fn new(resource_service: RS) -> Self { Self { resource_service } } } impl Service> for AxumService where RS::Error: IntoResponse + Send + Sync + 'static, RS::Principal: FromRequestParts, { type Error = Infallible; type Response = Response; type Future = BoxFuture<'static, Result>; #[inline] fn poll_ready( &mut self, _cx: &mut std::task::Context<'_>, ) -> std::task::Poll> { Ok(()).into() } #[inline] fn call(&mut self, req: Request) -> Self::Future { use crate::resource::methods::axum_route_delete; let mut propfind_service = Handler::with_state(axum_route_propfind::, self.resource_service.clone()); let mut proppatch_service = Handler::with_state(axum_route_proppatch::, self.resource_service.clone()); let mut delete_service = Handler::with_state(axum_route_delete::, self.resource_service.clone()); let mut move_service = Handler::with_state(axum_route_move::, self.resource_service.clone()); let mut copy_service = Handler::with_state(axum_route_copy::, self.resource_service.clone()); let mut options_service = Handler::with_state(route_options::, ()); match req.method().as_str() { "PROPFIND" => return Box::pin(Service::call(&mut propfind_service, req)), "PROPPATCH" => return Box::pin(Service::call(&mut proppatch_service, req)), "DELETE" => return Box::pin(Service::call(&mut delete_service, req)), "OPTIONS" => return Box::pin(Service::call(&mut options_service, req)), "MOVE" => return Box::pin(Service::call(&mut move_service, req)), "COPY" => return Box::pin(Service::call(&mut copy_service, req)), "REPORT" => { if let Some(svc) = RS::report() { return svc(self.resource_service.clone(), req); } } "GET" | "HEAD" => { if let Some(svc) = RS::get() { return svc(self.resource_service.clone(), req); } } "POST" => { if let Some(svc) = RS::post() { return svc(self.resource_service.clone(), req); } } "MKCOL" => { if let Some(svc) = RS::mkcol() { return svc(self.resource_service.clone(), req); } } "MKCALENDAR" => { if let Some(svc) = RS::mkcalendar() { return svc(self.resource_service.clone(), req); } } "PUT" => { if let Some(svc) = RS::put() { return svc(self.resource_service.clone(), req); } } "IMPORT" => { if let Some(svc) = RS::import() { return svc(self.resource_service.clone(), req); } } _ => {} } Box::pin(async move { Ok(Response::builder() .status(StatusCode::METHOD_NOT_ALLOWED) .body(Body::from("Method not allowed")) .unwrap()) }) } } async fn route_options() -> Response { // Semantically NO_CONTENT would also make sense, // but GNOME Accounts only works when returning OK // https://gitlab.gnome.org/GNOME/gnome-online-accounts/-/blob/master/src/goabackend/goadavclient.c#L289 let mut resp = Response::builder().status(StatusCode::OK); let headers = resp.headers_mut().unwrap(); headers.insert("DAV", HeaderValue::from_static(RS::DAV_HEADER)); headers.typed_insert(RS::allow_header()); resp.body(Body::empty()).unwrap() }