| #![allow( |
| clippy::needless_lifetimes, |
| clippy::needless_raw_string_hashes, |
| clippy::trivially_copy_pass_by_ref, |
| clippy::uninlined_format_args |
| )] |
| |
| use core::fmt::{self, Display}; |
| use thiserror::Error; |
| |
| fn assert<T: Display>(expected: &str, value: T) { |
| assert_eq!(expected, value.to_string()); |
| } |
| |
| #[test] |
| fn test_braced() { |
| #[derive(Error, Debug)] |
| #[error("braced error: {msg}")] |
| struct Error { |
| msg: String, |
| } |
| |
| let msg = "T".to_owned(); |
| assert("braced error: T", Error { msg }); |
| } |
| |
| #[test] |
| fn test_braced_unused() { |
| #[derive(Error, Debug)] |
| #[error("braced error")] |
| struct Error { |
| extra: usize, |
| } |
| |
| assert("braced error", Error { extra: 0 }); |
| } |
| |
| #[test] |
| fn test_tuple() { |
| #[derive(Error, Debug)] |
| #[error("tuple error: {0}")] |
| struct Error(usize); |
| |
| assert("tuple error: 0", Error(0)); |
| } |
| |
| #[test] |
| fn test_unit() { |
| #[derive(Error, Debug)] |
| #[error("unit error")] |
| struct Error; |
| |
| assert("unit error", Error); |
| } |
| |
| #[test] |
| fn test_enum() { |
| #[derive(Error, Debug)] |
| enum Error { |
| #[error("braced error: {id}")] |
| Braced { id: usize }, |
| #[error("tuple error: {0}")] |
| Tuple(usize), |
| #[error("unit error")] |
| Unit, |
| } |
| |
| assert("braced error: 0", Error::Braced { id: 0 }); |
| assert("tuple error: 0", Error::Tuple(0)); |
| assert("unit error", Error::Unit); |
| } |
| |
| #[test] |
| fn test_constants() { |
| #[derive(Error, Debug)] |
| #[error("{MSG}: {id:?} (code {CODE:?})")] |
| struct Error { |
| id: &'static str, |
| } |
| |
| const MSG: &str = "failed to do"; |
| const CODE: usize = 9; |
| |
| assert("failed to do: \"\" (code 9)", Error { id: "" }); |
| } |
| |
| #[test] |
| fn test_inherit() { |
| #[derive(Error, Debug)] |
| #[error("{0}")] |
| enum Error { |
| Some(&'static str), |
| #[error("other error")] |
| Other(&'static str), |
| } |
| |
| assert("some error", Error::Some("some error")); |
| assert("other error", Error::Other("...")); |
| } |
| |
| #[test] |
| fn test_brace_escape() { |
| #[derive(Error, Debug)] |
| #[error("fn main() {{}}")] |
| struct Error; |
| |
| assert("fn main() {}", Error); |
| } |
| |
| #[test] |
| fn test_expr() { |
| #[derive(Error, Debug)] |
| #[error("1 + 1 = {}", 1 + 1)] |
| struct Error; |
| assert("1 + 1 = 2", Error); |
| } |
| |
| #[test] |
| fn test_nested() { |
| #[derive(Error, Debug)] |
| #[error("!bool = {}", not(.0))] |
| struct Error(bool); |
| |
| #[allow(clippy::trivially_copy_pass_by_ref)] |
| fn not(bool: &bool) -> bool { |
| !*bool |
| } |
| |
| assert("!bool = false", Error(true)); |
| } |
| |
| #[test] |
| fn test_match() { |
| #[derive(Error, Debug)] |
| #[error("{intro}: {0}", intro = match .1 { |
| Some(n) => format!("error occurred with {}", n), |
| None => "there was an empty error".to_owned(), |
| })] |
| struct Error(String, Option<usize>); |
| |
| assert( |
| "error occurred with 1: ...", |
| Error("...".to_owned(), Some(1)), |
| ); |
| assert( |
| "there was an empty error: ...", |
| Error("...".to_owned(), None), |
| ); |
| } |
| |
| #[test] |
| fn test_nested_display() { |
| // Same behavior as the one in `test_match`, but without String allocations. |
| #[derive(Error, Debug)] |
| #[error("{}", { |
| struct Msg<'a>(&'a String, &'a Option<usize>); |
| impl<'a> Display for Msg<'a> { |
| fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result { |
| match self.1 { |
| Some(n) => write!(formatter, "error occurred with {}", n), |
| None => write!(formatter, "there was an empty error"), |
| }?; |
| write!(formatter, ": {}", self.0) |
| } |
| } |
| Msg(.0, .1) |
| })] |
| struct Error(String, Option<usize>); |
| |
| assert( |
| "error occurred with 1: ...", |
| Error("...".to_owned(), Some(1)), |
| ); |
| assert( |
| "there was an empty error: ...", |
| Error("...".to_owned(), None), |
| ); |
| } |
| |
| #[test] |
| fn test_void() { |
| #[allow(clippy::empty_enum)] |
| #[derive(Error, Debug)] |
| #[error("...")] |
| pub enum Error {} |
| |
| let _: Error; |
| } |
| |
| #[test] |
| fn test_mixed() { |
| #[derive(Error, Debug)] |
| #[error("a={a} :: b={} :: c={c} :: d={d}", 1, c = 2, d = 3)] |
| struct Error { |
| a: usize, |
| d: usize, |
| } |
| |
| assert("a=0 :: b=1 :: c=2 :: d=3", Error { a: 0, d: 0 }); |
| } |
| |
| #[test] |
| fn test_ints() { |
| #[derive(Error, Debug)] |
| enum Error { |
| #[error("error {0}")] |
| Tuple(usize, usize), |
| #[error("error {0}", '?')] |
| Struct { v: usize }, |
| } |
| |
| assert("error 9", Error::Tuple(9, 0)); |
| assert("error ?", Error::Struct { v: 0 }); |
| } |
| |
| #[test] |
| fn test_trailing_comma() { |
| #[derive(Error, Debug)] |
| #[error( |
| "error {0}", |
| )] |
| #[rustfmt::skip] |
| struct Error(char); |
| |
| assert("error ?", Error('?')); |
| } |
| |
| #[test] |
| fn test_field() { |
| #[derive(Debug)] |
| struct Inner { |
| data: usize, |
| } |
| |
| #[derive(Error, Debug)] |
| #[error("{}", .0.data)] |
| struct Error(Inner); |
| |
| assert("0", Error(Inner { data: 0 })); |
| } |
| |
| #[test] |
| fn test_nested_tuple_field() { |
| #[derive(Debug)] |
| struct Inner(usize); |
| |
| #[derive(Error, Debug)] |
| #[error("{}", .0.0)] |
| struct Error(Inner); |
| |
| assert("0", Error(Inner(0))); |
| } |
| |
| #[test] |
| fn test_pointer() { |
| #[derive(Error, Debug)] |
| #[error("{field:p}")] |
| pub struct Struct { |
| field: Box<i32>, |
| } |
| |
| let s = Struct { |
| field: Box::new(-1), |
| }; |
| assert_eq!(s.to_string(), format!("{:p}", s.field)); |
| } |
| |
| #[test] |
| fn test_macro_rules_variant_from_call_site() { |
| // Regression test for https://github.com/dtolnay/thiserror/issues/86 |
| |
| macro_rules! decl_error { |
| ($variant:ident($value:ident)) => { |
| #[derive(Error, Debug)] |
| pub enum Error0 { |
| #[error("{0:?}")] |
| $variant($value), |
| } |
| |
| #[derive(Error, Debug)] |
| #[error("{0:?}")] |
| pub enum Error1 { |
| $variant($value), |
| } |
| }; |
| } |
| |
| decl_error!(Repro(u8)); |
| |
| assert("0", Error0::Repro(0)); |
| assert("0", Error1::Repro(0)); |
| } |
| |
| #[test] |
| fn test_macro_rules_message_from_call_site() { |
| // Regression test for https://github.com/dtolnay/thiserror/issues/398 |
| |
| macro_rules! decl_error { |
| ($($errors:tt)*) => { |
| #[derive(Error, Debug)] |
| pub enum Error { |
| $($errors)* |
| } |
| }; |
| } |
| |
| decl_error! { |
| #[error("{0}")] |
| Unnamed(u8), |
| #[error("{x}")] |
| Named { x: u8 }, |
| } |
| |
| assert("0", Error::Unnamed(0)); |
| assert("0", Error::Named { x: 0 }); |
| } |
| |
| #[test] |
| fn test_raw() { |
| #[derive(Error, Debug)] |
| #[error("braced raw error: {fn}")] |
| struct Error { |
| r#fn: &'static str, |
| } |
| |
| assert("braced raw error: T", Error { r#fn: "T" }); |
| } |
| |
| #[test] |
| fn test_raw_enum() { |
| #[derive(Error, Debug)] |
| enum Error { |
| #[error("braced raw error: {fn}")] |
| Braced { r#fn: &'static str }, |
| } |
| |
| assert("braced raw error: T", Error::Braced { r#fn: "T" }); |
| } |
| |
| #[test] |
| fn test_keyword() { |
| #[derive(Error, Debug)] |
| #[error("error: {type}", type = 1)] |
| struct Error; |
| |
| assert("error: 1", Error); |
| } |
| |
| #[test] |
| fn test_self() { |
| #[derive(Error, Debug)] |
| #[error("error: {self:?}")] |
| struct Error; |
| |
| assert("error: Error", Error); |
| } |
| |
| #[test] |
| fn test_str_special_chars() { |
| #[derive(Error, Debug)] |
| pub enum Error { |
| #[error("brace left {{")] |
| BraceLeft, |
| #[error("brace left 2 \x7B\x7B")] |
| BraceLeft2, |
| #[error("brace left 3 \u{7B}\u{7B}")] |
| BraceLeft3, |
| #[error("brace right }}")] |
| BraceRight, |
| #[error("brace right 2 \x7D\x7D")] |
| BraceRight2, |
| #[error("brace right 3 \u{7D}\u{7D}")] |
| BraceRight3, |
| #[error( |
| "new_\ |
| line" |
| )] |
| NewLine, |
| #[error("escape24 \u{78}")] |
| Escape24, |
| } |
| |
| assert("brace left {", Error::BraceLeft); |
| assert("brace left 2 {", Error::BraceLeft2); |
| assert("brace left 3 {", Error::BraceLeft3); |
| assert("brace right }", Error::BraceRight); |
| assert("brace right 2 }", Error::BraceRight2); |
| assert("brace right 3 }", Error::BraceRight3); |
| assert("new_line", Error::NewLine); |
| assert("escape24 x", Error::Escape24); |
| } |
| |
| #[test] |
| fn test_raw_str() { |
| #[derive(Error, Debug)] |
| pub enum Error { |
| #[error(r#"raw brace left {{"#)] |
| BraceLeft, |
| #[error(r#"raw brace left 2 \x7B"#)] |
| BraceLeft2, |
| #[error(r#"raw brace right }}"#)] |
| BraceRight, |
| #[error(r#"raw brace right 2 \x7D"#)] |
| BraceRight2, |
| } |
| |
| assert(r#"raw brace left {"#, Error::BraceLeft); |
| assert(r#"raw brace left 2 \x7B"#, Error::BraceLeft2); |
| assert(r#"raw brace right }"#, Error::BraceRight); |
| assert(r#"raw brace right 2 \x7D"#, Error::BraceRight2); |
| } |
| |
| mod util { |
| use core::fmt::{self, Octal}; |
| |
| pub fn octal<T: Octal>(value: &T, formatter: &mut fmt::Formatter) -> fmt::Result { |
| write!(formatter, "0o{:o}", value) |
| } |
| } |
| |
| #[test] |
| fn test_fmt_path() { |
| fn unit(formatter: &mut fmt::Formatter) -> fmt::Result { |
| formatter.write_str("unit=") |
| } |
| |
| fn pair(k: &i32, v: &i32, formatter: &mut fmt::Formatter) -> fmt::Result { |
| write!(formatter, "pair={k}:{v}") |
| } |
| |
| #[derive(Error, Debug)] |
| pub enum Error { |
| #[error(fmt = unit)] |
| Unit, |
| #[error(fmt = pair)] |
| Tuple(i32, i32), |
| #[error(fmt = pair)] |
| Entry { k: i32, v: i32 }, |
| #[error(fmt = crate::util::octal)] |
| I16(i16), |
| #[error(fmt = crate::util::octal::<i32>)] |
| I32 { n: i32 }, |
| #[error(fmt = core::fmt::Octal::fmt)] |
| I64(i64), |
| #[error("...{0}")] |
| Other(bool), |
| } |
| |
| assert("unit=", Error::Unit); |
| assert("pair=10:0", Error::Tuple(10, 0)); |
| assert("pair=10:0", Error::Entry { k: 10, v: 0 }); |
| assert("0o777", Error::I16(0o777)); |
| assert("0o777", Error::I32 { n: 0o777 }); |
| assert("777", Error::I64(0o777)); |
| assert("...false", Error::Other(false)); |
| } |
| |
| #[test] |
| fn test_fmt_path_inherited() { |
| #[derive(Error, Debug)] |
| #[error(fmt = crate::util::octal)] |
| pub enum Error { |
| I16(i16), |
| I32 { |
| n: i32, |
| }, |
| #[error(fmt = core::fmt::Octal::fmt)] |
| I64(i64), |
| #[error("...{0}")] |
| Other(bool), |
| } |
| |
| assert("0o777", Error::I16(0o777)); |
| assert("0o777", Error::I32 { n: 0o777 }); |
| assert("777", Error::I64(0o777)); |
| assert("...false", Error::Other(false)); |
| } |