• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 #![allow(
2     // Clippy bug: https://github.com/rust-lang/rust-clippy/issues/7422
3     clippy::nonstandard_macro_braces,
4 )]
5 
6 mod drop;
7 
8 use crate::drop::{DetectDrop, Flag};
9 use anyhow::{Context, Error, Result};
10 use std::fmt::{self, Display};
11 use thiserror::Error;
12 
13 // https://github.com/dtolnay/anyhow/issues/18
14 #[test]
test_inference() -> Result<()>15 fn test_inference() -> Result<()> {
16     let x = "1";
17     let y: u32 = x.parse().context("...")?;
18     assert_eq!(y, 1);
19     Ok(())
20 }
21 
22 macro_rules! context_type {
23     ($name:ident) => {
24         #[derive(Debug)]
25         struct $name {
26             message: &'static str,
27             drop: DetectDrop,
28         }
29 
30         impl Display for $name {
31             fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
32                 f.write_str(self.message)
33             }
34         }
35     };
36 }
37 
38 context_type!(HighLevel);
39 context_type!(MidLevel);
40 
41 #[derive(Error, Debug)]
42 #[error("{message}")]
43 struct LowLevel {
44     message: &'static str,
45     drop: DetectDrop,
46 }
47 
48 struct Dropped {
49     low: Flag,
50     mid: Flag,
51     high: Flag,
52 }
53 
54 impl Dropped {
none(&self) -> bool55     fn none(&self) -> bool {
56         !self.low.get() && !self.mid.get() && !self.high.get()
57     }
58 
all(&self) -> bool59     fn all(&self) -> bool {
60         self.low.get() && self.mid.get() && self.high.get()
61     }
62 }
63 
make_chain() -> (Error, Dropped)64 fn make_chain() -> (Error, Dropped) {
65     let dropped = Dropped {
66         low: Flag::new(),
67         mid: Flag::new(),
68         high: Flag::new(),
69     };
70 
71     let low = LowLevel {
72         message: "no such file or directory",
73         drop: DetectDrop::new(&dropped.low),
74     };
75 
76     // impl Context for Result<T, E>
77     let mid = Err::<(), LowLevel>(low)
78         .context(MidLevel {
79             message: "failed to load config",
80             drop: DetectDrop::new(&dropped.mid),
81         })
82         .unwrap_err();
83 
84     // impl Context for Result<T, Error>
85     let high = Err::<(), Error>(mid)
86         .context(HighLevel {
87             message: "failed to start server",
88             drop: DetectDrop::new(&dropped.high),
89         })
90         .unwrap_err();
91 
92     (high, dropped)
93 }
94 
95 #[test]
test_downcast_ref()96 fn test_downcast_ref() {
97     let (err, dropped) = make_chain();
98 
99     assert!(!err.is::<String>());
100     assert!(err.downcast_ref::<String>().is_none());
101 
102     assert!(err.is::<HighLevel>());
103     let high = err.downcast_ref::<HighLevel>().unwrap();
104     assert_eq!(high.to_string(), "failed to start server");
105 
106     assert!(err.is::<MidLevel>());
107     let mid = err.downcast_ref::<MidLevel>().unwrap();
108     assert_eq!(mid.to_string(), "failed to load config");
109 
110     assert!(err.is::<LowLevel>());
111     let low = err.downcast_ref::<LowLevel>().unwrap();
112     assert_eq!(low.to_string(), "no such file or directory");
113 
114     assert!(dropped.none());
115     drop(err);
116     assert!(dropped.all());
117 }
118 
119 #[test]
test_downcast_high()120 fn test_downcast_high() {
121     let (err, dropped) = make_chain();
122 
123     let err = err.downcast::<HighLevel>().unwrap();
124     assert!(!dropped.high.get());
125     assert!(dropped.low.get() && dropped.mid.get());
126 
127     drop(err);
128     assert!(dropped.all());
129 }
130 
131 #[test]
test_downcast_mid()132 fn test_downcast_mid() {
133     let (err, dropped) = make_chain();
134 
135     let err = err.downcast::<MidLevel>().unwrap();
136     assert!(!dropped.mid.get());
137     assert!(dropped.low.get() && dropped.high.get());
138 
139     drop(err);
140     assert!(dropped.all());
141 }
142 
143 #[test]
test_downcast_low()144 fn test_downcast_low() {
145     let (err, dropped) = make_chain();
146 
147     let err = err.downcast::<LowLevel>().unwrap();
148     assert!(!dropped.low.get());
149     assert!(dropped.mid.get() && dropped.high.get());
150 
151     drop(err);
152     assert!(dropped.all());
153 }
154 
155 #[test]
test_unsuccessful_downcast()156 fn test_unsuccessful_downcast() {
157     let (err, dropped) = make_chain();
158 
159     let err = err.downcast::<String>().unwrap_err();
160     assert!(dropped.none());
161 
162     drop(err);
163     assert!(dropped.all());
164 }
165 
166 #[test]
test_root_cause()167 fn test_root_cause() {
168     let (err, _) = make_chain();
169 
170     assert_eq!(err.root_cause().to_string(), "no such file or directory");
171 }
172