page_hunter/
errors.rs

1use std::fmt::{Debug, Display, Formatter, Result};
2
3#[cfg(feature = "sqlx")]
4use sqlx::Error as SqlxError;
5
6#[allow(unused_imports)]
7use crate::Page;
8
9/// Provides a way to categorize the pagination error.
10pub enum ErrorKind {
11    /// Raised when the value of a field is invalid.
12    InvalidValue(String),
13
14    /// Raised during a database operation using the [`sqlx`] crate. Only available when the `sqlx` feature is enabled.
15    #[cfg(feature = "sqlx")]
16    SQLx(SqlxError),
17}
18
19impl ErrorKind {
20    /// Check if the [`ErrorKind`] is a [`ErrorKind::InvalidValue`].
21    pub fn is_invalid_value_error(&self) -> bool {
22        matches!(self, ErrorKind::InvalidValue(_))
23    }
24
25    /// Check if the [`ErrorKind`] is a [`ErrorKind::SQLx`]. Only available when the `sqlx` feature is enabled.
26    #[cfg(feature = "sqlx")]
27    pub fn is_sqlx_error(&self) -> bool {
28        matches!(self, ErrorKind::SQLx(_))
29    }
30}
31
32/// Implementation of [`Display`] for [`ErrorKind`].
33impl Display for ErrorKind {
34    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
35        match self {
36            ErrorKind::InvalidValue(detail) => write!(f, "INVALID VALUE ERROR- {detail}"),
37
38            #[cfg(feature = "sqlx")]
39            ErrorKind::SQLx(detail) => write!(f, "SQLX ERROR- {detail}"),
40        }
41    }
42}
43
44/// Implementation of [`Debug`] for [`ErrorKind`].
45impl Debug for ErrorKind {
46    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
47        match self {
48            ErrorKind::InvalidValue(detail) => write!(f, "InvalidValueError({detail:?})"),
49
50            #[cfg(feature = "sqlx")]
51            ErrorKind::SQLx(detail) => write!(f, "SQLxError({detail:?})"),
52        }
53    }
54}
55
56/// Error type used throughout the library for error handling.
57pub struct PaginationError {
58    kind: ErrorKind,
59}
60
61impl PaginationError {
62    /// Get the [`ErrorKind`].
63    pub fn get_error_kind(&self) -> &ErrorKind {
64        &self.kind
65    }
66}
67
68/// Implementation of [`Display`] for [`PaginationError`].
69impl Display for PaginationError {
70    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
71        write!(f, "{w}", w = self.get_error_kind())
72    }
73}
74
75/// Implementation of [`Debug`] for [`PaginationError`].
76impl Debug for PaginationError {
77    fn fmt(&self, f: &mut Formatter<'_>) -> Result {
78        write!(f, "PaginationError {{ kind: {:?} }}", self.get_error_kind())
79    }
80}
81
82/// Implementation of [`From`]<[`ErrorKind`]> for [`PaginationError`].
83impl From<ErrorKind> for PaginationError {
84    fn from(value: ErrorKind) -> Self {
85        Self { kind: value }
86    }
87}
88
89/// Implementation of [`From`]<[`sqlx::Error`]> for [`PaginationError`]. Only available when the `sqlx` feature is enabled.
90#[cfg(feature = "sqlx")]
91impl From<SqlxError> for PaginationError {
92    fn from(value: sqlx::Error) -> Self {
93        Self {
94            kind: ErrorKind::SQLx(value),
95        }
96    }
97}
98
99#[cfg(test)]
100mod test_errors {
101    use crate::*;
102
103    #[cfg(feature = "sqlx")]
104    use sqlx::Error as SqlxError;
105
106    /// Test [`ErrorKind`] `is_field_value_error` method.
107    #[cfg(feature = "sqlx")]
108    #[test]
109    fn test_error_kind_is_invalid_value_error() {
110        let error_kind: ErrorKind = ErrorKind::InvalidValue(String::from("Invalid value"));
111        assert!(error_kind.is_invalid_value_error());
112        assert!(!error_kind.is_sqlx_error());
113    }
114
115    /// Test [`ErrorKind`] `is_database_error` method.
116    #[cfg(feature = "sqlx")]
117    #[test]
118    fn test_error_kind_is_sqlx_error() {
119        let error_kind: ErrorKind = ErrorKind::SQLx(SqlxError::RowNotFound);
120        assert!(error_kind.is_sqlx_error());
121        assert!(!error_kind.is_invalid_value_error());
122    }
123
124    /// Test [`std::fmt::Display`] implementation for [`ErrorKind::InvalidValue`].
125    #[test]
126    fn test_error_kind_invalid_value_error_display() {
127        let error_kind_field_value_error: ErrorKind =
128            ErrorKind::InvalidValue(String::from("Invalid value"));
129        assert_eq!(
130            format!("{error_kind_field_value_error}"),
131            "INVALID VALUE ERROR- Invalid value"
132        );
133    }
134
135    /// Test [`std::fmt::Display`] implementation for [`ErrorKind::SQLx`].
136    #[cfg(feature = "sqlx")]
137    #[test]
138    fn test_error_kind_sqlx_error_display() {
139        let error_kind_sqlx_error: ErrorKind = ErrorKind::SQLx(SqlxError::PoolClosed);
140        assert_eq!(
141            format!("{error_kind_sqlx_error}"),
142            "SQLX ERROR- attempted to acquire a connection on a closed pool"
143        );
144    }
145
146    /// Test [`std::fmt::Debug`] implementation for [`ErrorKind::InvalidValue`].
147    #[test]
148    fn test_error_kind_invalid_value_error_debug() {
149        let error_kind_field_value_error: ErrorKind =
150            ErrorKind::InvalidValue(String::from("Invalid value"));
151
152        assert_eq!(
153            format!("{:?}", error_kind_field_value_error),
154            "InvalidValueError(\"Invalid value\")"
155        );
156    }
157
158    /// Test [`std::fmt::Debug`] implementation for [`ErrorKind::SQLx`].
159    #[cfg(feature = "sqlx")]
160    #[test]
161    fn test_error_kind_sqlx_error_debug() {
162        let error_kind_sqlx_error: ErrorKind = ErrorKind::SQLx(SqlxError::PoolTimedOut);
163        assert_eq!(
164            format!("{:?}", error_kind_sqlx_error),
165            "SQLxError(PoolTimedOut)"
166        );
167    }
168
169    /// Test [`std::fmt::Display`] implementation for [`PaginationError`].
170    #[test]
171    fn test_pagination_error_display() {
172        let kind: ErrorKind = ErrorKind::InvalidValue(String::from("Invalid value"));
173        let pagination_error: PaginationError = PaginationError::from(kind);
174        assert_eq!(
175            format!("{pagination_error}"),
176            "INVALID VALUE ERROR- Invalid value"
177        );
178    }
179
180    /// Test [`std::fmt::Debug`] implementation for [`PaginationError`].
181    #[test]
182    fn test_pagination_error_debug() {
183        let kind: ErrorKind = ErrorKind::InvalidValue(String::from("Invalid value"));
184        let pagination_error: PaginationError = PaginationError::from(kind);
185        assert_eq!(
186            format!("{:?}", pagination_error),
187            "PaginationError { kind: InvalidValueError(\"Invalid value\") }"
188        );
189    }
190
191    /// Test [`PaginationError`] from [`ErrorKind`].
192    #[test]
193    fn test_pagination_error_from_error_kind() {
194        let error_kind: ErrorKind = ErrorKind::InvalidValue(String::from("Unknown error"));
195        let pagination_error: PaginationError = error_kind.into();
196        assert!(pagination_error.get_error_kind().is_invalid_value_error());
197    }
198}