├── .gitignore ├── derive-error-chain-tests ├── Cargo.toml └── src │ └── main.rs ├── derive-error-chain-tests-nightly ├── Cargo.toml └── src │ └── main.rs ├── derive-error-chain-tests-no-backtrace ├── Cargo.toml └── src │ └── main.rs ├── .travis.yml ├── README.md ├── derive-error-chain ├── Cargo.toml └── src │ └── lib.rs └── CHANGELOG.md /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | 3 | Cargo.lock 4 | -------------------------------------------------------------------------------- /derive-error-chain-tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "derive-error-chain-tests" 3 | version = "0.1.0" 4 | authors = ["Arnavion "] 5 | publish = false 6 | 7 | [dependencies] 8 | error-chain = "0.11.x" 9 | derive-error-chain = { path = "../derive-error-chain" } 10 | -------------------------------------------------------------------------------- /derive-error-chain-tests-nightly/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "derive-error-chain-tests-nightly" 3 | version = "0.1.0" 4 | authors = ["Arnavion "] 5 | publish = false 6 | 7 | [dependencies] 8 | error-chain = "0.11.x" 9 | derive-error-chain = { path = "../derive-error-chain" } 10 | -------------------------------------------------------------------------------- /derive-error-chain-tests-no-backtrace/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "derive-error-chain-tests-no-backtrace" 3 | version = "0.1.0" 4 | authors = ["Arnavion "] 5 | publish = false 6 | 7 | [dependencies] 8 | error-chain = { version = "0.11.x", default-features = false } 9 | derive-error-chain = { path = "../derive-error-chain" } 10 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | rust: 3 | - stable 4 | - beta 5 | - nightly 6 | 7 | script: 8 | - (if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then cd derive-error-chain && cargo test --verbose; fi) 9 | - (cd derive-error-chain-tests && cargo run --verbose) 10 | - (cd derive-error-chain-tests-no-backtrace && cargo run --verbose) 11 | - (if [[ "$TRAVIS_RUST_VERSION" == "nightly" ]]; then cd derive-error-chain-tests-nightly && cargo run --verbose; else echo 'Skipping nightly-only test'; fi) 12 | 13 | sudo: false 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This crate is no longer maintained. 2 | 3 | If you have questions about this repository, open an issue at https://github.com/Arnavion/archived-repos-issues 4 | 5 | --- 6 | 7 | # derive-error-chain 8 | 9 | [![Build Status](https://travis-ci.org/Arnavion/derive-error-chain.svg?branch=master)](https://travis-ci.org/Arnavion/derive-error-chain) 10 | 11 | A Macros 1.1 implementation of [error-chain.](https://crates.io/crates/error-chain) 12 | 13 | [crates.io](https://crates.io/crates/derive-error-chain) 14 | 15 | [Documentation](https://docs.rs/derive-error-chain) 16 | 17 | ## Supported Rust version 18 | 19 | 1.15.0 and higher. 20 | 21 | ## License 22 | 23 | MIT/Apache-2.0 24 | -------------------------------------------------------------------------------- /derive-error-chain/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "derive-error-chain" 3 | version = "0.11.2" 4 | license = "MIT/Apache-2.0" 5 | authors = [ 6 | "Brian Anderson ", 7 | "Paul Colomiets ", 8 | "Colin Kiegel ", 9 | "Yamakaky ", 10 | "Arnav Singh " 11 | ] 12 | description = "A Macros 1.1 implementation of error-chain" 13 | documentation = "https://docs.rs/derive-error-chain" 14 | repository = "https://github.com/Arnavion/derive-error-chain" 15 | 16 | [dependencies] 17 | proc-macro2 = "0.4.x" 18 | quote = "0.6.x" 19 | syn = { version = "0.14.x", features = ["derive", "full", "printing"] } 20 | syntex_fmt_macros = "0.5.x" 21 | 22 | [dev-dependencies] 23 | error-chain = "0.11.x" 24 | 25 | [lib] 26 | proc-macro = true 27 | -------------------------------------------------------------------------------- /derive-error-chain-tests-no-backtrace/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | //! Test crate for derive-error-chain. If it runs, it's tested. 4 | 5 | #![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] 6 | #![cfg_attr(feature = "cargo-clippy", allow( 7 | missing_docs_in_private_items, 8 | use_debug, 9 | ))] 10 | 11 | #[macro_use] 12 | extern crate derive_error_chain; 13 | 14 | fn main() { 15 | can_disable_backtrace(); 16 | } 17 | 18 | fn can_disable_backtrace() { 19 | #[derive(Debug, ErrorChain)] 20 | #[error_chain(backtrace = "false")] 21 | pub enum ErrorKind { 22 | Msg(String), 23 | } 24 | 25 | let err: Error = ErrorKind::Msg("foo".to_string()).into(); 26 | assert!(err.backtrace().is_none()); 27 | assert_eq!( 28 | r#"Error(Msg("foo"), State { next_error: None })"#, 29 | format!("{:?}", err) 30 | ); 31 | } 32 | 33 | #[deny(dead_code)] 34 | mod allow_dead_code { 35 | #[derive(Debug, ErrorChain)] 36 | #[error_chain(result = "", backtrace = "false")] 37 | pub enum ErrorKind { 38 | Msg(String), 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # v0.11.2 (2018-05-23) 2 | 3 | - Deps updated because of proc macro API breakage in nightly Rust. No API changes in `derive-error-chain` itself. 4 | 5 | 6 | # v0.11.1 (2018-04-01) 7 | 8 | - The `ErrorKind` no longer needs to have `pub` visibility. The generated `Error`, `Result` and `ResultExt` items will have the same visibility as the `ErrorKind`. 9 | 10 | When the `proc_macro` nightly feature is enabled, the following new features become available. See the crate documentation for more details. 11 | 12 | - The `display` and `description` attributes can now have a raw function expression value instead of a string containing the function expression. Eg `#[error_chain(display = |e| write!(f, "http request returned an unsuccessful status code: {}", e))]` 13 | - Those `display` and `description` attributes that only contained a single call like `write!(f, ...)` can now use the `const()` shorthand. Eg `#[error_chain(description = const("Chainable's description"))]` and `#[error_chain(display = const("Custom's display: {code}"))]`. 14 | - The `link` attribute of chainable links can now have a raw path value instead of a string containing the path. Eg `#[error_chain(link = other_error::Error)]`. 15 | 16 | # v0.11.0 (2017-09-07) 17 | 18 | - BREAKING CHANGE: The enum attribute `#[derive(error_chain)]` is now `#[derive(ErrorChain)]` to comply with upcoming Rust changes - `#[derive(error_chain)]` would conflict with `#[error_chain(...)]` since the latter appears to be a valid attribute macro. The variant attribute `#[error_chain]` is unchanged, but check the "Conflicts with `error-chain` macros when the `proc_macro` feature is enabled" section in the docs for a caveat. 19 | - The `Msg(String)` variant is now optional. If omitted, the ErrorKind and Error will not impl `From` and `From<&str>` 20 | 21 | # v0.10.1 (2017-05-13) 22 | 23 | - Added support for generic errorkinds. 24 | 25 | # v0.10.0 (2017-02-25) 26 | 27 | - Fixed `ResultExt` to use the correct names for the error kind and error structs instead of `ErrorKind` and `Error`. 28 | - Added new `with_chain` function to `ChainedError` impl for compatibility with `error-chain` v0.10.0 29 | 30 | # v0.9.0 (2017-02-07) 31 | 32 | - BREAKING CHANGE: Removed `Sync` bound on the `ResultExt` trait and `ChainedError::extract_backtrace` function for compatibility with `error-chain` v0.9.0 33 | - Clippy's `redundant_closure_call` warning is now suppressed on inline lambdas passed to `#[error_chain(description / display / cause = "...")]` attributes via `#[allow]` gated on the `cargo-clippy` feature. This feature is automatically defined when running clippy through cargo as `cargo clippy` 34 | 35 | # v0.8.1 (2017-02-01) 36 | 37 | - If giving inline lambdas to `#[error_chain(description / display / cause = "...")]` attributes, the lambdas no longer need to be wrapped in parentheses. 38 | - If the value of the `#[error_chain(display)]` attribute is an inline lambda that isn't wrapped in parentheses, this lambda can omit the `&mut ::std::fmt::Formatter` parameter. Instead, it can capture `f` from its environment. Lambdas in parentheses continue to need the parameter, so this isn't a breaking change. 39 | 40 | # v0.8.0 (2017-01-20) 41 | 42 | - BREAKING CHANGE: Custom description and display function expressions now receive the variant fields as separate parameters instead of in a single tuple. 43 | - BREAKING CHANGE: Added `Sync` bound on the `ResultExt` trait and `ChainedError::extract_backtrace` function for compatibility with `error-chain` v0.8.0 44 | - Added new `cause = ...` item to variant attribute to override the default value returned by `::std::error::Error::cause()` 45 | 46 | # v0.7.2 (2016-12-28) 47 | 48 | - Removed `#[doc(hidden)]` on members of `Error` struct. 49 | - Added new methods to `ChainedError` impl for compatibility with `error-chain` v0.7.2 50 | 51 | # v0.7.1 (2016-12-23) 52 | 53 | - Fixed error message for malformed chainable links. 54 | 55 | # v0.7.0 (2016-12-05) 56 | 57 | - BREAKING CHANGE: The types generated by this proc macro now use some items from the `error-chain`crate. Thus your crate will now require a dependency on the `error-chain` in addition to this `derive-error-chain` crate. 58 | - For the same reason, the backtrace functionality uses `error-chain`'s re-exported `backtrace` crate, so your crate no longer needs to depend on the `backtrace` crate. 59 | - BREAKING CHANGE: For compatibility with the new `ErrorKind` enum generated by `error-chain`, the chainable link variants of an `ErrorKind` now contain only the chainable error kind object and not the chainable error itself. Thus chainable link variants have a breaking change in syntax, from `Foo(SomeError, SomeErrorKind)` to `#[error_chain(link = "SomeError")] Foo(SomeErrorKind)` 60 | - The version number of this crate will now follow `error-chain`. This implies that version 0.7 of `derive-error-chain` is expected to be similar in functionality to version 0.7 of `error-chain`, and so on. 61 | 62 | # v0.1.2 (2016-11-18) 63 | 64 | - BREAKING CHANGE: Due to new proc_macro semantics, every `ErrorKind` enum now needs a special `Msg(String)` member. 65 | - Added `backtrace = ...` item to top-level `#[error_chain]` attribute to allow overriding the backtrace type or disabling backtrace functionality completely. 66 | 67 | # v0.1.1 (2016-11-12) 68 | 69 | - Fixed default name for `ChainErr` trait to be `ChainErr`, and added `chain_err = "Foo"` item to top-level `#[error_chain]` attribute to override the name. 70 | 71 | # v0.1.0 (2016-11-12) 72 | 73 | First release. 74 | -------------------------------------------------------------------------------- /derive-error-chain-tests-nightly/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | #![feature(use_extern_macros)] 3 | 4 | //! Test crate for derive-error-chain. If it runs, it's tested. 5 | 6 | #![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] 7 | #![cfg_attr(feature = "cargo-clippy", allow( 8 | missing_docs_in_private_items, 9 | ))] 10 | 11 | #[macro_use] 12 | extern crate derive_error_chain; 13 | extern crate error_chain; 14 | 15 | fn main() { 16 | macro_conflicts_use(); 17 | macro_conflicts_fully_qualified(); 18 | raw_path_chainable_link(); 19 | lambda_description_and_display_and_cause(); 20 | const_format_string_tuple_variants(); 21 | const_format_string_struct_variants(); 22 | } 23 | 24 | fn macro_conflicts_use() { 25 | use error_chain::{ bail, error_chain as error_chain_macro, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace, quick_main }; 26 | 27 | #[derive(Debug, ErrorChain)] 28 | #[error_chain(result = "MyResult")] 29 | pub enum ErrorKind { 30 | Msg(String), 31 | 32 | #[error_chain(custom)] 33 | Code(i32), 34 | } 35 | 36 | error_chain_macro! { 37 | types { ECError, ECErrorKind, ECResultExt, ECResult; } 38 | } 39 | 40 | quick_main!(|| -> MyResult<()> { 41 | bail!("failed") 42 | }); 43 | 44 | fn foo() -> MyResult<()> { 45 | bail!("failed") 46 | } 47 | 48 | match foo() { 49 | Ok(_) => unreachable!(), 50 | Err(err) => match *err.kind() { 51 | ErrorKind::Msg(ref s) if s == "failed" => (), 52 | _ => unreachable!(), 53 | }, 54 | } 55 | } 56 | 57 | fn macro_conflicts_fully_qualified() { 58 | use error_chain::{ error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace }; 59 | 60 | #[derive(Debug, ErrorChain)] 61 | #[error_chain(result = "MyResult")] 62 | pub enum ErrorKind { 63 | Msg(String), 64 | 65 | #[error_chain(custom)] 66 | Code(i32), 67 | } 68 | 69 | error_chain::error_chain! { 70 | types { ECError, ECErrorKind, ECResultExt, ECResult; } 71 | } 72 | 73 | error_chain::quick_main!(|| -> MyResult<()> { 74 | error_chain::bail!("failed") 75 | }); 76 | 77 | fn foo() -> MyResult<()> { 78 | error_chain::bail!("failed") 79 | } 80 | 81 | match foo() { 82 | Ok(_) => unreachable!(), 83 | Err(err) => match *err.kind() { 84 | ErrorKind::Msg(ref s) if s == "failed" => (), 85 | _ => unreachable!(), 86 | }, 87 | } 88 | } 89 | 90 | fn raw_path_chainable_link() { 91 | mod other_error { 92 | #[derive(Debug, ErrorChain)] 93 | pub enum ErrorKind { 94 | Msg(String), 95 | } 96 | } 97 | 98 | #[derive(Debug, ErrorChain)] 99 | pub enum ErrorKind { 100 | #[error_chain(link = other_error::Error)] 101 | Another(other_error::ErrorKind), 102 | } 103 | 104 | let other_err: other_error::Error = other_error::ErrorKind::Msg("other error".to_string()).into(); 105 | let _: Error = other_err.into(); 106 | } 107 | 108 | fn lambda_description_and_display_and_cause() { 109 | #[derive(Debug, ErrorChain)] 110 | pub enum ErrorKind { 111 | Msg(String), 112 | 113 | #[error_chain(custom)] 114 | #[error_chain(description = |_| "http request returned an unsuccessful status code")] 115 | #[error_chain(display = |e| write!(f, "http request returned an unsuccessful status code: {}", e))] 116 | HttpStatus(u32), 117 | 118 | #[error_chain(custom)] 119 | #[error_chain(cause = |_, err| err)] 120 | FileIO(::std::path::PathBuf, ::std::io::Error), 121 | } 122 | 123 | let err: Error = ErrorKind::HttpStatus(5).into(); 124 | assert_eq!("http request returned an unsuccessful status code", ::std::error::Error::description(&err)); 125 | assert_eq!("http request returned an unsuccessful status code: 5".to_string(), format!("{}", err)); 126 | 127 | let err: Error = ErrorKind::FileIO(::std::path::PathBuf::new(), ::std::io::Error::from_raw_os_error(1)).into(); 128 | assert!(::std::error::Error::cause(&err).is_some()); 129 | } 130 | 131 | fn const_format_string_tuple_variants() { 132 | mod test { 133 | #[derive(Debug, ErrorChain)] 134 | pub enum ErrorKind { 135 | Msg(String), 136 | } 137 | } 138 | 139 | #[derive(Debug, ErrorChain)] 140 | pub enum ErrorKind { 141 | Msg(String), 142 | 143 | #[error_chain(link = "test::Error")] 144 | #[error_chain(description = const("Chainable's description"))] 145 | #[error_chain(display = const("Chainable's display: {0}"))] 146 | Chainable(test::ErrorKind), 147 | 148 | #[error_chain(foreign)] 149 | #[error_chain(description = const("Foreign's description"))] 150 | #[error_chain(display = const("Foreign's display: {0}"))] 151 | Foreign(::std::io::Error), 152 | 153 | #[error_chain(custom)] 154 | #[error_chain(description = const("Custom's description: {0}"))] 155 | #[error_chain(display = const("Custom's display: {0}"))] 156 | Custom(u32, u32), 157 | } 158 | 159 | let err: test::Error = "foo".into(); 160 | let err: Error = err.into(); 161 | assert_eq!("Chainable's description", ::std::error::Error::description(&err)); 162 | assert_eq!("Chainable's display: foo".to_string(), format!("{}", err)); 163 | 164 | let err: Error = ::std::io::Error::new(std::io::ErrorKind::NotFound, "abcde".to_string()).into(); 165 | assert_eq!("Foreign's description", ::std::error::Error::description(&err)); 166 | assert_eq!("Foreign's display: abcde".to_string(), format!("{}", err)); 167 | 168 | let err: Error = ErrorKind::Custom(5, 6).into(); 169 | // No parameter substitution for `const()` description since it must be `&str` 170 | assert_eq!("Custom's description: {0}", ::std::error::Error::description(&err)); 171 | assert_eq!("Custom's display: 5".to_string(), format!("{}", err)); 172 | } 173 | 174 | fn const_format_string_struct_variants() { 175 | #[derive(Debug, ErrorChain)] 176 | pub enum ErrorKind { 177 | Msg(String), 178 | 179 | #[error_chain(custom)] 180 | #[error_chain(description = const("Custom's description: {code}"))] 181 | #[error_chain(display = const("Custom's display: {code}"))] 182 | Custom { code: u32, extra: u32, }, 183 | } 184 | 185 | let err: Error = (ErrorKind::Custom { code: 5, extra: 6, }).into(); 186 | // No parameter substitution for `const()` description since it must be `&str` 187 | assert_eq!("Custom's description: {code}", ::std::error::Error::description(&err)); 188 | assert_eq!("Custom's display: 5".to_string(), format!("{}", err)); 189 | } 190 | -------------------------------------------------------------------------------- /derive-error-chain-tests/src/main.rs: -------------------------------------------------------------------------------- 1 | #![allow(dead_code)] 2 | 3 | //! Test crate for derive-error-chain. If it runs, it's tested. 4 | 5 | #![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] 6 | #![cfg_attr(feature = "cargo-clippy", allow( 7 | missing_docs_in_private_items, 8 | option_unwrap_used, 9 | similar_names, 10 | use_debug, 11 | ))] 12 | 13 | #[macro_use] 14 | extern crate derive_error_chain; 15 | #[macro_use] 16 | extern crate error_chain; 17 | 18 | fn main() { 19 | smoke_test_1(); 20 | smoke_test_2(); 21 | smoke_test_4(); 22 | smoke_test_8(); 23 | chain_err(); 24 | links(); 25 | 26 | foreign_link_test::display_underlying_error(); 27 | foreign_link_test::finds_cause(); 28 | foreign_link_test::iterates(); 29 | 30 | with_result(); 31 | without_result(); 32 | documentation(); 33 | rustup_regression(); 34 | error_patterns(); 35 | rewrapping(); 36 | 37 | public_api_test(); 38 | cause(); 39 | inlined_description_and_display_and_cause(); 40 | test_without_msg_1(); 41 | test_without_msg_2(); 42 | macro_conflicts(); 43 | } 44 | 45 | // Upstream tests 46 | 47 | fn smoke_test_1() { 48 | #[derive(Debug, ErrorChain)] 49 | #[error_chain(error = "Error", result_ext = "ResultExt", result = "Result")] 50 | pub enum ErrorKind { 51 | Msg(String), 52 | } 53 | } 54 | 55 | fn smoke_test_2() { 56 | #[derive(Debug, ErrorChain)] 57 | pub enum ErrorKind { 58 | Msg(String), 59 | } 60 | } 61 | 62 | #[cfg_attr(feature = "cargo-clippy", allow(items_after_statements))] 63 | fn smoke_test_4() { 64 | #[derive(Debug, ErrorChain)] 65 | pub enum ErrorKind { 66 | Msg(String), 67 | 68 | #[error_chain(custom, description = "http_status_description", display = "http_status_display")] 69 | HttpStatus(u32), 70 | } 71 | 72 | let err: Error = ErrorKind::HttpStatus(5).into(); 73 | assert_eq!("http request returned an unsuccessful status code", ::std::error::Error::description(&err)); 74 | assert_eq!("http request returned an unsuccessful status code: 5".to_string(), format!("{}", err)); 75 | 76 | fn http_status_description(_: &u32) -> &str { 77 | "http request returned an unsuccessful status code" 78 | } 79 | 80 | fn http_status_display(f: &mut ::std::fmt::Formatter, e: &u32) -> ::std::fmt::Result { 81 | write!(f, "http request returned an unsuccessful status code: {}", e) 82 | } 83 | } 84 | 85 | fn smoke_test_8() { 86 | #[derive(Debug, ErrorChain)] 87 | pub enum ErrorKind { 88 | Msg(String), 89 | 90 | #[error_chain(custom)] 91 | FileNotFound, 92 | 93 | #[error_chain(custom)] 94 | AccessDenied, 95 | } 96 | } 97 | 98 | fn chain_err() { 99 | use std::fmt; 100 | 101 | #[derive(Debug, ErrorChain)] 102 | pub enum ErrorKind { 103 | Msg(String), 104 | 105 | #[error_chain(custom)] 106 | Test, 107 | } 108 | 109 | let _: Result<()> = Err(fmt::Error).chain_err(|| ""); 110 | let _: Result<()> = Err(Error::from_kind(ErrorKind::Test)).chain_err(|| ""); 111 | } 112 | 113 | fn links() { 114 | mod test { 115 | #[derive(Debug, ErrorChain)] 116 | pub enum ErrorKind { 117 | Msg(String), 118 | } 119 | } 120 | 121 | #[derive(Debug, ErrorChain)] 122 | pub enum ErrorKind { 123 | Msg(String), 124 | 125 | #[error_chain(link = "test::Error")] 126 | Test(test::ErrorKind), 127 | } 128 | } 129 | 130 | mod foreign_link_test { 131 | use std::fmt; 132 | 133 | #[derive(Debug)] 134 | pub struct ForeignError { 135 | cause: ForeignErrorCause, 136 | } 137 | 138 | impl ::std::error::Error for ForeignError { 139 | fn description(&self) -> &'static str { 140 | "Foreign error description" 141 | } 142 | 143 | fn cause(&self) -> Option<&::std::error::Error> { Some(&self.cause) } 144 | } 145 | 146 | impl fmt::Display for ForeignError { 147 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 148 | write!(formatter, "Foreign error display") 149 | } 150 | } 151 | 152 | #[derive(Debug)] 153 | pub struct ForeignErrorCause { } 154 | 155 | impl ::std::error::Error for ForeignErrorCause { 156 | fn description(&self) -> &'static str { 157 | "Foreign error cause description" 158 | } 159 | 160 | fn cause(&self) -> Option<&::std::error::Error> { None } 161 | } 162 | 163 | impl fmt::Display for ForeignErrorCause { 164 | fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { 165 | write!(formatter, "Foreign error cause display") 166 | } 167 | } 168 | 169 | #[derive(Debug, ErrorChain)] 170 | pub enum ErrorKind { 171 | Msg(String), 172 | 173 | #[error_chain(foreign)] 174 | Foreign(ForeignError), 175 | 176 | #[error_chain(foreign)] 177 | Io(::std::io::Error), 178 | } 179 | 180 | pub fn display_underlying_error() { 181 | let chained_error = try_foreign_error().err().unwrap(); 182 | assert_eq!( 183 | format!("{}", ForeignError { cause: ForeignErrorCause { } }), 184 | format!("{}", chained_error) 185 | ); 186 | } 187 | 188 | pub fn finds_cause() { 189 | let chained_error = try_foreign_error().err().unwrap(); 190 | assert_eq!( 191 | format!("{}", ForeignErrorCause { }), 192 | format!("{}", ::std::error::Error::cause(&chained_error).unwrap()) 193 | ); 194 | } 195 | 196 | pub fn iterates() { 197 | let chained_error = try_foreign_error().err().unwrap(); 198 | let mut error_iter = chained_error.iter(); 199 | assert_eq!( 200 | format!("{}", ForeignError { cause: ForeignErrorCause { } }), 201 | format!("{}", error_iter.next().unwrap()) 202 | ); 203 | assert_eq!( 204 | format!("{}", ForeignErrorCause { }), 205 | format!("{}", error_iter.next().unwrap()) 206 | ); 207 | assert_eq!( 208 | format!("{:?}", None as Option<&::std::error::Error>), 209 | format!("{:?}", error_iter.next()) 210 | ); 211 | } 212 | 213 | fn try_foreign_error() -> Result<()> { 214 | try!(Err(ForeignError { 215 | cause: ForeignErrorCause { } 216 | })); 217 | Ok(()) 218 | } 219 | } 220 | 221 | mod attributes_test { 222 | #[allow(unused_imports)] 223 | use std::io; 224 | 225 | #[cfg(foo)] 226 | mod inner { 227 | #[derive(Debug, ErrorChain)] 228 | pub enum ErrorKind { 229 | Msg(String), 230 | } 231 | } 232 | 233 | #[derive(Debug, ErrorChain)] 234 | pub enum ErrorKind { 235 | Msg(String), 236 | 237 | #[cfg(foo)] 238 | #[error_chain(link = "inner::Error")] 239 | Inner(inner::ErrorKind), 240 | 241 | #[cfg(foo)] 242 | #[error_chain(foreign)] 243 | Io(io::Error), 244 | 245 | #[cfg(foo)] 246 | #[error_chain(custom)] 247 | AnError, 248 | } 249 | } 250 | 251 | fn with_result() { 252 | #[derive(Debug, ErrorChain)] 253 | pub enum ErrorKind { 254 | Msg(String), 255 | } 256 | 257 | let _: Result<()> = Ok(()); 258 | } 259 | 260 | fn without_result() { 261 | #[derive(Debug, ErrorChain)] 262 | #[error_chain(result = "")] 263 | pub enum ErrorKind { 264 | Msg(String), 265 | } 266 | 267 | let _: Result<(), ()> = Ok(()); 268 | } 269 | 270 | fn documentation() { 271 | mod inner { 272 | #[derive(Debug, ErrorChain)] 273 | pub enum ErrorKind { 274 | Msg(String), 275 | } 276 | } 277 | 278 | #[derive(Debug, ErrorChain)] 279 | pub enum ErrorKind { 280 | Msg(String), 281 | 282 | /// Doc 283 | #[error_chain(link = "inner::Error")] 284 | Inner(inner::ErrorKind), 285 | 286 | /// Doc 287 | #[error_chain(foreign)] 288 | Io(::std::io::Error), 289 | 290 | /// Doc 291 | #[error_chain(custom)] 292 | Variant, 293 | } 294 | } 295 | 296 | mod multiple_error_same_mod { 297 | #[derive(Debug, ErrorChain)] 298 | #[error_chain(error = "MyError", result_ext = "MyResultExt", result = "MyResult")] 299 | pub enum MyErrorKind { 300 | Msg(String), 301 | } 302 | 303 | #[derive(Debug, ErrorChain)] 304 | pub enum ErrorKind { 305 | Msg(String), 306 | } 307 | } 308 | 309 | #[deny(dead_code)] 310 | mod allow_dead_code { 311 | #[derive(Debug, ErrorChain)] 312 | #[error_chain(result = "")] 313 | pub enum ErrorKind { 314 | Msg(String), 315 | } 316 | } 317 | 318 | // Make sure links actually work! 319 | fn rustup_regression() { 320 | mod mock { 321 | #[derive(Debug, ErrorChain)] 322 | pub enum ErrorKind { 323 | Msg(String), 324 | } 325 | } 326 | 327 | #[derive(Debug, ErrorChain)] 328 | pub enum ErrorKind { 329 | Msg(String), 330 | 331 | #[error_chain(link = "mock::Error")] 332 | Download(mock::ErrorKind), 333 | 334 | #[error_chain(custom)] 335 | #[error_chain(description = r#"|| "could not locate working directory""#)] 336 | LocatingWorkingDir, 337 | } 338 | } 339 | 340 | fn error_patterns() { 341 | #[derive(Debug, ErrorChain)] 342 | pub enum ErrorKind { 343 | Msg(String), 344 | } 345 | 346 | // Tuples look nice when matching errors 347 | match Error::from("Test") { 348 | Error(ErrorKind::Msg(_), _) => { 349 | } 350 | } 351 | } 352 | 353 | #[cfg_attr(feature = "cargo-clippy", allow(non_ascii_literal))] 354 | fn rewrapping() { 355 | use std::env::VarError::{self, NotPresent, NotUnicode}; 356 | 357 | #[derive(Debug, ErrorChain)] 358 | #[error_chain(error = "MyError", result_ext = "MyResultExt", result = "MyResult")] 359 | pub enum MyErrorKind { 360 | Msg(String), 361 | 362 | #[error_chain(foreign)] 363 | VarErr(VarError), 364 | } 365 | 366 | let result_a_from_func: Result = Err(VarError::NotPresent); 367 | let result_b_from_func: Result = Err(VarError::NotPresent); 368 | 369 | let our_error_a = result_a_from_func.map_err(|e| match e { 370 | NotPresent => MyError::with_chain(e, "env var wasn't provided"), 371 | NotUnicode(_) => MyError::with_chain(e, "env var was bork文字化ã"), 372 | }); 373 | 374 | let our_error_b = result_b_from_func.or_else(|e| match e { 375 | NotPresent => Err(e).chain_err(|| "env var wasn't provided"), 376 | NotUnicode(_) => Err(e).chain_err(|| "env var was bork文字化ã"), 377 | }); 378 | 379 | assert_eq!( 380 | format!("{}", our_error_a.unwrap_err()), 381 | format!("{}", our_error_b.unwrap_err()) 382 | ); 383 | } 384 | 385 | // Own tests 386 | 387 | mod test2 { 388 | #[derive(Debug, ErrorChain)] 389 | pub enum ErrorKind { 390 | Msg(String), 391 | 392 | #[error_chain(custom)] 393 | HttpStatus(u32), 394 | } 395 | } 396 | 397 | fn public_api_test() { 398 | use test2::{ Error, ErrorKind, ResultExt, Result }; 399 | 400 | let err: Error = ErrorKind::HttpStatus(5).into(); 401 | let result: Result<()> = Err(err); 402 | 403 | let _: Result<()> = result.chain_err(|| "An HTTP error occurred"); 404 | } 405 | 406 | fn cause() { 407 | #[derive(Debug, ErrorChain)] 408 | pub enum ErrorKind { 409 | Msg(String), 410 | 411 | #[error_chain(custom)] 412 | #[error_chain(cause = "file_io_error_cause")] 413 | FileIO(::std::path::PathBuf, ::std::io::Error), 414 | } 415 | 416 | fn file_io_error_cause<'a>(_: &::std::path::Path, err: &'a ::std::io::Error) -> &'a ::std::error::Error { 417 | err 418 | } 419 | 420 | let err: Error = ErrorKind::FileIO(::std::path::PathBuf::new(), ::std::io::Error::from_raw_os_error(1)).into(); 421 | assert!(::std::error::Error::cause(&err).is_some()); 422 | } 423 | 424 | fn inlined_description_and_display_and_cause() { 425 | #[derive(Debug, ErrorChain)] 426 | pub enum ErrorKind { 427 | Msg(String), 428 | 429 | #[error_chain(custom)] 430 | #[error_chain(description = r#"|_| "http request returned an unsuccessful status code""#)] 431 | #[error_chain(display = r#"|e| write!(f, "http request returned an unsuccessful status code: {}", e)"#)] 432 | HttpStatus(u32), 433 | 434 | #[error_chain(custom)] 435 | #[error_chain(cause = "|_, err| err")] 436 | FileIO(::std::path::PathBuf, ::std::io::Error), 437 | } 438 | 439 | let err: Error = ErrorKind::HttpStatus(5).into(); 440 | assert_eq!("http request returned an unsuccessful status code", ::std::error::Error::description(&err)); 441 | assert_eq!("http request returned an unsuccessful status code: 5".to_string(), format!("{}", err)); 442 | 443 | let err: Error = ErrorKind::FileIO(::std::path::PathBuf::new(), ::std::io::Error::from_raw_os_error(1)).into(); 444 | assert!(::std::error::Error::cause(&err).is_some()); 445 | } 446 | 447 | mod generics_test { 448 | use std::{error, fmt, io}; 449 | 450 | mod inner1 { 451 | use std::fmt; 452 | #[derive(Debug, ErrorChain)] 453 | pub enum ErrorKind { 454 | Msg(String), 455 | 456 | #[error_chain(custom)] 457 | CustomGeneric(T) 458 | } 459 | } 460 | 461 | mod inner2 { 462 | #[derive(Debug, ErrorChain)] 463 | pub enum ErrorKind { 464 | Msg(String), 465 | } 466 | } 467 | 468 | #[derive(Debug, ErrorChain)] 469 | pub enum ErrorKind 470 | where U: Send + fmt::Debug + fmt::Display + 'static { 471 | Msg(String), 472 | 473 | #[error_chain(custom)] 474 | #[error_chain(description = r#"|_| "custom error""#)] 475 | #[error_chain(display = r#"|t| write!(f, "custom error: {}", t)"#)] 476 | Custom(String), 477 | 478 | #[error_chain(custom)] 479 | #[error_chain(description = r#"|_| "custom generic error""#)] 480 | #[error_chain(display = r#"|t| write!(f, "custom generic error: {}", t)"#)] 481 | CustomGeneric(T), 482 | 483 | #[error_chain(custom)] 484 | #[error_chain(description = r#"|_| "custom generic boxed error""#)] 485 | #[error_chain(display = r#"|t| write!(f, "custom generic boxed error: {}", t)"#)] 486 | CustomGenericBoxed(Box), 487 | 488 | #[error_chain(link = "inner1::Error")] 489 | LinkGeneric(inner1::ErrorKind), 490 | 491 | #[error_chain(link = "inner2::Error")] 492 | Link(inner2::ErrorKind), 493 | 494 | #[error_chain(foreign)] 495 | ForeignGeneric(T), 496 | 497 | #[error_chain(foreign)] 498 | ForeignGenericBoxed(Box), 499 | 500 | #[error_chain(foreign)] 501 | Foreign(io::Error), 502 | } 503 | } 504 | 505 | fn test_without_msg_1() { 506 | #[derive(Debug, ErrorChain)] 507 | pub enum ErrorKind { 508 | #[error_chain(custom)] 509 | HttpStatus(u32), 510 | } 511 | } 512 | 513 | fn test_without_msg_2() { 514 | #[derive(Debug, ErrorChain)] 515 | pub enum ErrorKind { 516 | #[error_chain(custom)] 517 | HttpStatus(u32), 518 | } 519 | 520 | // Should not conflict 521 | #[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))] 522 | impl<'a> From<&'a str> for ErrorKind { 523 | fn from(_: &'a str) -> Self { 524 | unimplemented!() 525 | } 526 | } 527 | 528 | #[cfg_attr(feature = "cargo-clippy", allow(fallible_impl_from))] 529 | impl From for ErrorKind { 530 | fn from(_: String) -> Self { 531 | unimplemented!() 532 | } 533 | } 534 | } 535 | 536 | fn macro_conflicts() { 537 | #[derive(Debug, ErrorChain)] 538 | #[error_chain(result = "MyResult")] 539 | pub enum ErrorKind { 540 | Msg(String), 541 | 542 | #[error_chain(custom)] 543 | Code(i32), 544 | } 545 | 546 | error_chain! { 547 | types { ECError, ECErrorKind, ECResultExt, ECResult; } 548 | } 549 | 550 | quick_main!(|| -> MyResult<()> { 551 | bail!("failed") 552 | }); 553 | 554 | fn foo() -> MyResult<()> { 555 | bail!("failed") 556 | } 557 | 558 | match foo() { 559 | Ok(_) => unreachable!(), 560 | Err(err) => match *err.kind() { 561 | ErrorKind::Msg(ref s) if s == "failed" => (), 562 | _ => unreachable!(), 563 | }, 564 | } 565 | } 566 | 567 | mod need_not_be_pub { 568 | #[derive(Debug, ErrorChain)] 569 | enum ErrorKind { 570 | Msg(String), 571 | } 572 | } 573 | -------------------------------------------------------------------------------- /derive-error-chain/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![recursion_limit = "300"] 2 | 3 | #![cfg_attr(feature = "cargo-clippy", deny(clippy, clippy_pedantic))] 4 | #![cfg_attr(feature = "cargo-clippy", allow( 5 | large_enum_variant, 6 | too_many_arguments, 7 | use_self, 8 | ))] 9 | 10 | //! A Macros 1.1 implementation of 11 | //! 12 | //! The `error-chain` example 13 | //! 14 | //! ``` 15 | //! # #[macro_use] extern crate error_chain; 16 | //! # 17 | //! mod other_error { 18 | //! error_chain! {} 19 | //! } 20 | //! 21 | //! error_chain! { 22 | //! types { 23 | //! Error, ErrorKind, ResultExt, Result; 24 | //! } 25 | //! 26 | //! links { 27 | //! Another(other_error::Error, other_error::ErrorKind) #[cfg(unix)]; 28 | //! } 29 | //! 30 | //! foreign_links { 31 | //! Fmt(::std::fmt::Error); 32 | //! Io(::std::io::Error) #[cfg(unix)]; 33 | //! } 34 | //! 35 | //! errors { 36 | //! InvalidToolchainName(t: String) { 37 | //! description("invalid toolchain name") 38 | //! display("invalid toolchain name: '{}'", t) 39 | //! } 40 | //! } 41 | //! } 42 | //! ``` 43 | //! 44 | //! becomes 45 | //! 46 | //! ``` 47 | //! # #[macro_use] extern crate derive_error_chain; 48 | //! # #[macro_use] extern crate error_chain; 49 | //! # 50 | //! mod other_error { 51 | //! #[derive(Debug, ErrorChain)] 52 | //! pub enum ErrorKind { 53 | //! Msg(String), 54 | //! } 55 | //! } 56 | //! 57 | //! #[derive(Debug, ErrorChain)] 58 | //! pub enum ErrorKind { 59 | //! Msg(String), 60 | //! 61 | //! #[cfg(unix)] 62 | //! #[error_chain(link = "other_error::Error")] 63 | //! Another(other_error::ErrorKind), 64 | //! 65 | //! #[error_chain(foreign)] 66 | //! Fmt(::std::fmt::Error), 67 | //! 68 | //! #[cfg(unix)] 69 | //! #[error_chain(foreign)] 70 | //! Io(::std::io::Error), 71 | //! 72 | //! #[error_chain(custom)] 73 | //! #[error_chain(description = r#"|_| "invalid toolchain name""#)] 74 | //! #[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)] 75 | //! InvalidToolchainName(String), 76 | //! } 77 | //! ``` 78 | //! 79 | //! So the obvious differences from `error_chain!` are: 80 | //! 81 | //! - The `ErrorKind` is an enum instead of a macro invocation. 82 | //! - Error links are variants of the enum instead of lines inside the macro. 83 | //! - Links have explicit annotations marking them as chainable / foreign / custom instead of being grouped into corresponding sections of the macro. 84 | //! - Attributes like `#[cfg]` are applied to the variants directly instead of needing special syntax. 85 | //! - `description` and `display` are defined as function expressions specified as attribute values, instead of shorthands integrated into the macro syntax. 86 | //! 87 | //! The less obvious differences are: 88 | //! 89 | //! - The `ErrorKind` must explicitly implement `::std::fmt::Debug`, either automatically using `#[derive]` or manually implemented separately. `error_chain!` does this implicitly. 90 | //! - Unlike `error_chain!`, the `ErrorKind` need not have `pub` visibility. The generated `Error`, `Result` and `ResultExt` will have the same visibility as the `ErrorKind`. 91 | //! - The `ErrorKind` can have a special `Msg(String)` member for converting strings to the `ErrorKind`. `error_chain!` does this implicitly. 92 | //! - Unlike `error-chain`, the `Msg(String)` member is optional. If absent, the `ErrorKind` and `Error` will not impl `From` and `From<&str>`. 93 | //! - Doc comments, since they're effectively attributes, can be applied on the enum variants without any special syntax like `error_chain!` has. 94 | //! - The `ErrorKind` can be generic. 95 | //! 96 | //! # Enum attributes 97 | //! 98 | //! - `#[error_chain(error = "ErrorName")]` 99 | //! 100 | //! Override the name of the generated `Error` struct to the given name. If not provided, the struct will be named `Error`. 101 | //! 102 | //! - `#[error_chain(result_ext = "ResultExtName")]` 103 | //! 104 | //! Override the name of the generated `ResultExt` trait to the given name. If not provided, the trait will be named `ResultExt`. 105 | //! 106 | //! - `#[error_chain(result = "ResultName")]` 107 | //! 108 | //! Override the name of the generated `Result` type alias to the given name. If not provided, the alias will be named `Result`. 109 | //! If set to the empty string `""`, the alias will not be generated at all. 110 | //! 111 | //! - `#[error_chain(backtrace = "false")]` or `#[error_chain(backtrace = false)]` 112 | //! 113 | //! Disable backtrace functionality in the generated code. This should be kept in sync with the value of the `backtrace` feature of the `error-chain` crate. 114 | //! In other words, if you set `backtrace = "false"` here, you must also specify `default-features = false` for `error-chain` in your `Cargo.toml` 115 | //! 116 | //! # Variant definitions 117 | //! 118 | //! - Chainable links 119 | //! 120 | //! ``` 121 | //! # #[macro_use] extern crate derive_error_chain; 122 | //! # 123 | //! # mod other_error { 124 | //! # #[derive(Debug, ErrorChain)] 125 | //! # pub enum ErrorKind { 126 | //! # Msg(String), 127 | //! # } 128 | //! # } 129 | //! # 130 | //! # #[derive(Debug, ErrorChain)] 131 | //! # pub enum ErrorKind { 132 | //! #[error_chain(link = "other_error::Error")] 133 | //! Another(other_error::ErrorKind), 134 | //! # } 135 | //! ``` 136 | //! 137 | //! A chainable link is an error and errorkind that have been generated using `error-chain` or `derive-error-chain`. The variant must have a single field 138 | //! to hold the chained errorkind, and the `link` attribute must specify a path to the chained error. 139 | //! 140 | //! When the `use_extern_macros` feature is enabled, the value of the `link` attribute does not need to be stringified: 141 | //! 142 | //! ``` 143 | //! # #![feature(use_extern_macros)] 144 | //! # 145 | //! # #[macro_use] extern crate derive_error_chain; 146 | //! # 147 | //! # mod other_error { 148 | //! # #[derive(Debug, ErrorChain)] 149 | //! # pub enum ErrorKind { 150 | //! # Msg(String), 151 | //! # } 152 | //! # } 153 | //! # 154 | //! # #[derive(Debug, ErrorChain)] 155 | //! # pub enum ErrorKind { 156 | //! #[error_chain(link = other_error::Error)] 157 | //! Another(other_error::ErrorKind), 158 | //! # } 159 | //! ``` 160 | //! 161 | //! - Foreign links 162 | //! 163 | //! ``` 164 | //! # #[macro_use] extern crate derive_error_chain; 165 | //! # 166 | //! # #[derive(Debug, ErrorChain)] 167 | //! # pub enum ErrorKind { 168 | //! #[error_chain(foreign)] 169 | //! Fmt(::std::fmt::Error), 170 | //! # } 171 | //! ``` 172 | //! 173 | //! A foreign link is an error that implements `::std::error::Error` but otherwise does not follow `error-chain`'s conventions. The variant must have 174 | //! a single field to hold the foreign error. 175 | //! 176 | //! - Custom links 177 | //! 178 | //! ``` 179 | //! # #[macro_use] extern crate derive_error_chain; 180 | //! # 181 | //! # #[derive(Debug, ErrorChain)] 182 | //! # pub enum ErrorKind { 183 | //! #[error_chain(custom)] 184 | //! InvalidToolchainName(String), 185 | //! # } 186 | //! ``` 187 | //! 188 | //! A custom link is an arbitrary variant that can hold any members. 189 | //! 190 | //! # Variant attributes 191 | //! 192 | //! In addition to the above attributes that identify the type of the variant's link, the below attributes can be used on all links. 193 | //! 194 | //! - `#[error_chain(description = "some_function_expression")]` 195 | //! 196 | //! Specifies a function expression to be used to implement `ErrorKind::description()`. 197 | //! This value is also returned from the implementation of `::std::error::Error::description()` on the generated `Error`. 198 | //! 199 | //! This can be an inline lambda: 200 | //! 201 | //! ``` 202 | //! # #[macro_use] extern crate derive_error_chain; 203 | //! # 204 | //! # #[derive(Debug, ErrorChain)] 205 | //! # pub enum ErrorKind { 206 | //! # #[error_chain(custom)] 207 | //! #[error_chain(description = r#"|_| "invalid toolchain name""#)] 208 | //! InvalidToolchainName(String), 209 | //! # } 210 | //! ``` 211 | //! 212 | //! or it can be a separate function: 213 | //! 214 | //! ``` 215 | //! # #[macro_use] extern crate derive_error_chain; 216 | //! # 217 | //! # #[derive(Debug, ErrorChain)] 218 | //! # pub enum ErrorKind { 219 | //! # #[error_chain(custom)] 220 | //! #[error_chain(description = "invalid_toolchain_name_error_description")] 221 | //! InvalidToolchainName(String), 222 | //! # } 223 | //! 224 | //! // 225 | //! 226 | //! fn invalid_toolchain_name_error_description(_: &str) -> &str { 227 | //! "invalid toolchain name" 228 | //! } 229 | //! ``` 230 | //! 231 | //! The function expression must have the signature `(...) -> &'static str`. It should have one parameter for each field of the variant. 232 | //! The fields are passed in by reference. 233 | //! 234 | //! Thus in the above example, since `InvalidToolchainName` had a single field of type `String`, the function expression needed to be of type 235 | //! `(&str) -> &'static str` 236 | //! 237 | //! If not specified, the default implementation behaves in this way: 238 | //! 239 | //! - Chainable links: Forwards to the chained error kind's `description()` 240 | //! - Foreign links: Forwards to the foreign error's implementation of `::std::error::Error::description()` 241 | //! - Custom links: Returns the stringified name of the variant. 242 | //! 243 | //! When the `use_extern_macros` feature is enabled, the value does not need to be stringified: 244 | //! 245 | //! ``` 246 | //! # #![feature(use_extern_macros)] 247 | //! # 248 | //! # #[macro_use] extern crate derive_error_chain; 249 | //! # 250 | //! # #[derive(Debug, ErrorChain)] 251 | //! # pub enum ErrorKind { 252 | //! # #[error_chain(custom)] 253 | //! #[error_chain(description = |_| "invalid toolchain name")] 254 | //! InvalidToolchainName(String), 255 | //! # } 256 | //! ``` 257 | //! 258 | //! ``` 259 | //! # #![feature(use_extern_macros)] 260 | //! # 261 | //! # #[macro_use] extern crate derive_error_chain; 262 | //! # 263 | //! # #[derive(Debug, ErrorChain)] 264 | //! # pub enum ErrorKind { 265 | //! # #[error_chain(custom)] 266 | //! #[error_chain(description = invalid_toolchain_name_error_description)] 267 | //! InvalidToolchainName(String), 268 | //! # } 269 | //! # 270 | //! # fn invalid_toolchain_name_error_description(_: &str) -> &str { 271 | //! # "invalid toolchain name" 272 | //! # } 273 | //! ``` 274 | //! 275 | //! When the `use_extern_macros` feature is enabled, closure expressions that only call `write!` on the `::std::fmt::Formatter` can instead use a shorthand: 276 | //! 277 | //! ``` 278 | //! # #![feature(use_extern_macros)] 279 | //! # 280 | //! # #[macro_use] extern crate derive_error_chain; 281 | //! # 282 | //! # #[derive(Debug, ErrorChain)] 283 | //! # pub enum ErrorKind { 284 | //! # #[error_chain(custom)] 285 | //! #[error_chain(description = const("invalid toolchain name"))] 286 | //! InvalidToolchainName(String), 287 | //! # } 288 | //! ``` 289 | //! 290 | //! - `#[error_chain(display = "some_function_expression")]` 291 | //! 292 | //! Specifies a function expression to be used to implement `::std::fmt::Display::fmt()` on the `ErrorKind` and generated `Error` 293 | //! 294 | //! This can be an inline lambda: 295 | //! 296 | //! ``` 297 | //! # #[macro_use] extern crate derive_error_chain; 298 | //! # 299 | //! # #[derive(Debug, ErrorChain)] 300 | //! # pub enum ErrorKind { 301 | //! # #[error_chain(custom)] 302 | //! #[error_chain(display = r#"|t| write!(f, "invalid toolchain name: '{}'", t)"#)] 303 | //! InvalidToolchainName(String), 304 | //! # } 305 | //! ``` 306 | //! 307 | //! or it can be a separate function: 308 | //! 309 | //! ``` 310 | //! # #[macro_use] extern crate derive_error_chain; 311 | //! # 312 | //! # #[derive(Debug, ErrorChain)] 313 | //! # pub enum ErrorKind { 314 | //! # #[error_chain(custom)] 315 | //! #[error_chain(display = "invalid_toolchain_name_error_display")] 316 | //! InvalidToolchainName(String), 317 | //! # } 318 | //! 319 | //! // 320 | //! 321 | //! fn invalid_toolchain_name_error_display(f: &mut ::std::fmt::Formatter, t: &str) -> ::std::fmt::Result { 322 | //! write!(f, "invalid toolchain name: '{}'", t) 323 | //! } 324 | //! ``` 325 | //! 326 | //! The function expression must have the signature `(&mut ::std::fmt::Formatter, ...) -> ::std::fmt::Result`. 327 | //! It should have one `&mut ::std::fmt::Formatter` parameter, and one parameter for each field of the variant. The fields are passed in by reference. 328 | //! For brevity, closure expressions do not need the `&mut ::std::fmt::Formatter` parameter and instead capture `f` from the closure environment. 329 | //! 330 | //! Thus in the above example, since `InvalidToolchainName` had a single field of type `String`, the function expression needed to be of type 331 | //! `(&mut ::std::fmt::Formatter, &str) -> ::std::fmt::Result` 332 | //! 333 | //! If not specified, the default implementation of `::std::fmt::Display::fmt()` behaves in this way: 334 | //! 335 | //! - Chainable links: Forwards to the chained errorkind's implementation of `::std::fmt::Display::fmt()` 336 | //! - Foreign links: Forwards to the foreign error's implementation of `::std::fmt::Display::fmt()` 337 | //! - Custom links: Writes the description of the variant to the formatter. 338 | //! 339 | //! When the `use_extern_macros` feature is enabled, the value does not need to be stringified: 340 | //! 341 | //! ``` 342 | //! # #![feature(use_extern_macros)] 343 | //! # 344 | //! # #[macro_use] extern crate derive_error_chain; 345 | //! # 346 | //! # #[derive(Debug, ErrorChain)] 347 | //! # pub enum ErrorKind { 348 | //! # #[error_chain(custom)] 349 | //! #[error_chain(display = |t| write!(f, "invalid toolchain name: '{}'", t))] 350 | //! InvalidToolchainName(String), 351 | //! # } 352 | //! ``` 353 | //! 354 | //! ``` 355 | //! # #![feature(use_extern_macros)] 356 | //! # 357 | //! # #[macro_use] extern crate derive_error_chain; 358 | //! # 359 | //! # #[derive(Debug, ErrorChain)] 360 | //! # pub enum ErrorKind { 361 | //! # #[error_chain(custom)] 362 | //! #[error_chain(display = invalid_toolchain_name_error_display)] 363 | //! InvalidToolchainName(String), 364 | //! # } 365 | //! # 366 | //! # fn invalid_toolchain_name_error_display(f: &mut ::std::fmt::Formatter, t: &str) -> ::std::fmt::Result { 367 | //! # write!(f, "invalid toolchain name: '{}'", t) 368 | //! # } 369 | //! ``` 370 | //! 371 | //! When the `use_extern_macros` feature is enabled, closure expressions that only call `write!` on the `::std::fmt::Formatter` can instead use a shorthand: 372 | //! 373 | //! ``` 374 | //! # #![feature(use_extern_macros)] 375 | //! # 376 | //! # #[macro_use] extern crate derive_error_chain; 377 | //! # 378 | //! # #[derive(Debug, ErrorChain)] 379 | //! # pub enum ErrorKind { 380 | //! // Tuple variants use `{0}`, `{1}`, and so on 381 | //! # #[error_chain(custom)] 382 | //! #[error_chain(display = const("invalid toolchain name: '{0}'"))] 383 | //! InvalidToolchainName(String), 384 | //! # } 385 | //! ``` 386 | //! 387 | //! ``` 388 | //! # #![feature(use_extern_macros)] 389 | //! # 390 | //! # #[macro_use] extern crate derive_error_chain; 391 | //! # 392 | //! # #[derive(Debug, ErrorChain)] 393 | //! # pub enum ErrorKind { 394 | //! // Struct variants use `{name_of_the_field}` 395 | //! # #[error_chain(custom)] 396 | //! #[error_chain(display = const("invalid toolchain name: '{name}'"))] 397 | //! InvalidToolchainName { name: String }, 398 | //! # } 399 | //! ``` 400 | //! 401 | //! - `#[error_chain(cause = "some_function_expression")]` 402 | //! 403 | //! Specifies a function expression to be used to implement `::std::fmt::Error::cause()` on the generated `Error` 404 | //! 405 | //! This can be an inline lambda: 406 | //! 407 | //! ``` 408 | //! # #[macro_use] extern crate derive_error_chain; 409 | //! # 410 | //! # #[derive(Debug, ErrorChain)] 411 | //! # pub enum ErrorKind { 412 | //! # #[error_chain(custom)] 413 | //! #[error_chain(cause = "|_, err| err")] 414 | //! Io(::std::path::PathBuf, ::std::io::Error), 415 | //! # } 416 | //! ``` 417 | //! 418 | //! or it can be a separate function: 419 | //! 420 | //! ``` 421 | //! # #[macro_use] extern crate derive_error_chain; 422 | //! # 423 | //! # #[derive(Debug, ErrorChain)] 424 | //! # pub enum ErrorKind { 425 | //! # #[error_chain(custom)] 426 | //! #[error_chain(cause = "parse_file_error_cause")] 427 | //! Io(::std::path::PathBuf, ::std::io::Error), 428 | //! # } 429 | //! 430 | //! // 431 | //! 432 | //! fn parse_file_error_cause<'a>(_: &::std::path::Path, err: &'a ::std::io::Error) -> &'a ::std::error::Error { 433 | //! err 434 | //! } 435 | //! ``` 436 | //! 437 | //! The function expression must have the signature `(...) -> &::std::error::Error`. It should have one parameter for each field of the variant. 438 | //! The fields are passed in by reference. The result is wrapped in `Option::Some()` for returning from `::std::error::Error::cause()` 439 | //! 440 | //! Thus in the above example, since `Io` had two fields of type `::std::path::PathBuf` and `::std::io::Error`, the function expression needed to be of type 441 | //! `(&::std::path::Path, &::std::io::Error) -> &::std::error::Error` 442 | //! 443 | //! If not specified, the default implementation of `::std::error::Error::cause()` behaves in this way: 444 | //! 445 | //! - Chainable links: Returns `None` 446 | //! - Foreign links: Forwards to the foreign error's implementation of `::std::error::Error::cause()` 447 | //! - Custom links: Returns `None` 448 | //! 449 | //! When the `use_extern_macros` feature is enabled, the value does not need to be stringified: 450 | //! 451 | //! ``` 452 | //! # #![feature(use_extern_macros)] 453 | //! # 454 | //! # #[macro_use] extern crate derive_error_chain; 455 | //! # 456 | //! # #[derive(Debug, ErrorChain)] 457 | //! # pub enum ErrorKind { 458 | //! # #[error_chain(custom)] 459 | //! #[error_chain(cause = |_, err| err)] 460 | //! Io(::std::path::PathBuf, ::std::io::Error), 461 | //! # } 462 | //! ``` 463 | //! 464 | //! ``` 465 | //! # #![feature(use_extern_macros)] 466 | //! # 467 | //! # #[macro_use] extern crate derive_error_chain; 468 | //! # 469 | //! # #[derive(Debug, ErrorChain)] 470 | //! # pub enum ErrorKind { 471 | //! # #[error_chain(custom)] 472 | //! #[error_chain(cause = parse_file_error_cause)] 473 | //! Io(::std::path::PathBuf, ::std::io::Error), 474 | //! # } 475 | //! # 476 | //! # fn parse_file_error_cause<'a>(_: &::std::path::Path, err: &'a ::std::io::Error) -> &'a ::std::error::Error { 477 | //! # err 478 | //! # } 479 | //! ``` 480 | //! 481 | //! # Conflicts with `error-chain` macros when the `use_extern_macros` feature is enabled 482 | //! 483 | //! If you have the `use_extern_macros` feature enabled and have code like this: 484 | //! 485 | //! ```compile_fail 486 | //! #![feature(use_extern_macros)] 487 | //! 488 | //! #[macro_use] extern crate derive_error_chain; 489 | //! #[macro_use] extern crate error_chain; // Want to use `bail!` and `quick_main!` 490 | //! 491 | //! #[derive(Debug, ErrorChain)] 492 | //! #[error_chain(result = "MyResult")] 493 | //! enum ErrorKind { 494 | //! Msg(String), 495 | //! } 496 | //! 497 | //! quick_main!(|| -> MyResult<()> { 498 | //! bail!("failed"); 499 | //! }); 500 | //! ``` 501 | //! 502 | //! it'll fail to compile with: 503 | //! 504 | //! ```text,ignore 505 | //! error: macro `error_chain` may not be used in attributes 506 | //! ``` 507 | //! 508 | //! This is because the compiler thinks `#[error_chain(result = "MyResult")]` is the invocation of an attribute macro, notices that `error_chain!` is 509 | //! a `macro_rules` macro brought into scope from the `error-chain` crate, and thus complains that a `macro_rules` macro cannot be used as 510 | //! an attribute macro. It does this even though there is no attribute macro named `error_chain` and that the custom derive from this crate 511 | //! has registered `error_chain` as an attribute it supports. 512 | //! 513 | //! See for the discussion. 514 | //! 515 | //! To work around this, don't use `#[macro_use]` with the `error-chain` crate. Instead, either `use` the macros you need from it: 516 | //! 517 | //! ``` 518 | //! #![feature(use_extern_macros)] 519 | //! 520 | //! #[macro_use] extern crate derive_error_chain; 521 | //! extern crate error_chain; 522 | //! 523 | //! use error_chain::{ bail, quick_main }; 524 | //! 525 | //! #[derive(Debug, ErrorChain)] 526 | //! #[error_chain(result = "MyResult")] 527 | //! enum ErrorKind { 528 | //! Msg(String), 529 | //! } 530 | //! 531 | //! quick_main!(|| -> MyResult<()> { 532 | //! bail!("failed"); 533 | //! }); 534 | //! ``` 535 | //! 536 | //! or fully qualify their paths: 537 | //! 538 | //! ``` 539 | //! #![feature(use_extern_macros)] 540 | //! 541 | //! #[macro_use] extern crate derive_error_chain; 542 | //! extern crate error_chain; 543 | //! 544 | //! #[derive(Debug, ErrorChain)] 545 | //! #[error_chain(result = "MyResult")] 546 | //! enum ErrorKind { 547 | //! Msg(String), 548 | //! } 549 | //! 550 | //! error_chain::quick_main!(|| -> MyResult<()> { 551 | //! error_chain::bail!("failed"); 552 | //! }); 553 | //! ``` 554 | //! 555 | //! `use`ing the `error_chain!` macro itself is more complicated: it must be renamed so that it doesn't just cause the above error again, 556 | //! and other macros it uses must also be imported, even though they're an implementation detail: 557 | //! 558 | //! ``` 559 | //! #![feature(use_extern_macros)] 560 | //! 561 | //! #[macro_use] extern crate derive_error_chain; 562 | //! extern crate error_chain; 563 | //! 564 | //! use error_chain::{ error_chain as error_chain_macro, error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace }; 565 | //! 566 | //! #[derive(Debug, ErrorChain)] 567 | //! #[error_chain(error = "MyError", result = "MyResult", result_ext = "MyResultExt")] 568 | //! enum MyErrorKind { 569 | //! Msg(String), 570 | //! } 571 | //! 572 | //! error_chain_macro! { 573 | //! } 574 | //! ``` 575 | //! 576 | //! To use it fully-qualified, the macros it depends on must still be `use`d to bring them into scope: 577 | //! 578 | //! ``` 579 | //! #![feature(use_extern_macros)] 580 | //! 581 | //! #[macro_use] extern crate derive_error_chain; 582 | //! extern crate error_chain; 583 | //! 584 | //! use error_chain::{ error_chain_processing, impl_error_chain_kind, impl_error_chain_processed, impl_extract_backtrace }; 585 | //! 586 | //! #[derive(Debug, ErrorChain)] 587 | //! #[error_chain(error = "MyError", result = "MyResult", result_ext = "MyResultExt")] 588 | //! enum MyErrorKind { 589 | //! Msg(String), 590 | //! } 591 | //! 592 | //! error_chain::error_chain! { 593 | //! } 594 | //! ``` 595 | //! 596 | //! It's possible this experience will be made better before the `use_extern_macros` feature stabilizes. 597 | 598 | extern crate proc_macro; 599 | extern crate proc_macro2; 600 | #[macro_use] 601 | extern crate quote; 602 | #[macro_use] 603 | extern crate syn; 604 | extern crate syntex_fmt_macros; 605 | 606 | #[proc_macro_derive(ErrorChain, attributes(error_chain))] 607 | pub fn derive_error_chain(input: proc_macro::TokenStream) -> proc_macro::TokenStream { 608 | let ast: syn::DeriveInput = syn::parse(input).unwrap(); 609 | 610 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); 611 | 612 | let mut generics_lifetime = ast.generics.clone(); 613 | generics_lifetime.params = std::iter::once(parse_quote!('__a)).chain(generics_lifetime.params).collect(); 614 | let (impl_generics_lifetime, _, _) = generics_lifetime.split_for_impl(); 615 | 616 | let mut result_generics = ast.generics.clone(); 617 | result_generics.params.push(parse_quote!(__T)); 618 | let (_, result_ty_generics, _) = result_generics.split_for_impl(); 619 | 620 | let mut result_ext_generics_t = ast.generics.clone(); 621 | result_ext_generics_t.params.push(parse_quote!(__T)); 622 | let (result_ext_impl_generics_t, result_ext_ty_generics_t, _) = result_ext_generics_t.split_for_impl(); 623 | 624 | let mut result_ext_generics_t_e = result_ext_generics_t.clone(); 625 | result_ext_generics_t_e.params.push(parse_quote!(__E: ::std::error::Error + ::std::marker::Send + 'static)); 626 | let (result_ext_impl_generics_t_e, _, _) = result_ext_generics_t_e.split_for_impl(); 627 | 628 | let generics: std::collections::HashSet<_> = 629 | ast.generics.params.iter() 630 | .filter_map(|param| 631 | if let syn::GenericParam::Type(syn::TypeParam { ref ident, .. }) = *param { 632 | Some(ident) 633 | } 634 | else { 635 | None 636 | }) 637 | .collect(); 638 | 639 | let TopLevelProperties { 640 | error_kind_name, 641 | error_kind_vis, 642 | error_name, 643 | result_ext_name, 644 | result_name, 645 | support_backtrace, 646 | error_chain_name, 647 | } = (&ast).into(); 648 | 649 | let result = match ast.data { 650 | syn::Data::Enum(syn::DataEnum { variants, .. }) => { 651 | let links: Vec = variants.into_iter().map(Into::into).collect(); 652 | 653 | let error_kind_description_cases = links.iter().map(|link| link.error_kind_description(&error_kind_name)); 654 | 655 | let error_kind_display_cases = links.iter().map(|link| link.error_kind_display_case(&error_kind_name)); 656 | 657 | let error_kind_from_impls = 658 | links.iter().filter_map(|link| 659 | link.error_kind_from_impl( 660 | &error_kind_name, 661 | &impl_generics, &impl_generics_lifetime, &ty_generics, where_clause, 662 | )); 663 | 664 | let error_cause_cases = links.iter().filter_map(|link| link.error_cause_case(&error_kind_name)); 665 | 666 | let error_doc_comment = format!(r"The Error type. 667 | 668 | This struct is made of three things: 669 | 670 | - `{0}` which is used to determine the type of the error. 671 | - a backtrace, generated when the error is created. 672 | - an error chain, used for the implementation of `Error::cause()`.", error_kind_name); 673 | 674 | let error_from_impls = 675 | links.iter().filter_map(|link| 676 | link.error_from_impl( 677 | &error_kind_name, &error_name, 678 | &generics, 679 | &impl_generics, &impl_generics_lifetime, &ty_generics, where_clause, 680 | )); 681 | 682 | let extract_backtrace_fn = if support_backtrace { 683 | let chained_error_extract_backtrace_cases = links.iter().filter_map(Link::chained_error_extract_backtrace_case); 684 | 685 | Some(quote! { 686 | fn extract_backtrace(err: &(::std::error::Error + Send + 'static)) -> Option<::std::sync::Arc<#error_chain_name::Backtrace>> { 687 | if let Some(err) = err.downcast_ref::() { 688 | return err.1.backtrace.clone(); 689 | } 690 | 691 | #(#chained_error_extract_backtrace_cases)* 692 | 693 | None 694 | } 695 | }) 696 | } 697 | else { 698 | None 699 | }; 700 | 701 | let result_ext_chain_err_doc_comment = format!("\ 702 | If the `Result` is an `Err` then `chain_err` evaluates the closure, \ 703 | which returns *some type that can be converted to `{}`*, \ 704 | boxes the original error to store as the cause, then returns a new error \ 705 | containing the original error.\ 706 | ", error_kind_name); 707 | 708 | let result_wrapper = result_name.map(|result_name| quote! { 709 | /// Convenient wrapper around `::std::result::Result` 710 | #error_kind_vis type #result_name #result_ty_generics = ::std::result::Result<__T, #error_name #ty_generics>; 711 | }); 712 | 713 | quote! { 714 | extern crate error_chain as #error_chain_name; 715 | 716 | impl #impl_generics #error_kind_name #ty_generics #where_clause { 717 | /// A string describing the error kind. 718 | pub fn description(&self) -> &str { 719 | #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] 720 | match *self { 721 | #(#error_kind_description_cases)* 722 | } 723 | } 724 | } 725 | 726 | impl #impl_generics ::std::fmt::Display for #error_kind_name #ty_generics #where_clause { 727 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 728 | #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] 729 | match *self { 730 | #(#error_kind_display_cases)* 731 | } 732 | } 733 | } 734 | 735 | #(#error_kind_from_impls)* 736 | 737 | impl #impl_generics From<#error_name #ty_generics> for #error_kind_name #ty_generics #where_clause { 738 | fn from(err: #error_name #ty_generics) -> Self { err.0 } 739 | } 740 | 741 | #[doc = #error_doc_comment] 742 | #[derive(Debug)] 743 | #error_kind_vis struct #error_name #impl_generics ( 744 | /// The kind of the error. 745 | pub #error_kind_name #ty_generics, 746 | 747 | /// Contains the error chain and the backtrace. 748 | pub #error_chain_name::State, 749 | ) #where_clause ; 750 | 751 | #[allow(unused)] 752 | impl #impl_generics #error_name #ty_generics #where_clause { 753 | /// Constructs an error from a kind, and generates a backtrace. 754 | pub fn from_kind(kind: #error_kind_name #ty_generics) -> Self { 755 | #error_name(kind, #error_chain_name::State::default()) 756 | } 757 | 758 | /// Constructs a chained error from another error and a kind, and generates a backtrace. 759 | pub fn with_chain<__E, __K>(error: __E, kind: __K) -> Self 760 | where __E: ::std::error::Error + Send + 'static, __K: Into<#error_kind_name #ty_generics> 761 | { 762 | #error_name::with_boxed_chain(Box::new(error), kind) 763 | } 764 | 765 | /// Constructs a chained error from another boxed error and a kind, and generates a backtrace 766 | pub fn with_boxed_chain<__K>(error: Box<::std::error::Error + Send>, kind: __K) -> #error_name #ty_generics 767 | where __K: Into<#error_kind_name #ty_generics> 768 | { 769 | #error_name(kind.into(), #error_chain_name::State::new::(error)) 770 | } 771 | 772 | /// Returns the kind of the error. 773 | pub fn kind(&self) -> &#error_kind_name #ty_generics { &self.0 } 774 | 775 | /// Iterates over the error chain. 776 | pub fn iter(&self) -> #error_chain_name::Iter { 777 | #error_chain_name::ChainedError::iter(self) 778 | } 779 | 780 | /// Returns the backtrace associated with this error. 781 | pub fn backtrace(&self) -> Option<&#error_chain_name::Backtrace> { 782 | self.1.backtrace() 783 | } 784 | 785 | /// Extends the error chain with a new entry. 786 | pub fn chain_err<__F, __EK>(self, error: __F) -> Self where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> { 787 | #error_name::with_chain(self, Self::from_kind(error().into())) 788 | } 789 | } 790 | 791 | impl #impl_generics ::std::error::Error for #error_name #ty_generics #where_clause { 792 | fn description(&self) -> &str { self.0.description() } 793 | 794 | fn cause(&self) -> Option<&::std::error::Error> { 795 | #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] 796 | match self.1.next_error { 797 | Some(ref c) => Some(&**c), 798 | None => match self.0 { 799 | #(#error_cause_cases)* 800 | 801 | _ => None, 802 | }, 803 | } 804 | } 805 | } 806 | 807 | impl #impl_generics ::std::fmt::Display for #error_name #ty_generics #where_clause { 808 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 809 | ::std::fmt::Display::fmt(&self.0, f) 810 | } 811 | } 812 | 813 | #(#error_from_impls)* 814 | 815 | impl #impl_generics From<#error_kind_name #ty_generics> for #error_name #ty_generics #where_clause { 816 | fn from(kind: #error_kind_name #ty_generics) -> Self { Self::from_kind(kind) } 817 | } 818 | 819 | impl #impl_generics ::std::ops::Deref for #error_name #ty_generics #where_clause { 820 | type Target = #error_kind_name #ty_generics; 821 | 822 | fn deref(&self) -> &Self::Target { &self.0 } 823 | } 824 | 825 | impl #impl_generics #error_chain_name::ChainedError for #error_name #ty_generics #where_clause { 826 | type ErrorKind = #error_kind_name #ty_generics; 827 | 828 | fn new(kind: Self::ErrorKind, state: #error_chain_name::State) -> Self { 829 | #error_name(kind, state) 830 | } 831 | 832 | fn from_kind(kind: Self::ErrorKind) -> Self { 833 | Self::from_kind(kind) 834 | } 835 | 836 | fn with_chain<__E, __K>(error: __E, kind: __K) -> Self 837 | where __E: ::std::error::Error + Send + 'static, __K: Into { 838 | 839 | Self::with_chain(error, kind) 840 | } 841 | 842 | fn kind(&self) -> &Self::ErrorKind { 843 | self.kind() 844 | } 845 | 846 | fn iter(&self) -> #error_chain_name::Iter { 847 | #error_chain_name::Iter::new(Some(self)) 848 | } 849 | 850 | fn backtrace(&self) -> Option<&#error_chain_name::Backtrace> { 851 | self.backtrace() 852 | } 853 | 854 | fn chain_err<__F, __EK>(self, error: __F) -> Self where __F: FnOnce() -> __EK, __EK: Into { 855 | self.chain_err(error) 856 | } 857 | 858 | #extract_backtrace_fn 859 | } 860 | 861 | /// Additional methods for `Result` and `Option`, for easy interaction with this crate. 862 | #error_kind_vis trait #result_ext_name #result_ext_impl_generics_t #where_clause { 863 | #[doc = #result_ext_chain_err_doc_comment] 864 | fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics> 865 | where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics>; 866 | } 867 | 868 | impl #result_ext_impl_generics_t_e #result_ext_name #result_ext_ty_generics_t for ::std::result::Result<__T, __E> #where_clause { 869 | fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics> 870 | where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> { 871 | self.map_err(move |e| { 872 | let state = #error_chain_name::State::new::<#error_name #ty_generics>(Box::new(e)); 873 | #error_chain_name::ChainedError::new(callback().into(), state) 874 | }) 875 | } 876 | } 877 | 878 | impl #result_ext_impl_generics_t #result_ext_name #result_ext_ty_generics_t for ::std::option::Option<__T> #where_clause { 879 | fn chain_err<__F, __EK>(self, callback: __F) -> ::std::result::Result<__T, #error_name #ty_generics> 880 | where __F: FnOnce() -> __EK, __EK: Into<#error_kind_name #ty_generics> { 881 | self.ok_or_else(move || { 882 | #error_chain_name::ChainedError::from_kind(callback().into()) 883 | }) 884 | } 885 | } 886 | 887 | #result_wrapper 888 | } 889 | }, 890 | 891 | _ => panic!("#[derive(ErrorChain] can only be used with enums."), 892 | }; 893 | 894 | result.into() 895 | } 896 | 897 | struct TopLevelProperties { 898 | error_kind_name: proc_macro2::Ident, 899 | error_kind_vis: syn::Visibility, 900 | error_name: proc_macro2::Ident, 901 | result_ext_name: proc_macro2::Ident, 902 | result_name: Option, 903 | error_chain_name: proc_macro2::Ident, 904 | support_backtrace: bool, 905 | } 906 | 907 | impl<'a> From<&'a syn::DeriveInput> for TopLevelProperties { 908 | fn from(ast: &'a syn::DeriveInput) -> Self { 909 | let mut error_name = proc_macro2::Ident::new("Error", proc_macro2::Span::call_site()); 910 | let mut result_ext_name = proc_macro2::Ident::new("ResultExt", proc_macro2::Span::call_site()); 911 | let mut result_name = Some(proc_macro2::Ident::new("Result", proc_macro2::Span::call_site())); 912 | let mut support_backtrace = true; 913 | 914 | for attr in &ast.attrs { 915 | if !is_error_chain_attribute(attr) { 916 | continue; 917 | } 918 | 919 | match attr.interpret_meta() { 920 | Some(syn::Meta::List(syn::MetaList { nested, .. })) => { 921 | for nested_meta in nested { 922 | match nested_meta { 923 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(value), .. })) => { 924 | let value = &value.value(); 925 | 926 | match &*ident.to_string() { 927 | "error" => error_name = syn::parse_str(value).unwrap_or_else(|err| 928 | panic!("Could not parse `error` value as an identifier - {}", err)), 929 | 930 | "result_ext" => result_ext_name = syn::parse_str(value).unwrap_or_else(|err| 931 | panic!("Could not parse `result_ext` value as an identifier - {}", err)), 932 | 933 | "result" => result_name = 934 | if value == "" { 935 | None 936 | } 937 | else { 938 | Some(syn::parse_str(value).unwrap_or_else(|err| 939 | panic!("Could not parse `result` value as an identifier - {}", err))) 940 | }, 941 | 942 | "backtrace" => support_backtrace = value.parse().unwrap_or_else(|err| 943 | panic!("Could not parse `backtrace` value - {}", err)), 944 | 945 | _ => 946 | panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace` but got {}", ident), 947 | } 948 | }, 949 | 950 | syn::NestedMeta::Meta(syn::Meta::NameValue( 951 | syn::MetaNameValue { ref ident, lit: syn::Lit::Bool(syn::LitBool { value, .. }), .. })) 952 | if ident == "backtrace" => support_backtrace = value, 953 | 954 | _ => panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace`"), 955 | } 956 | } 957 | }, 958 | 959 | _ => panic!("Could not parse `error_chain` attribute - expected one of `error`, `result_ext`, `result`, `backtrace`"), 960 | } 961 | } 962 | 963 | let error_chain_name = syn::parse_str(&format!("{}_error_chain", error_name)).unwrap_or_else(|err| 964 | panic!("Could not generate error_chain crate name as a valid ident - {}", err)); 965 | 966 | TopLevelProperties { 967 | error_kind_name: ast.ident.clone(), 968 | error_kind_vis: ast.vis.clone(), 969 | error_name, 970 | result_ext_name, 971 | result_name, 972 | error_chain_name, 973 | support_backtrace, 974 | } 975 | } 976 | } 977 | 978 | struct Link { 979 | variant_ident: proc_macro2::Ident, 980 | variant_fields: syn::Fields, 981 | link_type: LinkType, 982 | custom_description: Option, 983 | custom_display: Option, 984 | custom_cause: Option, 985 | } 986 | 987 | enum LinkType { 988 | Msg, 989 | Chainable(syn::Type, syn::Type), 990 | Foreign(syn::Type), 991 | Custom, 992 | } 993 | 994 | impl From for Link { 995 | fn from(syn::Variant { ident: variant_ident, attrs, fields: variant_fields, .. }: syn::Variant) -> Self { 996 | let is_msg = loop { 997 | if variant_ident != "Msg" { 998 | break false; 999 | } 1000 | 1001 | if let syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) = variant_fields { 1002 | if unnamed.len() == 1 { 1003 | if let syn::Type::Path(syn::TypePath { ref path, .. }) = unnamed[0].ty { 1004 | if !path.global() && path.segments.len() == 1 && path.segments[0].ident == "String" { 1005 | break true; 1006 | } 1007 | } 1008 | } 1009 | } 1010 | 1011 | panic!("Expected Msg member to be a tuple of String"); 1012 | }; 1013 | 1014 | if is_msg { 1015 | return Link { 1016 | variant_ident, 1017 | variant_fields, 1018 | link_type: LinkType::Msg, 1019 | custom_description: None, 1020 | custom_display: None, 1021 | custom_cause: None, 1022 | }; 1023 | } 1024 | 1025 | let mut link_type = None; 1026 | let mut custom_description = None; 1027 | let mut custom_display = None; 1028 | let mut custom_cause: Option = None; 1029 | 1030 | for attr in attrs { 1031 | if !is_error_chain_attribute(&attr) { 1032 | continue; 1033 | } 1034 | 1035 | if let Some(syn::Meta::List(syn::MetaList { nested, .. })) = attr.interpret_meta() { 1036 | for nested_meta in nested { 1037 | match nested_meta { 1038 | syn::NestedMeta::Meta(syn::Meta::Word(ident)) => match &*ident.to_string() { 1039 | "foreign" => match variant_fields { 1040 | syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => 1041 | link_type = Some(LinkType::Foreign(unnamed[0].ty.clone())), 1042 | 1043 | _ => panic!("Foreign link {} must be a tuple of one element (the foreign error type).", variant_ident), 1044 | }, 1045 | 1046 | "custom" => link_type = Some(LinkType::Custom), 1047 | 1048 | _ => panic!( 1049 | "Could not parse `error_chain` attribute of member {} - expected one of `foreign`, `custom` but got {}", 1050 | variant_ident, ident), 1051 | }, 1052 | 1053 | syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue { ident, lit: syn::Lit::Str(value), .. })) => { 1054 | let value = &value.value(); 1055 | 1056 | match &*ident.to_string() { 1057 | "link" => match variant_fields { 1058 | syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => 1059 | link_type = Some(LinkType::Chainable( 1060 | syn::parse_str(value).unwrap_or_else(|err| 1061 | panic!("Could not parse `link` attribute of member {} as a type - {}", variant_ident, err)), 1062 | unnamed[0].ty.clone())), 1063 | 1064 | _ => panic!("Chainable link {} must be a tuple of one element (the chainable error kind).", variant_ident), 1065 | }, 1066 | 1067 | "description" => custom_description = Some(CustomFormatter::Expr(syn::parse_str(value).unwrap_or_else(|err| 1068 | panic!("Could not parse `description` attribute of member {} as an expression - {}", variant_ident, err)))), 1069 | 1070 | "display" => custom_display = Some(CustomFormatter::Expr(syn::parse_str(value).unwrap_or_else(|err| 1071 | panic!("Could not parse `display` attribute of member {} as an expression - {}", variant_ident, err)))), 1072 | 1073 | "cause" => custom_cause = Some(syn::parse_str(value).unwrap_or_else(|err| 1074 | panic!("Could not parse `cause` attribute of member {} as an expression - {}", variant_ident, err))), 1075 | 1076 | _ => panic!( 1077 | "Could not parse `error_chain` attribute of member {} - expected one of `link`, `description`, `display`, `cause` but got {}", 1078 | variant_ident, ident), 1079 | } 1080 | }, 1081 | 1082 | _ => panic!("Could not parse `error_chain` attribute of member {} - expected term or name-value meta item", variant_ident), 1083 | } 1084 | } 1085 | } 1086 | else { 1087 | let mut tts = { 1088 | let mut tts = attr.tts.into_iter(); 1089 | 1090 | let tt = match tts.next() { 1091 | Some(proc_macro2::TokenTree::Group(ref group)) if group.delimiter() == proc_macro2::Delimiter::Parenthesis => group.stream(), 1092 | Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected `(tokens)` but found {}", variant_ident, tt), 1093 | None => panic!("Could not parse `error_chain` attribute of member {} - expected `(tokens)`", variant_ident), 1094 | }; 1095 | 1096 | if let Some(tt) = tts.next() { 1097 | panic!("Could not parse `error_chain` attribute of member {} - unexpected token {} after `(tokens)`", variant_ident, tt); 1098 | } 1099 | 1100 | tt.into_iter() 1101 | }; 1102 | 1103 | let ident = match tts.next() { 1104 | Some(proc_macro2::TokenTree::Ident(ident)) => ident, 1105 | Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected a term but got {}", variant_ident, tt), 1106 | None => panic!("Could not parse `error_chain` attribute of member {} - expected a term", variant_ident), 1107 | }; 1108 | let ident = ident.to_string(); 1109 | 1110 | match tts.next() { 1111 | Some(proc_macro2::TokenTree::Punct(ref punct)) if punct.as_char() == '=' => (), 1112 | Some(tt) => panic!("Could not parse `error_chain` attribute of member {} - expected `=` but got {}", variant_ident, tt), 1113 | None => panic!("Could not parse `error_chain` attribute of member {} - expected `=`", variant_ident), 1114 | } 1115 | 1116 | let value: proc_macro2::TokenStream = tts.collect(); 1117 | if value.is_empty() { 1118 | panic!("Could not parse `error_chain` attribute of member {} - expected tokens after `=`", variant_ident); 1119 | } 1120 | 1121 | match &*ident { 1122 | "link" => match variant_fields { 1123 | syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => 1124 | link_type = Some(LinkType::Chainable( 1125 | syn::parse2(value).unwrap_or_else(|err| 1126 | panic!("Could not parse `link` attribute of member {} as a type - {}", variant_ident, err)), 1127 | unnamed[0].ty.clone())), 1128 | 1129 | _ => panic!("Chainable link {} must be a tuple of one element (the chainable error kind).", variant_ident), 1130 | }, 1131 | 1132 | "description" => custom_description = Some(CustomFormatter::parse(value, "description", &variant_ident, &variant_fields)), 1133 | 1134 | "display" => custom_display = Some(CustomFormatter::parse(value, "display", &variant_ident, &variant_fields)), 1135 | 1136 | "cause" => custom_cause = Some(syn::parse2(value).unwrap_or_else(|err| 1137 | panic!("Could not parse `cause` attribute of member {} as an expression - {}", variant_ident, err))), 1138 | 1139 | _ => panic!( 1140 | "Could not parse `error_chain` attribute of member {} - expected one of `link`, `description`, `display`, `cause` but got {}", 1141 | variant_ident, ident), 1142 | } 1143 | } 1144 | } 1145 | 1146 | let link_type = link_type.unwrap_or_else(|| 1147 | panic!(r#"Member {} does not have any of #[error_chain(link = "...")] or #[error_chain(foreign)] or #[error_chain(custom)]."#, variant_ident)); 1148 | 1149 | Link { 1150 | variant_ident, 1151 | variant_fields, 1152 | link_type, 1153 | custom_description, 1154 | custom_display, 1155 | custom_cause, 1156 | } 1157 | } 1158 | } 1159 | 1160 | impl Link { 1161 | fn error_kind_description(&self, error_kind_name: &proc_macro2::Ident) -> proc_macro2::TokenStream { 1162 | let variant_ident = &self.variant_ident; 1163 | 1164 | match (self.custom_description.as_ref(), &self.link_type) { 1165 | (_, &LinkType::Msg) => quote! { 1166 | #error_kind_name::#variant_ident(ref s) => s, 1167 | }, 1168 | 1169 | (Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Chainable(_, _)) | 1170 | (Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Foreign(_)) => quote! { 1171 | #error_kind_name::#variant_ident(_) => #format_string, 1172 | }, 1173 | 1174 | (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Chainable(_, _)) | 1175 | (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Foreign(_)) if is_closure(custom_description) => quote! { 1176 | #error_kind_name::#variant_ident(ref err) => { 1177 | #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))] 1178 | { (#custom_description)(err) } 1179 | }, 1180 | }, 1181 | 1182 | (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Chainable(_, _)) | 1183 | (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Foreign(_)) => quote! { 1184 | #error_kind_name::#variant_ident(ref err) => #custom_description(err), 1185 | }, 1186 | 1187 | (Some(&CustomFormatter::FormatString { ref format_string, .. }), &LinkType::Custom) => { 1188 | let pattern = fields_pattern_ignore(&self.variant_fields); 1189 | 1190 | quote! { 1191 | #error_kind_name::#variant_ident #pattern => #format_string, 1192 | } 1193 | }, 1194 | 1195 | (Some(&CustomFormatter::Expr(ref custom_description)), &LinkType::Custom) => { 1196 | let pattern = fields_pattern(&self.variant_fields); 1197 | let args = args(&self.variant_fields); 1198 | 1199 | if is_closure(custom_description) { 1200 | quote! { 1201 | #error_kind_name::#variant_ident #pattern => { 1202 | #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))] 1203 | { (#custom_description)(#args) } 1204 | }, 1205 | } 1206 | } 1207 | else { 1208 | quote! { 1209 | #error_kind_name::#variant_ident #pattern => #custom_description(#args), 1210 | } 1211 | } 1212 | }, 1213 | 1214 | (None, &LinkType::Chainable(_, _)) => quote! { 1215 | #error_kind_name::#variant_ident(ref kind) => kind.description(), 1216 | }, 1217 | 1218 | (None, &LinkType::Foreign(_)) => quote! { 1219 | #error_kind_name::#variant_ident(ref err) => ::std::error::Error::description(err), 1220 | }, 1221 | 1222 | (None, &LinkType::Custom) => { 1223 | let pattern = fields_pattern_ignore(&self.variant_fields); 1224 | 1225 | quote! { 1226 | #error_kind_name::#variant_ident #pattern => stringify!(#variant_ident), 1227 | } 1228 | }, 1229 | } 1230 | } 1231 | 1232 | fn error_kind_display_case( 1233 | &self, 1234 | error_kind_name: &proc_macro2::Ident, 1235 | ) -> proc_macro2::TokenStream { 1236 | let variant_ident = &self.variant_ident; 1237 | 1238 | match (self.custom_display.as_ref(), &self.link_type) { 1239 | (_, &LinkType::Msg) => quote! { 1240 | #error_kind_name::#variant_ident(ref s) => ::std::fmt::Display::fmt(s, f), 1241 | }, 1242 | 1243 | (Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Chainable(_, _)) => quote! { 1244 | #error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args), 1245 | }, 1246 | 1247 | (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Chainable(_, _)) if is_closure(custom_display) => quote! { 1248 | #error_kind_name::#variant_ident(ref kind) => { 1249 | #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))] 1250 | { (#custom_display)(kind) } 1251 | }, 1252 | }, 1253 | 1254 | (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Chainable(_, _)) => quote! { 1255 | #error_kind_name::#variant_ident(ref kind) => #custom_display(f, kind), 1256 | }, 1257 | 1258 | (Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Foreign(_)) => quote! { 1259 | #error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args), 1260 | }, 1261 | 1262 | (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Foreign(_)) if is_closure(custom_display) => quote! { 1263 | #error_kind_name::#variant_ident(ref err) => { 1264 | #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))] 1265 | { (#custom_display)(err) } 1266 | }, 1267 | }, 1268 | 1269 | (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Foreign(_)) => quote! { 1270 | #error_kind_name::#variant_ident(ref err) => #custom_display(f, err), 1271 | }, 1272 | 1273 | (Some(&CustomFormatter::FormatString { ref format_string, ref pattern, ref args }), &LinkType::Custom) => quote! { 1274 | #error_kind_name::#variant_ident #pattern => write!(f, #format_string, #args), 1275 | }, 1276 | 1277 | (Some(&CustomFormatter::Expr(ref custom_display)), &LinkType::Custom) => { 1278 | let pattern = fields_pattern(&self.variant_fields); 1279 | let args = args(&self.variant_fields); 1280 | 1281 | if is_closure(custom_display) { 1282 | quote! { 1283 | #error_kind_name::#variant_ident #pattern => { 1284 | #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))] 1285 | { (#custom_display)(#args) } 1286 | }, 1287 | } 1288 | } 1289 | else { 1290 | quote! { 1291 | #error_kind_name::#variant_ident #pattern => #custom_display(f, #args), 1292 | } 1293 | } 1294 | }, 1295 | 1296 | (None, &LinkType::Chainable(_, _)) => quote! { 1297 | #error_kind_name::#variant_ident(ref kind) => ::std::fmt::Display::fmt(kind, f), 1298 | }, 1299 | 1300 | (None, &LinkType::Foreign(_)) => quote! { 1301 | #error_kind_name::#variant_ident(ref err) => ::std::fmt::Display::fmt(err, f), 1302 | }, 1303 | 1304 | (None, &LinkType::Custom) => { 1305 | let pattern = fields_pattern_ignore(&self.variant_fields); 1306 | 1307 | quote! { 1308 | #error_kind_name::#variant_ident #pattern => ::std::fmt::Display::fmt(self.description(), f), 1309 | } 1310 | }, 1311 | } 1312 | } 1313 | 1314 | fn error_kind_from_impl( 1315 | &self, 1316 | error_kind_name: &proc_macro2::Ident, 1317 | impl_generics: &syn::ImplGenerics, impl_generics_lifetime: &syn::ImplGenerics, ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>, 1318 | ) -> Option { 1319 | let variant_ident = &self.variant_ident; 1320 | 1321 | match self.link_type { 1322 | LinkType::Msg => Some(quote! { 1323 | impl #impl_generics_lifetime From<&'__a str> for #error_kind_name #ty_generics #where_clause { 1324 | fn from(s: &'__a str) -> Self { #error_kind_name::#variant_ident(s.to_string()) } 1325 | } 1326 | 1327 | impl #impl_generics From for #error_kind_name #ty_generics #where_clause { 1328 | fn from(s: String) -> Self { #error_kind_name::#variant_ident(s) } 1329 | } 1330 | }), 1331 | 1332 | LinkType::Chainable(_, ref error_kind_ty) => Some(quote! { 1333 | impl #impl_generics From<#error_kind_ty> for #error_kind_name #ty_generics #where_clause { 1334 | fn from(kind: #error_kind_ty) -> Self { 1335 | #error_kind_name::#variant_ident(kind) 1336 | } 1337 | } 1338 | }), 1339 | 1340 | LinkType::Foreign(_) | 1341 | LinkType::Custom => None, 1342 | } 1343 | } 1344 | 1345 | fn error_cause_case( 1346 | &self, 1347 | error_kind_name: &proc_macro2::Ident, 1348 | ) -> Option { 1349 | let variant_ident = &self.variant_ident; 1350 | 1351 | #[cfg_attr(feature = "cargo-clippy", allow(match_same_arms))] 1352 | match (self.custom_cause.as_ref(), &self.link_type) { 1353 | (_, &LinkType::Msg) => None, 1354 | 1355 | (Some(custom_cause), _) => Some({ 1356 | let pattern = fields_pattern(&self.variant_fields); 1357 | let args = args(&self.variant_fields); 1358 | 1359 | if is_closure(custom_cause) { 1360 | quote! { 1361 | #error_kind_name::#variant_ident #pattern => { 1362 | #[cfg_attr(feature = "cargo-clippy", allow(redundant_closure_call))] 1363 | let result = (#custom_cause)(#args); 1364 | Some(result) 1365 | }, 1366 | } 1367 | } 1368 | else { 1369 | quote! { 1370 | #error_kind_name::#variant_ident #pattern => Some(#custom_cause(#args)), 1371 | } 1372 | } 1373 | }), 1374 | 1375 | (None, &LinkType::Foreign(_)) => Some(quote! { 1376 | #error_kind_name::#variant_ident(ref err) => ::std::error::Error::cause(err), 1377 | }), 1378 | 1379 | (None, &LinkType::Chainable(_, _)) | 1380 | (None, &LinkType::Custom) => None, 1381 | } 1382 | } 1383 | 1384 | fn error_from_impl( 1385 | &self, 1386 | error_kind_name: &proc_macro2::Ident, error_name: &proc_macro2::Ident, 1387 | generics: &std::collections::HashSet<&proc_macro2::Ident>, 1388 | impl_generics: &syn::ImplGenerics, impl_generics_lifetime: &syn::ImplGenerics, ty_generics: &syn::TypeGenerics, where_clause: Option<&syn::WhereClause>, 1389 | ) -> Option { 1390 | let variant_ident = &self.variant_ident; 1391 | 1392 | match self.link_type { 1393 | LinkType::Msg => Some(quote! { 1394 | impl #impl_generics_lifetime From<&'__a str> for #error_name #ty_generics #where_clause { 1395 | fn from(s: &'__a str) -> Self { Self::from_kind(s.into()) } 1396 | } 1397 | 1398 | impl #impl_generics From for #error_name #ty_generics #where_clause { 1399 | fn from(s: String) -> Self { Self::from_kind(s.into()) } 1400 | } 1401 | }), 1402 | 1403 | LinkType::Chainable(ref error_ty, _) => Some(quote! { 1404 | impl #impl_generics From<#error_ty> for #error_name #ty_generics #where_clause { 1405 | fn from(err: #error_ty) -> Self { 1406 | #error_name(#error_kind_name::#variant_ident(err.0), err.1) 1407 | } 1408 | } 1409 | }), 1410 | 1411 | // Don't emit From impl for any generics of the errorkind because they cause conflicting trait impl errors. 1412 | // ie don't emit `impl From for Error` even if there's a variant `SomeError(T)` 1413 | LinkType::Foreign(syn::Type::Path(syn::TypePath { ref path, .. })) 1414 | if !path.global() && path.segments.len() == 1 && generics.contains(&path.segments[0].ident) => None, 1415 | 1416 | LinkType::Foreign(ref ty) => Some(quote! { 1417 | impl #impl_generics From<#ty> for #error_name #ty_generics #where_clause { 1418 | fn from(err: #ty) -> Self { 1419 | Self::from_kind(#error_kind_name::#variant_ident(err)) 1420 | } 1421 | } 1422 | }), 1423 | 1424 | LinkType::Custom => None, 1425 | } 1426 | } 1427 | 1428 | fn chained_error_extract_backtrace_case(&self) -> Option { 1429 | match self.link_type { 1430 | LinkType::Chainable(ref error_ty, _) => Some(quote! { 1431 | if let Some(err) = err.downcast_ref::<#error_ty>() { 1432 | return err.1.backtrace.clone(); 1433 | } 1434 | }), 1435 | 1436 | LinkType::Msg | 1437 | LinkType::Foreign(_) | 1438 | LinkType::Custom => None, 1439 | } 1440 | } 1441 | } 1442 | 1443 | enum CustomFormatter { 1444 | FormatString { format_string: String, pattern: proc_macro2::TokenStream, args: proc_macro2::TokenStream }, 1445 | Expr(syn::Expr), 1446 | } 1447 | 1448 | impl CustomFormatter { 1449 | fn parse(tokens: proc_macro2::TokenStream, attr_name: &str, variant_ident: &proc_macro2::Ident, variant_fields: &syn::Fields) -> Self { 1450 | let err = match syn::parse(tokens.clone().into()) { 1451 | Ok(expr) => return CustomFormatter::Expr(expr), 1452 | Err(err) => err, 1453 | }; 1454 | 1455 | let mut tts = tokens.into_iter(); 1456 | 1457 | match tts.next() { 1458 | Some(proc_macro2::TokenTree::Ident(ref ident)) if ident == "const" => (), 1459 | 1460 | Some(tt) => panic!( 1461 | "Could not parse `{}` attribute of member {}. Expression - {}. Format string - expected `const` but got {}", 1462 | attr_name, variant_ident, err, tt), 1463 | 1464 | _ => panic!( 1465 | "Could not parse `{}` attribute of member {}. Expression - {}. Format string - expected `const`", 1466 | attr_name, variant_ident, err), 1467 | } 1468 | 1469 | let value = match tts.next() { 1470 | Some(proc_macro2::TokenTree::Group(ref group)) if group.delimiter() == proc_macro2::Delimiter::Parenthesis => group.stream(), 1471 | 1472 | Some(tt) => panic!( 1473 | "Could not parse `{}` attribute of member {} - expected `(string literal)` but got {}", 1474 | attr_name, variant_ident, tt), 1475 | 1476 | _ => panic!( 1477 | "Could not parse `{}` attribute of member {} - expected `(string literal)`", 1478 | attr_name, variant_ident), 1479 | }; 1480 | 1481 | let format_string = match syn::parse2(value) { 1482 | Ok(syn::Lit::Str(value)) => value.value(), 1483 | 1484 | Ok(lit) => panic!( 1485 | "Could not parse `{}` attribute of member {} - expected string literal but got {}", 1486 | attr_name, variant_ident, quote!(#lit).to_string()), 1487 | 1488 | Err(err) => panic!( 1489 | "Could not parse `{}` attribute of member {} - {}", 1490 | attr_name, variant_ident, err), 1491 | }; 1492 | 1493 | if let Some(tt) = tts.next() { 1494 | panic!( 1495 | "Could not parse `{}` attribute of member {} - unexpected token {} after string literal", 1496 | attr_name, variant_ident, tt); 1497 | } 1498 | 1499 | match *variant_fields { 1500 | syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => { 1501 | let referenced_names = get_parameter_names(&format_string).unwrap_or_else(|err| panic!( 1502 | "Could not parse `{}` attribute of member {} - {}", 1503 | attr_name, variant_ident, err)); 1504 | 1505 | let (patterns, args): (Vec<_>, Vec<_>) = named.into_iter().map(|f| { 1506 | let field_name = f.ident.as_ref().unwrap(); 1507 | if referenced_names.contains(field_name) { 1508 | (quote!(ref #field_name), quote!(#field_name = #field_name,)) 1509 | } 1510 | else { 1511 | let ignored_field_name = proc_macro2::Ident::new(&format!("_{}", field_name), proc_macro2::Span::call_site()); 1512 | (quote!(#field_name: ref #ignored_field_name), quote!()) 1513 | } 1514 | }).unzip(); 1515 | 1516 | CustomFormatter::FormatString { 1517 | format_string, 1518 | pattern: quote!({ #(#patterns,)* }), 1519 | args: quote!(#(#args)*), 1520 | } 1521 | }, 1522 | 1523 | syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => { 1524 | let referenced_positions = get_parameter_positions(&format_string).unwrap_or_else(|err| panic!( 1525 | "Could not parse `{}` attribute of member {} - {}", 1526 | attr_name, variant_ident, err)); 1527 | 1528 | let (patterns, args): (Vec<_>, Vec<_>) = unnamed.into_iter().enumerate().map(|(i, _)| { 1529 | if referenced_positions.contains(&i) { 1530 | let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site()); 1531 | (quote!(ref #field_name), quote!(#field_name,)) 1532 | } 1533 | else { 1534 | (quote!(_), quote!()) 1535 | } 1536 | }).unzip(); 1537 | 1538 | CustomFormatter::FormatString { 1539 | format_string, 1540 | pattern: quote!((#(#patterns,)*)), 1541 | args: quote!(#(#args)*), 1542 | } 1543 | }, 1544 | 1545 | syn::Fields::Unit => { 1546 | ensure_no_parameters(&format_string).unwrap_or_else(|err| panic!( 1547 | "Could not parse `{}` attribute of member {} - {}", 1548 | attr_name, variant_ident, err)); 1549 | 1550 | CustomFormatter::FormatString { 1551 | format_string, 1552 | pattern: quote!(), 1553 | args: quote!(), 1554 | } 1555 | }, 1556 | } 1557 | } 1558 | } 1559 | 1560 | fn is_error_chain_attribute(attr: &syn::Attribute) -> bool { 1561 | if !attr.path.global() && attr.path.segments.len() == 1 { 1562 | let segment = &attr.path.segments[0]; 1563 | return segment.ident == "error_chain" && segment.arguments.is_empty(); 1564 | } 1565 | 1566 | false 1567 | } 1568 | 1569 | fn is_closure(expr: &syn::Expr) -> bool { 1570 | if let syn::Expr::Closure(..) = *expr { 1571 | true 1572 | } 1573 | else { 1574 | false 1575 | } 1576 | } 1577 | 1578 | fn fields_pattern(variant_fields: &syn::Fields) -> proc_macro2::TokenStream { 1579 | match *variant_fields { 1580 | syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => { 1581 | let fields = named.into_iter().map(|f| { 1582 | let field_name = f.ident.as_ref().unwrap(); 1583 | quote!(ref #field_name) 1584 | }); 1585 | quote!({ #(#fields,)* }) 1586 | }, 1587 | 1588 | syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => { 1589 | let fields = unnamed.into_iter().enumerate().map(|(i, _)| { 1590 | let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site()); 1591 | quote!(ref #field_name) 1592 | }); 1593 | quote!((#(#fields,)*)) 1594 | }, 1595 | 1596 | syn::Fields::Unit => quote!(), 1597 | } 1598 | } 1599 | 1600 | fn fields_pattern_ignore(variant_fields: &syn::Fields) -> proc_macro2::TokenStream { 1601 | match *variant_fields { 1602 | syn::Fields::Named(syn::FieldsNamed { .. }) => quote!({ .. }), 1603 | syn::Fields::Unnamed(_) => quote!((..)), 1604 | syn::Fields::Unit => quote!(), 1605 | } 1606 | } 1607 | 1608 | fn args(variant_fields: &syn::Fields) -> proc_macro2::TokenStream { 1609 | match *variant_fields { 1610 | syn::Fields::Named(syn::FieldsNamed { ref named, .. }) => { 1611 | let fields = named.into_iter().map(|f| { 1612 | let field_name = f.ident.as_ref().unwrap(); 1613 | quote!(#field_name) 1614 | }); 1615 | quote!(#(#fields,)*) 1616 | }, 1617 | 1618 | syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) => { 1619 | let fields = unnamed.into_iter().enumerate().map(|(i, _)| { 1620 | let field_name = proc_macro2::Ident::new(&format!("value{}", i), proc_macro2::Span::call_site()); 1621 | quote!(#field_name) 1622 | }); 1623 | quote!(#(#fields,)*) 1624 | }, 1625 | 1626 | syn::Fields::Unit => quote!(), 1627 | } 1628 | } 1629 | 1630 | fn get_parameter_names(format_string: &str) -> Result, String> { 1631 | let parser = syntex_fmt_macros::Parser::new(format_string); 1632 | 1633 | parser 1634 | .filter_map(|piece| match piece { 1635 | syntex_fmt_macros::Piece::String(_) => None, 1636 | 1637 | syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position { 1638 | syntex_fmt_macros::Position::ArgumentNext => Some(Err("expected named parameter but found `{}`".to_string())), 1639 | syntex_fmt_macros::Position::ArgumentIs(index) => Some(Err(format!("expected named parameter but found `{{{}}}`", index))), 1640 | syntex_fmt_macros::Position::ArgumentNamed(name) => Some(syn::parse_str(name).map_err(|err| format!("could not parse named parameter `{{{}}}` - {}", name, err))), 1641 | }, 1642 | }) 1643 | .collect() 1644 | } 1645 | 1646 | fn get_parameter_positions(format_string: &str) -> Result, String> { 1647 | let parser = syntex_fmt_macros::Parser::new(format_string); 1648 | 1649 | parser 1650 | .filter_map(|piece| match piece { 1651 | syntex_fmt_macros::Piece::String(_) => None, 1652 | 1653 | syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position { 1654 | syntex_fmt_macros::Position::ArgumentNext => Some(Err("expected positional parameter but found `{}`".to_string())), 1655 | syntex_fmt_macros::Position::ArgumentIs(index) => Some(Ok(index)), 1656 | syntex_fmt_macros::Position::ArgumentNamed(name) => Some(Err(format!("expected positional parameter but found `{{{}}}`", name))), 1657 | }, 1658 | }) 1659 | .collect() 1660 | } 1661 | 1662 | fn ensure_no_parameters(format_string: &str) -> Result<(), String> { 1663 | let parser = syntex_fmt_macros::Parser::new(format_string); 1664 | 1665 | for piece in parser { 1666 | match piece { 1667 | syntex_fmt_macros::Piece::String(_) => (), 1668 | 1669 | syntex_fmt_macros::Piece::NextArgument(syntex_fmt_macros::Argument { position, .. }) => match position { 1670 | syntex_fmt_macros::Position::ArgumentNext => return Err("expected no parameters but found `{}`".to_string()), 1671 | syntex_fmt_macros::Position::ArgumentIs(index) => return Err(format!("expected no parameters but found `{{{}}}`", index)), 1672 | syntex_fmt_macros::Position::ArgumentNamed(name) => return Err(format!("expected no parameters but found `{{{}}}`", name)), 1673 | }, 1674 | } 1675 | } 1676 | 1677 | Ok(()) 1678 | } 1679 | --------------------------------------------------------------------------------