diff --git a/.gitignore b/.gitignore index 98171f1fe..c1dc5c38e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ *~ target/ -Cargo.lock \ No newline at end of file +Cargo.lock +rusty-tags.vi diff --git a/.travis.yml b/.travis.yml index 6bbf37eb3..360f49569 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,9 +38,12 @@ env: - RUSTFLAGS="-D warnings" matrix: - FEATURES=--features=backtrace + - FEATURES=--features=log - FEATURES=--no-default-features matrix: exclude: - env: FEATURES=--features=backtrace rust: 1.10.0 + - env: FEATURES=--features=log + rust: 1.10.0 diff --git a/Cargo.toml b/Cargo.toml index 34122badb..6b0dbcb08 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -24,3 +24,4 @@ example_generated = [] [dependencies] backtrace = { version = "0.3.3", optional = true } +log = { version = "0.3", optional = true } diff --git a/examples/all.rs b/examples/all.rs index ccc3ab703..efb5cbfb3 100644 --- a/examples/all.rs +++ b/examples/all.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; diff --git a/examples/chain_err.rs b/examples/chain_err.rs index bd8effdaf..fc16e9c3b 100644 --- a/examples/chain_err.rs +++ b/examples/chain_err.rs @@ -1,6 +1,9 @@ //! Demonstrates usage of `Error::caused` method. This method enables chaining errors //! like `ResultExt::chain_err` but doesn't require the presence of a `Result` wrapper. +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; diff --git a/examples/doc.rs b/examples/doc.rs index 999ac9cef..ffc8e6f3b 100644 --- a/examples/doc.rs +++ b/examples/doc.rs @@ -2,6 +2,9 @@ //! This module is used to check that all generated items are documented. +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; diff --git a/examples/quickstart.rs b/examples/quickstart.rs index 2e3e2b5d3..1cd846c01 100644 --- a/examples/quickstart.rs +++ b/examples/quickstart.rs @@ -6,6 +6,9 @@ // Import the macro. Don't forget to add `error-chain` in your // `Cargo.toml`! +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; diff --git a/examples/size.rs b/examples/size.rs index 3e180684e..7a2846b43 100644 --- a/examples/size.rs +++ b/examples/size.rs @@ -1,3 +1,6 @@ +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; diff --git a/src/bin/has_backtrace.rs b/src/bin/has_backtrace.rs index c5dac058a..dc8904605 100644 --- a/src/bin/has_backtrace.rs +++ b/src/bin/has_backtrace.rs @@ -2,9 +2,14 @@ //! Used by tests to make sure backtraces are available when they should be. Should not be used //! outside of the tests. +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; + + error_chain! { errors { MyError diff --git a/src/error_chain.rs b/src/error_chain.rs index 9b95a77d3..b1174e4e2 100644 --- a/src/error_chain.rs +++ b/src/error_chain.rs @@ -10,15 +10,17 @@ macro_rules! impl_error_chain_processed { impl_error_chain_processed! { types { Error, ErrorKind, ResultExt, Result; + result_log_ext = ResultLogExt; } $( $rest )* } }; - // With `Result` wrapper. + // with result wrapper and logext ( types { $error_name:ident, $error_kind_name:ident, $result_ext_name:ident, $result_name:ident; + result_log_ext = $result_log_ext_name:ident; } $( $rest: tt )* ) => { @@ -26,6 +28,44 @@ macro_rules! impl_error_chain_processed { types { $error_name, $error_kind_name, $result_ext_name; + result_log_ext = $result_log_ext_name; + } + $( $rest )* + } + /// Convenient wrapper around `std::Result`. + #[allow(unused)] + pub type $result_name = ::std::result::Result; + }; + // without result wrapper and no logext + ( + types { + $error_name:ident, $error_kind_name:ident, + $result_ext_name:ident; + } + $( $rest: tt )* + ) => { + impl_error_chain_processed! { + types { + $error_name, $error_kind_name, + $result_ext_name; + result_log_ext = ResultLogExt3; + } + $( $rest )* + } + }; + // With `Result` wrapper no log ext + ( + types { + $error_name:ident, $error_kind_name:ident, + $result_ext_name:ident, $result_name:ident; + } + $( $rest: tt )* + ) => { + impl_error_chain_processed! { + types { + $error_name, $error_kind_name, + $result_ext_name; + result_log_ext = ResultLogExti2; } $( $rest )* } @@ -38,6 +78,7 @@ macro_rules! impl_error_chain_processed { types { $error_name:ident, $error_kind_name:ident, $result_ext_name:ident; + result_log_ext = $result_log_ext_name:ident; } links { @@ -342,6 +383,9 @@ macro_rules! impl_error_chain_processed { } + #[cfg(feature = "log")] + impl_result_log_ext!{ $result_log_ext_name , $error_name } + }; } diff --git a/src/lib.rs b/src/lib.rs index ac185c643..7d12ad11a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -101,6 +101,8 @@ //! define an `errors` module and inside it call [`error_chain!`]: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! mod other_error { //! error_chain! {} @@ -182,6 +184,8 @@ //! Introducing new error chains, with a string message: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! # error_chain! {} @@ -193,6 +197,8 @@ //! Introducing new error chains, with an [`ErrorKind`]: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! error_chain! { @@ -216,6 +222,8 @@ //! So the below is equivalent to the previous: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! # error_chain! { errors { FooError } } @@ -237,6 +245,8 @@ //! With [`bail!`] the previous examples look like: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! # error_chain! { errors { FooError } } @@ -264,6 +274,8 @@ //! To extend the error chain: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! # error_chain! {} @@ -290,6 +302,8 @@ //! To chain an error directly, use [`with_chain`]: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! # error_chain! {} @@ -306,6 +320,8 @@ //! To convert an error from another error chain to this error chain: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() {} //! # mod other { error_chain! {} } @@ -334,6 +350,8 @@ //! making dispatching on error kinds relatively compact: //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! # fn main() { //! error_chain! { @@ -356,6 +374,8 @@ //! Chained errors are also matched with (relatively) compact syntax //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! mod utils { //! error_chain! { @@ -390,6 +410,8 @@ //! of causing errors. For reporting purposes, this information can be accessed as follows. //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] extern crate error_chain; //! use error_chain::ChainedError; // for e.display_chain() //! @@ -466,6 +488,8 @@ //! within your own project. //! //! ``` +//! # #[cfg(feature = "log")] +//! # #[macro_use] extern crate log; //! # #[macro_use] //! # extern crate error_chain; //! # mod errors { @@ -540,6 +564,13 @@ use std::error; use std::iter::Iterator; use std::fmt; +#[cfg(feature = "log")] +#[macro_use] +extern crate log; + +#[macro_use] +mod log_ext; + #[macro_use] mod impl_error_chain_kind; #[macro_use] @@ -692,6 +723,8 @@ impl State { /// `bail!(expr)` is equivalent to writing. /// /// ``` +/// # #[cfg(feature = "log")] +/// # #[macro_use] extern crate log; /// # #[macro_use] extern crate error_chain; /// # error_chain! { } /// # fn main() { } @@ -704,6 +737,8 @@ impl State { /// And as shorthand it takes a formatting string a la `println!`: /// /// ``` +/// # #[cfg(feature = "log")] +/// # #[macro_use] extern crate log; /// # #[macro_use] extern crate error_chain; /// # error_chain! { } /// # fn main() { } @@ -718,6 +753,8 @@ impl State { /// Bailing on a custom error: /// /// ``` +/// # #[cfg(feature = "log")] +/// # #[macro_use] extern crate log; /// # #[macro_use] extern crate error_chain; /// # fn main() {} /// error_chain! { @@ -738,6 +775,8 @@ impl State { /// Bailing on a formatted string: /// /// ``` +/// # #[cfg(feature = "log")] +/// # #[macro_use] extern crate log; /// # #[macro_use] extern crate error_chain; /// # fn main() {} /// error_chain! { } @@ -770,6 +809,8 @@ macro_rules! bail { /// As an example, `ensure!(condition, "error code: {}", errcode)` is equivalent to /// /// ``` +/// # #[cfg(feature = "log")] +/// # #[macro_use] extern crate log; /// # #[macro_use] extern crate error_chain; /// # error_chain! { } /// # fn main() { } diff --git a/src/log_ext.rs b/src/log_ext.rs new file mode 100644 index 000000000..9586a209e --- /dev/null +++ b/src/log_ext.rs @@ -0,0 +1,106 @@ +#[cfg(feature = "log")] +#[macro_use] +mod impl_log_ext { +#[macro_export] +macro_rules! impl_result_log_ext { + ( $result_log_ext_name:ident, $error_name:ident ) => ( + /// Extend chained errors to be able to log them on the spot using the log crate. + /// See [`loge`], [`logw`], [`logi`], [`logd`], [`logt`] functions. + pub trait $result_log_ext_name { + /// Log error using the log crate `error!` macro. + fn loge(self) -> Self; + /// Log error using the log crate `warn!` macro. + fn logw(self) -> Self; + /// Log error using the log crate `info!` macro. + fn logi(self) -> Self; + /// Log error using the log crate `debug!` macro. + fn logd(self) -> Self; + /// Log error using the log crate `trace!` macro. + fn logt(self) -> Self; + } + + impl $result_log_ext_name for ::std::result::Result + { + impl_make_log_fn_for_result!( loge, error, Error); + impl_make_log_fn_for_result!( logw, warn, Warn); + impl_make_log_fn_for_result!( logi, info, Info); + impl_make_log_fn_for_result!( logd, debug, Debug); + impl_make_log_fn_for_result!( logt, trace, Trace); + } + + impl $result_log_ext_name for $error_name + { + impl_make_log_fn_for_chained_error!( loge, error, Error); + impl_make_log_fn_for_chained_error!( logw, warn, Warn); + impl_make_log_fn_for_chained_error!( logi, info, Info); + impl_make_log_fn_for_chained_error!( logd, debug, Debug); + impl_make_log_fn_for_chained_error!( logt, trace, Trace); + } + + ) +} + +/// Internal macro used to implement the logX() functions +/// It logs the causes of the error using the specified log crate level: +/// For example: +/// +/// `log_causes!(err,info)` +// #[cfg(feature = "log")] +#[macro_export] +macro_rules! impl_log_causes { + ($e:expr, $level:ident) => ( + for c in $e.iter().skip(1) { + $level!(" caused by: {}", c); + } + + if let Some(backtrace) = $e.backtrace() { + $level!("backtrace: {:?}", backtrace); + } + ) +} + +/// Internal macro used to implement the logX() functions +/// It generates a function that logs the error and its causes +/// using the specified log crate level. +/// 1st argument -$name: Function name +/// 2nd argument -$level: log macro to use (error, warn, info, debug, trace) +/// 3nd argument -$lvlchk: Do not execute the code +/// if logging is not enabled for this level +/// (Error, Warnm, Indo, Debug, Trace) +/// +/// For example: +/// +/// `log_error!(err,info,Info)` +#[cfg(feature = "log")] +#[macro_export] +macro_rules! impl_make_log_fn_for_result { + ($name:ident, $level:ident, $lvlchk:ident) => ( + fn $name(self) -> Self { + use log; + if let Err(ref e) = self { + if log_enabled!(log::LogLevel::$lvlchk) { + $level!("{}", e); + impl_log_causes!(e,$level); + } + }; + self + } + ) +} + +/// Internal implementation macro for logging the chained error type +#[cfg(feature = "log")] +#[macro_export] +macro_rules! impl_make_log_fn_for_chained_error { + ($name:ident, $level:ident, $lvlchk:ident) => ( + fn $name(self) -> Self { + use log; + if log_enabled!(log::LogLevel::$lvlchk) { + $level!("{}", self); + impl_log_causes!(self,$level); + }; + self + } + ) +} +} diff --git a/src/quick_main.rs b/src/quick_main.rs index f81e7d704..faee476ac 100644 --- a/src/quick_main.rs +++ b/src/quick_main.rs @@ -38,6 +38,10 @@ /// Err("error".into()) /// } /// ``` +#[cfg(features = "log")] +#[macro_use] +extern crate log; + #[macro_export] macro_rules! quick_main { ($main:expr) => { diff --git a/tests/log_ext.rs b/tests/log_ext.rs new file mode 100644 index 000000000..8bce8cfd2 --- /dev/null +++ b/tests/log_ext.rs @@ -0,0 +1,79 @@ +#![allow(dead_code)] + +#[cfg(feature = "log")] +#[macro_use] +extern crate log; + +#[cfg(feature = "log")] +#[macro_use] +extern crate error_chain; + +#[cfg(feature = "log")] +#[cfg(test)] +mod log_ext_tests { + + #[test] + fn logext_macro_call_for_error() { + macro_rules! check { + ( $what:ident , $fun:expr ) => ( + match $what { + Error(ErrorKind::Msg(_), ..) => (), + _ => panic!("{} did not return an error: {:?}",$fun, $what) + } + ) + } + error_chain! { + errors { + Test + } + } + let msg1 = "My test error"; + let msg2 = "My test warn"; + let msg3 = "My test info"; + let msg4 = "My test debug"; + let msg5 = "My test trace"; + fn base() -> Error { Error::from(ErrorKind::Test) } + let erre = base().chain_err(|| msg1).loge(); + let errw = base().chain_err(|| msg2).logw(); + let erri = base().chain_err(|| msg3).logi(); + let errd = base().chain_err(|| msg4).logd(); + let errt = base().chain_err(|| msg5).logt(); + + check!(erre,"loge"); + check!(errw,"logw"); + check!(erri,"logi"); + check!(errd,"logd"); + check!(errt,"logt"); + } + + #[test] + fn logext_macro_call_for_result() { + macro_rules! check { + ( $what:ident , $fun:expr) => ( + match $what { + Err(Error(..)) => (), + _ => panic!("{} did not return a result type!",$fun) + } + ) + } + error_chain! { + errors { + Test + } + + } + + fn base() -> Result<()> { Err( Error::from(ErrorKind::Test) ) } + + let rese = base().chain_err(|| "My test error").loge(); + let resw = base().chain_err(|| "My test warn").logw(); + let resi = base().chain_err(|| "My test info").logi(); + let resd = base().chain_err(|| "My test debug").logd(); + let rest = base().chain_err(|| "My test trace").logt(); + check!(rese,"loge"); + check!(resw,"logw"); + check!(resi,"logi"); + check!(resd,"logd"); + check!(rest,"logt"); + } +} diff --git a/tests/quick_main.rs b/tests/quick_main.rs index 4ada3b4e0..25c00d9dc 100644 --- a/tests/quick_main.rs +++ b/tests/quick_main.rs @@ -1,4 +1,7 @@ #![allow(dead_code)] +#[cfg(feature = "log")] +#[macro_use] +extern crate log; #[macro_use] extern crate error_chain; diff --git a/tests/tests.rs b/tests/tests.rs index 0e38e5448..5661a3a5e 100644 --- a/tests/tests.rs +++ b/tests/tests.rs @@ -1,5 +1,9 @@ #![allow(dead_code)] +#[cfg(feature = "log")] +#[macro_use] +extern crate log; + #[macro_use] extern crate error_chain;