use super::callables::{DataBox, FromContext, FromContextParts, IntoDataBox, IntoOutput};
use super::specs::{
ArgPart, CallError, DataValue, IntoArgPart, IntoReturnPart, ReturnPart, TypeSchema,
};
use crate::routes::{BodyBytes, Form, Json, JsonStr, Path, Query};
use crate::validation::{Valid, Validate, ValidationSchema};
use crate::{
Site,
auth::{ApiKey, AuthUser},
site,
};
use schemars::JsonSchema;
use std::borrow::Cow;
use std::sync::Arc;
pub trait HasSite {
fn site(&self) -> &Site;
}
pub trait FromSite: Sized + Send {
fn from_site(site: &Site) -> Result<Self, CallError>;
}
impl<C: HasSite, T: FromSite> FromContextParts<C> for T {
fn from_context_parts(ctx: &C) -> Result<Self, CallError> {
T::from_site(ctx.site())
}
}
#[derive(Debug)]
pub struct Data<T: DataValue>(pub Arc<T>);
impl<T: DataValue> Clone for Data<T> {
fn clone(&self) -> Self {
Self(Arc::clone(&self.0))
}
}
impl<T: DataValue> Data<T> {
pub fn new(inner: T) -> Data<T> {
Data(Arc::new(inner))
}
pub fn from_arc(inner: Arc<T>) -> Data<T> {
Data(inner)
}
pub fn into_inner(self) -> Arc<T> {
self.0
}
}
impl<T: DataValue> std::ops::Deref for Data<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T: DataValue> AsRef<T> for Data<T> {
fn as_ref(&self) -> &T {
&self.0
}
}
impl<T: DataValue> From<Arc<T>> for Data<T> {
fn from(value: Arc<T>) -> Self {
Data::from_arc(value)
}
}
impl<T: DataValue> From<T> for Data<T> {
fn from(value: T) -> Self {
Data::new(value)
}
}
impl<C, T> FromContext<C> for Data<T>
where
C: IntoDataBox + Send + 'static,
T: DataValue,
{
fn from_context(ctx: C) -> Result<Self, CallError> {
let payload_data = ctx.into_data_box();
let value = payload_data
.downcast_arc::<T>()
.ok_or_else(|| CallError::TypeMismatch)?;
Ok(Data::from(value))
}
fn deserializer() -> Option<fn(&str) -> Result<DataBox, CallError>> {
Some(|s: &str| {
let value: T = serde_json::from_str(s).map_err(|_| CallError::DeserializeFailed)?;
Ok(DataBox::new_data(value))
})
}
}
impl<T: DataValue> IntoArgPart for Data<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::wrap_unvalidated::<T>(),
"application/json".into(),
)
}
}
impl<T: DataValue> IntoReturnPart for Data<T> {
fn into_return_part() -> ReturnPart {
ReturnPart::Body(TypeSchema::wrap::<T>(), "application/json".into())
}
}
impl<C, E, T> FromContext<C> for Valid<E>
where
E: FromContext<C> + std::ops::Deref<Target = T>,
T: Validate,
{
fn from_context(ctx: C) -> Result<Self, CallError> {
let extracted = E::from_context(ctx)?;
extracted
.validate()
.map(|()| Valid(extracted))
.map_err(CallError::Validation)
}
fn deserializer() -> Option<fn(&str) -> Result<DataBox, CallError>> {
E::deserializer()
}
}
impl<T> super::specs::HasData<T> for Valid<Data<T>> where T: DataValue {}
impl<T: DataValue, E> IntoOutput<E> for Data<T> {
fn into_output(self) -> Result<DataBox, E> {
Ok(DataBox::from_data_arc(self.0))
}
}
impl<T> axum::extract::FromRequest<crate::Site> for Data<T>
where
T: DataValue,
{
type Rejection = crate::errors::ErrorReport;
async fn from_request(
req: axum::extract::Request,
state: &crate::Site,
) -> Result<Self, Self::Rejection> {
Json::<T>::from_request(req, state)
.await
.map(|Json(value)| Data::new(value))
}
}
impl<T> axum::response::IntoResponse for Data<T>
where
T: DataValue,
{
fn into_response(self) -> axum::response::Response {
axum::Json(self.0).into_response()
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for Path<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Path(TypeSchema::wrap_unvalidated::<T>())
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for Query<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Query(TypeSchema::wrap_unvalidated::<T>())
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for Form<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::wrap_unvalidated::<T>(),
Cow::Borrowed("application/x-www-form-urlencoded"),
)
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for Json<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::wrap_unvalidated::<T>(),
Cow::Borrowed("application/json"),
)
}
}
impl IntoArgPart for BodyBytes {
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::binary_body(),
Cow::Borrowed("application/octet-stream"),
)
}
}
impl<T> IntoArgPart for Valid<Path<T>>
where
T: JsonSchema + Validate + ValidationSchema + Send + 'static,
{
fn into_arg_part() -> ArgPart {
ArgPart::Path(TypeSchema::wrap_valid::<T>())
}
}
impl<T> IntoArgPart for Valid<Query<T>>
where
T: JsonSchema + Validate + ValidationSchema + Send + 'static,
{
fn into_arg_part() -> ArgPart {
ArgPart::Query(TypeSchema::wrap_valid::<T>())
}
}
impl<T> IntoArgPart for Valid<Form<T>>
where
T: JsonSchema + Validate + ValidationSchema + Send + 'static,
{
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::wrap_valid::<T>(),
Cow::Borrowed("application/x-www-form-urlencoded"),
)
}
}
impl<T> IntoArgPart for Valid<Json<T>>
where
T: JsonSchema + Validate + ValidationSchema + Send + 'static,
{
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::wrap_valid::<T>(),
Cow::Borrowed("application/json"),
)
}
}
impl<T> IntoArgPart for Valid<Data<T>>
where
T: DataValue + Validate + ValidationSchema,
{
fn into_arg_part() -> ArgPart {
ArgPart::Body(
TypeSchema::wrap_valid::<T>(),
Cow::Borrowed("application/json"),
)
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for axum::extract::Path<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Path(TypeSchema::wrap::<T>())
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for axum::extract::Query<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Query(TypeSchema::wrap::<T>())
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for axum_extra::extract::Query<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Query(TypeSchema::wrap::<T>())
}
}
impl<T: JsonSchema + Send + 'static> IntoArgPart for axum::extract::Json<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Body(TypeSchema::wrap::<T>(), Cow::Borrowed("application/json"))
}
}
impl<T: Clone + Send + Sync + 'static> IntoArgPart for axum::extract::State<T> {
fn into_arg_part() -> ArgPart {
ArgPart::Zone }
}
impl IntoReturnPart for axum::response::Response {
fn into_return_part() -> ReturnPart {
ReturnPart::Unknown
}
}
impl<T: JsonSchema + Send + 'static> IntoReturnPart for axum::extract::Json<T> {
fn into_return_part() -> ReturnPart {
ReturnPart::Body(TypeSchema::wrap::<T>(), Cow::Borrowed("application/json"))
}
}
impl<T: JsonSchema + Send + 'static> IntoReturnPart for Json<T> {
fn into_return_part() -> ReturnPart {
ReturnPart::Body(TypeSchema::wrap::<T>(), Cow::Borrowed("application/json"))
}
}
impl IntoReturnPart for axum::http::StatusCode {
fn into_return_part() -> ReturnPart {
ReturnPart::Empty
}
}
impl<T: IntoReturnPart> IntoReturnPart for (axum::http::StatusCode, T) {
fn into_return_part() -> ReturnPart {
T::into_return_part()
}
}
impl<T: IntoReturnPart> IntoReturnPart for (axum_extra::extract::CookieJar, T) {
fn into_return_part() -> ReturnPart {
T::into_return_part()
}
}
impl IntoReturnPart for axum::response::Html<String> {
fn into_return_part() -> ReturnPart {
ReturnPart::Body(TypeSchema::wrap::<String>(), "text/html".into())
}
}
impl IntoReturnPart for () {
fn into_return_part() -> ReturnPart {
ReturnPart::Empty
}
}
impl<T, E> IntoReturnPart for Result<T, E>
where
T: IntoReturnPart,
E: Send,
{
fn into_return_part() -> ReturnPart {
T::into_return_part()
}
}
impl<T> IntoReturnPart for Option<T>
where
T: IntoReturnPart,
{
fn into_return_part() -> ReturnPart {
T::into_return_part()
}
}
impl<'a> IntoReturnPart for JsonStr {
fn into_return_part() -> ReturnPart {
ReturnPart::Body(TypeSchema::wrap::<String>(), "application/json".into())
}
}
impl IntoArgPart for AuthUser {
fn into_arg_part() -> ArgPart {
ArgPart::Security {
scheme: "bearerAuth".into(),
join_all: true,
scopes: Vec::new(),
}
}
}
impl IntoArgPart for ApiKey {
fn into_arg_part() -> ArgPart {
ArgPart::Security {
scheme: "apiKeyAuth".into(),
join_all: true,
scopes: Vec::new(),
}
}
}
impl IntoArgPart for site::Site {
fn into_arg_part() -> ArgPart {
ArgPart::Ignore
}
}
impl FromSite for site::Site {
fn from_site(site: &site::Site) -> Result<Self, CallError> {
Ok(site.clone())
}
}
impl<C: HasSite> FromContext<C> for site::Site {
fn from_context(ctx: C) -> Result<Self, CallError> {
Ok(ctx.site().clone())
}
}