1use proc_macro2::TokenStream;
2use quote::{ToTokens, TokenStreamExt, quote};
3use std::{
4 borrow::Cow,
5 cmp::min,
6 collections::BTreeMap,
7 ffi::{CStr, CString},
8 fmt::Write,
9 ptr,
10};
11use syn::Path;
12
13#[derive(Clone)]
14pub enum EitherIterator<A, B>
16where
17 A: Iterator,
18 B: Iterator<Item = A::Item>,
19{
20 Left(A),
21 Right(B),
22}
23impl<A, B> Iterator for EitherIterator<A, B>
24where
25 A: Iterator,
26 B: Iterator<Item = A::Item>,
27{
28 type Item = A::Item;
29 fn next(&mut self) -> Option<Self::Item> {
30 match self {
31 EitherIterator::Left(a) => a.next(),
32 EitherIterator::Right(b) => b.next(),
33 }
34 }
35}
36
37pub fn quote_btree_map<K: ToTokens, V: ToTokens>(value: &BTreeMap<K, V>) -> TokenStream {
39 let mut tokens = TokenStream::new();
40 for (k, v) in value {
41 let ks = k.to_token_stream();
42 let vs = v.to_token_stream();
43 tokens.append_all(quote! {
44 (#ks, #vs),
45 });
46 }
47 quote! {
48 ::std::collections::BTreeMap::from([
49 #tokens
50 ])
51 }
52}
53
54pub fn quote_cow<T: ToOwned + ToTokens + ?Sized>(value: &Cow<T>) -> TokenStream
56where
57 <T as ToOwned>::Owned: ToTokens,
58{
59 match value {
60 Cow::Borrowed(v) => quote! { ::std::borrow::Cow::Borrowed(#v) },
61 Cow::Owned(v) => quote! { ::std::borrow::Cow::Borrowed(#v) },
62 }
63}
64
65pub fn quote_option<T: ToTokens>(value: &Option<T>) -> TokenStream {
67 match value {
68 None => quote! { None },
69 Some(v) => quote! { Some(#v) },
70 }
71}
72
73pub fn matches_path(path: &Path, expect: &[&str]) -> bool {
75 let len = min(path.segments.len(), expect.len());
76 path.segments
77 .iter()
78 .rev()
79 .take(len)
80 .map(|v| &v.ident)
81 .eq(expect.iter().rev().take(len))
82}
83
84pub fn separated_by<T, F>(
86 out: &mut String,
87 values: impl IntoIterator<Item = T>,
88 mut f: F,
89 separator: &str,
90) where
91 F: FnMut(&mut String, T),
92{
93 let mut len = out.len();
94 for v in values {
95 if out.len() > len {
96 out.push_str(separator);
97 }
98 len = out.len();
99 f(out, v);
100 }
101}
102
103pub fn as_c_string(str: impl Into<Vec<u8>>) -> CString {
105 CString::new(
106 str.into()
107 .into_iter()
108 .map(|b| if b == 0 { b'?' } else { b })
109 .collect::<Vec<u8>>(),
110 )
111 .unwrap_or_default()
112}
113
114pub fn error_message_from_ptr<'a>(ptr: &'a *const i8) -> Cow<'a, str> {
115 unsafe {
116 if *ptr != ptr::null() {
117 CStr::from_ptr(*ptr).to_string_lossy()
118 } else {
119 Cow::Borrowed("Unknown error: could not extract the error message")
120 }
121 }
122}
123
124pub fn consume_while<'s>(input: &mut &'s str, predicate: impl FnMut(&char) -> bool) -> &'s str {
126 let len = input.chars().take_while(predicate).count();
127 if len == 0 {
128 return "";
129 }
130 let result = &input[..len];
131 *input = &input[len..];
132 result
133}
134
135pub fn extract_number<'s, const SIGNED: bool>(input: &mut &'s str) -> &'s str {
136 let mut end = 0;
137 let mut chars = input.chars().peekable();
138 if SIGNED && matches!(chars.peek(), Some('+') | Some('-')) {
139 chars.next();
140 end += 1;
141 }
142 for _ in chars.take_while(char::is_ascii_digit) {
143 end += 1;
144 }
145 let result = &input[..end];
146 *input = &input[end..];
147 result
148}
149
150pub fn print_timer(out: &mut String, quote: &str, h: i64, m: u8, s: u8, ns: u32) {
151 let mut subsecond = ns;
152 let mut width = 9;
153 while width > 1 && subsecond % 10 == 0 {
154 subsecond /= 10;
155 width -= 1;
156 }
157 let _ = write!(
158 out,
159 "{quote}{h:02}:{m:02}:{s:02}.{subsecond:0width$}{quote}",
160 );
161}
162
163#[macro_export]
164macro_rules! number_to_month {
165 ($month:expr, $throw:expr $(,)?) => {
166 match $month {
167 1 => Month::January,
168 2 => Month::February,
169 3 => Month::March,
170 4 => Month::April,
171 5 => Month::May,
172 6 => Month::June,
173 7 => Month::July,
174 8 => Month::August,
175 9 => Month::September,
176 10 => Month::October,
177 11 => Month::November,
178 12 => Month::December,
179 _ => $throw,
180 }
181 };
182}
183
184#[macro_export]
185macro_rules! month_to_number {
186 ($month:expr $(,)?) => {
187 match $month {
188 Month::January => 1,
189 Month::February => 2,
190 Month::March => 3,
191 Month::April => 4,
192 Month::May => 5,
193 Month::June => 6,
194 Month::July => 7,
195 Month::August => 8,
196 Month::September => 9,
197 Month::October => 10,
198 Month::November => 11,
199 Month::December => 12,
200 }
201 };
202}
203
204#[macro_export]
205macro_rules! possibly_parenthesized {
207 ($out:ident, $cond:expr, $v:expr) => {
208 if $cond {
209 $out.push('(');
210 $v;
211 $out.push(')');
212 } else {
213 $v;
214 }
215 };
216}
217
218#[macro_export]
219macro_rules! truncate_long {
235 ($query:expr) => {
236 format_args!(
237 "{}{}",
238 &$query[..::std::cmp::min($query.len(), 497)].trim(),
239 if $query.len() > 497 { "...\n" } else { "" },
240 )
241 };
242}
243
244#[macro_export]
256macro_rules! send_value {
257 ($tx:ident, $value:expr) => {{
258 if let Err(e) = $tx.send($value) {
259 log::error!("{e:#}");
260 }
261 }};
262}
263
264#[doc(hidden)]
270#[macro_export]
271macro_rules! take_until {
272 ($original:expr, $($parser:expr),+ $(,)?) => {{
273 let macro_local_input = $original.fork();
274 let mut macro_local_result = (
275 TokenStream::new(),
276 ($({
277 let _ = $parser;
278 None
279 }),+),
280 );
281 loop {
282 if macro_local_input.is_empty() {
283 break;
284 }
285 let mut parsed = false;
286 let produced = ($({
287 let attempt = macro_local_input.fork();
288 if let Ok(content) = ($parser)(&attempt) {
289 macro_local_input.advance_to(&attempt);
290 parsed = true;
291 Some(content)
292 } else {
293 None
294 }
295 }),+);
296 if parsed {
297 macro_local_result.1 = produced;
298 break;
299 }
300 macro_local_result.0.append(macro_local_input.parse::<TokenTree>()?);
301 }
302 $original.advance_to(¯o_local_input);
303 macro_local_result
304 }};
305}
306
307#[macro_export]
308macro_rules! impl_executor_transaction {
334 ($driver:ty, $transaction:ident $(< $lt:lifetime >)?, $connection:ident) => {
336 impl $(<$lt>)? ::tank_core::Executor for $transaction $(<$lt>)? {
337 type Driver = $driver;
338
339 fn accepts_multiple_statements(&self) -> bool {
340 self.$connection.accepts_multiple_statements()
341 }
342
343 fn prepare(
344 &mut self,
345 query: String,
346 ) -> impl Future<Output = ::tank_core::Result<::tank_core::Query<Self::Driver>>> + Send
347 {
348 self.$connection.prepare(query)
349 }
350
351 fn run<'s>(
352 &'s mut self,
353 query: impl ::tank_core::AsQuery<Self::Driver> + 's,
354 ) -> impl ::tank_core::stream::Stream<
355 Item = ::tank_core::Result<::tank_core::QueryResult>,
356 > + Send {
357 self.$connection.run(query)
358 }
359
360 fn fetch<'s>(
361 &'s mut self,
362 query: impl ::tank_core::AsQuery<Self::Driver> + 's,
363 ) -> impl ::tank_core::stream::Stream<
364 Item = ::tank_core::Result<::tank_core::RowLabeled>,
365 > + Send
366 + 's {
367 self.$connection.fetch(query)
368 }
369
370 fn execute<'s>(
371 &'s mut self,
372 query: impl ::tank_core::AsQuery<Self::Driver> + 's,
373 ) -> impl Future<Output = ::tank_core::Result<::tank_core::RowsAffected>> + Send {
374 self.$connection.execute(query)
375 }
376
377 fn append<'a, E, It>(
378 &mut self,
379 entities: It,
380 ) -> impl Future<Output = ::tank_core::Result<::tank_core::RowsAffected>> + Send
381 where
382 E: ::tank_core::Entity + 'a,
383 It: IntoIterator<Item = &'a E> + Send,
384 <It as IntoIterator>::IntoIter: Send,
385 {
386 self.$connection.append(entities)
387 }
388 }
389 }
390}