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<S: Into<Vec<u8>>>(str: S) -> CString {
98 CString::new(str.into()).expect("Expected a valid C string")
99}
100
101pub fn consume_while<'s>(input: &mut &'s str, predicate: impl FnMut(&char) -> bool) -> &'s str {
103 let len = input.chars().take_while(predicate).count();
104 if len == 0 {
105 return "";
106 }
107 let result = &input[..len];
108 *input = &input[len..];
109 result
110}
111
112pub fn extract_number<'s, const SIGNED: bool>(input: &mut &'s str) -> &'s str {
113 let mut end = 0;
114 let mut chars = input.chars().peekable();
115 if SIGNED && matches!(chars.peek(), Some('+') | Some('-')) {
116 chars.next();
117 end += 1;
118 }
119 for _ in chars.take_while(char::is_ascii_digit) {
120 end += 1;
121 }
122 let result = &input[..end];
123 *input = &input[end..];
124 result
125}
126
127pub fn print_timer(out: &mut String, quote: &str, h: i64, m: u8, s: u8, ns: u32) {
128 let mut subsecond = ns;
129 let mut width = 9;
130 while width > 1 && subsecond % 10 == 0 {
131 subsecond /= 10;
132 width -= 1;
133 }
134 let _ = write!(
135 out,
136 "{quote}{h:02}:{m:02}:{s:02}.{subsecond:0width$}{quote}",
137 );
138}
139
140#[macro_export]
141macro_rules! number_to_month {
142 ($month:expr, $throw:expr $(,)?) => {
143 match $month {
144 1 => Month::January,
145 2 => Month::February,
146 3 => Month::March,
147 4 => Month::April,
148 5 => Month::May,
149 6 => Month::June,
150 7 => Month::July,
151 8 => Month::August,
152 9 => Month::September,
153 10 => Month::October,
154 11 => Month::November,
155 12 => Month::December,
156 _ => $throw,
157 }
158 };
159}
160
161#[macro_export]
162macro_rules! month_to_number {
163 ($month:expr $(,)?) => {
164 match $month {
165 Month::January => 1,
166 Month::February => 2,
167 Month::March => 3,
168 Month::April => 4,
169 Month::May => 5,
170 Month::June => 6,
171 Month::July => 7,
172 Month::August => 8,
173 Month::September => 9,
174 Month::October => 10,
175 Month::November => 11,
176 Month::December => 12,
177 }
178 };
179}
180
181#[macro_export]
182macro_rules! possibly_parenthesized {
184 ($out:ident, $cond:expr, $v:expr) => {
185 if $cond {
186 $out.push('(');
187 $v;
188 $out.push(')');
189 } else {
190 $v;
191 }
192 };
193}
194
195#[macro_export]
196macro_rules! truncate_long {
212 ($query:expr) => {
213 format_args!(
214 "{}{}",
215 &$query[..::std::cmp::min($query.len(), 497)].trim(),
216 if $query.len() > 497 { "...\n" } else { "" },
217 )
218 };
219}
220
221#[macro_export]
233macro_rules! send_value {
234 ($tx:ident, $value:expr) => {{
235 if let Err(e) = $tx.send($value) {
236 log::error!("{e:#}");
237 }
238 }};
239}
240
241#[doc(hidden)]
247#[macro_export]
248macro_rules! take_until {
249 ($original:expr, $($parser:expr),+ $(,)?) => {{
250 let macro_local_input = $original.fork();
251 let mut macro_local_result = (
252 TokenStream::new(),
253 ($({
254 let _ = $parser;
255 None
256 }),+),
257 );
258 loop {
259 if macro_local_input.is_empty() {
260 break;
261 }
262 let mut parsed = false;
263 let produced = ($({
264 let attempt = macro_local_input.fork();
265 if let Ok(content) = ($parser)(&attempt) {
266 macro_local_input.advance_to(&attempt);
267 parsed = true;
268 Some(content)
269 } else {
270 None
271 }
272 }),+);
273 if parsed {
274 macro_local_result.1 = produced;
275 break;
276 }
277 macro_local_result.0.append(macro_local_input.parse::<TokenTree>()?);
278 }
279 $original.advance_to(¯o_local_input);
280 macro_local_result
281 }};
282}
283
284#[macro_export]
285macro_rules! impl_executor_transaction {
311 ($driver:ty, $transaction:ident $(< $lt:lifetime >)?, $connection:ident) => {
313 impl $(<$lt>)? ::tank_core::Executor for $transaction $(<$lt>)? {
314 type Driver = $driver;
315
316 fn driver(&self) -> &Self::Driver {
317 self.$connection.driver()
318 }
319
320 fn prepare(
321 &mut self,
322 query: String,
323 ) -> impl Future<Output = ::tank_core::Result<::tank_core::Query<Self::Driver>>> + Send
324 {
325 self.$connection.prepare(query)
326 }
327
328 fn run<'s>(
329 &'s mut self,
330 query: impl ::tank_core::AsQuery<Self::Driver> + 's,
331 ) -> impl ::tank_core::stream::Stream<
332 Item = ::tank_core::Result<::tank_core::QueryResult>,
333 > + Send {
334 self.$connection.run(query)
335 }
336
337 fn fetch<'s>(
338 &'s mut self,
339 query: impl ::tank_core::AsQuery<Self::Driver> + 's,
340 ) -> impl ::tank_core::stream::Stream<
341 Item = ::tank_core::Result<::tank_core::RowLabeled>,
342 > + Send
343 + 's {
344 self.$connection.fetch(query)
345 }
346
347 fn execute<'s>(
348 &'s mut self,
349 query: impl ::tank_core::AsQuery<Self::Driver> + 's,
350 ) -> impl Future<Output = ::tank_core::Result<::tank_core::RowsAffected>> + Send {
351 self.$connection.execute(query)
352 }
353
354 fn append<'a, E, It>(
355 &mut self,
356 entities: It,
357 ) -> impl Future<Output = ::tank_core::Result<::tank_core::RowsAffected>> + Send
358 where
359 E: ::tank_core::Entity + 'a,
360 It: IntoIterator<Item = &'a E> + Send,
361 {
362 self.$connection.append(entities)
363 }
364 }
365 }
366}