Skip to main content

miden_node_db/
errors.rs

1use std::any::type_name;
2use std::io;
3
4use deadpool_sync::InteractError;
5use thiserror::Error;
6
7use crate::DatabaseTypeConversionError;
8
9// SCHEMA VERIFICATION ERROR
10// =================================================================================================
11
12/// Errors that can occur during schema verification.
13#[derive(Debug, Error)]
14pub enum SchemaVerificationError {
15    #[error("failed to apply migrations to reference database")]
16    MigrationApplication(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
17    #[error(
18        "schema mismatch: expected {expected_count} objects, found {actual_count} \
19         ({missing_count} missing, {extra_count} unexpected)"
20    )]
21    Mismatch {
22        expected_count: usize,
23        actual_count: usize,
24        missing_count: usize,
25        extra_count: usize,
26    },
27}
28
29// DATABASE ERROR
30// =================================================================================================
31
32#[derive(Debug, Error)]
33pub enum DatabaseError {
34    #[error("SQLite pool interaction failed: {0}")]
35    InteractError(String),
36    #[error("setup deadpool connection pool failed")]
37    ConnectionPoolObtainError(#[from] Box<dyn std::error::Error + Send + Sync + 'static>),
38    #[error("conversion from SQL to rust type {to} failed")]
39    ConversionSqlToRust {
40        #[source]
41        inner: Option<Box<dyn std::error::Error + Send + Sync + 'static>>,
42        to: &'static str,
43    },
44    #[error(transparent)]
45    Rusqlite(#[from] rusqlite::Error),
46    #[error("failed to apply database migrations")]
47    Migration(#[source] Box<dyn std::error::Error + Send + Sync + 'static>),
48    #[error("schema verification failed")]
49    SchemaVerification(#[from] SchemaVerificationError),
50    #[error("I/O error")]
51    Io(#[from] io::Error),
52    #[error("pool build error")]
53    PoolBuild(#[from] deadpool::managed::BuildError),
54    #[error("failed to cast")]
55    ConversionError(#[from] DatabaseTypeConversionError),
56}
57
58impl DatabaseError {
59    /// Converts from `InteractError`
60    ///
61    /// Note: Required since `InteractError` has at least one enum
62    /// variant that is _not_ `Send + Sync` and hence prevents the
63    /// `Sync` auto implementation.
64    /// This does an internal conversion to string while maintaining
65    /// convenience.
66    ///
67    /// Using `MSG` as const so it can be called as
68    /// `.map_err(DatabaseError::interact::<"Your message">)`
69    pub fn interact(msg: &(impl ToString + ?Sized), e: &InteractError) -> Self {
70        let msg = msg.to_string();
71        Self::InteractError(format!("{msg} failed: {e:?}"))
72    }
73
74    /// Creates a database migration error with the original source error.
75    pub fn migration(
76        source: impl Into<Box<dyn std::error::Error + Send + Sync + 'static>>,
77    ) -> Self {
78        Self::Migration(source.into())
79    }
80
81    /// Failed to convert an SQL entry to a rust representation
82    pub fn conversiont_from_sql<RT, E, MaybeE>(err: MaybeE) -> DatabaseError
83    where
84        MaybeE: Into<Option<E>>,
85        E: std::error::Error + Send + Sync + 'static,
86    {
87        DatabaseError::ConversionSqlToRust {
88            inner: err.into().map(|err| Box::new(err) as Box<dyn std::error::Error + Send + Sync>),
89            to: type_name::<RT>(),
90        }
91    }
92
93    /// Creates a deserialization error with a static context string and the original error.
94    ///
95    /// This is a convenience wrapper around [`ConversionSqlToRust`](Self::ConversionSqlToRust).
96    pub fn deserialization(
97        context: &'static str,
98        source: impl std::error::Error + Send + Sync + 'static,
99    ) -> Self {
100        Self::ConversionSqlToRust {
101            inner: Some(Box::new(source)),
102            to: context,
103        }
104    }
105}