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