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}