1#![allow(non_snake_case)]
4use std::{
5 fmt::Display,
6 iter::FlatMap,
7 slice::Iter,
8 str::FromStr,
9};
10
11use freya_core::integration::Element;
12
13#[derive(Debug, PartialEq)]
15pub struct RouteParseError<E: Display> {
16 pub attempted_routes: Vec<E>,
18}
19
20impl<E: Display> Display for RouteParseError<E> {
21 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
22 write!(f, "Route did not match:\nAttempted Matches:\n")?;
23 for (i, route) in self.attempted_routes.iter().enumerate() {
24 writeln!(f, "{}) {route}", i + 1)?;
25 }
26 Ok(())
27 }
28}
29
30#[rustversion::attr(
31 since(1.78.0),
32 diagnostic::on_unimplemented(
33 message = "`FromQuery` is not implemented for `{Self}`",
34 label = "spread query",
35 note = "FromQuery is automatically implemented for types that implement `From<&str>`. You need to either implement From<&str> or implement FromQuery manually."
36 )
37)]
38pub trait FromQuery {
39 fn from_query(query: &str) -> Self;
41}
42
43impl<T: for<'a> From<&'a str>> FromQuery for T {
44 fn from_query(query: &str) -> Self {
45 T::from(query)
46 }
47}
48
49#[rustversion::attr(
50 since(1.78.0),
51 diagnostic::on_unimplemented(
52 message = "`FromQueryArgument` is not implemented for `{Self}`",
53 label = "query argument",
54 note = "FromQueryArgument is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromQueryArgument manually."
55 )
56)]
57pub trait FromQueryArgument: Default {
58 type Err;
60
61 fn from_query_argument(argument: &str) -> Result<Self, Self::Err>;
63}
64
65impl<T: Default + FromStr> FromQueryArgument for T
66where
67 <T as FromStr>::Err: Display,
68{
69 type Err = <T as FromStr>::Err;
70
71 fn from_query_argument(argument: &str) -> Result<Self, Self::Err> {
72 match T::from_str(argument) {
73 Ok(result) => Ok(result),
74 Err(err) => Err(err),
75 }
76 }
77}
78
79#[rustversion::attr(
80 since(1.78.0),
81 diagnostic::on_unimplemented(
82 message = "`FromHashFragment` is not implemented for `{Self}`",
83 label = "hash fragment",
84 note = "FromHashFragment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromHashFragment manually."
85 )
86)]
87pub trait FromHashFragment {
88 fn from_hash_fragment(hash: &str) -> Self;
90}
91
92impl<T> FromHashFragment for T
93where
94 T: FromStr + Default,
95 T::Err: std::fmt::Display,
96{
97 fn from_hash_fragment(hash: &str) -> Self {
98 T::from_str(hash).unwrap_or_default()
99 }
100}
101
102#[rustversion::attr(
103 since(1.78.0),
104 diagnostic::on_unimplemented(
105 message = "`FromRouteSegment` is not implemented for `{Self}`",
106 label = "route segment",
107 note = "FromRouteSegment is automatically implemented for types that implement `FromStr` and `Default`. You need to either implement FromStr and Default or implement FromRouteSegment manually."
108 )
109)]
110pub trait FromRouteSegment: Sized {
111 type Err;
113
114 fn from_route_segment(route: &str) -> Result<Self, Self::Err>;
116}
117
118impl<T: FromStr> FromRouteSegment for T
119where
120 <T as FromStr>::Err: Display,
121{
122 type Err = <T as FromStr>::Err;
123
124 fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
125 T::from_str(route)
126 }
127}
128
129#[test]
130fn full_circle() {
131 let route = "testing 1234 hello world";
132 assert_eq!(String::from_route_segment(route).unwrap(), route);
133}
134
135pub trait ToRouteSegments {
136 fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
138}
139
140impl<I, T: Display> ToRouteSegments for I
142where
143 for<'a> &'a I: IntoIterator<Item = &'a T>,
144{
145 fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
146 for segment in self {
147 write!(f, "/")?;
148 let segment = segment.to_string();
149 let encoded = urlencoding::encode(&segment);
150 write!(f, "{encoded}")?;
151 }
152 Ok(())
153 }
154}
155
156#[test]
157fn to_route_segments() {
158 struct DisplaysRoute;
159
160 impl Display for DisplaysRoute {
161 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
162 let segments = vec!["hello", "world"];
163 segments.display_route_segments(f)
164 }
165 }
166
167 assert_eq!(DisplaysRoute.to_string(), "/hello/world");
168}
169
170#[rustversion::attr(
171 since(1.78.0),
172 diagnostic::on_unimplemented(
173 message = "`FromRouteSegments` is not implemented for `{Self}`",
174 label = "spread route segments",
175 note = "FromRouteSegments is automatically implemented for types that implement `FromIterator` with an `Item` type that implements `Display`. You need to either implement FromIterator or implement FromRouteSegments manually."
176 )
177)]
178pub trait FromRouteSegments: Sized {
179 type Err: std::fmt::Display;
181
182 fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
186}
187
188impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
189 type Err = <String as FromRouteSegment>::Err;
190
191 fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
192 segments
193 .iter()
194 .map(|s| String::from_route_segment(s))
195 .collect()
196 }
197}
198
199type SiteMapFlattened<'a> = FlatMap<
202 Iter<'a, SiteMapSegment>,
203 Vec<Vec<SegmentType>>,
204 fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
205>;
206
207#[rustversion::attr(
208 since(1.78.0),
209 diagnostic::on_unimplemented(
210 message = "`Routable` is not implemented for `{Self}`",
211 label = "Route",
212 note = "Routable should generally be derived using the `#[derive(Routable)]` macro."
213 )
214)]
215pub trait Routable: FromStr<Err: Display> + Display + Clone + 'static {
216 const SITE_MAP: &'static [SiteMapSegment];
218
219 fn render(&self, level: usize) -> Element;
221
222 fn is_child_of(&self, other: &Self) -> bool {
224 let self_str = self.to_string();
225 let self_str = self_str
226 .split_once('#')
227 .map(|(route, _)| route)
228 .unwrap_or(&self_str);
229 let self_str = self_str
230 .split_once('?')
231 .map(|(route, _)| route)
232 .unwrap_or(self_str);
233 let self_str = self_str.trim_end_matches('/');
234 let other_str = other.to_string();
235 let other_str = other_str
236 .split_once('#')
237 .map(|(route, _)| route)
238 .unwrap_or(&other_str);
239 let other_str = other_str
240 .split_once('?')
241 .map(|(route, _)| route)
242 .unwrap_or(other_str);
243 let other_str = other_str.trim_end_matches('/');
244
245 let mut self_segments = self_str.split('/');
246 let mut other_segments = other_str.split('/');
247 loop {
248 match (self_segments.next(), other_segments.next()) {
249 (None, Some(_)) | (None, None) => {
252 return false;
253 }
254 (Some(self_seg), Some(other_seg)) => {
256 if self_seg != other_seg {
257 return false;
258 }
259 }
260 (Some(_), None) => break,
262 }
263 }
264 true
265 }
266
267 fn parent(&self) -> Option<Self> {
269 let as_str = self.to_string();
270 let (route_and_query, _) = as_str.split_once('#').unwrap_or((&as_str, ""));
271 let (route, _) = route_and_query
272 .split_once('?')
273 .unwrap_or((route_and_query, ""));
274 let route = route.trim_end_matches('/');
275 let segments = route.split_inclusive('/');
276 let segment_count = segments.clone().count();
277 let new_route: String = segments.take(segment_count.saturating_sub(1)).collect();
278 Self::from_str(&new_route).ok()
279 }
280
281 fn flatten_site_map<'a>() -> SiteMapFlattened<'a> {
283 Self::SITE_MAP.iter().flat_map(SiteMapSegment::flatten)
284 }
285
286 fn static_routes() -> Vec<Self> {
289 Self::flatten_site_map()
290 .filter_map(|segments| {
291 let mut route = String::new();
292 for segment in segments.iter() {
293 match segment {
294 SegmentType::Static(s) => {
295 route.push('/');
296 route.push_str(s)
297 }
298 SegmentType::Child => {}
299 _ => return None,
300 }
301 }
302
303 route.parse().ok()
304 })
305 .collect()
306 }
307}
308
309#[derive(Debug, Clone, PartialEq)]
311pub struct SiteMapSegment {
312 pub segment_type: SegmentType,
314 pub children: &'static [SiteMapSegment],
316}
317
318impl SiteMapSegment {
319 pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
321 let mut routes = Vec::new();
322 self.flatten_inner(&mut routes, Vec::new());
323 routes
324 }
325
326 fn flatten_inner(&self, routes: &mut Vec<Vec<SegmentType>>, current: Vec<SegmentType>) {
327 let mut current = current;
328 current.push(self.segment_type.clone());
329 if self.children.is_empty() {
330 routes.push(current);
331 } else {
332 for child in self.children {
333 child.flatten_inner(routes, current.clone());
334 }
335 }
336 }
337}
338
339#[derive(Debug, Clone, PartialEq)]
341#[non_exhaustive]
342pub enum SegmentType {
343 Static(&'static str),
345 Dynamic(&'static str),
347 CatchAll(&'static str),
349 Child,
351}
352
353impl SegmentType {
354 pub fn to_static(&self) -> Option<&'static str> {
356 match self {
357 SegmentType::Static(s) => Some(*s),
358 _ => None,
359 }
360 }
361
362 pub fn to_dynamic(&self) -> Option<&'static str> {
364 match self {
365 SegmentType::Dynamic(s) => Some(*s),
366 _ => None,
367 }
368 }
369
370 pub fn to_catch_all(&self) -> Option<&'static str> {
372 match self {
373 SegmentType::CatchAll(s) => Some(*s),
374 _ => None,
375 }
376 }
377
378 pub fn to_child(&self) -> Option<()> {
380 match self {
381 SegmentType::Child => Some(()),
382 _ => None,
383 }
384 }
385}
386
387impl Display for SegmentType {
388 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
389 match &self {
390 SegmentType::Static(s) => write!(f, "/{s}"),
391 SegmentType::Child => Ok(()),
392 SegmentType::Dynamic(s) => write!(f, "/:{s}"),
393 SegmentType::CatchAll(s) => write!(f, "/:..{s}"),
394 }
395 }
396}