Skip to main content

reifydb_value/error/
macro.rs

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