router.rs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398
  1. use std::{
  2. any::Any,
  3. collections::HashSet,
  4. rc::Rc,
  5. sync::{Arc, RwLock},
  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: Rc<RefCell<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 = Rc::new(RefCell::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.borrow_mut();
  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.borrow();
  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.borrow().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.borrow().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.borrow_mut().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.borrow_mut().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(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(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. .borrow()
  176. .history
  177. .current_route()
  178. .downcast::<R>()
  179. .unwrap()
  180. .as_ref()
  181. .clone()
  182. }
  183. /// The route that is currently active.
  184. pub fn current_route_string(&self) -> String {
  185. self.any_route_to_string(&*self.state.borrow().history.current_route())
  186. }
  187. pub(crate) fn any_route_to_string(&self, route: &dyn Any) -> String {
  188. (self.any_route_to_string)(route)
  189. }
  190. pub(crate) fn resolve_into_routable(
  191. &self,
  192. into_routable: IntoRoutable,
  193. ) -> NavigationTarget<Rc<dyn Any>> {
  194. match into_routable {
  195. IntoRoutable::FromStr(url) => {
  196. let parsed_route: NavigationTarget<Rc<dyn Any>> = match self.route_from_str(&url) {
  197. Ok(route) => NavigationTarget::Internal(route),
  198. Err(_) => NavigationTarget::External(url),
  199. };
  200. parsed_route
  201. }
  202. IntoRoutable::Route(route) => NavigationTarget::Internal(route),
  203. }
  204. }
  205. /// The prefix that is currently active.
  206. pub fn prefix(&self) -> Option<String> {
  207. self.state.borrow().prefix.clone()
  208. }
  209. fn external(&self, external: String) -> Option<ExternalNavigationFailure> {
  210. let mut state = self.state_mut();
  211. match state.history.external(external.clone()) {
  212. true => None,
  213. false => {
  214. let failure = ExternalNavigationFailure(external);
  215. state.unresolved_error = Some(failure.clone());
  216. self.update_subscribers();
  217. Some(failure)
  218. }
  219. }
  220. }
  221. fn state_mut(&self) -> RefMut<MutableRouterState> {
  222. self.state.borrow_mut()
  223. }
  224. /// Manually subscribe to the current route
  225. pub fn subscribe(&self, id: ScopeId) {
  226. self.subscribers.write().unwrap().insert(id);
  227. }
  228. /// Manually unsubscribe from the current route
  229. pub fn unsubscribe(&self, id: ScopeId) {
  230. self.subscribers.write().unwrap().remove(&id);
  231. }
  232. fn update_subscribers(&self) {
  233. for &id in self.subscribers.read().unwrap().iter() {
  234. (self.subscriber_update)(id);
  235. }
  236. }
  237. /// Clear any unresolved errors
  238. pub fn clear_error(&self) {
  239. self.state.borrow_mut().unresolved_error = None;
  240. self.update_subscribers();
  241. }
  242. pub(crate) fn render_error<'a>(&self, cx: Scope<'a>) -> Element<'a> {
  243. self.state
  244. .borrow()
  245. .unresolved_error
  246. .as_ref()
  247. .and_then(|_| (self.failure_external_navigation)(cx))
  248. }
  249. fn change_route(&self) -> Option<ExternalNavigationFailure> {
  250. if let Some(callback) = &self.routing_callback {
  251. let myself = self.clone();
  252. if let Some(new) = callback(myself) {
  253. let mut state = self.state_mut();
  254. match new {
  255. NavigationTarget::Internal(p) => state.history.replace(p),
  256. NavigationTarget::External(e) => return self.external(e),
  257. }
  258. }
  259. }
  260. self.update_subscribers();
  261. None
  262. }
  263. }
  264. pub struct GenericRouterContext<R> {
  265. inner: RouterContext,
  266. _marker: std::marker::PhantomData<R>,
  267. }
  268. impl<R> GenericRouterContext<R>
  269. where
  270. R: Routable,
  271. {
  272. /// Check whether there is a previous page to navigate back to.
  273. #[must_use]
  274. pub fn can_go_back(&self) -> bool {
  275. self.inner.can_go_back()
  276. }
  277. /// Check whether there is a future page to navigate forward to.
  278. #[must_use]
  279. pub fn can_go_forward(&self) -> bool {
  280. self.inner.can_go_forward()
  281. }
  282. /// Go back to the previous location.
  283. ///
  284. /// Will fail silently if there is no previous location to go to.
  285. pub fn go_back(&self) {
  286. self.inner.go_back();
  287. }
  288. /// Go back to the next location.
  289. ///
  290. /// Will fail silently if there is no next location to go to.
  291. pub fn go_forward(&self) {
  292. self.inner.go_forward();
  293. }
  294. /// Push a new location.
  295. ///
  296. /// The previous location will be available to go back to.
  297. pub fn push(
  298. &self,
  299. target: impl Into<NavigationTarget<R>>,
  300. ) -> Option<ExternalNavigationFailure> {
  301. self.inner.push(target.into())
  302. }
  303. /// Replace the current location.
  304. ///
  305. /// The previous location will **not** be available to go back to.
  306. pub fn replace(
  307. &self,
  308. target: impl Into<NavigationTarget<R>>,
  309. ) -> Option<ExternalNavigationFailure> {
  310. self.inner.replace(target.into())
  311. }
  312. /// The route that is currently active.
  313. pub fn current(&self) -> R
  314. where
  315. R: Clone,
  316. {
  317. self.inner.current()
  318. }
  319. /// The prefix that is currently active.
  320. pub fn prefix(&self) -> Option<String> {
  321. self.inner.prefix()
  322. }
  323. /// Manually subscribe to the current route
  324. pub fn subscribe(&self, id: ScopeId) {
  325. self.inner.subscribe(id)
  326. }
  327. /// Manually unsubscribe from the current route
  328. pub fn unsubscribe(&self, id: ScopeId) {
  329. self.inner.unsubscribe(id)
  330. }
  331. /// Clear any unresolved errors
  332. pub fn clear_error(&self) {
  333. self.inner.clear_error()
  334. }
  335. }