pub mod impl_;
use headers::{Allow, HeaderMap, HeaderMapExt};
use http::Method;
use if_chain::if_chain;
use manas_http::header::{
accept_patch::AcceptPatch, accept_post::AcceptPost, accept_put::AcceptPut,
common::media_type::MediaType,
};
use manas_space::{
resource::{kind::SolidResourceKind, slot::SolidResourceSlot},
SolidStorageSpace,
};
pub trait MethodPolicy {
fn supported_methods(&self) -> &[Method];
fn accept_post_for_container(&self) -> Option<AcceptPost> {
Some(AcceptPost {
media_ranges: vec![mime::STAR_STAR],
})
}
#[allow(unused_variables)]
fn accept_put_for_existing(&self, rep_content_type: &MediaType) -> Option<AcceptPut> {
Some(AcceptPut {
media_ranges: vec![mime::STAR_STAR],
})
}
#[inline]
fn accept_put_for_non_existing(&self) -> Option<AcceptPut> {
Some(AcceptPut {
media_ranges: vec![mime::STAR_STAR],
})
}
fn accept_patch_for_existing(&self, rep_content_type: &MediaType) -> Option<AcceptPatch>;
fn accept_patch_for_non_existing(&self) -> Option<AcceptPatch>;
}
mod seal {
use super::MethodPolicy;
pub trait Sealed {}
impl<T: MethodPolicy> Sealed for T {}
}
pub trait MethodPolicyExt: MethodPolicy + seal::Sealed {
const NEW_RESOURCE_ALLOWED_METHODS: &'static [Method] = &[Method::PUT, Method::PATCH];
fn resolve_allowed_methods_for_existing<StSpace: SolidStorageSpace>(
&self,
res_slot: &SolidResourceSlot<StSpace>,
) -> Vec<Method> {
self.supported_methods()
.iter()
.filter_map(|method| {
if (*method == Method::DELETE && (res_slot.is_root_slot() || res_slot.is_root_acl_slot()))
|| (*method == Method::POST && res_slot.res_kind() != SolidResourceKind::Container)
{
None
} else {
Some(method.clone())
}
})
.collect()
}
fn resolve_allowed_methods_for_non_existing(&self) -> Vec<Method> {
Self::NEW_RESOURCE_ALLOWED_METHODS
.iter()
.filter_map(|m| {
if self.supported_methods().contains(m) {
Some(m.clone())
} else {
None
}
})
.collect()
}
#[inline]
fn set_allow_accept_headers_for_existing<StSpace: SolidStorageSpace>(
&self,
headers: &mut HeaderMap,
res_slot: &SolidResourceSlot<StSpace>,
rep_content_type: &MediaType,
) {
let allowed_methods = self.resolve_allowed_methods_for_existing(res_slot);
if_chain! {
if allowed_methods.contains(&Method::POST);
if let Some(accept_post) = self.accept_post_for_container();
then {
headers.typed_insert(accept_post);
}
}
if_chain! {
if allowed_methods.contains(&Method::PUT);
if let Some(accept_put) = self.accept_put_for_existing(
rep_content_type
);
then {
headers.typed_insert(accept_put);
}
}
if_chain! {
if allowed_methods.contains(&Method::PATCH);
if let Some(accept_patch) = self.accept_patch_for_existing(
rep_content_type
);
then {
headers.typed_insert(accept_patch);
}
}
headers.typed_insert(Allow::from_iter(allowed_methods));
}
#[inline]
fn set_allow_accept_headers_for_non_existing(&self, headers: &mut HeaderMap) {
let allowed_methods = self.resolve_allowed_methods_for_non_existing();
if_chain! {
if allowed_methods.contains(&Method::PUT);
if let Some(accept_put) = self.accept_put_for_non_existing();
then {
headers.typed_insert(accept_put);
}
}
if_chain! {
if allowed_methods.contains(&Method::PATCH);
if let Some(accept_patch) = self.accept_patch_for_non_existing();
then {
headers.typed_insert(accept_patch);
}
}
headers.typed_insert(Allow::from_iter(allowed_methods));
}
}
impl<T: MethodPolicy> MethodPolicyExt for T {}