// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use std::str::Utf8Error; use anki_i18n::I18n; use rusqlite::{types::FromSqlError, Error}; use snafu::Snafu; use super::AnkiError; #[derive(Debug, PartialEq, Eq, Snafu)] pub struct DbError { pub info: String, pub kind: DbErrorKind, } #[derive(Debug, PartialEq, Eq)] pub enum DbErrorKind { FileTooNew, FileTooOld, MissingEntity, Corrupt, Locked, Utf8, Other, } impl AnkiError { pub(crate) fn db_error(info: impl Into, kind: DbErrorKind) -> Self { AnkiError::DbError { source: DbError { info: info.into(), kind, }, } } } impl From for AnkiError { fn from(err: Error) -> Self { if let Error::SqliteFailure(error, Some(reason)) = &err { if error.code == rusqlite::ErrorCode::DatabaseBusy { return AnkiError::DbError { source: DbError { info: "".to_string(), kind: DbErrorKind::Locked, }, }; } if reason.contains("regex parse error") { return AnkiError::InvalidRegex { info: reason.to_owned(), }; } } AnkiError::DbError { source: DbError { info: format!("{:?}", err), kind: DbErrorKind::Other, }, } } } impl From for AnkiError { fn from(err: FromSqlError) -> Self { if let FromSqlError::Other(ref err) = err { if let Some(_err) = err.downcast_ref::() { return AnkiError::DbError { source: DbError { info: "".to_string(), kind: DbErrorKind::Utf8, }, }; } } AnkiError::DbError { source: DbError { info: format!("{:?}", err), kind: DbErrorKind::Other, }, } } } impl DbError { pub fn message(&self, _tr: &I18n) -> String { match self.kind { DbErrorKind::Corrupt => self.info.clone(), // fixme: i18n DbErrorKind::Locked => "Anki already open, or media currently syncing.".into(), _ => format!("{:?}", self), } } }