1#![cfg_attr(feature = "nightly", feature(min_specialization))]
2
3use std::{any::Any, backtrace::Backtrace, fmt::{Debug, Display}};
4
5#[cfg(test)]
6mod tests {
7 #[allow(deprecated)]
8 use crate::ErrorChain;
9 use crate::ErrorLink_;
10 use crate::ErrorLinkable;
11 use crate::NextLink;
12 #[cfg(feature = "nightly")]
13 use crate::ResultExt;
14 use std::backtrace::Backtrace;
15 use crate::LinkableResult1of2;
16 use crate::LinkableResult2of2;
17
18 #[allow(dead_code)]
19 trait X {
20 fn letter() -> char;
21 }
22 impl<T> X for T {
23 fn letter() -> char {
24 'X'
25 }
26 }
27 struct B;
28 impl B {
29 fn letter() -> char {
30 'B'
31 }
32 }
33
34 #[test]
35 fn test_favour_concrete() {
36 assert!(B::letter() == 'B');
37 }
38
39 fn is_output_default(default_output: &str) {
40 assert_eq!(
41 default_output
42 .matches("Link no. 0: Higher level error.\nLink no. 1: Underlying error.")
43 .collect::<Vec<_>>()
44 .len(),
45 1
46 );
47 has_only_one_backtrace(default_output);
48 }
49 fn has_only_one_backtrace(formatted_link: &str) {
50 assert_eq!(
51 formatted_link
52 .matches("Approximate backtrace of link no. ")
53 .collect::<Vec<_>>()
54 .len(),
55 1
56 );
57 }
58
59 #[test]
60 #[allow(non_snake_case)]
61 #[allow(deprecated)]
62 fn test__chaining_non_error_chain() {
63 let format_output = format!(
64 "{}",
65 Err::<(), _>(std::io::Error::other("Underlying error."))
66 .map_err(ErrorChain::link_fn("Higher level error."))
67 .expect_err("look above")
68 );
69 println!("{}", format_output);
70 is_output_default(&format_output);
71 }
72 #[test]
73 #[allow(non_snake_case)]
74 fn test__chaining_non_error_link_() {
75 let error_link: ErrorLink_<String> = Err::<(), _>(std::io::Error::other("Underlying error."))
76 .map_err(|e| e.as_link())
77 .map_err(|e| e.link("Higher level error."))
78 .expect_err("look above");
79 let format_output = format!(
80 "{}", error_link
81 );
82 println!("{}", format_output);
83 is_output_default(&format_output);
84 }
85
86 #[test]
87 #[allow(deprecated)]
88 #[allow(non_snake_case)]
89 fn test__chaining_error_chain() {
90 let format_output = format!(
91 "{}",
92 Err::<(), _>(ErrorChain::start("Underlying error."))
93 .map_err(ErrorChain::link_fn("Higher level error."))
94 .expect_err("look above")
95 );
96 println!("{}", format_output);
97 is_output_default(&format_output);
98 }
99
100 #[test]
101 #[allow(non_snake_case)]
102 fn test__chaining_error_link_() {
103 let error_link: ErrorLink_<String> = Err::<(), _>(ErrorLink_::new_string("Underlying error."))
104 .map_err(|e| e.as_link() as ErrorLink_<String>)
105 .map_err(|e| e.link("Higher level error."))
106 .expect_err("look above");
107 let format_output = format!("{error_link}");
108 println!("{}", format_output);
109 is_output_default(&format_output);
110 }
111
112 impl ErrorLink_<i32> {
113 pub fn new_i32(error_number: impl Into<i32>) -> Self {
114 Self(error_number.into(), NextLink::None(Backtrace::capture()))
115 }
116 }
117 #[derive(Debug, PartialEq)]
118 enum ErrorReasons {
119 One,
120 Two,
121 #[allow(dead_code)]
122 Three
123 }
124 impl ErrorLink_<ErrorReasons> {
125 pub fn new_reason(error_reason: ErrorReasons) -> Self {
126 Self(error_reason, NextLink::None(Backtrace::capture()))
127 }
128 }
129 impl std::fmt::Display for ErrorReasons {
130 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
131 let as_string = match self {
132 ErrorReasons::One => "First reason for underlying error.",
133 ErrorReasons::Two => "Second reason for underlying error.",
134 ErrorReasons::Three => "Third reason for underlying error.",
135 };
136 write!(f, "{}", as_string)
137 }
138 }
139 impl std::error::Error for ErrorReasons {}
140
141 #[test]
142 #[allow(non_snake_case)]
143 fn test__chaining_error_link__non_string_payload() {
144 let error_link: ErrorLink_<String> = Err::<(), _>(ErrorLink_::new_i32(100))
145 .map_err(|e| e.link("Higher level error."))
146 .expect_err("look above");
147 let mut format_output = format!("{error_link}");
148 println!("{}", format_output);
149 assert_eq!(
150 format_output
151 .matches("Link no. 0: Higher level error.\nLink no. 1: 100")
152 .collect::<Vec<_>>()
153 .len(),
154 1
155 );
156 has_only_one_backtrace(&format_output);
157
158 let error_link: ErrorLink_<String> = Err::<(), _>(ErrorLink_::new_reason(ErrorReasons::One))
159 .map_err(|e| e.link("Higher level error."))
160 .expect_err("look above");
161 format_output = format!("{}", error_link);
162 println!("{}", format_output);
163 assert_eq!(
164 format_output
165 .matches("Link no. 0: Higher level error.\nLink no. 1: First reason for underlying error.")
166 .collect::<Vec<_>>()
167 .len(),
168 1
169 );
170 has_only_one_backtrace(&format_output);
171
172 match Err::<(), _>(ErrorLink_::new_reason(ErrorReasons::Two)) {
173 Err(error_chain) if error_chain.0 == ErrorReasons::Two => {
174 format_output = format!("{}", error_chain);
175 println!("{}", format_output);
176 assert_eq!(
177 format_output
178 .matches("Link no. 0: Second reason for underlying error.")
179 .collect::<Vec<_>>()
180 .len(),
181 1
182 );
183 has_only_one_backtrace(&format_output);
184 },
185 _ => panic!("look above"),
186 }
187 }
188
189 #[test]
190 #[allow(deprecated)]
191 #[allow(non_snake_case)]
192 fn test__chaining_non_error_chain_and_non_error_trait() {
193 let format_output = format!(
194 "{}",
195 Err::<(), _>(String::from("Underlying error."))
196 .map_err(ErrorChain::onboard_fn("Higher level error."))
197 .expect_err("look above")
198 );
199 println!("{}", format_output);
200 is_output_default(&format_output);
201 }
202
203 #[test]
204 #[allow(non_snake_case)]
205 fn test__chaining_non_error_link_and_non_error_trait() {
206 let format_output = format!(
207 "{}",
208 Err::<(), _>(String::from("Underlying error."))
209 .map_err(|e| e.link("Higher level error."))
210 .expect_err("look above")
211 );
212 println!("{}", format_output);
213 is_output_default(&format_output);
214 }
215
216 #[test]
217 #[allow(non_snake_case)]
218 fn test__resultext__chaining_non() {
219 let format_output = format!(
220 "{}",
221 Err::<(), _>(String::from("Underlying error."))
222 .map_err(|e| e.link("Higher level error."))
223 .expect_err("look above")
224 );
225 println!("{}", format_output);
226 is_output_default(&format_output);
227 }
228
229 #[cfg(feature = "nightly")]
230 #[test]
231 #[allow(non_snake_case)]
232 fn test__resultext__chaining_error_link__string_payload() {
233 let format_output = format!(
234 "{}",
235 Err::<(), _>(ErrorLink_::new_string("Underlying error."))
236 .me_al()
237 .me_l("Higher level error.")
238 .expect_err("look above")
239 );
240 println!("{}", format_output);
241 is_output_default(&format_output);
242 }
243
244 #[cfg(feature = "nightly")]
245 #[test]
246 #[allow(non_snake_case)]
247 fn test__resultext__chaining_error_link__non_string_payload() {
248 let format_output = format!(
249 "{}",
250 Err::<(), _>(ErrorLink_::new_i32(100))
251 .me_al()
252 .expect_err("look above")
253 );
254 println!("{}", format_output);
255 assert_eq!(
256 format_output
257 .matches("100")
258 .collect::<Vec<_>>()
259 .len(),
260 1
261 );
262 has_only_one_backtrace(&format_output);
263 }
264
265 #[cfg(feature = "nightly")]
266 #[test]
267 #[allow(non_snake_case)]
268 fn test__resultext__chaining_non_error_link__string_payload() {
269 let format_output = format!(
270 "{}",
271 Err::<(), _>(String::from("Underlying error."))
272 .me_al()
273 .expect_err("look above")
274 );
275 println!("{}", format_output);
276 assert_eq!(
277 format_output
278 .matches("Underlying error.")
279 .collect::<Vec<_>>()
280 .len(),
281 1
282 );
283 has_only_one_backtrace(&format_output);
284 }
285
286 #[cfg(feature = "nightly")]
287 #[test]
288 #[allow(non_snake_case)]
289 fn test__resultext__chaining_non_error_link__non_string_payload() {
290 let format_output = format!(
291 "{}",
292 Err::<(), _>(100)
293 .me_al()
294 .expect_err("look above")
295 );
296 println!("{}", format_output);
297 assert_eq!(
298 format_output
299 .matches("100")
300 .collect::<Vec<_>>()
301 .len(),
302 1
303 );
304 has_only_one_backtrace(&format_output);
305 }
306
307 #[allow(dead_code)]
308 struct DisplayableAndIntoStringable(String);
309 impl From<DisplayableAndIntoStringable> for String {
310 fn from(value: DisplayableAndIntoStringable) -> Self {
311 value.0
312 }
313 }
314 impl std::fmt::Display for DisplayableAndIntoStringable {
315 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
316 write!(f, "DisplayableAndIntoStringable")
317 }
318 }
319
320 #[test]
321 #[allow(non_snake_case)]
322 fn test__link_conversion__to_error_link_string() {
323 let _ = || -> Result<(), ErrorLink_<String>> {
324 Err::<(), _>(ErrorLink_::new("These `Result`s would be typically from function calls."))?;
325 Err::<(), _>(String::from(
326 "So, if the error is an `ErrorLink_` of same payload, or the payload type itself, \
327 `?` can be used."
328 ))?;
329 #[cfg(feature = "nightly")]
330 Err::<(), _>(std::io::Error::other(
331 "`ErrorLink_<String>` is like a terminal type. `me_as_slink()?` can always be called, \
332 and all can turn into it."
333 )).me_as_slink()?;
334 Err::<(), _>(ErrorLink_::<DisplayableAndIntoStringable>::new(
335 DisplayableAndIntoStringable(String::from(
336 "`impl<T> from <T> for T` exists, so `?` cannot cover all `ErrorLink_` to \
337 `ErrorLink_`s. Hence `me_as_link`."
338 ))
339 )).me_as_link()?;
340 Ok(())
341 }();
342 }
343
344 #[test]
345 #[allow(non_snake_case)]
346 fn test__adding_link__to_error_link_string() {
347 let _ = || -> Result<(), ErrorLink_<String>> {
348 Err::<(), _>(ErrorLink_::<std::io::Error>::new(
349 std::io::Error::other("Some data relating to an unhappy code path.")
350 ))
351 .me_link("Some more information.")?;
352 Ok(())
353 }();
354 }
355
356 #[test]
357 #[allow(non_snake_case)]
358 fn test__replacing_payload__to_error_link_string() {
359 let _ = || -> Result<(), ErrorLink_<String>> {
360 Err::<(), _>(ErrorLink_::<std::io::Error>::new(std::io::Error::other("Something ugly.")))
361 .map_err(|_| ErrorLink_::new("Get replaced."))?;
362 Err::<(), _>(ErrorLink_::<std::io::Error>::new(std::io::Error::other("Something ugly.")))
363 .map_err(|e| e.replace("Get replaced. But keep its linkage."))?;
364 Ok(())
365 }();
366 }
367
368 #[test]
369 #[allow(non_snake_case)]
370 fn test__link_conversion__to_error_link_non_string() {
371 let _ = || -> Result<(), ErrorLink_<i32>> {
372 Err::<(), _>(ErrorLink_::new(88))?;
373 Err::<(), _>(44)?;
374 Err::<(), _>(ErrorLink_::<i8>::new(8)).me_as_link()?;
375 Ok(())
376 }();
377 }
378
379 #[test]
380 #[allow(non_snake_case)]
381 fn test__adding_link__to_error_link_non_string() {
382 let _ = || -> Result<(), ErrorLink_<i32>> {
383 Err::<(), _>(ErrorLink_::new_string(""))
384 .me_link(100)?;
385 Ok(())
386 }();
387 }
388
389 #[test]
390 #[allow(non_snake_case)]
391 fn test__replacing_payload__to_error_link_non_string() {
392 let _ = || -> Result<(), ErrorLink_<i32>> {
393 Err::<(), _>(ErrorLink_::<std::io::Error>::new(std::io::Error::other("Something ugly.")))
394 .map_err(|_| ErrorLink_::new(3))?;
395 Err::<(), _>(ErrorLink_::<std::io::Error>::new(std::io::Error::other("Something ugly.")))
396 .map_err(|e| e.replace(1))?;
397 Ok(())
398 }();
399 }
400}
401
402#[derive(Debug)]
403#[deprecated(since = "0.3.0", note="use `NextLink` instead")]
404pub enum ErrorLink {
405 Severed(Backtrace),
406 Continued(String, Box<ErrorLink>)
407}
408
409#[allow(deprecated)]
410impl ErrorLink {
411 pub fn severed() -> Self {
412 Self::Severed(Backtrace::capture())
413 }
414
415 pub fn continued(
416 error_message: impl Into<String>,
417 next_link: impl Into<Box<ErrorLink>>
418 ) -> Self {
419 Self::Continued(error_message.into(), next_link.into())
420 }
421}
422
423#[derive(Debug)]
424#[deprecated(since = "0.3.0", note="use `ErrorLink_` instead")]
425#[allow(deprecated)]
426pub struct ErrorChain<T: Display>(pub T, pub ErrorLink);
427
428#[allow(deprecated)]
429impl<T: std::error::Error> From<T> for ErrorChain<String> {
430 fn from(value: T) -> Self {
431 ErrorChain(value.to_string(), ErrorLink::severed())
432 }
433}
434
435#[allow(deprecated)]
436impl ErrorChain<String> {
437 pub fn start(error_message: impl Into<String>) -> Self {
438 Self(error_message.into(), ErrorLink::severed())
439 }
440
441 pub fn add<T: Display>(error_message: impl Into<String>, current_chain: ErrorChain<T>) -> Self {
442 Self::add_fn(error_message)(current_chain)
443 }
444
445 pub fn add_fn<T: Display>(error_message: impl Into<String>) -> impl FnOnce(ErrorChain<T>) -> Self {
446 move |current_chain| {
447 Self(
448 error_message.into(),
449 ErrorLink::continued(current_chain.0.to_string(), current_chain.1)
450 )
451 }
452 }
453
454 pub fn onboard<T: Display>(error_message: impl Into<String>, underlying_error: T) -> Self {
455 Self::onboard_fn(error_message)(underlying_error)
456 }
457
458 pub fn onboard_fn<T: Display>(error_message: impl Into<String>) -> impl FnOnce(T) -> Self {
459 move |underlying_error| {
460 Self(
461 error_message.into(),
462 ErrorLink::continued(underlying_error.to_string(), Box::new(ErrorLink::severed()))
463 )
464 }
465 }
466
467 pub fn link(error_message: impl Into<String>, current_chain: impl Into<Self>) -> Self {
468 ErrorChain::link_fn(error_message)(current_chain)
469 }
470
471 pub fn link_fn<T: Into<Self>>(error_message: impl Into<String>) -> impl FnOnce(T) -> Self {
472 move |current_chain| {
473 let current_chain = current_chain.into();
474 Self(
475 error_message.into(),
476 ErrorLink::continued(current_chain.0, current_chain.1)
477 )
478 }
479 }
480}
481
482#[allow(deprecated)]
483impl<T: std::fmt::Display> std::fmt::Display for ErrorChain<T> {
484 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
485 write!(f, "Link no. 0: {}\n", self.0)?;
486 let mut error_link = &self.1;
487 for error_number in 1.. {
488 error_link = match error_link {
489 ErrorLink::Severed(end_backtrace) => {
490 write!(
491 f, "Approximate backtrace of link no. {}:\n{end_backtrace}",
492 error_number - 1
493 )?;
494 break;
495 },
496 ErrorLink::Continued(error_message, error_link) => {
497 write!(f, "Link no. {error_number}: {error_message}\n")?;
498 error_link
499 },
500 }
501 }
502
503 Ok(())
504 }
505}
506
507pub trait LinkableResult1of2<OkVariant> {
508 fn me_link<ToPayload: Display>(self, error_payload: impl Into<ToPayload>)
509 -> Result<OkVariant, ErrorLink_<ToPayload>>;
510 fn me_as_slink(self) -> Result<OkVariant, ErrorLink_<String>>;
511}
512
513#[cfg(feature = "nightly")]
514impl<OkVariant, ErrorVariant: Display> LinkableResult1of2<OkVariant>
515for Result<OkVariant, ErrorVariant> {
516 default fn me_link<ToPayload: Display>(self, error_payload: impl Into<ToPayload>)
517 -> Result<OkVariant, ErrorLink_<ToPayload>> {
518 self.map_err(|e| {
519 let next_link = Box::new(ErrorLink_(
520 e.to_string(),
521 NextLink::None(Backtrace::capture())
522 ));
523 ErrorLink_(error_payload.into(), NextLink::Some(next_link))
524 })
525 }
526
527 default fn me_as_slink(self) -> Result<OkVariant, ErrorLink_<String>> {
528 self.map_err(|e| ErrorLink_(
529 e.to_string(),
530 NextLink::None(Backtrace::capture())
531 ))
532 }
533}
534
535impl<OkVariant, FromPayload: Display> LinkableResult1of2<OkVariant>
536for Result<OkVariant, ErrorLink_<FromPayload>> {
537 fn me_link<ToPayload: Display>(self, error_payload: impl Into<ToPayload>)
538 -> Result<OkVariant, ErrorLink_<ToPayload>> {
539 self.map_err(|e| {
540 let next_link = Box::new(ErrorLink_(e.0.to_string(), e.1));
541 ErrorLink_(error_payload.into(), NextLink::Some(next_link))
542 })
543 }
544
545 fn me_as_slink(self) -> Result<OkVariant, ErrorLink_<String>> {
546 self.map_err(|e| ErrorLink_(e.0.to_string(), e.1))
547 }
548}
549
550pub trait LinkableResult2of2<OkVariant, FromPayload: Display> {
551 fn me_as_link<ToPayload: From<FromPayload> + Display>(self)
552 -> Result<OkVariant, ErrorLink_<ToPayload>>;
553}
554
555impl<OkVariant, FromPayload: Display> LinkableResult2of2<OkVariant, FromPayload>
556for Result<OkVariant, ErrorLink_<FromPayload>> {
557 fn me_as_link<ToPayload: From<FromPayload> + Display>(self)
558 -> Result<OkVariant, ErrorLink_<ToPayload>> {
559 self.map_err(|e| ErrorLink_(e.0.into(), e.1))
560 }
561}
562
563impl<P: Display> From<P> for ErrorLink_<P> {
564 fn from(value: P) -> Self {
565 ErrorLink_(value, NextLink::None(Backtrace::capture()))
566 }
567}
568
569#[cfg(feature = "nightly")]
570pub trait ResultExt<OkVariant, ToPayload: Display> {
571 fn me_l(self, error_payload: impl Into<ToPayload>)
572 -> Result<OkVariant, ErrorLink_<ToPayload>>;
573 fn me_al(self) -> Result<OkVariant, ErrorLink_<ToPayload>>;
574}
575
576#[cfg(feature = "nightly")]
577impl<OkVariant, ErrorVariant: Display> ResultExt<OkVariant, String>
578for Result<OkVariant, ErrorVariant> {
579 default fn me_l(self, error_payload: impl Into<String>)
580 -> Result<OkVariant, ErrorLink_<String>> {
581 self.map_err(|e| {
582 let next_link = Box::new(ErrorLink_(
583 e.to_string(),
584 NextLink::None(Backtrace::capture()))
585 );
586 ErrorLink_(error_payload.into(), NextLink::Some(next_link))
587 })
588 }
589
590 default fn me_al(self) -> Result<OkVariant, ErrorLink_<String>> {
591 self.map_err(|e| ErrorLink_(
592 e.to_string(),
593 NextLink::None(Backtrace::capture())
594 ))
595 }
596}
597
598#[cfg(feature = "nightly")]
599impl<OkVariant> ResultExt<OkVariant, String>
600for Result<OkVariant, String> {
601 fn me_l(self, error_payload: impl Into<String>)
602 -> Result<OkVariant, ErrorLink_<String>> {
603 self.map_err(|e| {
604 let next_link = Box::new(ErrorLink_(e,
605 NextLink::None(Backtrace::capture()))
606 );
607 ErrorLink_(error_payload.into(), NextLink::Some(next_link))
608 })
609 }
610
611 fn me_al(self) -> Result<OkVariant, ErrorLink_<String>> {
612 self.map_err(|e| ErrorLink_(
613 e,
614 NextLink::None(Backtrace::capture())
615 ))
616 }
617}
618
619#[cfg(feature = "nightly")]
620impl<OkVariant, FromPayload: Display> ResultExt<OkVariant, String>
621for Result<OkVariant, ErrorLink_<FromPayload>> {
622 default fn me_l(self, error_payload: impl Into<String>)
623 -> Result<OkVariant, ErrorLink_<String>> {
624 self.map_err(|e| {
625 let next_link = Box::new(ErrorLink_(e.0.to_string(), e.1));
626 ErrorLink_(
627 error_payload.into(),
628 NextLink::Some(next_link)
629 )
630 })
631 }
632
633 default fn me_al(self) -> Result<OkVariant, ErrorLink_<String>> {
634 self.map_err(|e| ErrorLink_(e.0.to_string(), e.1))
635 }
636}
637
638#[cfg(feature = "nightly")]
639impl<OkVariant> ResultExt<OkVariant, String>
640for Result<OkVariant, ErrorLink_<String>> {
641 fn me_l(self, error_payload: impl Into<String>)
642 -> Result<OkVariant, ErrorLink_<String>> {
643 self.map_err(|e| ErrorLink_(
644 error_payload.into(),
645 NextLink::Some(Box::new(e))
646 ))
647 }
648
649 fn me_al(self) -> Result<OkVariant, ErrorLink_<String>> {
650 self
651 }
652}
653
654#[derive(Debug)]
655pub enum NextLink {
656 None(Backtrace),
657 Some(Box<ErrorLink_<String>>)
658}
659
660#[derive(Debug)]
661pub struct ErrorLink_<Payload: Display>(pub Payload, pub NextLink);
662
663impl<Payload: Display> ErrorLink_<Payload> {
664 pub fn new(error_payload: impl Into<Payload>) -> Self {
665 Self(error_payload.into(), NextLink::None(Backtrace::capture()))
666 }
667
668 pub fn replace<NewPayload: Display>(
669 self, error_payload: impl Into<NewPayload>
670 ) -> ErrorLink_<NewPayload>{
671 ErrorLink_(error_payload.into(), self.1)
672 }
673
674 pub fn link<ToPayload: Display>(self, error_payload: impl Into<ToPayload>) -> ErrorLink_<ToPayload> {
675 Self::link_fn(error_payload)(self)
676 }
677
678 pub fn link_fn<ToPayload: Display>(error_payload: impl Into<ToPayload>) -> impl FnOnce(Self) -> ErrorLink_<ToPayload> {
679 move |underlying_error| {
680 let next_link = Box::new(ErrorLink_(underlying_error.0.to_string(), underlying_error.1));
681 ErrorLink_(error_payload.into(), NextLink::Some(next_link))
682 }
683 }
684
685 pub fn as_link<ToPayload: From<Payload> + Display>(self) -> ErrorLink_<ToPayload> {
686 ErrorLink_(self.0.into(), self.1)
687 }
688}
689
690impl ErrorLink_<String> {
691 pub fn new_string(error_message: impl Into<String>) -> Self {
692 Self(error_message.into(), NextLink::None(Backtrace::capture()))
693 }
694}
695
696pub trait ErrorLinkable<Self_, Payload: Display>: Any + Display {
697 fn link(self, error_payload: impl Into<Payload>) -> ErrorLink_<Payload>;
698 fn link_fn(error_payload: impl Into<Payload>) -> impl FnOnce(Self_) -> ErrorLink_<Payload>;
699 fn as_link(self) -> ErrorLink_<Payload>;
700}
701
702impl<T: Any + Display> ErrorLinkable<T, String> for T {
703 fn link(self, error_message: impl Into<String>) -> ErrorLink_<String> {
704 Self::link_fn(error_message)(self)
705 }
706
707 fn link_fn(error_message: impl Into<String>) -> impl FnOnce(Self) -> ErrorLink_<String> {
708 move |underlying_error| {
709 let next_link = Box::new(ErrorLink_(
710 String::from(underlying_error.to_string()),
711 NextLink::None(Backtrace::capture())
712 ));
713 ErrorLink_(error_message.into(), NextLink::Some(next_link))
714 }
715 }
716
717 fn as_link(self) -> ErrorLink_<String> {
718 ErrorLink_(
719 self.to_string(),
720 NextLink::None(Backtrace::capture())
721 )
722 }
723}
724
725impl<Payload: Display> Display for ErrorLink_<Payload> {
726 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
727 write!(f, "An error occurred.\n")?;
728 write!(f, "Link no. 0: {}\n", self.0)?;
729 let mut next_link = &self.1;
730 for error_number in 1.. {
731 next_link = match next_link {
732 NextLink::None(end_backtrace) => {
733 write!(
734 f, "Approximate backtrace of link no. {}:\n{end_backtrace}",
735 error_number - 1
736 )?;
737 break;
738 },
739 NextLink::Some(error_link) => {
740 write!(f, "Link no. {error_number}: {}\n", error_link.0)?;
741 &error_link.1
742 },
743 }
744 }
745
746 Ok(())
747 }
748}
749
750impl<Payload: Display + Debug> std::error::Error for ErrorLink_<Payload> {
751 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
752 match &self.1 {
753 NextLink::None(_) => None,
754 NextLink::Some(next_link) => Some(next_link)
755 }
756 }
757}