tank_core/
util.rs

1use proc_macro2::TokenStream;
2use quote::{ToTokens, quote};
3use std::{borrow::Cow, cmp::min, ffi::CString};
4use syn::Path;
5
6#[derive(Clone)]
7pub enum EitherIterator<A, B>
8where
9    A: Iterator,
10    B: Iterator<Item = A::Item>,
11{
12    Left(A),
13    Right(B),
14}
15impl<A, B> Iterator for EitherIterator<A, B>
16where
17    A: Iterator,
18    B: Iterator<Item = A::Item>,
19{
20    type Item = A::Item;
21    fn next(&mut self) -> Option<Self::Item> {
22        match self {
23            EitherIterator::Left(a) => a.next(),
24            EitherIterator::Right(b) => b.next(),
25        }
26    }
27}
28
29pub fn quote_cow<T: ToOwned + ToTokens + ?Sized>(value: &Cow<T>) -> TokenStream
30where
31    <T as ToOwned>::Owned: ToTokens,
32{
33    match value {
34        Cow::Borrowed(v) => quote! { ::std::borrow::Cow::Borrowed(#v) },
35        Cow::Owned(v) => quote! { ::std::borrow::Cow::Borrowed(#v) },
36    }
37}
38
39pub fn quote_option<T: ToTokens>(value: &Option<T>) -> TokenStream {
40    match value {
41        None => quote! { None },
42        Some(v) => quote! { Some(#v) },
43    }
44}
45
46pub fn matches_path(path: &Path, expect: &[&str]) -> bool {
47    let len = min(path.segments.len(), expect.len());
48    path.segments
49        .iter()
50        .rev()
51        .take(len)
52        .map(|v| &v.ident)
53        .eq(expect.iter().rev().take(len))
54}
55
56pub fn separated_by<T, F>(
57    out: &mut String,
58    values: impl IntoIterator<Item = T>,
59    mut f: F,
60    separator: &str,
61) where
62    F: FnMut(&mut String, T),
63{
64    let mut len = out.len();
65    for v in values {
66        if out.len() > len {
67            out.push_str(separator);
68        }
69        len = out.len();
70        f(out, v);
71    }
72}
73
74pub fn as_c_string<S: Into<Vec<u8>>>(str: S) -> CString {
75    CString::new(str.into()).expect("Expected a valid C string")
76}
77
78pub fn consume_while<'s>(input: &mut &'s str, predicate: impl FnMut(&char) -> bool) -> &'s str {
79    let len = input.chars().take_while(predicate).count();
80    if len == 0 {
81        return "";
82    }
83    let result = &input[..len];
84    *input = &input[len..];
85    result
86}
87
88#[macro_export]
89macro_rules! possibly_parenthesized {
90    ($buff:ident, $cond:expr, $v:expr) => {
91        if $cond {
92            $buff.push('(');
93            $v;
94            $buff.push(')');
95        } else {
96            $v;
97        }
98    };
99}
100
101#[macro_export]
102macro_rules! truncate_long {
103    ($query:expr) => {
104        format_args!(
105            "{}{}\n",
106            &$query[..::std::cmp::min($query.len(), 497)].trim_end(),
107            if $query.len() > 497 { "..." } else { "" },
108        )
109    };
110}
111
112/// Sends the value through the channel and logs in case of error.
113#[macro_export]
114macro_rules! send_value {
115    ($tx:ident, $value:expr) => {{
116        if let Err(e) = $tx.send($value) {
117            log::error!("{:#}", e);
118        }
119    }};
120}
121
122/// Accumulates the tokens until a parser matches.
123///
124/// It returns the accumulated `TokenStream` as well as the result of the match (if any).
125#[macro_export]
126macro_rules! take_until {
127    ($original:expr, $($parser:expr),+ $(,)?) => {{
128        let macro_local_input = $original.fork();
129        let mut macro_local_result = (
130            TokenStream::new(),
131            ($({
132                let _ = $parser;
133                None
134            }),+),
135        );
136        loop {
137            if macro_local_input.is_empty() {
138                break;
139            }
140            let mut parsed = false;
141            let produced = ($({
142                let attempt = macro_local_input.fork();
143                if let Ok(content) = ($parser)(&attempt) {
144                    macro_local_input.advance_to(&attempt);
145                    parsed = true;
146                    Some(content)
147                } else {
148                    None
149                }
150            }),+);
151            if parsed {
152                macro_local_result.1 = produced;
153                break;
154            }
155            macro_local_result.0.append(macro_local_input.parse::<TokenTree>()?);
156        }
157        $original.advance_to(&macro_local_input);
158        macro_local_result
159    }};
160}