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}