trace.rs 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155
  1. //
  2. // imag - the personal information management suite for the commandline
  3. // Copyright (C) 2015, 2016 Matthias Beyer <mail@beyermatthias.de> and contributors
  4. //
  5. // This library is free software; you can redistribute it and/or
  6. // modify it under the terms of the GNU Lesser General Public
  7. // License as published by the Free Software Foundation; version
  8. // 2.1 of the License.
  9. //
  10. // This library is distributed in the hope that it will be useful,
  11. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. // Lesser General Public License for more details.
  14. //
  15. // You should have received a copy of the GNU Lesser General Public
  16. // License along with this library; if not, write to the Free Software
  17. // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  18. //
  19. use std::error::Error;
  20. use std::io::Write;
  21. use std::io::stderr;
  22. use ansi_term::Colour::Red;
  23. /// Print an Error type and its cause recursively
  24. ///
  25. /// The error is printed with "Error NNNN :" as prefix, where "NNNN" is a number which increases
  26. /// which each recursion into the errors cause. The error description is used to visualize what
  27. /// failed and if there is a cause "-- caused by:" is appended, and the cause is printed on the next
  28. /// line.
  29. ///
  30. /// Example output:
  31. ///
  32. /// ```ignore
  33. /// Error 1 : Some error -- caused by:
  34. /// Error 2 : Some other error -- caused by:
  35. /// Error 3 : Yet another Error -- caused by:
  36. /// ...
  37. ///
  38. /// Error <NNNN> : <Error description>
  39. /// ```
  40. pub fn trace_error(e: &Error) {
  41. print_trace_maxdepth(count_error_causes(e), e, ::std::u64::MAX);
  42. write!(stderr(), "\n").ok();
  43. }
  44. /// Convenience function: calls `trace_error()` with `e` and afterwards `std::process::exit()`
  45. /// with `code`
  46. pub fn trace_error_exit(e: &Error, code: i32) -> ! {
  47. use std::process::exit;
  48. debug!("Tracing error...");
  49. trace_error(e);
  50. debug!("Calling exit()");
  51. exit(code);
  52. }
  53. /// Print an Error type and its cause recursively, but only `max` levels
  54. ///
  55. /// Output is the same as for `trace_error()`, though there are only `max` levels printed.
  56. pub fn trace_error_maxdepth(e: &Error, max: u64) {
  57. let n = count_error_causes(e);
  58. let msg = Red.blink().paint(format!("{}/{} Levels of errors will be printed\n",
  59. (if max > n { n } else { max }), n));
  60. write!(stderr(), "{}", msg).ok();
  61. print_trace_maxdepth(n, e, max);
  62. write!(stderr(), "").ok();
  63. }
  64. /// Print an Error type and its cause recursively with the debug!() macro
  65. ///
  66. /// Output is the same as for `trace_error()`.
  67. pub fn trace_error_dbg(e: &Error) {
  68. print_trace_dbg(0, e);
  69. }
  70. /// Helper function for `trace_error()` and `trace_error_maxdepth()`.
  71. ///
  72. /// Returns the cause of the last processed error in the recursion, so `None` if all errors where
  73. /// processed.
  74. fn print_trace_maxdepth(idx: u64, e: &Error, max: u64) -> Option<&Error> {
  75. if e.cause().is_some() && idx > 0 {
  76. e.cause().map(|cause| {
  77. match print_trace_maxdepth(idx - 1, cause, max) {
  78. None => write!(stderr(), "\n").ok(),
  79. Some(_) => write!(stderr(), " -- caused:\n").ok(),
  80. };
  81. });
  82. } else {
  83. write!(stderr(), "\n").ok();
  84. }
  85. write!(stderr(), "{}: {}", Red.paint(format!("ERROR[{:>4}]", idx)), e.description()).ok();
  86. e.cause()
  87. }
  88. /// Count errors in `Error::cause()` recursively
  89. fn count_error_causes(e: &Error) -> u64 {
  90. 1 + e.cause().map(|c| count_error_causes(c)).unwrap_or(0)
  91. }
  92. fn print_trace_dbg(idx: u64, e: &Error) {
  93. debug!("{}: {}", Red.blink().paint(format!("ERROR[{:>4}]", idx)), e.description());
  94. if e.cause().is_some() {
  95. e.cause().map(|c| print_trace_dbg(idx + 1, c));
  96. }
  97. }
  98. /// Helper functions for `Result<T, E>` types to reduce overhead in the following situations:
  99. ///
  100. /// ```ignore
  101. /// function().map_err(|e| { trace_error(&e); e })
  102. /// ```
  103. ///
  104. /// and variants
  105. pub trait MapErrTrace {
  106. fn map_err_trace(self) -> Self;
  107. fn map_err_dbg_trace(self) -> Self;
  108. fn map_err_trace_exit(self, code: i32) -> Self;
  109. fn map_err_trace_maxdepth(self, max: u64) -> Self;
  110. }
  111. impl<U, E: Error> MapErrTrace for Result<U, E> {
  112. /// Simply call `trace_error()` on the Err (if there is one) and return the error.
  113. ///
  114. /// This does nothing besides the side effect of printing the error trace
  115. fn map_err_trace(self) -> Self {
  116. self.map_err(|e| { trace_error(&e); e })
  117. }
  118. /// Simply call `trace_error_dbg()` on the Err (if there is one) and return the error.
  119. ///
  120. /// This does nothing besides the side effect of printing the error trace
  121. fn map_err_dbg_trace(self) -> Self {
  122. self.map_err(|e| { trace_error_dbg(&e); e })
  123. }
  124. /// Simply call `trace_error_exit(code)` on the Err (if there is one).
  125. ///
  126. /// This does not return if there is an Err(e).
  127. fn map_err_trace_exit(self, code: i32) -> Self {
  128. self.map_err(|e| { trace_error_exit(&e, code) })
  129. }
  130. /// Simply call `trace_error_maxdepth(max)` on the Err (if there is one) and return the error.
  131. ///
  132. /// This does nothing besides the side effect of printing the error trace to a certain depth
  133. fn map_err_trace_maxdepth(self, max: u64) -> Self {
  134. self.map_err(|e| { trace_error_maxdepth(&e, max); e })
  135. }
  136. }