Skip to main content

nautilus_persistence/
errors.rs

1// -------------------------------------------------------------------------------------------------
2//  Copyright (C) 2015-2026 Nautech Systems Pty Ltd. All rights reserved.
3//  https://nautechsystems.io
4//
5//  Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
6//  You may not use this file except in compliance with the License.
7//  You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
8//
9//  Unless required by applicable law or agreed to in writing, software
10//  distributed under the License is distributed on an "AS IS" BASIS,
11//  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//  See the License for the specific language governing permissions and
13//  limitations under the License.
14// -------------------------------------------------------------------------------------------------
15
16//! Typed persistence errors.
17//!
18//! Most public APIs in this crate return [`anyhow::Result`] for ergonomic error chaining.
19//! This module exposes a small thiserror enum with the variants callers most often need to
20//! distinguish (e.g. "operation not supported" vs "operation failed"). Producers wrap the
21//! enum in `anyhow::Error` and consumers downcast:
22//!
23//! ```ignore
24//! use nautilus_persistence::errors::PersistenceError;
25//!
26//! if matches!(
27//!     err.downcast_ref::<PersistenceError>(),
28//!     Some(PersistenceError::Unsupported(_))
29//! ) {
30//!     // The concrete backend does not support the requested operation.
31//! }
32//! ```
33//!
34//! Migrating individual functions to return `Result<T, PersistenceError>` directly is a
35//! follow-up; the downcast pattern lets new variants ship without breaking existing
36//! `anyhow::Result` signatures.
37
38use thiserror::Error;
39
40/// Typed errors that callers may want to programmatically distinguish.
41#[derive(Debug, Error)]
42pub enum PersistenceError {
43    /// The operation is not supported by this concrete backend.
44    #[error("Operation not supported by this backend: {0}")]
45    Unsupported(String),
46}
47
48impl PersistenceError {
49    /// Convenience constructor for [`PersistenceError::Unsupported`].
50    #[must_use]
51    pub fn unsupported(operation: impl Into<String>) -> Self {
52        Self::Unsupported(operation.into())
53    }
54}
55
56#[cfg(test)]
57mod tests {
58    use rstest::rstest;
59
60    use super::*;
61
62    #[rstest]
63    fn unsupported_downcasts_through_anyhow() {
64        let err: anyhow::Error =
65            anyhow::Error::from(PersistenceError::unsupported("vacuum_catalog"));
66        match err.downcast_ref::<PersistenceError>() {
67            Some(PersistenceError::Unsupported(op)) => assert_eq!(op, "vacuum_catalog"),
68            other => panic!("Expected Unsupported, received {other:?}"),
69        }
70    }
71}