// Copyright: Ankitects Pty Ltd and contributors // License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html use snafu::{Backtrace, OptionExt, ResultExt, Snafu}; use crate::prelude::*; /// General-purpose error for unexpected [Err]s, [None]s, and other /// violated constraints. #[derive(Debug, Snafu)] #[snafu(visibility(pub), display("{message}"), whatever)] pub struct InvalidInputError { pub message: String, #[snafu(source(from(Box, Some)))] pub source: Option>, pub backtrace: Option, } impl InvalidInputError { pub fn message(&self) -> String { self.message.clone() } pub fn context(&self) -> String { if let Some(source) = &self.source { format!("{}", source) } else { String::new() } } } impl PartialEq for InvalidInputError { fn eq(&self, other: &Self) -> bool { self.message == other.message } } impl Eq for InvalidInputError {} /// Allows generating [AnkiError::InvalidInput] from [None] and the /// typical [Err]. pub trait OrInvalid { type Value; fn or_invalid(self, message: impl Into) -> Result; } impl OrInvalid for Option { type Value = T; fn or_invalid(self, message: impl Into) -> Result { self.whatever_context::<_, InvalidInputError>(message) .map_err(Into::into) } } impl OrInvalid for Result { type Value = T; fn or_invalid(self, message: impl Into) -> Result { self.whatever_context::<_, InvalidInputError>(message) .map_err(Into::into) } } /// Returns an [AnkiError::InvalidInput] with the provided format string and an /// optional underlying error. #[macro_export] macro_rules! invalid_input { ($fmt:literal$(, $($arg:expr),* $(,)?)?) => { return core::result::Result::Err({ $crate::error::AnkiError::InvalidInput { source: snafu::FromString::without_source( format!($fmt$(, $($arg),*)*), ) }}) }; ($source:expr, $fmt:literal$(, $($arg:expr),* $(,)?)?) => { return core::result::Result::Err({ $crate::error::AnkiError::InvalidInput { source: snafu::FromString::with_source( core::convert::Into::into($source), format!($fmt$(, $($arg),*)*), ) }}) }; } /// Returns an [AnkiError::InvalidInput] unless the condition is true. #[macro_export] macro_rules! require { ($condition:expr, $fmt:literal$(, $($arg:expr),* $(,)?)?) => { if !$condition { return core::result::Result::Err({ $crate::error::AnkiError::InvalidInput { source: snafu::FromString::without_source( format!($fmt$(, $($arg),*)*), ) }}); } }; }