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}