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//! - `log` (default) - Uses the standard `log` crate for logging
104//! - `tracing` - Uses the `tracing` crate for structured logging (mutually exclusive with `log`)
105
106#![doc(html_root_url = "https://docs.rs/gpui_navigator/0.1.0")]
107#![cfg_attr(docsrs, feature(doc_cfg))]
108// Lints are configured in Cargo.toml [lints] section
109
110// Logging abstraction
111pub mod logging;
112
113// Cache (optional)
114#[cfg(feature = "cache")]
115pub mod cache;
116
117// Core routing modules
118pub mod history;
119pub mod matcher;
120#[cfg(feature = "middleware")]
121pub mod middleware;
122pub mod route;
123pub mod state;
124
125// Error handling
126pub mod error;
127
128// Route lifecycle
129pub mod lifecycle;
130
131// Guards
132#[cfg(feature = "guard")]
133pub mod guards;
134
135// Transitions
136#[cfg(feature = "transition")]
137pub mod transition;
138
139// Other modules
140pub mod nested;
141pub mod params;
142pub mod widgets;
143
144// Context module (router context integration)
145mod context;
146
147// Re-export main types for convenient access
148#[cfg(feature = "cache")]
149pub use cache::{CacheStats, RouteCache, RouteId};
150pub use context::{
151 current_path, init_router, navigate, GlobalRouter, NavigationRequest, Navigator,
152 NavigatorHandle, UseRouter,
153};
154pub use error::{ErrorHandler, ErrorHandlers, NavigationError, NavigationResult, NotFoundHandler};
155#[cfg(feature = "guard")]
156pub use guards::{
157 guard_fn, AuthGuard, BoxedGuard, GuardBuilder, GuardContext, GuardResult, Guards, NotGuard,
158 PermissionGuard, RoleGuard, RouteGuard,
159};
160pub use lifecycle::{BoxedLifecycle, LifecycleResult, RouteLifecycle};
161#[cfg(feature = "middleware")]
162pub use middleware::{middleware_fn, BoxedMiddleware, RouteMiddleware};
163pub use nested::{build_child_path, resolve_child_route};
164pub use params::{QueryParams, RouteParams};
165pub use route::{
166 validate_route_path, BuilderFn, IntoRoute, NamedRoute, NamedRouteRegistry, PageRoute, Route,
167 RouteConfig, RouteDescriptor,
168};
169pub use state::{Router, RouterState};
170#[cfg(feature = "transition")]
171pub use transition::{SlideDirection, Transition, TransitionConfig};
172pub use widgets::{
173 render_router_outlet, router_link, router_outlet, router_outlet_named, DefaultPages,
174 RouterLink, RouterOutlet,
175};
176
177use std::collections::HashMap;
178
179/// Route path matching result.
180///
181/// Contains the matched path along with any extracted parameters and query strings.
182///
183/// # Example
184///
185/// ```
186/// use gpui_navigator::RouteMatch;
187///
188/// let route_match = RouteMatch::new("/users/123".to_string())
189/// .with_param("id".to_string(), "123".to_string());
190///
191/// assert_eq!(route_match.params.get("id"), Some(&"123".to_string()));
192/// ```
193#[derive(Debug, Clone)]
194pub struct RouteMatch {
195 /// The matched path
196 pub path: String,
197 /// Extracted route parameters (e.g., `:id` -> "123")
198 pub params: HashMap<String, String>,
199 /// Parsed query string parameters
200 pub query: HashMap<String, String>,
201}
202
203impl RouteMatch {
204 /// Create a new route match with the given path.
205 #[must_use]
206 pub fn new(path: String) -> Self {
207 Self {
208 path,
209 params: HashMap::new(),
210 query: HashMap::new(),
211 }
212 }
213
214 /// Add a route parameter to the match.
215 #[must_use]
216 pub fn with_param(mut self, key: String, value: String) -> Self {
217 self.params.insert(key, value);
218 self
219 }
220
221 /// Add a query parameter to the match.
222 #[must_use]
223 pub fn with_query(mut self, key: String, value: String) -> Self {
224 self.query.insert(key, value);
225 self
226 }
227}
228
229/// Navigation direction indicator.
230///
231/// Used to determine the direction of navigation for animations and history management.
232#[derive(Debug, Clone, Copy, PartialEq, Eq)]
233pub enum NavigationDirection {
234 /// Navigating forward to a new route
235 Forward,
236 /// Navigating back in history
237 Back,
238 /// Replacing the current route without affecting history direction
239 Replace,
240}
241
242/// Event emitted when the route changes.
243///
244/// Contains information about the navigation that occurred, including
245/// the source and destination paths and the direction of navigation.
246#[derive(Debug, Clone)]
247pub struct RouteChangeEvent {
248 /// The previous path (None if this is the first navigation)
249 pub from: Option<String>,
250 /// The new path being navigated to
251 pub to: String,
252 /// The direction of navigation
253 pub direction: NavigationDirection,
254}