use crate::{TextEndpoint, TextHandlerImpl};
use alux_http::{HttpSelectorAlg, RouteAlg, SelectorAlg, append_path};
#[derive(Debug, Clone, PartialEq, Eq)]
enum TextSelectorPart {
Method(&'static str),
Path(String),
Prefix(String),
}
#[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 {
let mut path = String::new();
for part in &self.parts {
if let TextSelectorPart::Path(value) | TextSelectorPart::Prefix(value) = part {
append_path(&mut path, value);
}
}
if path.is_empty() {
path.push('/');
}
path
}
pub fn label(&self) -> String {
let method = self
.parts
.iter()
.rev()
.find_map(|part| match part {
TextSelectorPart::Method(value) => Some(*value),
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_get(&self) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Method("GET")] }
}
fn http_post(&self) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Method("POST")] }
}
fn http_path(&self, path: &str) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Path(path.into())] }
}
fn http_prefix(&self, prefix: &str) -> TextSelector {
TextSelector { parts: vec![TextSelectorPart::Prefix(prefix.into())] }
}
}