// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::{any, fmt}; use convert_case::{Case, Casing}; use snafu::{Backtrace, OptionExt, Snafu}; use crate::prelude::*; /// Something was unexpectedly missing from the database. #[derive(Debug, Snafu)] #[snafu(visibility(pub))] pub struct NotFoundError { pub type_name: String, pub identifier: String, pub backtrace: Option, } impl NotFoundError { pub fn message(&self, tr: &I18n) -> String { tr.errors_inconsistent_db_state().into() } pub fn context(&self) -> String { format!("No such {}: '{}'", self.type_name, self.identifier) } } impl PartialEq for NotFoundError { fn eq(&self, other: &Self) -> bool { self.type_name == other.type_name && self.identifier == other.identifier } } impl Eq for NotFoundError {} /// Allows generating [AnkiError::NotFound] from [None]. pub trait OrNotFound { type Value; fn or_not_found(self, identifier: impl fmt::Display) -> Result; } impl OrNotFound for Option { type Value = T; fn or_not_found(self, identifier: impl fmt::Display) -> Result { self.with_context(|| NotFoundSnafu { type_name: unqualified_lowercase_type_name::(), identifier: format!("{identifier}"), }) .map_err(Into::into) } } fn unqualified_lowercase_type_name() -> String { any::type_name::() .split("::") .last() .unwrap_or_default() .to_case(Case::Lower) } #[cfg(test)] mod test { use super::*; #[test] fn test_unqualified_lowercase_type_name() { assert_eq!(unqualified_lowercase_type_name::(), "card id"); } }