/ errors / src / common / macros.rs
macros.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  /// A macro that given an enum, exit code mask, error code string prefix,
 18  /// and error methods generated through a DSL creates and generates errors
 19  /// with a unique error code.
 20  #[macro_export]
 21  macro_rules! create_messages {
 22      (@step $code:expr,) => {
 23          #[inline(always)]
 24          // Returns the number of unique exit codes that this error type can take on.
 25          pub fn num_exit_codes() -> i32 {
 26              $code
 27          }
 28      };
 29      ($(#[$error_type_docs:meta])* $type_:ident, code_mask: $code_mask:expr, code_prefix: $code_prefix:expr, $($(#[$docs:meta])* @$formatted_or_backtraced_list:ident $names:ident { args: ($($arg_names:ident: $arg_types:ty$(,)?)*), msg: $messages:expr, help: $helps:expr, })*) => {
 30          #[allow(unused_imports)] // Allow unused for errors that only use formatted or backtraced errors.
 31          use $crate::{Backtraced, Formatted, AdlMessageCode};
 32  
 33          use backtrace::Backtrace;
 34  
 35          // Generates the enum and implements from FormattedError and BacktracedErrors.
 36          #[derive(Clone, Debug, Error)]
 37          $(#[$error_type_docs])*
 38          pub enum $type_ {
 39              #[error(transparent)]
 40              Formatted(#[from] Formatted),
 41  
 42  	        #[error(transparent)]
 43              Backtraced(#[from] Backtraced),
 44          }
 45  
 46          /// Implements the trait for AdlError Codes.
 47          impl AdlMessageCode for $type_ {
 48              #[inline(always)]
 49              fn exit_code(&self) -> i32 {
 50                  match self {
 51                      Self::Formatted(formatted) => formatted.exit_code(),
 52                      Self::Backtraced(backtraced) => backtraced.exit_code()
 53                  }
 54              }
 55  
 56              #[inline(always)]
 57              fn error_code(&self) -> String {
 58                  match self {
 59                      Self::Formatted(formatted) => formatted.error_code(),
 60                      Self::Backtraced(backtraced) => backtraced.error_code()
 61                  }
 62              }
 63  
 64              #[inline(always)]
 65              fn warning_code(&self) -> String {
 66                  match self {
 67                      Self::Formatted(formatted) => formatted.warning_code(),
 68                      Self::Backtraced(backtraced) => backtraced.warning_code()
 69                  }
 70              }
 71  
 72              #[inline(always)]
 73              fn code_mask() -> i32 {
 74                  $code_mask
 75              }
 76  
 77              #[inline(always)]
 78              fn message_type() -> String {
 79                  $code_prefix.to_string()
 80              }
 81  
 82              #[inline(always)]
 83              fn is_error() -> bool {
 84                  stringify!($type_).contains("Error")
 85              }
 86          }
 87  
 88  
 89          // Steps over the list of functions with an initial code of 0.
 90          impl $type_ {
 91              create_messages!(@step 0i32, $(($(#[$docs])* $formatted_or_backtraced_list, $names($($arg_names: $arg_types,)*), $messages, $helps),)*);
 92  
 93              // Return an instance of `Self` (i.e. of error type `$type_`) which has labels.
 94              pub fn with_labels(self, labels: Vec<$crate::Label>) -> Self {
 95                  match self {
 96                      Self::Formatted(f) => Self::Formatted(f.with_labels(labels)),
 97                      Self::Backtraced(_) => {
 98                          // Adding labels to Backtraced errors does nothing at the moment
 99                          self
100                      }
101                  }
102              }
103          }
104      };
105      // Matches the function if it is a formatted message.
106      (@step $code:expr, ($(#[$error_func_docs:meta])* formatted, $name:ident($($arg_names:ident: $arg_types:ty,)*), $message:expr, $help:expr), $(($(#[$docs:meta])* $formatted_or_backtraced_tail:ident, $names:ident($($tail_arg_names:ident: $tail_arg_types:ty,)*), $messages:expr, $helps:expr),)*) => {
107          // Formatted errors always takes a span.
108          $(#[$error_func_docs])*
109          // Expands additional arguments for the error defining function.
110          pub fn $name($($arg_names: $arg_types,)* span: adl_span::Span) -> Self {
111              Self::Formatted(
112                  Formatted::new_from_span(
113                      $message,
114                      $help,
115                      $code + Self::code_mask(),
116                      Self::code_identifier(),
117                      Self::message_type(),
118                      Self::is_error(),
119                      span,
120                      // Each function always generates its own backtrace for backtrace clarity to originate from the error function.
121                      Backtrace::new(),
122                  )
123              )
124          }
125  
126          // Steps the code value by one and calls on the rest of the functions.
127          create_messages!(@step $code + 1i32, $(($(#[$docs])* $formatted_or_backtraced_tail, $names($($tail_arg_names: $tail_arg_types,)*), $messages, $helps),)*);
128      };
129      // matches the function if it is a backtraced message.
130      (@step $code:expr, ($(#[$error_func_docs:meta])* backtraced, $name:ident($($arg_names:ident: $arg_types:ty,)*), $message:expr, $help:expr), $(($(#[$docs:meta])* $formatted_or_backtraced_tail:ident, $names:ident($($tail_arg_names:ident: $tail_arg_types:ty,)*), $messages:expr, $helps:expr),)*) => {
131          $(#[$error_func_docs])*
132          // Expands additional arguments for the error defining function.
133          pub fn $name($($arg_names: $arg_types,)*) -> Self {
134              Self::Backtraced(
135                  Backtraced::new_from_backtrace(
136                      $message,
137                      $help,
138                      $code + Self::code_mask(),
139                      Self::code_identifier(),
140                      Self::message_type(),
141                      Self::is_error(),
142                      // Each function always generates its own backtrace for backtrace clarity to originate from the error function.
143                      Backtrace::new(),
144                  )
145              )
146          }
147  
148          // Steps the code value by one and calls on the rest of the functions.
149          create_messages!(@step $code + 1i32, $(($(#[$docs])* $formatted_or_backtraced_tail, $names($($tail_arg_names: $tail_arg_types,)*), $messages, $helps),)*);
150      };
151  }