1use crate::{TextEndpoint, TextHandlerImpl};
4use alux_http::{HttpMethod, HttpSelectorAlg, RouteAlg, RoutePath, SelectorAlg, describe_path};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7enum TextSelectorPart {
8 Method(HttpMethod),
9 Path(RoutePath),
10 Prefix(RoutePath),
11}
12
13#[derive(Debug, Clone, Default, PartialEq, Eq)]
15pub struct TextSelector {
16 parts: Vec<TextSelectorPart>,
17}
18
19#[derive(Debug, Clone, PartialEq, Eq)]
20struct TextRouteEntry {
21 selector: TextSelector,
22 endpoint: TextEndpoint,
23}
24
25#[derive(Debug, Clone, Default, PartialEq, Eq)]
27pub struct TextRoute {
28 entries: Vec<TextRouteEntry>,
29}
30
31impl TextRoute {
32 pub fn labels(&self) -> Vec<String> {
34 self.entries.iter().map(|entry| entry.selector.label()).collect()
35 }
36
37 pub fn paths(&self) -> Vec<String> {
39 self.entries.iter().map(|entry| entry.selector.path()).collect()
40 }
41
42 pub fn lines(&self) -> Vec<String> {
44 self.entries
45 .iter()
46 .map(|entry| {
47 format!(
48 concat!(
49 "### {}\n",
50 "- `handler`: `{}`\n",
51 "- `inputs`: `{}`\n",
52 "- `args`: `{}`\n",
53 "- `result`: `{}`\n",
54 "- `transform`: `{}`\n",
55 "- `output`: `{}`",
56 ),
57 entry.selector.label(),
58 entry.endpoint.handler,
59 entry.endpoint.inputs,
60 entry.endpoint.args,
61 entry.endpoint.result,
62 entry.endpoint.transform,
63 entry.endpoint.output,
64 )
65 })
66 .collect()
67 }
68}
69
70impl TextSelector {
71 pub fn path(&self) -> String {
73 describe_path(self.parts.iter().filter_map(|part| match part {
74 TextSelectorPart::Path(path) | TextSelectorPart::Prefix(path) => Some(path),
75 TextSelectorPart::Method(_) => None,
76 }))
77 }
78
79 pub fn label(&self) -> String {
81 let method = self
82 .parts
83 .iter()
84 .rev()
85 .find_map(|part| match part {
86 TextSelectorPart::Method(method) => Some(method.label()),
87 TextSelectorPart::Path(_) | TextSelectorPart::Prefix(_) => None,
88 })
89 .unwrap_or("*");
90
91 format!("{method} {}", self.path())
92 }
93}
94
95impl SelectorAlg for TextHandlerImpl {
96 type Selector = TextSelector;
97
98 fn identity(&self) -> TextSelector {
99 TextSelector::default()
100 }
101
102 fn compose(&self, mut first: TextSelector, second: TextSelector) -> TextSelector {
103 first.parts.extend(second.parts);
104 first
105 }
106}
107
108impl RouteAlg for TextHandlerImpl {
109 type Route = TextRoute;
110 type Selector = TextSelector;
111 type Endpoint = TextEndpoint;
112
113 fn initial(&self) -> TextRoute {
114 TextRoute::default()
115 }
116
117 fn coproduct(&self, mut left: TextRoute, right: TextRoute) -> TextRoute {
118 left.entries.extend(right.entries);
119 left
120 }
121
122 fn precompose(&self, selector: TextSelector, mut route: TextRoute) -> TextRoute {
123 for entry in &mut route.entries {
124 entry.selector = self.compose(selector.clone(), core::mem::take(&mut entry.selector));
125 }
126 route
127 }
128
129 fn lift(&self, endpoint: TextEndpoint) -> TextRoute {
130 TextRoute { entries: vec![TextRouteEntry { selector: self.identity(), endpoint }] }
131 }
132}
133
134impl HttpSelectorAlg for TextHandlerImpl {
135 type Selector = TextSelector;
136
137 fn http_method(&self, method: HttpMethod) -> TextSelector {
138 TextSelector { parts: vec![TextSelectorPart::Method(method)] }
139 }
140
141 fn http_path(&self, path: &RoutePath) -> TextSelector {
142 TextSelector { parts: vec![TextSelectorPart::Path(path.clone())] }
143 }
144
145 fn http_prefix(&self, prefix: &RoutePath) -> TextSelector {
146 TextSelector { parts: vec![TextSelectorPart::Prefix(prefix.clone())] }
147 }
148}