#![deny(missing_docs)]
#). See [`skip_error_and_log!`]
and [`SkipError::skip_error_and_log()`] for more information.
"
)]
#[macro_export]
macro_rules! skip_error {
($result:expr) => {{
match $result {
Ok(value) => value,
Err(error) => {
continue;
}
}
}};
}
#[cfg_attr(all(feature = "log", not(feature = "tracing")), doc = "[`log::Level`]")]
#[cfg_attr(feature = "tracing", doc = "[`tracing::Level`]")]
#[cfg_attr(
all(feature = "log", not(feature = "tracing")),
doc = " let number: u32 = skip_error_and_log!(string_number.parse(), log::Level::Warn);"
)]
#[cfg_attr(
feature = "tracing",
doc = " let number: u32 = skip_error_and_log!(string_number.parse(), tracing::Level::WARN);"
)]
#[macro_export]
#[cfg(any(feature = "log", feature = "tracing"))]
macro_rules! skip_error_and_log {
($result:expr, $log_level:expr) => {{
match $result {
Ok(value) => value,
Err(error) => {
$crate::__log!(error, $log_level);
continue;
}
}
}};
}
#[cfg(any(feature = "log", feature = "tracing"))]
macro_rules! skip_error_macro_generation {
($macro_name:ident, $log_level:expr) => {
skip_error_macro_generation!($macro_name, $log_level, $log_level);
};
($macro_name:ident, $log_level:expr, $expected_log_level:expr) => {
#[doc = concat!(
"`",
stringify!($macro_name),
"` returns the value of a [`Result`] or log with [`",
stringify!($log_level),
"`] and continues the loop.\n\n",
"`",
stringify!($macro_name),
"` macro takes one parameter which is of type [`Result`].",
"The macro returns the value if `Result::Ok` and else,",
"it logs the [`Result::Err`] with level [`",
stringify!($log_level),
"`] and calls `continue`.\n\n",
"For example\n",
"```edition2018\n",
"# #[macro_use]\n",
"# extern crate skip_error;\n",
"# fn main() {\n",
"# testing_logger::setup();\n",
"for string_number in &[\"1\", \"2\", \"three\", \"4\"] {\n",
" let number: u32 = ", stringify!($macro_name), "!(string_number.parse());\n",
"}\n",
"testing_logger::validate(|captured_logs| {\n",
" assert!(captured_logs[0].body.contains(\"invalid digit found in string\"));\n",
" assert_eq!(captured_logs[0].level, ", stringify!($expected_log_level), ");\n",
"});\n",
"# }\n",
"```\n",
)]
#[macro_export]
macro_rules! $macro_name {
($result:expr) => {{
$crate::skip_error_and_log!($result, $log_level)
}};
}
};
}
#[cfg(all(feature = "log", not(feature = "tracing")))]
skip_error_macro_generation!(skip_error_and_error, log::Level::Error);
#[cfg(all(feature = "log", not(feature = "tracing")))]
skip_error_macro_generation!(skip_error_and_warn, log::Level::Warn);
#[cfg(all(feature = "log", not(feature = "tracing")))]
skip_error_macro_generation!(skip_error_and_info, log::Level::Info);
#[cfg(all(feature = "log", not(feature = "tracing")))]
skip_error_macro_generation!(skip_error_and_debug, log::Level::Debug);
#[cfg(all(feature = "log", not(feature = "tracing")))]
skip_error_macro_generation!(skip_error_and_trace, log::Level::Trace);
#[cfg(feature = "tracing")]
skip_error_macro_generation!(
skip_error_and_error,
tracing::Level::ERROR,
log::Level::Error
);
#[cfg(feature = "tracing")]
skip_error_macro_generation!(skip_error_and_warn, tracing::Level::WARN, log::Level::Warn);
#[cfg(feature = "tracing")]
skip_error_macro_generation!(skip_error_and_info, tracing::Level::INFO, log::Level::Info);
#[cfg(feature = "tracing")]
skip_error_macro_generation!(
skip_error_and_debug,
tracing::Level::DEBUG,
log::Level::Debug
);
#[cfg(feature = "tracing")]
skip_error_macro_generation!(
skip_error_and_trace,
tracing::Level::TRACE,
log::Level::Trace
);
#[doc(hidden)]
#[macro_export]
#[cfg(all(feature = "log", not(feature = "tracing")))]
macro_rules! __log {
($error:expr, $log_level:expr) => {{
log::log!(
std::convert::Into::<log::Level>::into($log_level),
"{}",
$error
);
}};
}
#[doc(hidden)]
#[macro_export]
#[cfg(feature = "tracing")]
macro_rules! __log {
($error:tt, $log_level:expr) => {{
match std::convert::Into::<tracing::Level>::into($log_level) {
tracing::Level::INFO => tracing::info!("{}", $error),
tracing::Level::WARN => tracing::warn!("{}", $error),
tracing::Level::ERROR => tracing::error!("{}", $error),
tracing::Level::DEBUG => tracing::debug!("{}", $error),
tracing::Level::TRACE => tracing::trace!("{}", $error),
}
}};
}
pub struct SkipErrorIter<I, T, E>
where
I: Iterator<Item = Result<T, E>>,
{
inner: I,
#[cfg(all(feature = "log", not(feature = "tracing")))]
log_level: Option<log::Level>,
#[cfg(feature = "tracing")]
log_level: Option<tracing::Level>,
}
impl<I, T, E> std::iter::Iterator for SkipErrorIter<I, T, E>
where
I: Iterator<Item = Result<T, E>>,
E: std::fmt::Display,
{
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.inner.next().and_then(|result| match result {
Ok(value) => Some(value),
Err(_error) => {
#[cfg(any(feature = "log", feature = "tracing"))]
if let Some(log_level) = self.log_level {
__log!(_error, log_level);
}
self.next()
}
})
}
}
#[cfg(any(feature = "log", feature = "tracing"))]
macro_rules! default_impl_skip_error_iterator {
($method_name:ident, $log_level:expr) => {
default_impl_skip_error_iterator!($method_name, $log_level, $log_level);
};
($method_name:ident, $log_level:expr, $expected_log_level:expr) => {
#[doc = concat!(
"Shortcut for [`SkipError::skip_error_and_log`] with a log level of [`",
stringify!($log_level),
"`].\n\n",
"For example\n",
"```edition2018\n",
"use skip_error::SkipError;\n",
"# fn main() {\n",
"# testing_logger::setup();\n",
"vec![Ok(1), Ok(2), Err(\"'three' is not a valid number\"), Ok(4)]\n",
" .into_iter()\n",
" .", stringify!($method_name), "()\n",
" .collect::<Vec<_>>();\n",
"testing_logger::validate(|captured_logs| {\n",
" assert!(captured_logs[0].body.contains(\"'three' is not a valid number\"));\n",
" assert_eq!(captured_logs[0].level, ", stringify!($expected_log_level), ");\n",
"});\n",
"# }\n",
"```\n"
)]
fn $method_name(self) -> SkipErrorIter<I, T, E> {
self.skip_error_and_log($log_level)
}
};
}
pub trait SkipError<I, T, E>: Sized
where
I: Iterator<Item = Result<T, E>>,
{
fn skip_error(self) -> SkipErrorIter<I, T, E>;
#[cfg(all(feature = "log", not(feature = "tracing")))]
fn skip_error_and_log<L>(self, log_level: L) -> SkipErrorIter<I, T, E>
where
L: Into<log::Level>;
#[cfg(feature = "tracing")]
fn skip_error_and_log<L>(self, trace_level: L) -> SkipErrorIter<I, T, E>
where
L: Into<tracing::Level>;
#[cfg(all(feature = "log", not(feature = "tracing")))]
default_impl_skip_error_iterator!(skip_error_and_trace, log::Level::Trace);
#[cfg(all(feature = "log", not(feature = "tracing")))]
default_impl_skip_error_iterator!(skip_error_and_debug, log::Level::Debug);
#[cfg(all(feature = "log", not(feature = "tracing")))]
default_impl_skip_error_iterator!(skip_error_and_error, log::Level::Error);
#[cfg(all(feature = "log", not(feature = "tracing")))]
default_impl_skip_error_iterator!(skip_error_and_warn, log::Level::Warn);
#[cfg(all(feature = "log", not(feature = "tracing")))]
default_impl_skip_error_iterator!(skip_error_and_info, log::Level::Info);
#[cfg(feature = "tracing")]
default_impl_skip_error_iterator!(
skip_error_and_trace,
tracing::Level::TRACE,
log::Level::Trace
);
#[cfg(feature = "tracing")]
default_impl_skip_error_iterator!(
skip_error_and_debug,
tracing::Level::DEBUG,
log::Level::Debug
);
#[cfg(feature = "tracing")]
default_impl_skip_error_iterator!(
skip_error_and_error,
tracing::Level::ERROR,
log::Level::Error
);
#[cfg(feature = "tracing")]
default_impl_skip_error_iterator!(skip_error_and_warn, tracing::Level::WARN, log::Level::Warn);
#[cfg(feature = "tracing")]
default_impl_skip_error_iterator!(skip_error_and_info, tracing::Level::INFO, log::Level::Info);
}
impl<I, T, E> SkipError<I, T, E> for I
where
I: Iterator<Item = Result<T, E>>,
{
fn skip_error(self) -> SkipErrorIter<I, T, E> {
SkipErrorIter {
inner: self,
#[cfg(any(feature = "log", feature = "tracing"))]
log_level: None,
}
}
#[cfg(all(feature = "log", not(feature = "tracing")))]
fn skip_error_and_log<L>(self, log_level: L) -> SkipErrorIter<I, T, E>
where
L: Into<log::Level>,
{
SkipErrorIter {
inner: self,
log_level: Some(log_level.into()),
}
}
#[cfg(feature = "tracing")]
fn skip_error_and_log<L>(self, log_level: L) -> SkipErrorIter<I, T, E>
where
L: Into<tracing::Level>,
{
SkipErrorIter {
inner: self,
log_level: Some(log_level.into()),
}
}
}