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 }