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 {
139 fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
141}
142
143impl<I, T: Display> ToRouteSegments for I
145where
146 for<'a> &'a I: IntoIterator<Item = &'a T>,
147{
148 fn display_route_segments(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
149 for segment in self {
150 write!(f, "/")?;
151 let segment = segment.to_string();
152 let encoded = urlencoding::encode(&segment);
153 write!(f, "{encoded}")?;
154 }
155 Ok(())
156 }
157}
158
159#[test]
160fn to_route_segments() {
161 struct DisplaysRoute;
162
163 impl Display for DisplaysRoute {
164 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
165 let segments = vec!["hello", "world"];
166 segments.display_route_segments(f)
167 }
168 }
169
170 assert_eq!(DisplaysRoute.to_string(), "/hello/world");
171}
172
173#[rustversion::attr(
174 since(1.78.0),
175 diagnostic::on_unimplemented(
176 message = "`FromRouteSegments` is not implemented for `{Self}`",
177 label = "spread route segments",
178 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."
179 )
180)]
181pub trait FromRouteSegments: Sized {
182 type Err: std::fmt::Display;
184
185 fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err>;
189}
190
191impl<I: std::iter::FromIterator<String>> FromRouteSegments for I {
192 type Err = <String as FromRouteSegment>::Err;
193
194 fn from_route_segments(segments: &[&str]) -> Result<Self, Self::Err> {
195 segments
196 .iter()
197 .map(|s| String::from_route_segment(s))
198 .collect()
199 }
200}
201
202type SiteMapFlattened<'a> = FlatMap<
205 Iter<'a, SiteMapSegment>,
206 Vec<Vec<SegmentType>>,
207 fn(&SiteMapSegment) -> Vec<Vec<SegmentType>>,
208>;
209
210#[rustversion::attr(
211 since(1.78.0),
212 diagnostic::on_unimplemented(
213 message = "`Routable` is not implemented for `{Self}`",
214 label = "Route",
215 note = "Routable should generally be derived using the `#[derive(Routable)]` macro."
216 )
217)]
218pub trait Routable: FromStr<Err: Display> + Display + Clone + 'static {
219 const SITE_MAP: &'static [SiteMapSegment];
221
222 fn render(&self, level: usize) -> Element;
226
227 fn is_child_of(&self, other: &Self) -> bool {
231 let self_str = self.to_string();
232 let self_str = self_str
233 .split_once('#')
234 .map(|(route, _)| route)
235 .unwrap_or(&self_str);
236 let self_str = self_str
237 .split_once('?')
238 .map(|(route, _)| route)
239 .unwrap_or(self_str);
240 let self_str = self_str.trim_end_matches('/');
241 let other_str = other.to_string();
242 let other_str = other_str
243 .split_once('#')
244 .map(|(route, _)| route)
245 .unwrap_or(&other_str);
246 let other_str = other_str
247 .split_once('?')
248 .map(|(route, _)| route)
249 .unwrap_or(other_str);
250 let other_str = other_str.trim_end_matches('/');
251
252 let mut self_segments = self_str.split('/');
253 let mut other_segments = other_str.split('/');
254 loop {
255 match (self_segments.next(), other_segments.next()) {
256 (None, Some(_)) | (None, None) => {
259 return false;
260 }
261 (Some(self_seg), Some(other_seg)) => {
263 if self_seg != other_seg {
264 return false;
265 }
266 }
267 (Some(_), None) => break,
269 }
270 }
271 true
272 }
273
274 fn parent(&self) -> Option<Self> {
276 let as_str = self.to_string();
277 let (route_and_query, _) = as_str.split_once('#').unwrap_or((&as_str, ""));
278 let (route, _) = route_and_query
279 .split_once('?')
280 .unwrap_or((route_and_query, ""));
281 let route = route.trim_end_matches('/');
282 let segments = route.split_inclusive('/');
283 let segment_count = segments.clone().count();
284 let new_route: String = segments.take(segment_count.saturating_sub(1)).collect();
285 Self::from_str(&new_route).ok()
286 }
287
288 fn flatten_site_map<'a>() -> SiteMapFlattened<'a> {
290 Self::SITE_MAP.iter().flat_map(SiteMapSegment::flatten)
291 }
292
293 fn static_routes() -> Vec<Self> {
297 Self::flatten_site_map()
298 .filter_map(|segments| {
299 let mut route = String::new();
300 for segment in segments.iter() {
301 match segment {
302 SegmentType::Static(s) => {
303 route.push('/');
304 route.push_str(s)
305 }
306 SegmentType::Child => {}
307 _ => return None,
308 }
309 }
310
311 route.parse().ok()
312 })
313 .collect()
314 }
315}
316
317#[derive(Debug, Clone, PartialEq)]
319pub struct SiteMapSegment {
320 pub segment_type: SegmentType,
322 pub children: &'static [SiteMapSegment],
324}
325
326impl SiteMapSegment {
327 pub fn flatten(&self) -> Vec<Vec<SegmentType>> {
329 let mut routes = Vec::new();
330 self.flatten_inner(&mut routes, Vec::new());
331 routes
332 }
333
334 fn flatten_inner(&self, routes: &mut Vec<Vec<SegmentType>>, current: Vec<SegmentType>) {
335 let mut current = current;
336 current.push(self.segment_type.clone());
337 if self.children.is_empty() {
338 routes.push(current);
339 } else {
340 for child in self.children {
341 child.flatten_inner(routes, current.clone());
342 }
343 }
344 }
345}
346
347#[derive(Debug, Clone, PartialEq)]
349#[non_exhaustive]
350pub enum SegmentType {
351 Static(&'static str),
353 Dynamic(&'static str),
355 CatchAll(&'static str),
357 Child,
359}
360
361impl SegmentType {
362 pub fn to_static(&self) -> Option<&'static str> {
364 match self {
365 SegmentType::Static(s) => Some(*s),
366 _ => None,
367 }
368 }
369
370 pub fn to_dynamic(&self) -> Option<&'static str> {
372 match self {
373 SegmentType::Dynamic(s) => Some(*s),
374 _ => None,
375 }
376 }
377
378 pub fn to_catch_all(&self) -> Option<&'static str> {
380 match self {
381 SegmentType::CatchAll(s) => Some(*s),
382 _ => None,
383 }
384 }
385
386 pub fn to_child(&self) -> Option<()> {
388 match self {
389 SegmentType::Child => Some(()),
390 _ => None,
391 }
392 }
393}
394
395impl Display for SegmentType {
396 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
397 match &self {
398 SegmentType::Static(s) => write!(f, "/{s}"),
399 SegmentType::Child => Ok(()),
400 SegmentType::Dynamic(s) => write!(f, "/:{s}"),
401 SegmentType::CatchAll(s) => write!(f, "/:..{s}"),
402 }
403 }
404}