leo_errors/common/
macros.rs

1// Copyright (C) 2019-2025 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo 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 Leo 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 Leo 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]
21macro_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, LeoMessageCode};
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 LeoError Codes.
47        impl LeoMessageCode 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: leo_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}