yew_router_nested/
switch.rs1use crate::route::Route;
3use std::fmt::Write;
4
5#[allow(bare_trait_objects)]
9pub type Routable = Switch;
10
11pub trait Switch: Sized {
51 fn switch<STATE>(route: Route<STATE>) -> Option<Self> {
53 Self::from_route_part(route.route, Some(route.state)).0
54 }
55
56 fn from_route_part<STATE>(part: String, state: Option<STATE>) -> (Option<Self>, Option<STATE>);
58
59 fn build_route_section<STATE>(self, route: &mut String) -> Option<STATE>;
61
62 fn key_not_available() -> Option<Self> {
71 None
72 }
73}
74
75#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
82pub struct LeadingSlash<T>(pub T);
83impl<U: Switch> Switch for LeadingSlash<U> {
84 fn from_route_part<STATE>(part: String, state: Option<STATE>) -> (Option<Self>, Option<STATE>) {
85 if let Some(part) = part.strip_prefix('/') {
86 let (inner, state) = U::from_route_part(part.to_owned(), state);
87 (inner.map(LeadingSlash), state)
88 } else {
89 (None, None)
90 }
91 }
92
93 fn build_route_section<T>(self, route: &mut String) -> Option<T> {
94 write!(route, "/").ok()?;
95 self.0.build_route_section(route)
96 }
97}
98
99#[derive(Debug, PartialEq, Clone, Copy)]
101pub struct Permissive<U>(pub Option<U>);
102
103impl<U: Switch> Switch for Permissive<U> {
104 fn from_route_part<STATE>(part: String, state: Option<STATE>) -> (Option<Self>, Option<STATE>) {
106 let (inner, inner_state) = U::from_route_part(part, state);
107 if inner.is_some() {
108 (Some(Permissive(inner)), inner_state)
109 } else {
110 (Some(Permissive(None)), None)
112 }
113 }
114
115 fn build_route_section<STATE>(self, route: &mut String) -> Option<STATE> {
116 if let Some(inner) = self.0 {
117 inner.build_route_section(route)
118 } else {
119 None
120 }
121 }
122
123 fn key_not_available() -> Option<Self> {
124 Some(Permissive(None))
125 }
126}
127
128#[derive(Debug, PartialEq, Clone, Copy)]
134pub struct AllowMissing<U: std::fmt::Debug>(pub Option<U>);
135impl<U: Switch + std::fmt::Debug> Switch for AllowMissing<U> {
136 fn from_route_part<STATE>(part: String, state: Option<STATE>) -> (Option<Self>, Option<STATE>) {
137 let route = part.clone();
138 let (inner, inner_state) = U::from_route_part(part, state);
139
140 if inner.is_some() {
141 (Some(AllowMissing(inner)), inner_state)
142 } else if route.is_empty()
143 || route.starts_with('/')
144 || route.starts_with('?')
145 || route.starts_with('&')
146 || route.starts_with('#')
147 {
148 (Some(AllowMissing(None)), inner_state)
149 } else {
150 (None, None)
151 }
152 }
153
154 fn build_route_section<STATE>(self, route: &mut String) -> Option<STATE> {
155 if let AllowMissing(Some(inner)) = self {
156 inner.build_route_section(route)
157 } else {
158 None
159 }
160 }
161}
162
163fn build_route_from_switch<SW: Switch, STATE: Default>(switch: SW) -> Route<STATE> {
165 let mut buf = String::with_capacity(255);
170 let state: STATE = switch.build_route_section(&mut buf).unwrap_or_default();
171 buf.shrink_to_fit();
172
173 Route { route: buf, state }
174}
175
176impl<SW: Switch, STATE: Default> From<SW> for Route<STATE> {
177 fn from(switch: SW) -> Self {
178 build_route_from_switch(switch)
179 }
180}
181
182impl<T: std::str::FromStr + std::fmt::Display> Switch for T {
183 fn from_route_part<U>(part: String, state: Option<U>) -> (Option<Self>, Option<U>) {
184 (::std::str::FromStr::from_str(&part).ok(), state)
185 }
186
187 fn build_route_section<U>(self, route: &mut String) -> Option<U> {
188 write!(route, "{}", self).expect("Writing to string should never fail.");
189 None
190 }
191}
192
193#[cfg(test)]
194mod test {
195 use super::*;
196 #[test]
197 fn isize_build_route() {
198 let mut route = "/".to_string();
199 let mut _state: Option<String> = None;
200 _state = _state.or_else(|| (-432isize).build_route_section(&mut route));
201 assert_eq!(route, "/-432".to_string());
202 }
203
204 #[test]
205 fn can_get_string_from_empty_str() {
206 let (s, _state) = String::from_route_part::<()>("".to_string(), Some(()));
207 assert_eq!(s, Some("".to_string()))
208 }
209
210 #[test]
211 fn uuid_from_route() {
212 let x = uuid::Uuid::switch::<()>(Route {
213 route: "5dc48134-35b5-4b8c-aa93-767bf00ae1d8".to_string(),
214 state: (),
215 });
216 assert!(x.is_some())
217 }
218 #[test]
219 fn uuid_to_route() {
220 use std::str::FromStr;
221 let id =
222 uuid::Uuid::from_str("5dc48134-35b5-4b8c-aa93-767bf00ae1d8").expect("should parse");
223 let mut buf = String::new();
224 id.build_route_section::<()>(&mut buf);
225 assert_eq!(buf, "5dc48134-35b5-4b8c-aa93-767bf00ae1d8".to_string())
226 }
227
228 #[test]
229 fn can_get_option_string_from_empty_str() {
230 let (s, _state): (Option<Permissive<String>>, Option<()>) =
231 Permissive::from_route_part("".to_string(), Some(()));
232 assert_eq!(s, Some(Permissive(Some("".to_string()))))
233 }
234}