nbindgen/bindgen/
utilities.rs1#![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 fn has_attr_word(&self, name: &str) -> bool;
65
66 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 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
153pub 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}