vorma 0.86.0-pre.3

Vorma framework.
Documentation
use std::future::Future;
use std::pin::Pin;
use std::sync::Arc;

use serde::Serialize;
use serde_json::Value;
use vorma_matcher::Params;
use vorma_tasks::Result as TaskResult;

use super::{ResourceCtx, ViewCtx};
use crate::api;
use crate::mux::{self, None, RequestCtx, RouteExecutionError};

#[doc(hidden)]
pub type RouteFuture<O, E> = Pin<Box<dyn Future<Output = TaskResult<O, E>> + Send + 'static>>;
#[doc(hidden)]
pub type ErasedRouteFuture<E> =
	Pin<Box<dyn Future<Output = Result<Value, RouteExecutionError<E>>> + Send + 'static>>;
#[doc(hidden)]
pub type ErasedRequestCtx<S, E> = RequestCtx<S, E, None>;
#[doc(hidden)]
pub type ErasedRouteHandler<S, E> = fn(ErasedRequestCtx<S, E>) -> ErasedRouteFuture<E>;

type StaticViewHandler<S, E, I, P, O> = fn(ViewCtx<S, E, I, P>) -> RouteFuture<O, E>;
type StaticResourceHandler<S, E, I, P, O> = fn(ResourceCtx<S, E, I, P>) -> RouteFuture<O, E>;
pub(super) type DynamicRouteHandler<S, E> =
	dyn Fn(ErasedRequestCtx<S, E>) -> ErasedRouteFuture<E> + Send + Sync;

pub(super) enum RouteRunner<S, E> {
	Static(ErasedRouteHandler<S, E>),
	Dynamic(Arc<DynamicRouteHandler<S, E>>),
}

impl<S, E> Clone for RouteRunner<S, E> {
	fn clone(&self) -> Self {
		match self {
			Self::Static(handler) => Self::Static(*handler),
			Self::Dynamic(handler) => Self::Dynamic(handler.clone()),
		}
	}
}

#[doc(hidden)]
pub fn run_static_view<S, E, I, P, O>(
	ctx: ErasedRequestCtx<S, E>,
	handler: StaticViewHandler<S, E, I, P, O>,
) -> ErasedRouteFuture<E>
where
	S: Send + Sync + 'static,
	E: Send + Sync + 'static,
	I: api::ViewInput,
	P: Clone + PathParams,
	O: Serialize + Send + Sync + 'static,
{
	Box::pin(async move {
		let input =
			api::parse_view_input::<I>(ctx.request()).map_err(RouteExecutionError::Input)?;
		let params = P::from_raw_path_params(ctx.params()).map_err(RouteExecutionError::Input)?;
		let handler_ctx = ViewCtx::new(ctx.with_input(input), params);
		let output = handler(handler_ctx)
			.await
			.map_err(RouteExecutionError::Task)?;
		serialize_route_output(output)
	})
}

#[doc(hidden)]
pub fn run_static_resource<S, E, I, P, O>(
	ctx: ErasedRequestCtx<S, E>,
	handler: StaticResourceHandler<S, E, I, P, O>,
) -> ErasedRouteFuture<E>
where
	S: Send + Sync + 'static,
	E: Send + Sync + 'static,
	I: api::ResourceInput,
	P: Clone + PathParams,
	O: Serialize + Send + Sync + 'static,
{
	Box::pin(async move {
		let input = api::parse_resource_input::<I>(ctx.request())
			.await
			.map_err(RouteExecutionError::Input)?;
		let params = P::from_raw_path_params(ctx.params()).map_err(RouteExecutionError::Input)?;
		let handler_ctx = ResourceCtx::new(ctx.with_input(input), params);
		let output = handler(handler_ctx)
			.await
			.map_err(RouteExecutionError::Task)?;
		serialize_route_output(output)
	})
}

pub(super) async fn run_route_runner<S, E>(
	handler: RouteRunner<S, E>,
	ctx: ErasedRequestCtx<S, E>,
) -> Result<Value, RouteExecutionError<E>>
where
	S: Send + Sync + 'static,
	E: Send + Sync + 'static,
{
	match handler {
		RouteRunner::Static(handler) => handler(ctx).await,
		RouteRunner::Dynamic(handler) => handler(ctx).await,
	}
}

#[doc(hidden)]
pub trait PathParams: Sized + Send + Sync + 'static {
	#[doc(hidden)]
	fn from_raw_path_params(params: &Params) -> Result<Self, mux::InputError>;
}

impl PathParams for () {
	fn from_raw_path_params(_: &Params) -> Result<Self, mux::InputError> {
		Ok(())
	}
}

pub(super) fn serialize_route_output<E>(
	output: impl Serialize,
) -> Result<Value, RouteExecutionError<E>> {
	serde_json::to_value(output).map_err(|err| {
		RouteExecutionError::Input(mux::InputError::internal(format!(
			"serialize route output: {err}"
		)))
	})
}