nanosql/
error.rs

1//! NanoSQL errors and results
2
3use core::str::Utf8Error;
4use core::fmt::{self, Display, Formatter};
5use std::io;
6use std::error::Error as StdError;
7use std::borrow::Cow;
8use rusqlite::{Error as SqlError, types::FromSqlError};
9use thiserror::Error;
10#[cfg(feature = "not-nan")]
11use ordered_float::FloatIsNan;
12
13
14/// NanoSQL errors.
15#[allow(missing_docs)]
16#[derive(Debug, Error)]
17pub enum Error {
18    #[error("UTF-8 error: {0}")]
19    Utf8Error(#[from] Utf8Error),
20
21    #[error("formatting error")]
22    Fmt(#[from] fmt::Error),
23
24    #[error("I/O error: {0}")]
25    Io(#[from] io::Error),
26
27    #[error("SQLite error: {0}")]
28    Sqlite(#[from] SqlError),
29
30    #[error("SQL conversion error: {0}")]
31    FromSql(#[from] FromSqlError),
32
33    #[error("query expects {expected} parameters but {actual} were bound")]
34    ParamCountMismatch {
35        expected: usize,
36        actual: usize,
37    },
38
39    #[error("expected {expected} rows but {actual} were returned")]
40    RowCountMismatch {
41        expected: RowCount,
42        actual: RowCount,
43    },
44
45    #[error("expected {expected} columns but result set has {actual}")]
46    ColumnCountMismatch {
47        expected: usize,
48        actual: usize,
49    },
50
51    #[error("unknown parameter name `{0}`")]
52    UnknownParam(Cow<'static, str>),
53
54    #[error(transparent)]
55    Other(#[from] Box<dyn StdError + Send + Sync + 'static>),
56}
57
58impl Error {
59    /// Creates an `Error` from any other underlying reason.
60    pub fn other<E>(error: E) -> Self
61    where
62        E: StdError + Send + Sync + 'static
63    {
64        Error::Other(Box::new(error))
65    }
66
67    /// Creates an `Error` from a message.
68    pub fn message<T: Display>(message: T) -> Self {
69        Error::Other(message.to_string().into())
70    }
71
72    /// Creates an `UnknownParam` error from a statically-known parameter name.
73    pub fn unknown_param(name: &'static str) -> Self {
74        Error::UnknownParam(Cow::Borrowed(name))
75    }
76
77    /// Creates an `UnknownParam` error from a dynamic parameter name.
78    pub fn unknown_param_dyn<T: Display>(message: T) -> Self {
79        Error::UnknownParam(Cow::Owned(message.to_string()))
80    }
81}
82
83#[cfg(feature = "not-nan")]
84impl From<FloatIsNan> for Error {
85    fn from(error: FloatIsNan) -> Self {
86        Error::other(error)
87    }
88}
89
90/// Convenience type alias for NanoSQL-related results.
91pub type Result<T, E = Error> = core::result::Result<T, E>;
92
93/// A helper type for producing accurate error messages about
94/// the actual and expected number of rows.
95#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
96pub struct RowCount {
97    /// At least this many rows were expected or found.
98    pub min: usize,
99    /// At most this many rows were expected or found.
100    /// If this is `None`, then there's no (required or known) upper limit.
101    pub max: Option<usize>,
102}
103
104impl RowCount {
105    /// Indicate that a specific number of rows in a range was expected.
106    pub const fn new(min: usize, max: usize) -> Self {
107        RowCount {
108            min,
109            max: Some(max),
110        }
111    }
112
113    /// Indicate that an exact number of rows were expected or found.
114    pub const fn exactly(count: usize) -> Self {
115        RowCount {
116            min: count,
117            max: Some(count),
118        }
119    }
120
121    /// Indicate that at least this many rows were expected or found.
122    pub const fn at_least(min: usize) -> Self {
123        RowCount {
124            min,
125            max: None,
126        }
127    }
128
129    /// Indicate that at most this many rows were expected or found.
130    pub const fn at_most(max: usize) -> Self {
131        RowCount {
132            min: 0,
133            max: Some(max),
134        }
135    }
136}
137
138impl Display for RowCount {
139    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
140        let min = self.min;
141        if let Some(max) = self.max {
142            if min == max {
143                write!(f, "exactly {min}")
144            } else if min == 0 {
145                write!(f, "at most {max}")
146            } else {
147                write!(f, "{min}...{max}")
148            }
149        } else {
150            write!(f, "at least {min}")
151        }
152    }
153}