reifydb_core/error/diagnostic/
internal.rs1use std::env;
5
6use reifydb_type::{error::Diagnostic, fragment::Fragment};
7
8pub fn internal_with_context(
11 reason: impl Into<String>,
12 file: &str,
13 line: u32,
14 column: u32,
15 function: &str,
16 module_path: &str,
17) -> Diagnostic {
18 let reason = reason.into();
19
20 let error_id = format!("ERR-{}:{}", file.rsplit('/').next().unwrap_or(file).replace(".rs", ""), line);
22
23 let detailed_message = format!("Internal error [{}]: {}", error_id, reason);
24
25 let location_info =
26 format!("Location: {}:{}:{}\nFunction: {}\nModule: {}", file, line, column, function, module_path);
27
28 let help_message = format!(
29 "This is an internal error that should never occur in normal operation.\n\n\
30 Please file a bug report at: https://github.com/reifydb/reifydb/issues\n\n\
31 Include the following information:\n\
32 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n\
33 Error ID: {}\n\
34 {}\n\
35 Version: {}\n\
36 Build: {} ({})\n\
37 Platform: {} {}\n\
38 ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━",
39 error_id,
40 location_info,
41 env!("CARGO_PKG_VERSION"),
42 option_env!("GIT_HASH").unwrap_or("unknown"),
43 option_env!("BUILD_DATE").unwrap_or("unknown"),
44 env::consts::OS,
45 env::consts::ARCH
46 );
47
48 Diagnostic {
49 code: "INTERNAL_ERROR".to_string(),
50 statement: None,
51 message: detailed_message,
52 column: None,
53 fragment: Fragment::None,
54 label: Some(format!("Internal invariant violated at {}:{}:{}", file, line, column)),
55 help: Some(help_message),
56 notes: vec![
57 format!("Error occurred in function: {}", function),
58 "This error indicates a critical internal inconsistency.".to_string(),
59 "Your database may be in an inconsistent state.".to_string(),
60 "Consider creating a backup before continuing operations.".to_string(),
61 format!("Error tracking ID: {}", error_id),
62 ],
63 cause: None,
64 operator_chain: None,
65 }
66}
67
68pub fn internal(reason: impl Into<String>) -> Diagnostic {
70 internal_with_context(reason, "unknown", 0, 0, "unknown", "unknown")
71}
72
73pub fn shutdown(component: impl Into<String>) -> Diagnostic {
76 let component = component.into();
77
78 Diagnostic {
79 code: "SHUTDOWN".to_string(),
80 statement: None,
81 message: format!("{} is shutting down", component),
82 column: None,
83 fragment: Fragment::None,
84 label: Some(format!("{} is no longer accepting requests", component)),
85 help: Some(format!(
86 "This operation failed because {} is shutting down.\n\
87 This is expected during database shutdown.",
88 component
89 )),
90 notes: vec![
91 "This is not an error - the system is shutting down gracefully".to_string(),
92 "Operations submitted during shutdown will be rejected".to_string(),
93 ],
94 cause: None,
95 operator_chain: None,
96 }
97}
98
99#[macro_export]
101macro_rules! internal {
102 ($reason:expr) => {
103 $crate::error::diagnostic::internal::internal_with_context(
104 $reason,
105 file!(),
106 line!(),
107 column!(),
108 {
109 fn f() {}
110 fn type_name_of<T>(_: T) -> &'static str {
111 std::any::type_name::<T>()
112 }
113 let name = type_name_of(f);
114 &name[..name.len() - 3]
115 },
116 module_path!()
117 )
118 };
119 ($fmt:expr, $($arg:tt)*) => {
120 $crate::error::diagnostic::internal::internal_with_context(
121 format!($fmt, $($arg)*),
122 file!(),
123 line!(),
124 column!(),
125 {
126 fn f() {}
127 fn type_name_of<T>(_: T) -> &'static str {
128 std::any::type_name::<T>()
129 }
130 let name = type_name_of(f);
131 &name[..name.len() - 3]
132 },
133 module_path!()
134 )
135 };
136}
137
138#[macro_export]
141macro_rules! internal_error {
142 ($reason:expr) => {
143 reifydb_type::error::Error(Box::new($crate::internal!($reason)))
144 };
145 ($fmt:expr, $($arg:tt)*) => {
146 reifydb_type::error::Error(Box::new($crate::internal!($fmt, $($arg)*)))
147 };
148}
149
150#[macro_export]
153macro_rules! internal_err {
154 ($reason:expr) => {
155 Err($crate::internal_error!($reason))
156 };
157 ($fmt:expr, $($arg:tt)*) => {
158 Err($crate::internal_error!($fmt, $($arg)*))
159 };
160}
161
162#[macro_export]
165macro_rules! return_internal_error {
166 ($reason:expr) => {
167 return $crate::internal_err!($reason)
168 };
169 ($fmt:expr, $($arg:tt)*) => {
170 return $crate::internal_err!($fmt, $($arg)*)
171 };
172}
173
174#[cfg(test)]
175pub mod tests {
176 use std::{thread::sleep, time::Duration};
177
178 use super::*;
179 use crate::error::Error;
180
181 #[derive(Debug)]
182 #[allow(dead_code)]
183 struct TestStruct {
184 value: i32,
185 name: String,
186 }
187
188 #[test]
189 fn test_internal_error_literal_string() {
190 let diagnostic = internal!("simple error message");
191
192 assert_eq!(diagnostic.code, "INTERNAL_ERROR");
193 assert!(diagnostic.message.contains("simple error message"));
194 assert!(diagnostic.help.is_some());
195 assert!(diagnostic.help.as_ref().unwrap().contains("bug report"));
196 assert!(diagnostic.notes.len() > 0);
197 }
198
199 #[test]
200 fn test_internal_error_with_format() {
201 let value = 42;
202 let name = "test";
203 let diagnostic = internal!("Error with value: {} and name: {}", value, name);
204
205 assert_eq!(diagnostic.code, "INTERNAL_ERROR");
206 assert!(diagnostic.message.contains("Error with value: 42 and name: test"));
207 assert!(diagnostic.label.is_some());
208 assert!(diagnostic.label.as_ref().unwrap().contains("Internal invariant violated"));
209 }
210
211 #[test]
212 fn test_internal_error_inline_variable_syntax() {
213 let test_struct = TestStruct {
214 value: 42,
215 name: "test".to_string(),
216 };
217
218 let diagnostic = internal!("Test struct: {:?}", test_struct);
219
220 assert_eq!(diagnostic.code, "INTERNAL_ERROR");
221 assert!(diagnostic.message.contains("TestStruct"));
222 assert!(diagnostic.message.contains("value: 42"));
223 assert!(diagnostic.message.contains("name: \"test\""));
224 }
225
226 #[test]
227 fn test_internal_err_literal_string() {
228 let result: Result<(), Error> = internal_err!("test error");
229
230 assert!(result.is_err());
231 let error = result.unwrap_err();
232 assert_eq!(error.0.code, "INTERNAL_ERROR");
233 assert!(error.0.message.contains("test error"));
234 }
235
236 #[test]
237 fn test_internal_err_with_format() {
238 let code = "ERR_123";
239 let line = 456;
240 let result: Result<(), Error> = internal_err!("Error code: {} at line {}", code, line);
241
242 assert!(result.is_err());
243 let error = result.unwrap_err();
244 assert_eq!(error.0.code, "INTERNAL_ERROR");
245 assert!(error.0.message.contains("Error code: ERR_123 at line 456"));
246 }
247
248 #[test]
249 fn test_internal_function() {
250 let diagnostic = internal("basic internal error");
251
252 assert_eq!(diagnostic.code, "INTERNAL_ERROR");
253 assert!(diagnostic.message.contains("basic internal error"));
254 assert!(diagnostic.label.is_some());
255 assert!(diagnostic.label.as_ref().unwrap().contains("unknown:0:0"));
256 }
257
258 #[test]
259 fn test_internal_with_context_function() {
260 let diagnostic =
261 internal_with_context("context error", "test.rs", 100, 20, "test_function", "test::module");
262
263 assert_eq!(diagnostic.code, "INTERNAL_ERROR");
264 assert!(diagnostic.message.contains("context error"));
265 assert!(diagnostic.label.is_some());
266 assert!(diagnostic.label.as_ref().unwrap().contains("test.rs:100:20"));
267 assert!(diagnostic.notes.iter().any(|n| n.contains("test_function")));
268 assert!(diagnostic.help.is_some());
269 let help = diagnostic.help.as_ref().unwrap();
270 assert!(help.contains("test.rs:100:20"));
271 assert!(help.contains("test::module"));
272 }
273
274 #[test]
275 fn test_return_internal_error_in_function() {
276 fn test_function_literal() -> Result<(), Error> {
277 return_internal_error!("function error");
278 }
279
280 let result = test_function_literal();
281 assert!(result.is_err());
282 let error = result.unwrap_err();
283 assert_eq!(error.0.code, "INTERNAL_ERROR");
284 assert!(error.0.message.contains("function error"));
285 }
286
287 #[test]
288 fn test_return_internal_error_with_format() {
289 fn test_function_format(val: u32) -> Result<(), Error> {
290 return_internal_error!("Invalid value: {:#04x}", val);
291 }
292
293 let result = test_function_format(255);
294 assert!(result.is_err());
295 let error = result.unwrap_err();
296 assert_eq!(error.0.code, "INTERNAL_ERROR");
297 assert!(error.0.message.contains("Invalid value: 0xff"));
298 }
299
300 #[test]
301 fn test_error_id_generation() {
302 let diagnostic1 = internal_with_context("error 1", "file1.rs", 10, 5, "func1", "mod1");
303
304 sleep(Duration::from_millis(2));
306
307 let diagnostic2 = internal_with_context("error 2", "file2.rs", 20, 10, "func2", "mod2");
308
309 let id1 = diagnostic1.message.split('[').nth(1).unwrap().split(']').nth(0).unwrap();
311 let id2 = diagnostic2.message.split('[').nth(1).unwrap().split(']').nth(0).unwrap();
312
313 assert_ne!(id1, id2);
315 assert!(id1.starts_with("ERR-"));
316 assert!(id2.starts_with("ERR-"));
317 }
318}