Skip to main content

reifydb_type/error/
macro.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright (c) 2025 ReifyDB
3
4/// Macro to create an Error from a diagnostic function call
5///
6/// Usage:
7/// - `error!(diagnostic_function(args))` - Creates an error without fragment
8/// - `error!(diagnostic_function(args), fragment)` - Creates an error with fragment
9///
10/// Expands to: `Error(diagnostic_function(args))` or
11/// `Error(diagnostic_function(args).with_fragment(fragment))`
12#[macro_export]
13macro_rules! error {
14	($diagnostic:expr) => {
15		$crate::error::Error(Box::new($diagnostic))
16	};
17	($diagnostic:expr, $fragment:expr) => {{
18		let mut diag = $diagnostic;
19		diag.with_fragment($fragment.into());
20		$crate::error::Error(Box::new(diag))
21	}};
22}
23
24#[macro_export]
25macro_rules! return_error {
26	($diagnostic:expr) => {
27		return Err($crate::error::Error(Box::new($diagnostic)))
28	};
29	($diagnostic:expr, $fragment:expr) => {{
30		let mut diag = $diagnostic;
31		diag.with_fragment($fragment.into());
32		return Err($crate::error::Error(Box::new(diag)));
33	}};
34}
35
36#[macro_export]
37macro_rules! err {
38	($diagnostic:expr) => {
39		Err($crate::error::Error(Box::new($diagnostic)))
40	};
41	($diagnostic:expr, $fragment:expr) => {{
42		let mut diag = $diagnostic;
43		diag.with_fragment($fragment.into());
44		Err($crate::error::Error(Box::new(diag)))
45	}};
46}
47
48#[cfg(test)]
49pub mod tests {
50	use std::sync::Arc;
51
52	use crate::{
53		error::{Error, IntoDiagnostic, TypeError},
54		fragment::{Fragment, StatementColumn, StatementLine},
55	};
56
57	#[test]
58	fn test_error_macro() {
59		// Test that error! macro creates correct Error type
60		let err = error!(TypeError::NanNotAllowed.into_diagnostic());
61
62		// Verify it creates the correct Error type
63		assert!(matches!(err, Error(_)));
64
65		// Test that the diagnostic is properly wrapped
66		let diagnostic = err.diagnostic();
67		assert!(diagnostic.message.contains("NaN"));
68	}
69
70	#[test]
71	fn test_return_error_macro() {
72		fn test_fn() -> Result<(), Error> {
73			return_error!(TypeError::NanNotAllowed.into_diagnostic());
74		}
75
76		let result = test_fn();
77		assert!(result.is_err());
78
79		if let Err(err) = result {
80			let diagnostic = err.diagnostic();
81			assert!(diagnostic.message.contains("NaN"));
82		}
83	}
84
85	#[test]
86	fn test_err_macro() {
87		// Test that err! macro creates correct Result type with Err
88		let result: Result<(), Error> = err!(TypeError::NanNotAllowed.into_diagnostic());
89
90		assert!(result.is_err());
91
92		if let Err(err) = result {
93			let diagnostic = err.diagnostic();
94			assert!(diagnostic.message.contains("NaN"));
95		}
96	}
97
98	#[test]
99	fn test_error_macro_with_fragment() {
100		// Create a test fragment
101		let fragment = Fragment::Statement {
102			line: StatementLine(42),
103			column: StatementColumn(10),
104			text: Arc::from("test fragment"),
105		};
106
107		// Test that error! macro with fragment creates correct Error
108		// type
109		let err = error!(TypeError::NanNotAllowed.into_diagnostic(), fragment.clone());
110
111		// Verify it creates the correct Error type
112		assert!(matches!(err, Error(_)));
113
114		// Test that the diagnostic has the origin set (via fragment)
115		let diagnostic = err.diagnostic();
116		let fragment = diagnostic.fragment();
117		assert!(fragment.is_some());
118		if let Some(Fragment::Statement {
119			line,
120			column,
121			..
122		}) = fragment.as_ref()
123		{
124			assert_eq!(line.0, 42);
125			assert_eq!(column.0, 10);
126		}
127	}
128
129	#[test]
130	fn test_return_error_macro_with_fragment() {
131		fn test_fn() -> Result<(), Error> {
132			let fragment = Fragment::Statement {
133				line: StatementLine(100),
134				column: StatementColumn(25),
135				text: Arc::from("error location"),
136			};
137			return_error!(TypeError::NanNotAllowed.into_diagnostic(), fragment);
138		}
139
140		let result = test_fn();
141		assert!(result.is_err());
142
143		if let Err(err) = result {
144			let diagnostic = err.diagnostic();
145			let fragment = diagnostic.fragment();
146			assert!(fragment.is_some());
147			if let Some(Fragment::Statement {
148				line,
149				column,
150				..
151			}) = fragment.as_ref()
152			{
153				assert_eq!(line.0, 100);
154				assert_eq!(column.0, 25);
155			}
156		}
157	}
158
159	#[test]
160	fn test_err_macro_with_fragment() {
161		let fragment = Fragment::Statement {
162			line: StatementLine(200),
163			column: StatementColumn(50),
164			text: Arc::from("err fragment test"),
165		};
166
167		// Test that err! macro with fragment creates correct Result
168		// type with Err
169		let result: Result<(), Error> = err!(TypeError::NanNotAllowed.into_diagnostic(), fragment);
170
171		assert!(result.is_err());
172
173		if let Err(err) = result {
174			let diagnostic = err.diagnostic();
175			let fragment = diagnostic.fragment();
176			assert!(fragment.is_some());
177			if let Some(Fragment::Statement {
178				line,
179				column,
180				..
181			}) = fragment.as_ref()
182			{
183				assert_eq!(line.0, 200);
184				assert_eq!(column.0, 50);
185			}
186		}
187	}
188
189	#[test]
190	fn test_macros_with_closure_fragment() {
191		// Test with closure that returns Fragment (implements
192		// Into<Fragment>)
193		let get_fragment = || Fragment::Statement {
194			line: StatementLine(300),
195			column: StatementColumn(75),
196			text: Arc::from("closure fragment"),
197		};
198
199		let err = error!(TypeError::NanNotAllowed.into_diagnostic(), get_fragment());
200		let diagnostic = err.diagnostic();
201		let fragment = diagnostic.fragment();
202		assert!(fragment.is_some());
203		assert_eq!(fragment.as_ref().unwrap().line().0, 300);
204	}
205}