use std::sync::Arc;
use http::Method;
use http::request::Parts;
use primitives_rs::tasks::TaskCtx;
use crate::build_output::{CspSources, StaticPublicAssets};
use crate::declarations::ApiEndpointKind;
use crate::href::PageHrefs;
use crate::pattern::{ApiPatternParams, DecodedMatch, ViewPatternParams};
pub(crate) struct RequestFacts {
pub(crate) target_resolver: Arc<dyn crate::outcome::ResolveTarget>,
pub(crate) head_rules: Option<Arc<crate::document::HeadRules>>,
pub(crate) request_parts: Arc<Parts>,
pub(crate) latest_known_client_build_id: String,
pub(crate) requested_client_build_id: Option<String>,
pub(crate) static_public_assets: Arc<StaticPublicAssets>,
pub(crate) csp_sources: Arc<CspSources>,
pub(crate) request_match: RequestMatch,
}
#[derive(Clone, Debug)]
pub enum RequestMatch {
Page(MatchedPage),
ApiEndpoint(MatchedApiEndpoint),
}
#[derive(Clone, Debug)]
pub struct MatchedPage {
pub(crate) pattern_matches: Vec<PatternMatch>,
}
#[derive(Clone, Debug)]
pub struct PatternMatch {
pub(crate) pattern: String,
pub(crate) decoded_match: DecodedMatch,
}
impl PatternMatch {
pub fn new(
pattern: impl Into<String>,
params: Vec<(String, String)>,
splat_values: Vec<String>,
) -> Self {
Self {
pattern: pattern.into(),
decoded_match: DecodedMatch::new(params, splat_values),
}
}
pub fn pattern(&self) -> &str {
&self.pattern
}
}
impl MatchedPage {
pub fn new(pattern_matches: Vec<PatternMatch>) -> Self {
Self { pattern_matches }
}
pub fn find_params<Params: ViewPatternParams>(&self) -> Option<Params> {
let pattern = Params::PATTERN;
self.pattern_matches
.iter()
.find(|pattern_match| pattern_match.pattern == pattern)
.and_then(|pattern_match| {
Params::from_decoded_match(pattern_match.decoded_match.clone()).ok()
})
}
pub fn pattern_matches(&self) -> impl Iterator<Item = &PatternMatch> {
self.pattern_matches.iter()
}
}
#[derive(Clone, Debug)]
pub struct MatchedApiEndpoint {
pub kind: ApiEndpointKind,
pub method: Method,
pub pattern: &'static str,
pub(crate) decoded_match: DecodedMatch,
}
impl MatchedApiEndpoint {
pub fn new(
kind: ApiEndpointKind,
method: Method,
pattern: &'static str,
params: Vec<(String, String)>,
splat_values: Vec<String>,
) -> Self {
Self {
kind,
method,
pattern,
decoded_match: DecodedMatch::new(params, splat_values),
}
}
pub fn find_params<Params: ApiPatternParams>(&self) -> Option<Params> {
(Params::PATTERN == self.pattern)
.then(|| Params::from_decoded_match(self.decoded_match.clone()).ok())
.flatten()
}
}
pub struct TaskMiddlewareCtx<State> {
pub state: Arc<State>,
pub request_parts: Arc<Parts>,
pub task_ctx: TaskCtx<State>,
pub static_public_assets: Arc<StaticPublicAssets>,
pub page_hrefs: PageHrefs,
pub csp_sources: Arc<CspSources>,
pub latest_known_client_build_id: String,
pub requested_client_build_id: Option<String>,
pub request_match: RequestMatch,
}
pub struct ViewLoaderCtx<State, Params, Input> {
pub state: Arc<State>,
pub params: Params,
pub input: Input,
pub pattern: &'static str,
pub request_parts: Arc<Parts>,
pub task_ctx: TaskCtx<State>,
pub static_public_assets: Arc<StaticPublicAssets>,
pub page_hrefs: PageHrefs,
pub csp_sources: Arc<CspSources>,
pub latest_known_client_build_id: String,
}
pub struct ApiEndpointCtx<State, Params, Input> {
pub state: Arc<State>,
pub params: Params,
pub input: Input,
pub pattern: &'static str,
pub request_parts: Arc<Parts>,
pub task_ctx: TaskCtx<State>,
pub static_public_assets: Arc<StaticPublicAssets>,
pub page_hrefs: PageHrefs,
pub latest_known_client_build_id: String,
pub requested_client_build_id: Option<String>,
}
impl RequestFacts {
fn to_page_hrefs(&self) -> PageHrefs {
PageHrefs::new(Arc::clone(&self.target_resolver))
}
pub(crate) fn to_task_middleware_ctx<State>(
&self,
state: Arc<State>,
task_ctx: TaskCtx<State>,
) -> TaskMiddlewareCtx<State> {
TaskMiddlewareCtx {
state,
request_parts: Arc::clone(&self.request_parts),
task_ctx,
static_public_assets: Arc::clone(&self.static_public_assets),
page_hrefs: self.to_page_hrefs(),
csp_sources: Arc::clone(&self.csp_sources),
latest_known_client_build_id: self.latest_known_client_build_id.clone(),
requested_client_build_id: self.requested_client_build_id.clone(),
request_match: self.request_match.clone(),
}
}
pub(crate) fn to_view_loader_ctx<State, Params, Input>(
&self,
handler_facts: HandlerFacts<State, Params, Input>,
) -> ViewLoaderCtx<State, Params, Input> {
ViewLoaderCtx {
state: handler_facts.state,
params: handler_facts.params,
input: handler_facts.input,
pattern: handler_facts.pattern,
request_parts: Arc::clone(&self.request_parts),
task_ctx: handler_facts.task_ctx,
static_public_assets: Arc::clone(&self.static_public_assets),
page_hrefs: self.to_page_hrefs(),
csp_sources: Arc::clone(&self.csp_sources),
latest_known_client_build_id: self.latest_known_client_build_id.clone(),
}
}
pub(crate) fn to_api_endpoint_ctx<State, Params, Input>(
&self,
handler_facts: HandlerFacts<State, Params, Input>,
) -> ApiEndpointCtx<State, Params, Input> {
ApiEndpointCtx {
state: handler_facts.state,
params: handler_facts.params,
input: handler_facts.input,
pattern: handler_facts.pattern,
request_parts: Arc::clone(&self.request_parts),
task_ctx: handler_facts.task_ctx,
static_public_assets: Arc::clone(&self.static_public_assets),
page_hrefs: self.to_page_hrefs(),
latest_known_client_build_id: self.latest_known_client_build_id.clone(),
requested_client_build_id: self.requested_client_build_id.clone(),
}
}
}
pub(crate) struct HandlerFacts<State, Params, Input> {
pub(crate) state: Arc<State>,
pub(crate) params: Params,
pub(crate) input: Input,
pub(crate) pattern: &'static str,
pub(crate) task_ctx: TaskCtx<State>,
}