surrealdb_core/mac/
mod.rs

1/// Throws an unreachable error with location details
2macro_rules! fail {
3	($($arg:tt)+) => {
4		$crate::err::Error::Unreachable(format!("{}:{}: {}", file!(), line!(), std::format_args!($($arg)+)))
5	};
6}
7
8/// Converts some text into a new line byte string
9macro_rules! bytes {
10	($expression:expr) => {
11		format!("{}\n", $expression).into_bytes()
12	};
13}
14
15/// Pauses and yields execution to the tokio runtime
16macro_rules! yield_now {
17	() => {
18		if tokio::runtime::Handle::try_current().is_ok() {
19			tokio::task::yield_now().await;
20		}
21	};
22}
23
24/// Creates a new b-tree map of key-value pairs
25macro_rules! map {
26    ($($k:expr $(, if let $grant:pat = $check:expr)? $(, if $guard:expr)? => $v:expr),* $(,)? $( => $x:expr )?) => {{
27        let mut m = ::std::collections::BTreeMap::new();
28    	$(m.extend($x.iter().map(|(k, v)| (k.clone(), v.clone())));)?
29		$( $(if let $grant = $check)? $(if $guard)? { m.insert($k, $v); };)+
30        m
31    }};
32}
33
34/// Extends a b-tree map of key-value pairs
35macro_rules! mrg {
36	($($m:expr, $x:expr)+) => {{
37		$($m.extend($x.iter().map(|(k, v)| (k.clone(), v.clone())));)+
38		$($m)+
39	}};
40}
41
42/// Matches on a specific config environment
43macro_rules! get_cfg {
44	($i:ident : $($s:expr),+) => (
45		let $i = || { $( if cfg!($i=$s) { return $s; } );+ "unknown"};
46	)
47}
48
49/// Runs a method on a transaction, ensuring that the transaction
50/// is cancelled and rolled back if the initial function fails.
51/// This can be used to ensure that the use of the `?` operator to
52/// fail fast and return an error from a function does not leave
53/// a transaction in an uncommitted state without rolling back.
54macro_rules! catch {
55	($txn:ident, $default:expr) => {
56		match $default {
57			Err(e) => {
58				let _ = $txn.cancel().await;
59				return Err(e);
60			}
61			Ok(v) => v,
62		}
63	};
64}
65
66/// Runs a method on a transaction, ensuring that the transaction
67/// is cancelled and rolled back if the initial function fails, or
68/// committed successfully if the initial function succeeds. This
69/// can be used to ensure that the use of the `?` operator to fail
70/// fast and return an error from a function does not leave a
71/// transaction in an uncommitted state without rolling back.
72macro_rules! run {
73	($txn:ident, $default:expr) => {
74		match $default {
75			Err(e) => {
76				let _ = $txn.cancel().await;
77				Err(e)
78			}
79			Ok(v) => match $txn.commit().await {
80				Err(e) => {
81					let _ = $txn.cancel().await;
82					Err(e)
83				}
84				Ok(_) => Ok(v),
85			},
86		}
87	};
88}
89
90/// A macro that allows lazily parsing a value from the environment variable,
91/// with a fallback default value if the variable is not set or parsing fails.
92///
93/// # Parameters
94///
95/// - `$key`: An expression representing the name of the environment variable.
96/// - `$t`: The type of the value to be parsed.
97/// - `$default`: The default value to fall back to if the environment variable
98///   is not set or parsing fails.
99///
100/// # Return Value
101///
102/// A lazy static variable of type `std::sync::LazyLock`, which holds the parsed value
103/// from the environment variable or the default value.
104#[macro_export]
105macro_rules! lazy_env_parse {
106	($key:expr, $t:ty) => {
107		std::sync::LazyLock::new(|| {
108			std::env::var($key)
109				.and_then(|s| Ok(s.parse::<$t>().unwrap_or_default()))
110				.unwrap_or_default()
111		})
112	};
113	($key:expr, $t:ty, $default:expr) => {
114		std::sync::LazyLock::new(|| {
115			std::env::var($key)
116				.and_then(|s| Ok(s.parse::<$t>().unwrap_or($default)))
117				.unwrap_or($default)
118		})
119	};
120}
121
122/// Lazily parses an environment variable into a specified type. If the environment variable is not set or the parsing fails,
123/// it returns a default value.
124///
125/// # Parameters
126///
127/// - `$key`: A string literal representing the name of the environment variable.
128/// - `$t`: The type to parse the environment variable into.
129/// - `$default`: A fallback function or constant value to be returned if the environment variable is not set or the parsing fails.
130///
131/// # Returns
132///
133/// A `Lazy` static variable that stores the parsed value or the default value.
134#[macro_export]
135macro_rules! lazy_env_parse_or_else {
136	($key:expr, $t:ty, $default:expr) => {
137		std::sync::LazyLock::new(|| {
138			std::env::var($key)
139				.and_then(|s| Ok(s.parse::<$t>().unwrap_or_else($default)))
140				.unwrap_or_else($default)
141		})
142	};
143}
144
145#[cfg(test)]
146macro_rules! async_defer{
147	(let $bind:ident = ($capture:expr) defer { $($d:tt)* } after { $($t:tt)* }) => {
148		async {
149			async_defer!(@captured);
150			async_defer!(@catch_unwind);
151
152			#[allow(unused_mut)]
153			let mut v = Some($capture);
154			#[allow(unused_mut)]
155			let mut $bind = Captured(&mut v);
156			let res = CatchUnwindFuture(async { $($t)* }).await;
157			#[allow(unused_variables,unused_mut)]
158			if let Some(mut $bind) = v.take(){
159				async { $($d)* }.await;
160			}
161			match res{
162				Ok(x) => x,
163				Err(e) => ::std::panic::resume_unwind(e)
164			}
165
166		}
167	};
168
169	(defer { $($d:tt)* } after { $($t:tt)* }) => {
170		async {
171			async_defer!(@catch_unwind);
172
173			let res = CatchUnwindFuture(async { $($t)* }).await;
174			#[allow(unused_variables)]
175			async { $($d)* }.await;
176			match res{
177				Ok(x) => x,
178				Err(e) => ::std::panic::resume_unwind(e)
179			}
180
181		}
182	};
183
184	(@captured) => {
185		// unwraps are save cause the value can only be taken by consuming captured.
186		pub struct Captured<'a,T>(&'a mut Option<T>);
187		impl<T> ::std::ops::Deref for Captured<'_,T>{
188			type Target = T;
189
190			fn deref(&self) -> &T{
191				self.0.as_ref().unwrap()
192			}
193		}
194		impl<T> ::std::ops::DerefMut for Captured<'_,T>{
195			fn deref_mut(&mut self) -> &mut T{
196				self.0.as_mut().unwrap()
197			}
198		}
199		impl<T> Captured<'_,T>{
200			#[allow(dead_code)]
201			pub fn take(self) -> T{
202				self.0.take().unwrap()
203			}
204		}
205	};
206
207	(@catch_unwind) => {
208		struct CatchUnwindFuture<F>(F);
209		impl<F,R> ::std::future::Future for CatchUnwindFuture<F>
210			where F: ::std::future::Future<Output = R>,
211		{
212			type Output = ::std::thread::Result<R>;
213
214			fn poll(self: ::std::pin::Pin<&mut Self>, cx: &mut ::std::task::Context) -> ::std::task::Poll<Self::Output>{
215				let pin = unsafe{ self.map_unchecked_mut(|x| &mut x.0) };
216				match ::std::panic::catch_unwind(::std::panic::AssertUnwindSafe(||{
217					pin.poll(cx)
218				})) {
219					Ok(x) => x.map(Ok),
220					Err(e) => ::std::task::Poll::Ready(Err(e))
221				}
222			}
223		}
224	};
225}
226
227/// Works like a match statement, but matches &str insensitive
228macro_rules! match_insensitive {
229    ($s:expr, { $($p:literal => $e:expr,)* _ => $fe:expr $(,)? }) => {{
230        let s = $s.to_lowercase();
231        $(if s == $p.to_lowercase() { $e }
232        else)* { $fe }
233    }};
234}
235
236#[cfg(test)]
237mod test {
238	use crate::err::Error;
239
240	#[tokio::test]
241	async fn async_defer_basic() {
242		let mut counter = 0;
243
244		async_defer!(defer {
245			assert_eq!(counter,1);
246		} after {
247			assert_eq!(counter,0);
248			counter += 1;
249		})
250		.await;
251
252		async_defer!(let t = (()) defer {
253			panic!("shouldn't be called");
254		} after {
255			assert_eq!(counter,1);
256			counter += 1;
257			t.take();
258		})
259		.await;
260	}
261
262	#[tokio::test]
263	#[should_panic(expected = "this is should be the message of the panic")]
264	async fn async_defer_panic() {
265		let mut counter = 0;
266
267		async_defer!(defer {
268			// This should still execute
269			assert_eq!(counter,1);
270			panic!("this is should be the message of the panic")
271		} after {
272			assert_eq!(counter,0);
273			counter += 1;
274			panic!("this panic should be caught")
275		})
276		.await;
277	}
278
279	#[test]
280	fn fail_literal() {
281		let Error::Unreachable(msg) = fail!("Reached unreachable code") else {
282			panic!()
283		};
284		assert_eq!("crates/core/src/mac/mod.rs:281: Reached unreachable code", msg);
285	}
286
287	#[test]
288	fn fail_arguments() {
289		let Error::Unreachable(msg) = fail!("Found {} but expected {}", "test", "other") else {
290			panic!()
291		};
292		assert_eq!("crates/core/src/mac/mod.rs:289: Found test but expected other", msg);
293	}
294}