lib

Core libraries for Radroots
git clone https://radroots.dev/git/lib.git
Log | Files | Refs | README

backend.rs (3697B)


      1 //! Redacted SQL failure classification shared by the existing storage adapters.
      2 
      3 use radroots_storage::Error;
      4 
      5 pub(crate) fn map_backend(source: sqlx::Error) -> Error {
      6     if is_capacity(&source) {
      7         Error::SpaceInsufficient
      8     } else {
      9         Error::BackendUnavailable
     10     }
     11 }
     12 
     13 pub(crate) fn is_capacity(source: &sqlx::Error) -> bool {
     14     match source {
     15         sqlx::Error::Database(error) => error
     16             .code()
     17             .and_then(|code| code.parse::<u32>().ok())
     18             .is_some_and(|code| code & 0xff == 13),
     19         sqlx::Error::Io(error) => is_io_capacity(error),
     20         _ => false,
     21     }
     22 }
     23 
     24 pub(crate) fn is_io_capacity(source: &std::io::Error) -> bool {
     25     matches!(
     26         source.kind(),
     27         std::io::ErrorKind::StorageFull | std::io::ErrorKind::QuotaExceeded
     28     )
     29 }
     30 
     31 pub(crate) fn startup_error(source: &sqlx::Error, fallback: crate::Error) -> crate::Error {
     32     if is_capacity(source) {
     33         crate::Error::SpaceInsufficient
     34     } else {
     35         fallback
     36     }
     37 }
     38 
     39 #[cfg(test)]
     40 mod tests {
     41     use super::*;
     42     use sqlx::error::{DatabaseError, ErrorKind};
     43     use std::{borrow::Cow, error::Error as StdError, fmt};
     44 
     45     #[derive(Debug)]
     46     struct Coded(Option<&'static str>);
     47     impl fmt::Display for Coded {
     48         fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     49             f.write_str("private path and raw database detail")
     50         }
     51     }
     52     impl StdError for Coded {}
     53     impl DatabaseError for Coded {
     54         fn message(&self) -> &str {
     55             "private path and raw database detail"
     56         }
     57         fn code(&self) -> Option<Cow<'_, str>> {
     58             self.0.map(Cow::Borrowed)
     59         }
     60         fn as_error(&self) -> &(dyn StdError + Send + Sync + 'static) {
     61             self
     62         }
     63         fn as_error_mut(&mut self) -> &mut (dyn StdError + Send + Sync + 'static) {
     64             self
     65         }
     66         fn into_error(self: Box<Self>) -> Box<dyn StdError + Send + Sync + 'static> {
     67             self
     68         }
     69         fn kind(&self) -> ErrorKind {
     70             ErrorKind::Other
     71         }
     72     }
     73 
     74     #[test]
     75     fn numeric_capacity_codes_are_redacted_and_other_database_errors_stay_generic() {
     76         for code in [Some("13"), Some("269"), Some("525")] {
     77             let mapped = map_backend(sqlx::Error::Database(Box::new(Coded(code))));
     78             assert_eq!(mapped, Error::SpaceInsufficient);
     79             assert_eq!(mapped.to_string(), "storage space is insufficient");
     80             assert_eq!(format!("{mapped:?}"), "SpaceInsufficient");
     81         }
     82         for code in [
     83             None,
     84             Some(""),
     85             Some("full"),
     86             Some("-13"),
     87             Some("4294967296"),
     88             Some("11"),
     89             Some("5"),
     90             Some("10"),
     91         ] {
     92             assert_eq!(
     93                 map_backend(sqlx::Error::Database(Box::new(Coded(code)))),
     94                 Error::BackendUnavailable
     95             );
     96         }
     97     }
     98 
     99     #[test]
    100     fn typed_io_capacity_is_distinct_without_inventing_a_no_effect_receipt() {
    101         use std::io::ErrorKind as Io;
    102         for kind in [Io::StorageFull, Io::QuotaExceeded] {
    103             let source = std::io::Error::new(kind, "private path and raw file detail");
    104             assert_eq!(
    105                 map_backend(sqlx::Error::Io(source)),
    106                 Error::SpaceInsufficient
    107             );
    108         }
    109         for kind in [Io::PermissionDenied, Io::Other] {
    110             assert_eq!(
    111                 map_backend(sqlx::Error::Io(std::io::Error::from(kind))),
    112                 Error::BackendUnavailable
    113             );
    114         }
    115         assert_eq!(
    116             map_backend(sqlx::Error::PoolClosed),
    117             Error::BackendUnavailable
    118         );
    119     }
    120 }