1 use core::fmt::{self, Debug, Display}; 2 3 use std::error::Error as StdError; 4 5 use crate::{Diagnostic, LabeledSpan, Report, SourceCode}; 6 7 use crate as miette; 8 9 #[repr(transparent)] 10 pub(crate) struct DisplayError<M>(pub(crate) M); 11 12 #[repr(transparent)] 13 pub(crate) struct MessageError<M>(pub(crate) M); 14 15 pub(crate) struct NoneError; 16 17 impl<M> Debug for DisplayError<M> 18 where 19 M: Display, 20 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result21 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 22 Display::fmt(&self.0, f) 23 } 24 } 25 26 impl<M> Display for DisplayError<M> 27 where 28 M: Display, 29 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result30 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 31 Display::fmt(&self.0, f) 32 } 33 } 34 35 impl<M> StdError for DisplayError<M> where M: Display + 'static {} 36 impl<M> Diagnostic for DisplayError<M> where M: Display + 'static {} 37 38 impl<M> Debug for MessageError<M> 39 where 40 M: Display + Debug, 41 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result42 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 43 Debug::fmt(&self.0, f) 44 } 45 } 46 47 impl<M> Display for MessageError<M> 48 where 49 M: Display + Debug, 50 { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result51 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 52 Display::fmt(&self.0, f) 53 } 54 } 55 56 impl<M> StdError for MessageError<M> where M: Display + Debug + 'static {} 57 impl<M> Diagnostic for MessageError<M> where M: Display + Debug + 'static {} 58 59 impl Debug for NoneError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 61 Debug::fmt("Option was None", f) 62 } 63 } 64 65 impl Display for NoneError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 67 Display::fmt("Option was None", f) 68 } 69 } 70 71 impl StdError for NoneError {} 72 impl Diagnostic for NoneError {} 73 74 #[repr(transparent)] 75 pub(crate) struct BoxedError(pub(crate) Box<dyn Diagnostic + Send + Sync>); 76 77 impl Diagnostic for BoxedError { code<'a>(&'a self) -> Option<Box<dyn Display + 'a>>78 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 79 self.0.code() 80 } 81 severity(&self) -> Option<miette::Severity>82 fn severity(&self) -> Option<miette::Severity> { 83 self.0.severity() 84 } 85 help<'a>(&'a self) -> Option<Box<dyn Display + 'a>>86 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 87 self.0.help() 88 } 89 url<'a>(&'a self) -> Option<Box<dyn Display + 'a>>90 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 91 self.0.url() 92 } 93 labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>>94 fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>> { 95 self.0.labels() 96 } 97 source_code(&self) -> Option<&dyn miette::SourceCode>98 fn source_code(&self) -> Option<&dyn miette::SourceCode> { 99 self.0.source_code() 100 } 101 related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>>102 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> { 103 self.0.related() 104 } 105 diagnostic_source(&self) -> Option<&dyn Diagnostic>106 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { 107 self.0.diagnostic_source() 108 } 109 } 110 111 impl Debug for BoxedError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 113 Debug::fmt(&self.0, f) 114 } 115 } 116 117 impl Display for BoxedError { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result118 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 119 Display::fmt(&self.0, f) 120 } 121 } 122 123 impl StdError for BoxedError { source(&self) -> Option<&(dyn StdError + 'static)>124 fn source(&self) -> Option<&(dyn StdError + 'static)> { 125 self.0.source() 126 } 127 description(&self) -> &str128 fn description(&self) -> &str { 129 #[allow(deprecated)] 130 self.0.description() 131 } 132 cause(&self) -> Option<&dyn StdError>133 fn cause(&self) -> Option<&dyn StdError> { 134 #[allow(deprecated)] 135 self.0.cause() 136 } 137 } 138 139 pub(crate) struct WithSourceCode<E, C> { 140 pub(crate) error: E, 141 pub(crate) source_code: C, 142 } 143 144 impl<E: Diagnostic, C: SourceCode> Diagnostic for WithSourceCode<E, C> { code<'a>(&'a self) -> Option<Box<dyn Display + 'a>>145 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 146 self.error.code() 147 } 148 severity(&self) -> Option<miette::Severity>149 fn severity(&self) -> Option<miette::Severity> { 150 self.error.severity() 151 } 152 help<'a>(&'a self) -> Option<Box<dyn Display + 'a>>153 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 154 self.error.help() 155 } 156 url<'a>(&'a self) -> Option<Box<dyn Display + 'a>>157 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 158 self.error.url() 159 } 160 labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>>161 fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>> { 162 self.error.labels() 163 } 164 source_code(&self) -> Option<&dyn miette::SourceCode>165 fn source_code(&self) -> Option<&dyn miette::SourceCode> { 166 self.error.source_code().or(Some(&self.source_code)) 167 } 168 related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>>169 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> { 170 self.error.related() 171 } 172 diagnostic_source(&self) -> Option<&dyn Diagnostic>173 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { 174 self.error.diagnostic_source() 175 } 176 } 177 178 impl<C: SourceCode> Diagnostic for WithSourceCode<Report, C> { code<'a>(&'a self) -> Option<Box<dyn Display + 'a>>179 fn code<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 180 self.error.code() 181 } 182 severity(&self) -> Option<miette::Severity>183 fn severity(&self) -> Option<miette::Severity> { 184 self.error.severity() 185 } 186 help<'a>(&'a self) -> Option<Box<dyn Display + 'a>>187 fn help<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 188 self.error.help() 189 } 190 url<'a>(&'a self) -> Option<Box<dyn Display + 'a>>191 fn url<'a>(&'a self) -> Option<Box<dyn Display + 'a>> { 192 self.error.url() 193 } 194 labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>>195 fn labels<'a>(&'a self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + 'a>> { 196 self.error.labels() 197 } 198 source_code(&self) -> Option<&dyn miette::SourceCode>199 fn source_code(&self) -> Option<&dyn miette::SourceCode> { 200 self.error.source_code().or(Some(&self.source_code)) 201 } 202 related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>>203 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> { 204 self.error.related() 205 } 206 diagnostic_source(&self) -> Option<&dyn Diagnostic>207 fn diagnostic_source(&self) -> Option<&dyn Diagnostic> { 208 self.error.diagnostic_source() 209 } 210 } 211 212 impl<E: Debug, C> Debug for WithSourceCode<E, C> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result213 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 214 Debug::fmt(&self.error, f) 215 } 216 } 217 218 impl<E: Display, C> Display for WithSourceCode<E, C> { fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result219 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 220 Display::fmt(&self.error, f) 221 } 222 } 223 224 impl<E: StdError, C> StdError for WithSourceCode<E, C> { source(&self) -> Option<&(dyn StdError + 'static)>225 fn source(&self) -> Option<&(dyn StdError + 'static)> { 226 self.error.source() 227 } 228 } 229 230 impl<C> StdError for WithSourceCode<Report, C> { source(&self) -> Option<&(dyn StdError + 'static)>231 fn source(&self) -> Option<&(dyn StdError + 'static)> { 232 self.error.source() 233 } 234 } 235 236 #[cfg(test)] 237 mod tests { 238 use thiserror::Error; 239 240 use crate::{Diagnostic, LabeledSpan, Report, SourceCode, SourceSpan}; 241 242 #[derive(Error, Debug)] 243 #[error("inner")] 244 struct Inner { 245 pub(crate) at: SourceSpan, 246 pub(crate) source_code: Option<String>, 247 } 248 249 impl Diagnostic for Inner { labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>>250 fn labels(&self) -> Option<Box<dyn Iterator<Item = LabeledSpan> + '_>> { 251 Some(Box::new(std::iter::once(LabeledSpan::underline(self.at)))) 252 } 253 source_code(&self) -> Option<&dyn SourceCode>254 fn source_code(&self) -> Option<&dyn SourceCode> { 255 self.source_code.as_ref().map(|s| s as _) 256 } 257 } 258 259 #[derive(Error, Debug)] 260 #[error("outer")] 261 struct Outer { 262 pub(crate) errors: Vec<Inner>, 263 } 264 265 impl Diagnostic for Outer { related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>>266 fn related<'a>(&'a self) -> Option<Box<dyn Iterator<Item = &'a dyn Diagnostic> + 'a>> { 267 Some(Box::new(self.errors.iter().map(|e| e as _))) 268 } 269 } 270 271 #[test] no_override()272 fn no_override() { 273 let inner_source = "hello world"; 274 let outer_source = "abc"; 275 276 let report = Report::from(Inner { 277 at: (0..5).into(), 278 source_code: Some(inner_source.to_string()), 279 }) 280 .with_source_code(outer_source.to_string()); 281 282 let underlined = String::from_utf8( 283 report 284 .source_code() 285 .unwrap() 286 .read_span(&(0..5).into(), 0, 0) 287 .unwrap() 288 .data() 289 .to_vec(), 290 ) 291 .unwrap(); 292 assert_eq!(underlined, "hello"); 293 } 294 295 #[test] 296 #[cfg(feature = "fancy")] two_source_codes()297 fn two_source_codes() { 298 let inner_source = "hello world"; 299 let outer_source = "abc"; 300 301 let report = Report::from(Outer { 302 errors: vec![ 303 Inner { 304 at: (0..5).into(), 305 source_code: Some(inner_source.to_string()), 306 }, 307 Inner { 308 at: (1..2).into(), 309 source_code: None, 310 }, 311 ], 312 }) 313 .with_source_code(outer_source.to_string()); 314 315 let message = format!("{:?}", report); 316 assert!(message.contains(inner_source)); 317 assert!(message.contains(outer_source)); 318 } 319 } 320