vorma 0.86.0-pre.3

Vorma framework.
Documentation
use std::collections::BTreeMap;
#[cfg(test)]
use std::sync::MutexGuard;
use std::sync::{Arc, Mutex};

use serde::Serialize;
use vorma_matcher::Params;
use vorma_tasks::ExecCtx;

use crate::error::Error as VormaError;
use crate::manifest::public_src_key;
use crate::response::ResponseEffects;

use super::request::RawRequest;

#[derive(Clone, Copy, Debug, Default, Eq, Hash, PartialEq, Serialize)]
pub struct None;

pub struct RequestCtx<S, E, I = None> {
	pub(in crate::mux) matched_pattern: String,
	pub(in crate::mux) params: Params,
	pub(in crate::mux) splat_values: Vec<String>,
	pub(in crate::mux) state: Arc<S>,
	pub(in crate::mux) exec_ctx: ExecCtx<E>,
	pub(in crate::mux) public_filemap: Arc<BTreeMap<String, String>>,
	pub(in crate::mux) response_effects: Arc<Mutex<ResponseEffects>>,
	pub(in crate::mux) request: RawRequest,
	pub(in crate::mux) input: I,
}

impl<S, E, I> RequestCtx<S, E, I> {
	pub fn matched_pattern(&self) -> &str {
		&self.matched_pattern
	}

	pub fn params(&self) -> &Params {
		&self.params
	}

	pub fn param(&self, key: &str) -> Option<&str> {
		self.params.get(key).map(String::as_str)
	}

	pub fn splat_values(&self) -> &[String] {
		&self.splat_values
	}

	pub fn state(&self) -> &S {
		&self.state
	}

	pub fn exec_ctx(&self) -> &ExecCtx<E> {
		&self.exec_ctx
	}

	pub fn public_url(&self, src_path: &str) -> std::result::Result<String, VormaError> {
		let clean = public_src_key(src_path)
			.ok_or_else(|| VormaError::runtime("public URL source path is empty"))?;
		self.public_filemap.get(clean).cloned().ok_or_else(|| {
			VormaError::runtime(format!(
				"file {src_path} not found in manifest public filemap"
			))
		})
	}

	#[cfg(test)]
	pub(crate) fn response_effects_mut(&self) -> MutexGuard<'_, ResponseEffects> {
		self.response_effects
			.lock()
			.expect("response effects lock poisoned")
	}

	pub(crate) fn response_effects(&self) -> Arc<Mutex<ResponseEffects>> {
		self.response_effects.clone()
	}

	pub fn request(&self) -> &RawRequest {
		&self.request
	}

	pub fn input(&self) -> &I {
		&self.input
	}

	pub(crate) fn with_input<J>(self, input: J) -> RequestCtx<S, E, J> {
		RequestCtx {
			matched_pattern: self.matched_pattern,
			params: self.params,
			splat_values: self.splat_values,
			state: self.state,
			exec_ctx: self.exec_ctx,
			public_filemap: self.public_filemap,
			response_effects: self.response_effects,
			request: self.request,
			input,
		}
	}
}

impl<S, E, I> Clone for RequestCtx<S, E, I>
where
	I: Clone,
{
	fn clone(&self) -> Self {
		Self {
			matched_pattern: self.matched_pattern.clone(),
			params: self.params.clone(),
			splat_values: self.splat_values.clone(),
			state: self.state.clone(),
			exec_ctx: self.exec_ctx.clone(),
			public_filemap: self.public_filemap.clone(),
			response_effects: self.response_effects.clone(),
			request: self.request.clone(),
			input: self.input.clone(),
		}
	}
}

pub(crate) struct RequestBase<S, E> {
	pub(in crate::mux) matched_pattern: String,
	pub(in crate::mux) params: Params,
	pub(in crate::mux) splat_values: Vec<String>,
	pub(in crate::mux) state: Arc<S>,
	pub(in crate::mux) exec_ctx: ExecCtx<E>,
	pub(in crate::mux) public_filemap: Arc<BTreeMap<String, String>>,
	pub(in crate::mux) response_effects: Arc<Mutex<ResponseEffects>>,
}

impl<S, E, I> RequestCtx<S, E, I> {
	pub(in crate::mux) fn clone_for_task<J>(
		&self,
		exec_ctx: ExecCtx<E>,
		input: J,
	) -> RequestCtx<S, E, J>
	where
		S: Send + Sync + 'static,
		E: Send + Sync + 'static,
		I: Send + Sync + 'static,
		J: Send + Sync + 'static,
	{
		RequestCtx {
			matched_pattern: self.matched_pattern.clone(),
			params: self.params.clone(),
			splat_values: self.splat_values.clone(),
			state: self.state.clone(),
			exec_ctx,
			public_filemap: self.public_filemap.clone(),
			response_effects: self.response_effects.clone(),
			request: self.request.clone(),
			input,
		}
	}
}