Skip to main content

gpui_navigator/
lib.rs

1//! # GPUI Router
2//!
3//! A declarative routing library for GPUI with support for:
4//!
5//! - **Route Transitions** - Fade, slide, scale animations with configurable duration
6//! - **Nested Routing** - Parent/child route hierarchies with `RouterOutlet`
7//! - **Route Guards** - Authentication, authorization, and custom access control
8//! - **Middleware** - Before/after hooks for navigation events
9//! - **Named Routes** - Navigate using route names instead of paths
10//! - **Route Matching** - Pattern matching with parameters and constraints
11//! - **Error Handling** - Custom 404 and error handlers
12//!
13//! # Quick Start
14//!
15//! ```ignore
16//! use gpui::*;
17//! use gpui_navigator::*;
18//!
19//! fn main() {
20//!     Application::new().run(|cx| {
21//!         init_router(cx, |router| {
22//!             router.add_route(
23//!                 Route::new("/", home_page)
24//!                     .transition(Transition::fade(300))
25//!             );
26//!         });
27//!
28//!         cx.open_window(WindowOptions::default(), |_, cx| {
29//!             cx.new(|_| AppView)
30//!         })
31//!     });
32//! }
33//!
34//! fn home_page(_cx: &mut App, _params: &RouteParams) -> AnyElement {
35//!     gpui::div().into_any_element()
36//! }
37//!
38//! struct AppView;
39//!
40//! impl Render for AppView {
41//!     fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
42//!         gpui::div()
43//!     }
44//! }
45//! ```
46//!
47//! # Navigation
48//!
49//! The library provides a simple navigation API:
50//!
51//! ```ignore
52//! use gpui_navigator::Navigator;
53//!
54//! // Push new route
55//! Navigator::push(cx, "/profile");
56//!
57//! // Replace current route
58//! Navigator::replace(cx, "/login");
59//!
60//! // Go back (pop)
61//! Navigator::pop(cx);
62//!
63//! // Go forward
64//! Navigator::forward(cx);
65//! ```
66//!
67//! # Route Guards
68//!
69//! Protect routes with authentication and authorization:
70//!
71//! ```no_run
72//! use gpui_navigator::*;
73//!
74//! Route::new("/admin", admin_page)
75//!     .guard(AuthGuard::new(is_logged_in, "/login"))
76//!     .guard(RoleGuard::new(get_user_role, "admin", Some("/forbidden")))
77//! # ;
78//! # fn admin_page(_: &mut gpui::Window, _: &mut gpui::App, _: &RouteParams) -> gpui::AnyElement { todo!() }
79//! # fn is_logged_in(_: &gpui::App) -> bool { todo!() }
80//! # fn get_user_role(_: &gpui::App) -> Option<String> { todo!() }
81//! ```
82//!
83//! # Nested Routes
84//!
85//! Create hierarchical route structures:
86//!
87//! ```no_run
88//! use gpui_navigator::*;
89//!
90//! Route::new("/dashboard", dashboard_layout)
91//!     .children(vec![
92//!         Route::new("overview", overview_page).into(),
93//!         Route::new("settings", settings_page).into(),
94//!     ])
95//! # ;
96//! # fn dashboard_layout(_: &mut gpui::Window, _: &mut gpui::App, _: &RouteParams) -> gpui::AnyElement { todo!() }
97//! # fn overview_page(_: &mut gpui::Window, _: &mut gpui::App, _: &RouteParams) -> gpui::AnyElement { todo!() }
98//! # fn settings_page(_: &mut gpui::Window, _: &mut gpui::App, _: &RouteParams) -> gpui::AnyElement { todo!() }
99//! ```
100//!
101//! # Feature Flags
102//!
103//! | Feature      | Default | Description |
104//! |--------------|---------|-------------|
105//! | `guard`      | yes     | Route guards (`AuthGuard`, `RoleGuard`, `PermissionGuard`) |
106//! | `middleware`  | yes     | Before/after navigation hooks |
107//! | `transition` | yes     | Fade, slide animations on route change |
108//! | `cache`      | yes     | LRU cache for route resolution (depends on `lru`) |
109//! | `log`        | yes     | Logging via the `log` crate |
110//! | `tracing`    | no      | Logging via `tracing` (mutually exclusive with `log`) |
111
112#![doc(html_root_url = "https://docs.rs/gpui_navigator/0.1.4")]
113#![cfg_attr(docsrs, feature(doc_cfg))]
114#![warn(missing_docs)]
115// Lints are configured in Cargo.toml [lints] section
116
117// Logging abstraction
118pub mod logging;
119
120// Cache (optional)
121#[cfg(feature = "cache")]
122pub mod cache;
123
124// Core routing modules
125pub mod history;
126#[cfg(feature = "middleware")]
127pub mod middleware;
128pub mod route;
129pub mod state;
130
131// Error handling
132pub mod error;
133
134// Route lifecycle
135pub mod lifecycle;
136
137// Guards
138#[cfg(feature = "guard")]
139pub mod guards;
140
141// Transitions
142#[cfg(feature = "transition")]
143pub mod transition;
144
145// Other modules
146pub mod nested;
147pub mod params;
148pub mod resolve;
149pub mod widgets;
150
151// Context module (router context integration)
152mod context;
153
154// Re-export main types for convenient access
155#[cfg(feature = "cache")]
156pub use cache::{CacheStats, RouteCache, RouteId};
157pub use context::{
158    current_path, init_router, navigate, GlobalRouter, NavigationRequest, Navigator,
159    NavigatorHandle, UseRouter,
160};
161pub use error::{ErrorHandler, ErrorHandlers, NavigationError, NavigationResult, NotFoundHandler};
162#[cfg(feature = "guard")]
163pub use guards::{
164    guard_fn, AuthGuard, GuardBuilder, Guards, NotGuard, PermissionGuard, RoleGuard, RouteGuard,
165};
166pub use history::{History, HistoryEntry, HistoryState};
167pub use lifecycle::{NavigationAction, RouteLifecycle};
168#[cfg(feature = "middleware")]
169pub use middleware::{middleware_fn, RouteMiddleware};
170pub use nested::{build_child_path, extract_param_name, normalize_path, resolve_child_route};
171pub use params::{QueryParams, RouteParams};
172pub use resolve::{resolve_match_stack, MatchEntry, MatchStack};
173pub use route::{
174    validate_route_path, BuilderFn, IntoRoute, NamedRoute, NamedRouteRegistry, PageRoute, Route,
175    RouteConfig, RouteDescriptor,
176};
177pub use state::RouterState;
178#[cfg(feature = "transition")]
179pub use transition::{SlideDirection, Transition, TransitionConfig};
180pub use widgets::{
181    render_router_outlet, router_link, router_outlet, router_outlet_named, router_view,
182    DefaultPages, RouterLink, RouterOutlet, RouterView,
183};
184
185use std::collections::HashMap;
186
187/// Route path matching result.
188///
189/// Contains the matched path along with any extracted parameters and query strings.
190///
191/// # Example
192///
193/// ```
194/// use gpui_navigator::RouteMatch;
195///
196/// let route_match = RouteMatch::new("/users/123".to_string())
197///     .with_param("id".to_string(), "123".to_string());
198///
199/// assert_eq!(route_match.params.get("id"), Some(&"123".to_string()));
200/// ```
201#[derive(Debug, Clone, PartialEq, Eq)]
202pub struct RouteMatch {
203    /// The matched path
204    pub path: String,
205    /// Extracted route parameters (e.g., `:id` -> "123")
206    pub params: HashMap<String, String>,
207    /// Parsed query string parameters
208    pub query: HashMap<String, String>,
209}
210
211impl RouteMatch {
212    /// Create a new route match with the given path.
213    #[must_use]
214    pub fn new(path: String) -> Self {
215        Self {
216            path,
217            params: HashMap::new(),
218            query: HashMap::new(),
219        }
220    }
221
222    /// Add a route parameter to the match.
223    #[must_use]
224    pub fn with_param(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
225        self.params.insert(key.into(), value.into());
226        self
227    }
228
229    /// Add a query parameter to the match.
230    #[must_use]
231    pub fn with_query(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
232        self.query.insert(key.into(), value.into());
233        self
234    }
235}
236
237/// Navigation direction indicator.
238///
239/// Used to determine the direction of navigation for animations and history management.
240#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
241#[non_exhaustive]
242pub enum NavigationDirection {
243    /// Navigating forward to a new route
244    Forward,
245    /// Navigating back in history
246    Back,
247    /// Replacing the current route without affecting history direction
248    Replace,
249}
250
251/// Event emitted when the route changes.
252///
253/// Contains information about the navigation that occurred, including
254/// the source and destination paths and the direction of navigation.
255#[derive(Debug, Clone, PartialEq, Eq)]
256pub struct RouteChangeEvent {
257    /// The previous path (None if this is the first navigation)
258    pub from: Option<String>,
259    /// The new path being navigated to
260    pub to: String,
261    /// The direction of navigation
262    pub direction: NavigationDirection,
263}