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