/ errors / src / common / backtraced.rs
backtraced.rs
  1  // Copyright (C) 2019-2025 Alpha-Delta Network Inc.
  2  // This file is part of the ADL library.
  3  
  4  // The ADL library is free software: you can redistribute it and/or modify
  5  // it under the terms of the GNU General Public License as published by
  6  // the Free Software Foundation, either version 3 of the License, or
  7  // (at your option) any later version.
  8  
  9  // The ADL library is distributed in the hope that it will be useful,
 10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
 11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 12  // GNU General Public License for more details.
 13  
 14  // You should have received a copy of the GNU General Public License
 15  // along with the ADL library. If not, see <https://www.gnu.org/licenses/>.
 16  
 17  use std::fmt;
 18  
 19  use adl_span::source_map::is_color;
 20  use backtrace::Backtrace;
 21  use color_backtrace::{BacktracePrinter, Verbosity};
 22  use colored::Colorize;
 23  use derivative::Derivative;
 24  
 25  /// The indent for an error message.
 26  pub(crate) const INDENT: &str = "    ";
 27  
 28  /// Backtraced compiler output type
 29  ///     undefined value `x`
 30  ///     --> file.adl: 2:8
 31  ///      = help: Initialize a variable `x` first.
 32  #[derive(Derivative)]
 33  #[derivative(Clone, Debug, Default, Hash, PartialEq, Eq)]
 34  pub struct Backtraced {
 35      /// The error message.
 36      pub message: String,
 37      /// The error help message if it exists.
 38      pub help: Option<String>,
 39      /// The error exit code.
 40      pub code: i32,
 41      /// The error leading digits identifier.
 42      pub code_identifier: i8,
 43      /// The characters representing the type of error.
 44      pub type_: String,
 45      /// Is this Backtrace a warning or error?
 46      pub error: bool,
 47      #[derivative(PartialEq = "ignore")]
 48      #[derivative(Hash = "ignore")]
 49      /// The backtrace representing where the error occurred in Adl.
 50      pub backtrace: Backtrace,
 51  }
 52  
 53  impl Backtraced {
 54      /// Creates a backtraced error from a backtrace.
 55      pub fn new_from_backtrace<S>(
 56          message: S,
 57          help: Option<String>,
 58          code: i32,
 59          code_identifier: i8,
 60          type_: String,
 61          error: bool,
 62          backtrace: Backtrace,
 63      ) -> Self
 64      where
 65          S: ToString,
 66      {
 67          Self { message: message.to_string(), help, code, code_identifier, type_, error, backtrace }
 68      }
 69  
 70      /// Gets the backtraced error exit code.
 71      pub fn exit_code(&self) -> i32 {
 72          let mut code: i32;
 73          if self.code_identifier > 99 {
 74              code = self.code_identifier as i32 * 100_000;
 75          } else if self.code_identifier as i32 > 9 {
 76              code = self.code_identifier as i32 * 10_000;
 77          } else {
 78              code = self.code_identifier as i32 * 1_000;
 79          }
 80          code += self.code;
 81  
 82          code
 83      }
 84  
 85      /// Gets a unique error identifier.
 86      pub fn error_code(&self) -> String {
 87          format!(
 88              "E{error_type}{code_identifier:0>3}{exit_code:0>4}",
 89              error_type = self.type_,
 90              code_identifier = self.code_identifier,
 91              exit_code = self.code,
 92          )
 93      }
 94  
 95      /// Gets a unique warning identifier.
 96      pub fn warning_code(&self) -> String {
 97          format!(
 98              "W{error_type}{code_identifier:0>3}{exit_code:0>4}",
 99              error_type = self.type_,
100              code_identifier = self.code_identifier,
101              exit_code = self.code,
102          )
103      }
104  }
105  
106  impl fmt::Display for Backtraced {
107      fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
108          let (kind, code) = if self.error { ("Error", self.error_code()) } else { ("Warning", self.warning_code()) };
109          let message = format!("{kind} [{code}]: {message}", message = self.message,);
110  
111          // To avoid the color enabling characters for comparison with test expectations.
112          if is_color() {
113              if self.error {
114                  writeln!(f, "{}", message.bold().red())?;
115              } else {
116                  writeln!(f, "{}", message.bold().yellow())?;
117              }
118          } else {
119              writeln!(f, "{message}")?;
120          };
121  
122          if let Some(help) = &self.help {
123              write!(
124                  f,
125                  "{INDENT     } |\n\
126              {INDENT     } = {help}",
127              )?;
128          }
129  
130          let adl_backtrace = std::env::var("ADL_BACKTRACE").unwrap_or_default().trim().to_owned();
131          match adl_backtrace.as_ref() {
132              "1" => {
133                  let mut printer = BacktracePrinter::default();
134                  printer = printer.lib_verbosity(Verbosity::Medium);
135                  let trace = printer.format_trace_to_string(&self.backtrace).map_err(|_| fmt::Error)?;
136                  write!(f, "{trace}")?;
137              }
138              "full" => {
139                  let mut printer = BacktracePrinter::default();
140                  printer = printer.lib_verbosity(Verbosity::Full);
141                  let trace = printer.format_trace_to_string(&self.backtrace).map_err(|_| fmt::Error)?;
142                  write!(f, "{trace}")?;
143              }
144              _ => {}
145          }
146  
147          Ok(())
148      }
149  }
150  
151  impl std::error::Error for Backtraced {
152      fn description(&self) -> &str {
153          &self.message
154      }
155  }