use std::{convert::Infallible, sync::Arc, task::Poll};
use headers::HeaderMapExt;
use http::{Response, StatusCode};
use http_api_problem::ApiError;
use hyper::Body;
use manas_http::{
header::link::{Link, LinkValue},
service::BoxHttpResponseFuture,
};
use manas_space::resource::kind::SolidResourceKind;
use rdf_vocabularies::ns;
use tower::Service;
use crate::{
service::method::{
common::snippet::authorization::attach_authorization_context, post::base::BasePostResponse,
},
SolidStorage,
};
#[derive(Debug, Clone, Default)]
pub struct DefaultBasePostResponseMarshallerConfig {
pub dev_mode: bool,
}
#[derive(Debug)]
pub struct DefaultBasePostResponseMarshaller<Storage> {
pub storage: Arc<Storage>,
pub marshal_config: DefaultBasePostResponseMarshallerConfig,
}
impl<Storage> Clone for DefaultBasePostResponseMarshaller<Storage> {
#[inline]
fn clone(&self) -> Self {
Self {
storage: self.storage.clone(),
marshal_config: self.marshal_config.clone(),
}
}
}
impl<Storage> DefaultBasePostResponseMarshaller<Storage> {
#[inline]
pub fn new(
storage: Arc<Storage>,
marshal_config: DefaultBasePostResponseMarshallerConfig,
) -> Self {
Self {
storage,
marshal_config,
}
}
}
impl<Storage> Service<Result<BasePostResponse<Storage>, ApiError>>
for DefaultBasePostResponseMarshaller<Storage>
where
Storage: SolidStorage,
{
type Response = Response<Body>;
type Error = Infallible;
type Future = BoxHttpResponseFuture<Body>;
#[inline]
fn poll_ready(&mut self, _cx: &mut std::task::Context<'_>) -> Poll<Result<(), Self::Error>> {
Poll::Ready(Ok(()))
}
#[inline]
#[tracing::instrument(skip_all, name = "DefaultBasePostResponseMarshaller::call")]
fn call(&mut self, req: Result<BasePostResponse<Storage>, ApiError>) -> Self::Future {
Box::pin(futures::future::ready(Ok(match req {
Ok(resp) => self.marshal_ok(resp),
Err(err) => self.marshal_err(err),
})))
}
}
impl<Storage> DefaultBasePostResponseMarshaller<Storage>
where
Storage: SolidStorage,
{
#[allow(clippy::vec_init_then_push)]
pub fn marshal_ok(&self, response: BasePostResponse<Storage>) -> Response<Body> {
let mut builder = Response::builder().status(StatusCode::CREATED);
let headers = builder.headers_mut().expect("Must be valid headers");
headers.insert(
"Location",
response
.created_resource_slot
.id()
.uri
.as_str()
.parse()
.expect("Must be valid header value"),
);
let mut links = Vec::<LinkValue>::new();
links.push(
LinkValue::try_new_basic(ns::ldp::Resource.to_string(), "type").expect("Must be valid"),
);
if response.created_resource_slot.res_kind() == SolidResourceKind::Container {
links.push(
LinkValue::try_new_basic(ns::ldp::BasicContainer.to_string(), "type")
.expect("Must be valid"),
);
}
headers.typed_insert(Link { values: links });
builder
.body(Body::empty())
.expect("Must be valid hyper response.")
}
pub fn marshal_err(&self, mut error: ApiError) -> Response<Body> {
if self.marshal_config.dev_mode {
attach_authorization_context::<Storage>(&mut error);
}
error.into_hyper_response()
}
}