Skip to main content

yew_nav_link/
errors.rs

1// SPDX-FileCopyrightText: 2024-2026 RAprogramm <andrey.rozanov-vl@gmail.com>
2// SPDX-License-Identifier: MIT
3
4//! # NavError
5//!
6//! Error types returned by navigation operations. Use [`NavResult<T>`] as a
7//! convenience alias for `Result<T, NavError>`.
8//!
9//! # Example
10//!
11//! ```rust
12//! use yew_nav_link::NavError;
13//!
14//! fn handle_error(err: &NavError) {
15//!     match err {
16//!         NavError::RouteNotFound => { /* handle */ }
17//!         NavError::InvalidRoute(msg) => { /* handle */ }
18//!         NavError::NavigationCancelled => { /* handle */ }
19//!         // Required because NavError is non_exhaustive.
20//!         _ => { /* handle future variants */ }
21//!     }
22//! }
23//! ```
24//!
25//! # Variants
26//!
27//! | Variant | Description |
28//! |---------|-------------|
29//! | `RouteNotFound` | Target route does not match any registered route |
30//! | `InvalidRoute(String)` | Route string could not be parsed |
31//! | `NavigationCancelled` | Navigation was cancelled |
32
33use std::{
34    error::Error,
35    fmt::{Display, Formatter}
36};
37
38/// Errors that can occur during navigation operations.
39///
40/// Returned by navigation hooks and helper functions when something
41/// goes wrong while resolving or activating a route.
42///
43/// # Stability
44///
45/// This enum is `#[non_exhaustive]`: future minor releases may add new
46/// variants without bumping the major version. Consumers matching on
47/// `NavError` must include a wildcard arm (`_ =>`) so the match remains
48/// exhaustive across upgrades.
49///
50/// ```rust
51/// use yew_nav_link::NavError;
52///
53/// fn describe(err: &NavError) -> &'static str {
54///     match err {
55///         NavError::RouteNotFound => "missing route",
56///         NavError::InvalidRoute(_) => "bad route",
57///         NavError::NavigationCancelled => "cancelled",
58///         // Required because NavError is non_exhaustive.
59///         _ => "unknown navigation error"
60///     }
61/// }
62/// ```
63#[derive(Debug, Clone, PartialEq, Eq)]
64#[must_use]
65#[non_exhaustive]
66pub enum NavError {
67    /// The target route does not match any registered route.
68    RouteNotFound,
69    /// A route string could not be parsed. Contains a diagnostic message.
70    InvalidRoute(String),
71    /// Navigation was cancelled before completion.
72    NavigationCancelled
73}
74
75impl Display for NavError {
76    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
77        match self {
78            Self::RouteNotFound => write!(f, "route not found"),
79            Self::InvalidRoute(msg) => write!(f, "invalid route: {msg}"),
80            Self::NavigationCancelled => write!(f, "navigation cancelled")
81        }
82    }
83}
84
85impl Error for NavError {}
86
87impl NavError {
88    /// Creates a [`NavError::RouteNotFound`] error.
89    pub const fn route_not_found() -> Self {
90        Self::RouteNotFound
91    }
92
93    /// Creates a [`NavError::InvalidRoute`] error with the given message.
94    pub fn invalid_route<S: Into<String>>(msg: S) -> Self {
95        Self::InvalidRoute(msg.into())
96    }
97
98    /// Creates a [`NavError::NavigationCancelled`] error.
99    pub const fn navigation_cancelled() -> Self {
100        Self::NavigationCancelled
101    }
102}
103
104/// A convenience alias for `Result<T, NavError>`.
105pub type NavResult<T> = Result<T, NavError>;
106
107#[cfg(test)]
108mod tests {
109    use super::*;
110
111    #[test]
112    fn route_not_found() {
113        let err = NavError::route_not_found();
114        assert_eq!(err, NavError::RouteNotFound);
115    }
116
117    #[test]
118    fn invalid_route() {
119        let err = NavError::invalid_route("bad route");
120        assert_eq!(err, NavError::InvalidRoute("bad route".to_string()));
121    }
122
123    #[test]
124    fn navigation_cancelled() {
125        let err = NavError::navigation_cancelled();
126        assert_eq!(err, NavError::NavigationCancelled);
127    }
128
129    #[test]
130    fn nav_error_display() {
131        assert_eq!(
132            format!("{}", NavError::route_not_found()),
133            "route not found"
134        );
135        assert_eq!(
136            format!("{}", NavError::invalid_route("foo")),
137            "invalid route: foo"
138        );
139        assert_eq!(
140            format!("{}", NavError::navigation_cancelled()),
141            "navigation cancelled"
142        );
143    }
144
145    #[test]
146    fn nav_error_debug() {
147        let err = NavError::route_not_found();
148        let debug_str = format!("{err:?}");
149        assert!(debug_str.contains("RouteNotFound"));
150    }
151
152    #[test]
153    fn nav_error_clone() {
154        let err1 = NavError::route_not_found();
155        let err2 = err1.clone();
156        assert_eq!(err1, err2);
157    }
158
159    #[test]
160    fn nav_error_partial_eq() {
161        assert_eq!(NavError::route_not_found(), NavError::route_not_found());
162        assert_eq!(
163            NavError::navigation_cancelled(),
164            NavError::navigation_cancelled()
165        );
166    }
167
168    #[test]
169    fn nav_result_alias() {
170        let ok: NavResult<i32> = Ok(42);
171        let err: NavResult<i32> = Err(NavError::route_not_found());
172        assert!(ok.is_ok());
173        assert!(err.is_err());
174    }
175}