use super::{Node, NodeContext, NodeKind, NodeRequest, NodeResponse}; use crate::{config::DynNode, error::ServiceError}; use futures::Future; use http::Uri; use regex::{Regex, RegexSet}; use serde_yml::Value; use std::{collections::BTreeMap, pin::Pin, sync::Arc}; pub struct PathsKind; struct Paths { matcher: RegexSet, extractors: Vec, handlers: Vec, } impl NodeKind for PathsKind { fn name(&self) -> &'static str { "paths" } fn instanciate(&self, config: Value) -> anyhow::Result> { let routes = serde_yml::from_value::>(config)? .into_iter() .collect::>(); let mut handlers = Vec::new(); let mut patterns = Vec::new(); let mut extractors = Vec::new(); for (k, v) in routes { let pattern = format!("^{k}$"); handlers.push(v); extractors.push(Regex::new(&pattern)?); patterns.push(pattern); } let matcher = RegexSet::new(&patterns)?; Ok(Arc::new(Paths { handlers, matcher, extractors, })) } } impl Node for Paths { fn handle<'a>( &'a self, context: &'a mut NodeContext, mut request: NodeRequest, ) -> Pin> + Send + Sync + 'a>> { Box::pin(async move { let path = request.uri().path(); let index = self .matcher .matches(path) .iter() .next() .ok_or(ServiceError::UnknownPath)?; let caps = self.extractors[index] .captures(path) .ok_or(ServiceError::Other)?; if let Some(rest) = caps.get(1) { let mut parts = http::uri::Parts::default(); parts.scheme = request.uri().scheme().cloned(); parts.authority = request.uri().authority().cloned(); parts.path_and_query = Some(rest.as_str().parse().map_err(|_| ServiceError::Other)?); *request.uri_mut() = Uri::from_parts(parts).map_err(|_| ServiceError::InvalidUri)? } self.handlers[index].handle(context, request).await }) } }