Skip to main content

nbindgen/bindgen/
utilities.rs

1/* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5#![allow(clippy::redundant_closure_call)]
6
7use syn;
8
9pub trait IterHelpers: Iterator {
10    fn try_skip_map<F, T, E>(&mut self, f: F) -> Result<Vec<T>, E>
11    where
12        F: FnMut(&Self::Item) -> Result<Option<T>, E>;
13}
14
15impl<I> IterHelpers for I
16where
17    I: Iterator,
18{
19    fn try_skip_map<F, T, E>(&mut self, mut f: F) -> Result<Vec<T>, E>
20    where
21        F: FnMut(&Self::Item) -> Result<Option<T>, E>,
22    {
23        let mut out = Vec::new();
24        while let Some(item) = self.next() {
25            if let Some(x) = f(&item)? {
26                out.push(x);
27            }
28        }
29        Ok(out)
30    }
31}
32
33pub fn find_first_some<T>(slice: &[Option<T>]) -> Option<&T> {
34    for x in slice {
35        if let Some(ref x) = *x {
36            return Some(x);
37        }
38    }
39    None
40}
41
42pub trait SynItemFnHelpers: SynItemHelpers {
43    fn exported_name(&self) -> Option<String>;
44}
45
46impl SynItemFnHelpers for syn::ItemFn {
47    fn exported_name(&self) -> Option<String> {
48        self.attrs
49            .attr_name_value_lookup("export_name")
50            .or_else(|| {
51                if self.is_no_mangle() {
52                    Some(self.sig.ident.to_string())
53                } else {
54                    None
55                }
56            })
57    }
58}
59
60pub trait SynItemHelpers {
61    /// Searches for attributes like `#[test]`.
62    /// Example:
63    /// - `item.has_attr_word("test")` => `#[test]`
64    fn has_attr_word(&self, name: &str) -> bool;
65
66    /// Searches for attributes like `#[cfg(test)]`.
67    /// Example:
68    /// - `item.has_attr_list("cfg", &["test"])` => `#[cfg(test)]`
69    fn has_attr_list(&self, name: &str, args: &[&str]) -> bool;
70
71    fn is_no_mangle(&self) -> bool {
72        self.has_attr_word("no_mangle")
73    }
74
75    /// Searches for attributes `#[test]` and/or `#[cfg(test)]`.
76    fn has_test_attr(&self) -> bool {
77        self.has_attr_list("cfg", &["test"]) || self.has_attr_word("test")
78    }
79}
80
81macro_rules! syn_item_match_helper {
82    ($s:ident => has_attrs: |$i:ident| $a:block, otherwise: || $b:block) => {
83        match *$s {
84            syn::Item::Const(ref item) => (|$i: &syn::ItemConst| $a)(item),
85            syn::Item::Enum(ref item) => (|$i: &syn::ItemEnum| $a)(item),
86            syn::Item::ExternCrate(ref item) => (|$i: &syn::ItemExternCrate| $a)(item),
87            syn::Item::Fn(ref item) => (|$i: &syn::ItemFn| $a)(item),
88            syn::Item::ForeignMod(ref item) => (|$i: &syn::ItemForeignMod| $a)(item),
89            syn::Item::Impl(ref item) => (|$i: &syn::ItemImpl| $a)(item),
90            syn::Item::Macro(ref item) => (|$i: &syn::ItemMacro| $a)(item),
91            syn::Item::Macro2(ref item) => (|$i: &syn::ItemMacro2| $a)(item),
92            syn::Item::Mod(ref item) => (|$i: &syn::ItemMod| $a)(item),
93            syn::Item::Static(ref item) => (|$i: &syn::ItemStatic| $a)(item),
94            syn::Item::Struct(ref item) => (|$i: &syn::ItemStruct| $a)(item),
95            syn::Item::Trait(ref item) => (|$i: &syn::ItemTrait| $a)(item),
96            syn::Item::Type(ref item) => (|$i: &syn::ItemType| $a)(item),
97            syn::Item::Union(ref item) => (|$i: &syn::ItemUnion| $a)(item),
98            syn::Item::Use(ref item) => (|$i: &syn::ItemUse| $a)(item),
99            syn::Item::TraitAlias(ref item) => (|$i: &syn::ItemTraitAlias| $a)(item),
100            syn::Item::Verbatim(_) => (|| $b)(),
101            _ => panic!("Unhandled syn::Item:  {:?}", $s),
102        }
103    };
104}
105
106impl SynItemHelpers for syn::Item {
107    fn has_attr_word(&self, name: &str) -> bool {
108        syn_item_match_helper!(self =>
109            has_attrs: |item| { item.has_attr_word(name) },
110            otherwise: || { false }
111        )
112    }
113
114    fn has_attr_list(&self, name: &str, args: &[&str]) -> bool {
115        syn_item_match_helper!(self =>
116            has_attrs: |item| { item.has_attr_list(name, args) },
117            otherwise: || { false }
118        )
119    }
120}
121
122macro_rules! impl_syn_item_helper {
123    ($t:ty) => {
124        impl SynItemHelpers for $t {
125            fn has_attr_word(&self, name: &str) -> bool {
126                self.attrs.has_attr_word(name)
127            }
128
129            fn has_attr_list(&self, name: &str, args: &[&str]) -> bool {
130                self.attrs.has_attr_list(name, args)
131            }
132        }
133    };
134}
135
136impl_syn_item_helper!(syn::ItemExternCrate);
137impl_syn_item_helper!(syn::ItemUse);
138impl_syn_item_helper!(syn::ItemStatic);
139impl_syn_item_helper!(syn::ItemConst);
140impl_syn_item_helper!(syn::ItemFn);
141impl_syn_item_helper!(syn::ItemMod);
142impl_syn_item_helper!(syn::ItemForeignMod);
143impl_syn_item_helper!(syn::ItemType);
144impl_syn_item_helper!(syn::ItemStruct);
145impl_syn_item_helper!(syn::ItemEnum);
146impl_syn_item_helper!(syn::ItemUnion);
147impl_syn_item_helper!(syn::ItemTrait);
148impl_syn_item_helper!(syn::ItemImpl);
149impl_syn_item_helper!(syn::ItemMacro);
150impl_syn_item_helper!(syn::ItemMacro2);
151impl_syn_item_helper!(syn::ItemTraitAlias);
152
153/// Helper function for accessing Abi information
154pub trait SynAbiHelpers {
155    fn is_c(&self) -> bool;
156    fn is_omitted(&self) -> bool;
157}
158
159impl SynAbiHelpers for Option<syn::Abi> {
160    fn is_c(&self) -> bool {
161        if let Some(ref abi) = *self {
162            if let Some(ref lit_string) = abi.name {
163                return lit_string.value() == String::from("C");
164            }
165        }
166        false
167    }
168    fn is_omitted(&self) -> bool {
169        if let Some(ref abi) = *self {
170            abi.name.is_none()
171        } else {
172            false
173        }
174    }
175}
176
177impl SynAbiHelpers for syn::Abi {
178    fn is_c(&self) -> bool {
179        if let Some(ref lit_string) = self.name {
180            lit_string.value() == String::from("C")
181        } else {
182            false
183        }
184    }
185    fn is_omitted(&self) -> bool {
186        self.name.is_none()
187    }
188}
189
190pub trait SynAttributeHelpers {
191    fn get_comment_lines(&self) -> Vec<String>;
192    fn has_attr_word(&self, name: &str) -> bool;
193    fn has_attr_list(&self, name: &str, args: &[&str]) -> bool;
194    fn attr_name_value_lookup(&self, name: &str) -> Option<String>;
195}
196
197impl SynAttributeHelpers for [syn::Attribute] {
198    fn has_attr_word(&self, name: &str) -> bool {
199        self.iter().filter_map(|x| x.parse_meta().ok()).any(|attr| {
200            if let syn::Meta::Path(ref path) = attr {
201                path.is_ident(name)
202            } else {
203                false
204            }
205        })
206    }
207
208    fn has_attr_list(&self, name: &str, args: &[&str]) -> bool {
209        self.iter().filter_map(|x| x.parse_meta().ok()).any(|attr| {
210            if let syn::Meta::List(syn::MetaList { path, nested, .. }) = attr {
211                if !path.is_ident(name) {
212                    return false;
213                }
214                args.iter().all(|arg| {
215                    nested.iter().any(|nested_meta| {
216                        if let syn::NestedMeta::Meta(syn::Meta::Path(path)) = nested_meta {
217                            path.is_ident(arg)
218                        } else {
219                            false
220                        }
221                    })
222                })
223            } else {
224                false
225            }
226        })
227    }
228
229    fn attr_name_value_lookup(&self, name: &str) -> Option<String> {
230        self.iter()
231            .filter_map(|attr| {
232                let attr = attr.parse_meta().ok()?;
233                if let syn::Meta::NameValue(syn::MetaNameValue {
234                    path,
235                    lit: syn::Lit::Str(lit),
236                    ..
237                }) = attr
238                {
239                    if path.is_ident(name) {
240                        return Some(lit.value());
241                    }
242                }
243                None
244            })
245            .next()
246    }
247
248    fn get_comment_lines(&self) -> Vec<String> {
249        let mut comment = Vec::new();
250
251        for attr in self {
252            if attr.style == syn::AttrStyle::Outer {
253                if let Ok(syn::Meta::NameValue(syn::MetaNameValue {
254                    path,
255                    lit: syn::Lit::Str(content),
256                    ..
257                })) = attr.parse_meta()
258                {
259                    if path.is_ident("doc") {
260                        let text = content.value().trim_end().to_owned();
261                        comment.push(text);
262                    }
263                }
264            }
265        }
266
267        comment
268    }
269}