Skip to main content

datumesh_store/
error.rs

1//! The store's error type.
2
3use std::fmt;
4
5use datumesh_core::ErrorCode;
6
7/// What a [`Store`](crate::Store) method or a migration can fail with.
8#[non_exhaustive]
9#[derive(Debug)]
10pub enum Error {
11    /// The core rejected the input: not canonical, not a receipt, a receipt that does not
12    /// verify against the stored key document, not an instant or hash, a key document that
13    /// does not satisfy SPEC 9.2, ... The core's error carries its code
14    /// ([`Error::code`]), JSON path and detail.
15    Invalid(datumesh_core::Error),
16    /// An append-only history refused a time that is not later than its latest entry:
17    /// `put_version` (`what` = `"version"`, `key` = the cell id), `bind_name` (`"name"`,
18    /// the function name) or `put_namespace` (`"namespace"`, the host; equal is allowed
19    /// there).
20    NotLater {
21        /// `"version"`, `"name"` or `"namespace"`.
22        what: &'static str,
23        /// The cell id, function name or host.
24        key: String,
25        /// The time given.
26        given: String,
27        /// The latest time already recorded.
28        latest: String,
29    },
30    /// Something the write refers to is not in the store: the receipt of a version, the
31    /// descriptor of a name binding.
32    NotFound {
33        /// `"receipt"` or `"descriptor"`.
34        what: &'static str,
35        /// Its hash.
36        key: String,
37    },
38    /// The receipt named by `put_version` is for another cell, or has another `tx`.
39    Mismatch {
40        /// `"cell_id"` or `"tx"`.
41        what: &'static str,
42        /// The value given to `put_version`.
43        expected: String,
44        /// The receipt's value.
45        found: String,
46    },
47    /// A receipt with this hash is stored with other bytes: the same payload with another
48    /// valid signature by the same key (the receipt hash does not cover `sig`; it covers
49    /// `computed_by`, so a rotated key gives another hash). Only a randomised signer makes
50    /// two valid signatures; RFC 8032 Ed25519 is deterministic. The stored receipt is kept.
51    Conflict {
52        /// The receipt hash.
53        hash: String,
54    },
55    /// The database refused or failed.
56    Database(postgres::Error),
57    /// No connection became free within the pool's checkout timeout.
58    PoolTimeout,
59    /// A migration could not be applied or checked.
60    Migration(String),
61}
62
63impl Error {
64    /// The core's error code, for [`Error::Invalid`].
65    pub fn code(&self) -> Option<ErrorCode> {
66        match self {
67            Error::Invalid(e) => Some(e.code()),
68            _ => None,
69        }
70    }
71}
72
73impl fmt::Display for Error {
74    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75        match self {
76            Error::Invalid(e) => write!(f, "invalid input: {e}"),
77            Error::NotLater { what, key, given, latest } => {
78                write!(f, "{what} {key}: {given} is not later than the latest, {latest}")
79            }
80            Error::NotFound { what, key } => write!(f, "no {what} {key} in the store"),
81            Error::Mismatch { what, expected, found } => {
82                write!(f, "the receipt's {what} is {found}, not {expected}")
83            }
84            Error::Conflict { hash } => {
85                write!(f, "receipt {hash} is stored with another valid signature; kept the stored one")
86            }
87            Error::Database(e) => match e.as_db_error() {
88                Some(d) => write!(f, "database: {} ({})", d.message(), d.code().code()),
89                None => write!(f, "database: {e}"),
90            },
91            Error::PoolTimeout => f.write_str("no database connection became free in time"),
92            Error::Migration(m) => write!(f, "migration: {m}"),
93        }
94    }
95}
96
97impl std::error::Error for Error {
98    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
99        match self {
100            Error::Invalid(e) => Some(e),
101            Error::Database(e) => Some(e),
102            _ => None,
103        }
104    }
105}
106
107impl From<postgres::Error> for Error {
108    fn from(e: postgres::Error) -> Error {
109        Error::Database(e)
110    }
111}
112
113impl From<datumesh_core::Error> for Error {
114    fn from(e: datumesh_core::Error) -> Error {
115        Error::Invalid(e)
116    }
117}
118
119/// `Result` with the store's [`Error`].
120pub type Result<T> = core::result::Result<T, Error>;