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 }