gpui_router/
routes.rs

1use crate::Route;
2use crate::RouterState;
3use gpui::prelude::*;
4use gpui::{App, Empty, SharedString, Window};
5use matchit::Router as MatchitRouter;
6use smallvec::SmallVec;
7
8/// Renders a branch of [`Route`](crate::Route) that best matches the current path.
9#[derive(IntoElement)]
10pub struct Routes {
11  basename: SharedString,
12  routes: SmallVec<[Route; 1]>,
13}
14
15impl Default for Routes {
16  fn default() -> Self {
17    Self::new()
18  }
19}
20
21impl Routes {
22  pub fn new() -> Self {
23    Self {
24      basename: SharedString::from("/"),
25      routes: SmallVec::new(),
26    }
27  }
28
29  /// Sets the base path for all child `Route`s.
30  pub fn basename(mut self, basename: impl Into<SharedString>) -> Self {
31    self.basename = basename.into();
32    self
33  }
34
35  /// Adds a `Route` as a child to the `Routes`.
36  pub fn child(mut self, child: Route) -> Self {
37    self.routes.push(child);
38    self
39  }
40
41  /// Adds multiple `Route`s as children to the `Routes`.
42  pub fn children(mut self, children: impl IntoIterator<Item = Route>) -> Self {
43    for child in children.into_iter() {
44      self = self.child(child);
45    }
46    self
47  }
48
49  #[cfg(test)]
50  pub fn routes(&self) -> &SmallVec<[Route; 1]> {
51    &self.routes
52  }
53}
54
55impl RenderOnce for Routes {
56  fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
57    if cfg!(debug_assertions) && !cx.has_global::<RouterState>() {
58      panic!("RouterState not initialized");
59    }
60
61    let mut route_map = MatchitRouter::new();
62    for route in self.routes.iter() {
63      route_map.merge(route.build_route_map(&self.basename)).unwrap();
64    }
65
66    let pathname = cx.global::<RouterState>().location.pathname.clone();
67    let matched = route_map.at(&pathname);
68
69    if let Ok(matched) = matched {
70      for (key, value) in matched.params.iter() {
71        cx.global_mut::<RouterState>()
72          .params
73          .insert(key.to_owned().into(), value.to_owned().into());
74      }
75      let route = self
76        .routes
77        .into_iter()
78        .find(|route| route.in_pattern(&self.basename, &pathname));
79      if let Some(route) = route {
80        return route.basename(self.basename).into_any_element();
81      }
82    }
83
84    Empty {}.into_any_element()
85  }
86}