router.rs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
  1. use std::{
  2. any::Any,
  3. collections::HashSet,
  4. rc::Rc,
  5. sync::{Arc, RwLock, RwLockWriteGuard},
  6. };
  7. use dioxus::prelude::*;
  8. use crate::{
  9. navigation::NavigationTarget,
  10. prelude::{AnyHistoryProvider, IntoRoutable},
  11. routable::Routable,
  12. router_cfg::RouterConfig,
  13. };
  14. /// An error that can occur when navigating.
  15. #[derive(Debug, Clone)]
  16. pub struct ExternalNavigationFailure(String);
  17. /// A function the router will call after every routing update.
  18. pub(crate) type RoutingCallback<R> =
  19. Arc<dyn Fn(GenericRouterContext<R>) -> Option<NavigationTarget<R>>>;
  20. pub(crate) type AnyRoutingCallback =
  21. Arc<dyn Fn(RouterContext) -> Option<NavigationTarget<Rc<dyn Any>>>>;
  22. struct MutableRouterState {
  23. /// The current prefix.
  24. prefix: Option<String>,
  25. history: Box<dyn AnyHistoryProvider>,
  26. unresolved_error: Option<ExternalNavigationFailure>,
  27. }
  28. /// A collection of router data that manages all routing functionality.
  29. #[derive(Clone)]
  30. pub struct RouterContext {
  31. state: Arc<RwLock<MutableRouterState>>,
  32. subscribers: Arc<RwLock<HashSet<ScopeId>>>,
  33. subscriber_update: Arc<dyn Fn(ScopeId)>,
  34. routing_callback: Option<AnyRoutingCallback>,
  35. failure_external_navigation: fn(Scope) -> Element,
  36. any_route_to_string: fn(&dyn Any) -> String,
  37. }
  38. impl RouterContext {
  39. pub(crate) fn new<R: Routable + 'static>(
  40. mut cfg: RouterConfig<R>,
  41. mark_dirty: Arc<dyn Fn(ScopeId) + Sync + Send>,
  42. ) -> Self
  43. where
  44. R: Clone,
  45. <R as std::str::FromStr>::Err: std::fmt::Display,
  46. {
  47. let state = Arc::new(RwLock::new(MutableRouterState {
  48. prefix: Default::default(),
  49. history: cfg.take_history(),
  50. unresolved_error: None,
  51. }));
  52. let subscriber_update = mark_dirty.clone();
  53. let subscribers = Arc::new(RwLock::new(HashSet::new()));
  54. let myself = Self {
  55. state,
  56. subscribers: subscribers.clone(),
  57. subscriber_update,
  58. routing_callback: cfg.on_update.map(|update| {
  59. Arc::new(move |ctx| {
  60. let ctx = GenericRouterContext {
  61. inner: ctx,
  62. _marker: std::marker::PhantomData,
  63. };
  64. update(ctx).map(|t| match t {
  65. NavigationTarget::Internal(r) => {
  66. NavigationTarget::Internal(Rc::new(r) as Rc<dyn Any>)
  67. }
  68. NavigationTarget::External(s) => NavigationTarget::External(s),
  69. })
  70. })
  71. as Arc<dyn Fn(RouterContext) -> Option<NavigationTarget<Rc<dyn Any>>>>
  72. }),
  73. failure_external_navigation: cfg.failure_external_navigation,
  74. any_route_to_string: |route| {
  75. route
  76. .downcast_ref::<R>()
  77. .unwrap_or_else(|| {
  78. panic!(
  79. "Route is not of the expected type: {}\n found typeid: {:?}\n expected typeid: {:?}",
  80. std::any::type_name::<R>(),
  81. route.type_id(),
  82. std::any::TypeId::of::<R>()
  83. )
  84. })
  85. .to_string()
  86. },
  87. };
  88. // set the updater
  89. {
  90. let mut state = myself.state.write().unwrap();
  91. state.history.updater(Arc::new(move || {
  92. for &id in subscribers.read().unwrap().iter() {
  93. (mark_dirty)(id);
  94. }
  95. }));
  96. }
  97. myself
  98. }
  99. pub(crate) fn route_from_str(&self, route: &str) -> Result<Rc<dyn Any>, String> {
  100. let state = self.state.read().unwrap();
  101. state.history.parse_route(route)
  102. }
  103. /// Check whether there is a previous page to navigate back to.
  104. #[must_use]
  105. pub fn can_go_back(&self) -> bool {
  106. self.state.read().unwrap().history.can_go_back()
  107. }
  108. /// Check whether there is a future page to navigate forward to.
  109. #[must_use]
  110. pub fn can_go_forward(&self) -> bool {
  111. self.state.read().unwrap().history.can_go_forward()
  112. }
  113. /// Go back to the previous location.
  114. ///
  115. /// Will fail silently if there is no previous location to go to.
  116. pub fn go_back(&self) {
  117. {
  118. self.state.write().unwrap().history.go_back();
  119. }
  120. self.change_route();
  121. }
  122. /// Go back to the next location.
  123. ///
  124. /// Will fail silently if there is no next location to go to.
  125. pub fn go_forward(&self) {
  126. {
  127. self.state.write().unwrap().history.go_forward();
  128. }
  129. self.change_route();
  130. }
  131. pub(crate) fn push_any(
  132. &self,
  133. target: NavigationTarget<Rc<dyn Any>>,
  134. ) -> Option<ExternalNavigationFailure> {
  135. match target {
  136. NavigationTarget::Internal(p) => {
  137. let mut state = self.state_mut();
  138. state.history.push(p)
  139. }
  140. NavigationTarget::External(e) => return self.external(e),
  141. }
  142. self.change_route()
  143. }
  144. /// Push a new location.
  145. ///
  146. /// The previous location will be available to go back to.
  147. pub fn push(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> {
  148. let target = self.resolve_into_routable(target.into());
  149. match target {
  150. NavigationTarget::Internal(p) => {
  151. let mut state = self.state_mut();
  152. state.history.push(Rc::new(p))
  153. }
  154. NavigationTarget::External(e) => return self.external(e),
  155. }
  156. self.change_route()
  157. }
  158. /// Replace the current location.
  159. ///
  160. /// The previous location will **not** be available to go back to.
  161. pub fn replace(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> {
  162. let target = self.resolve_into_routable(target.into());
  163. {
  164. let mut state = self.state_mut();
  165. match target {
  166. NavigationTarget::Internal(p) => state.history.replace(Rc::new(p)),
  167. NavigationTarget::External(e) => return self.external(e),
  168. }
  169. }
  170. self.change_route()
  171. }
  172. /// The route that is currently active.
  173. pub fn current<R: Routable>(&self) -> R {
  174. self.state
  175. .read()
  176. .unwrap()
  177. .history
  178. .current_route()
  179. .downcast::<R>()
  180. .unwrap()
  181. .as_ref()
  182. .clone()
  183. }
  184. /// The route that is currently active.
  185. pub fn current_route_string(&self) -> String {
  186. self.any_route_to_string(&*self.state.read().unwrap().history.current_route())
  187. }
  188. pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String {
  189. (self.any_route_to_string)(route)
  190. }
  191. pub(crate) fn resolve_into_routable(
  192. &self,
  193. into_routable: IntoRoutable,
  194. ) -> NavigationTarget<Rc<dyn Any>> {
  195. match into_routable {
  196. IntoRoutable::FromStr(url) => {
  197. let parsed_route: NavigationTarget<Rc<dyn Any>> = match self.route_from_str(&url) {
  198. Ok(route) => NavigationTarget::Internal(route),
  199. Err(err) => NavigationTarget::External(err),
  200. };
  201. parsed_route
  202. }
  203. IntoRoutable::Route(route) => NavigationTarget::Internal(route),
  204. }
  205. }
  206. /// The prefix that is currently active.
  207. pub fn prefix(&self) -> Option<String> {
  208. self.state.read().unwrap().prefix.clone()
  209. }
  210. fn external(&self, external: String) -> Option<ExternalNavigationFailure> {
  211. let mut state = self.state_mut();
  212. match state.history.external(external.clone()) {
  213. true => None,
  214. false => {
  215. let failure = ExternalNavigationFailure(external);
  216. state.unresolved_error = Some(failure.clone());
  217. self.update_subscribers();
  218. Some(failure)
  219. }
  220. }
  221. }
  222. fn state_mut(&self) -> RwLockWriteGuard<MutableRouterState> {
  223. self.state.write().unwrap()
  224. }
  225. /// Manually subscribe to the current route
  226. pub fn subscribe(&self, id: ScopeId) {
  227. self.subscribers.write().unwrap().insert(id);
  228. }
  229. /// Manually unsubscribe from the current route
  230. pub fn unsubscribe(&self, id: ScopeId) {
  231. self.subscribers.write().unwrap().remove(&id);
  232. }
  233. fn update_subscribers(&self) {
  234. for &id in self.subscribers.read().unwrap().iter() {
  235. (self.subscriber_update)(id);
  236. }
  237. }
  238. /// Clear any unresolved errors
  239. pub fn clear_error(&self) {
  240. self.state.write().unwrap().unresolved_error = None;
  241. self.update_subscribers();
  242. }
  243. pub(crate) fn render_error<'a>(&self, cx: Scope<'a>) -> Element<'a> {
  244. self.state
  245. .read()
  246. .unwrap()
  247. .unresolved_error
  248. .as_ref()
  249. .and_then(|_| (self.failure_external_navigation)(cx))
  250. }
  251. fn change_route(&self) -> Option<ExternalNavigationFailure> {
  252. if let Some(callback) = &self.routing_callback {
  253. let myself = self.clone();
  254. if let Some(new) = callback(myself) {
  255. let mut state = self.state_mut();
  256. match new {
  257. NavigationTarget::Internal(p) => state.history.replace(p),
  258. NavigationTarget::External(e) => return self.external(e),
  259. }
  260. }
  261. }
  262. self.update_subscribers();
  263. None
  264. }
  265. }
  266. pub struct GenericRouterContext<R> {
  267. inner: RouterContext,
  268. _marker: std::marker::PhantomData<R>,
  269. }
  270. impl<R> GenericRouterContext<R>
  271. where
  272. R: Routable,
  273. {
  274. /// Check whether there is a previous page to navigate back to.
  275. #[must_use]
  276. pub fn can_go_back(&self) -> bool {
  277. self.inner.can_go_back()
  278. }
  279. /// Check whether there is a future page to navigate forward to.
  280. #[must_use]
  281. pub fn can_go_forward(&self) -> bool {
  282. self.inner.can_go_forward()
  283. }
  284. /// Go back to the previous location.
  285. ///
  286. /// Will fail silently if there is no previous location to go to.
  287. pub fn go_back(&self) {
  288. self.inner.go_back();
  289. }
  290. /// Go back to the next location.
  291. ///
  292. /// Will fail silently if there is no next location to go to.
  293. pub fn go_forward(&self) {
  294. self.inner.go_forward();
  295. }
  296. /// Push a new location.
  297. ///
  298. /// The previous location will be available to go back to.
  299. pub fn push(
  300. &self,
  301. target: impl Into<NavigationTarget<R>>,
  302. ) -> Option<ExternalNavigationFailure> {
  303. self.inner.push(target.into())
  304. }
  305. /// Replace the current location.
  306. ///
  307. /// The previous location will **not** be available to go back to.
  308. pub fn replace(
  309. &self,
  310. target: impl Into<NavigationTarget<R>>,
  311. ) -> Option<ExternalNavigationFailure> {
  312. self.inner.replace(target.into())
  313. }
  314. /// The route that is currently active.
  315. pub fn current(&self) -> R
  316. where
  317. R: Clone,
  318. {
  319. self.inner.current()
  320. }
  321. /// The prefix that is currently active.
  322. pub fn prefix(&self) -> Option<String> {
  323. self.inner.prefix()
  324. }
  325. /// Manually subscribe to the current route
  326. pub fn subscribe(&self, id: ScopeId) {
  327. self.inner.subscribe(id)
  328. }
  329. /// Manually unsubscribe from the current route
  330. pub fn unsubscribe(&self, id: ScopeId) {
  331. self.inner.unsubscribe(id)
  332. }
  333. /// Clear any unresolved errors
  334. pub fn clear_error(&self) {
  335. self.inner.clear_error()
  336. }
  337. }