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//! - `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}