use crate::{TextEndpoint, TextHandlerImpl};
use alux_http::{HttpMethod, HttpSelectorAlg, RouteAlg, RoutePath, SelectorAlg, describe_path};
#[derive(Debug, Clone, PartialEq, Eq)]
enum TextSelectorPart {
Method(HttpMethod),
Path(RoutePath),
Prefix(RoutePath),
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TextSelector {
parts: Vec<TextSelectorPart>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct TextRouteEntry {
selector: TextSelector,
endpoint: TextEndpoint,
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct TextRoute {
entries: Vec<TextRouteEntry>,
}
impl TextRoute {
pub fn labels(&self) -> Vec<String> {
self.entries.iter().map(|entry| entry.selector.label()).collect()
}
pub fn paths(&self) -> Vec<String> {
self.entries.iter().map(|entry| entry.selector.path()).collect()
}
pub fn lines(&self) -> Vec<String> {
self.entries
.iter()
.map(|entry| {
format!(
concat!(
"### {}\n",
"- `handler`: `{}`\n",
"- `inputs`: `{}`\n",
"- `args`: `{}`\n",
"- `result`: `{}`\n",
"- `transform`: `{}`\n",
"- `output`: `{}`",
),
entry.selector.label(),
entry.endpoint.handler,
entry.endpoint.inputs,
entry.endpoint.args,
entry.endpoint.result,
entry.endpoint.transform,
entry.endpoint.output,
)
})
.collect()
}
}
impl TextSelector {
pub fn path(&self) -> String {
describe_path(self.parts.iter().filter_map(|part| match part {
TextSelectorPart::Path(path) | TextSelectorPart::Prefix(path) => Some(path),
TextSelectorPart::Method(_) => None,
}))
}
pub fn label(&self) -> String {
let method = self
.parts
.iter()
.rev()
.find_map(|part| match part {
TextSelectorPart::Method(method) => Some(method.label()),
TextSelectorPart::Path(_) | TextSelectorPart::Prefix(_) => None,
})
.unwrap_or("*");
format!("{method} {}", self.path())
}
}
impl SelectorAlg for TextHandlerImpl {
type Selector = TextSelector;
fn identity(&self) -> TextSelector {
TextSelector::default()
}
fn compose(&self, mut first: TextSelector, second: TextSelector) -> TextSelector {
first.parts.extend(second.parts);
first
}
}
impl RouteAlg for TextHandlerImpl {
type Route = TextRoute;
type Selector = TextSelector;
type Endpoint = TextEndpoint;
fn initial(&self) -> TextRoute {
TextRoute::default()
}
fn coproduct(&self, mut left: TextRoute, right: TextRoute) -> TextRoute {
left.entries.extend(right.entries);
left
}
fn precompose(&self, selector: TextSelector, mut route: TextRoute) -> TextRoute {
for entry in &mut route.entries {
entry.selector = self.compose(selector.clone(), core::mem::take(&mut entry.selector));
}
route
}
fn lift(&self, endpoint: TextEndpoint) -> TextRoute {
TextRoute { entries: vec![TextRouteEntry { selector: self.identity(), endpoint }] }
}
}
impl HttpSelectorAlg for TextHandlerImpl {
type Selector = TextSelector;
fn http_method(&self, method: HttpMethod) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Method(method)] }
}
fn http_path(&self, path: &RoutePath) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Path(path.clone())] }
}
fn http_prefix(&self, prefix: &RoutePath) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Prefix(prefix.clone())] }
}
}