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
5use std::fmt;
6use std::io::Write;
7
8use crate::bindgen::cargo::cargo_metadata::Dependency;
9use crate::bindgen::config::{Config, Language};
10use crate::bindgen::writer::SourceWriter;
11
12#[derive(PartialEq, Eq)]
13enum DefineKey<'a> {
14 Boolean(&'a str),
15 Named(&'a str, &'a str),
16}
17
18impl<'a> DefineKey<'a> {
19 fn load(key: &str) -> DefineKey {
20 // TODO: dirty parser
21 if !key.contains('=') {
22 return DefineKey::Boolean(key);
23 }
24
25 let mut splits = key.trim().split('=');
26
27 let name = match splits.next() {
28 Some(n) => n.trim(),
29 None => return DefineKey::Boolean(key),
30 };
31
32 let value = match splits.next() {
33 Some(v) => v.trim(),
34 None => return DefineKey::Boolean(key),
35 };
36
37 if splits.next().is_some() {
38 return DefineKey::Boolean(key);
39 }
40
41 DefineKey::Named(name, value)
42 }
43}
44
45#[derive(Debug, Clone)]
46pub enum Cfg {
47 Boolean(String),
48 Named(String, String),
49 Any(Vec<Cfg>),
50 All(Vec<Cfg>),
51 Not(Box<Cfg>),
52}
53
54impl fmt::Display for Cfg {
55 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
56 match self {
57 Cfg::Boolean(key) => write!(f, "{}", key),
58 Cfg::Named(key, value) => write!(f, "{} = {:?}", key, value),
59 Cfg::Any(cfgs) => {
60 write!(f, "any(")?;
61 for (index, cfg) in cfgs.iter().enumerate() {
62 if index > 0 {
63 write!(f, ", ")?;
64 }
65 write!(f, "{}", cfg)?;
66 }
67 write!(f, ")")
68 }
69 Cfg::All(cfgs) => {
70 write!(f, "all(")?;
71 for (index, cfg) in cfgs.iter().enumerate() {
72 if index > 0 {
73 write!(f, ", ")?;
74 }
75 write!(f, "{}", cfg)?;
76 }
77 write!(f, ")")
78 }
79 Cfg::Not(cfg) => write!(f, "not({})", cfg),
80 }
81 }
82}
83
84impl Cfg {
85 pub fn join(cfgs: &[Cfg]) -> Option<Cfg> {
86 if cfgs.is_empty() {
87 None
88 } else {
89 Some(Cfg::All(cfgs.to_owned()))
90 }
91 }
92
93 pub fn append(parent: Option<&Cfg>, child: Option<Cfg>) -> Option<Cfg> {
94 match (parent, child) {
95 (None, None) => None,
96 (None, Some(child)) => Some(child),
97 (Some(parent), None) => Some(parent.clone()),
98 (Some(parent), Some(child)) => Some(Cfg::All(vec![parent.clone(), child])),
99 }
100 }
101
102 pub fn load(attrs: &[syn::Attribute]) -> Option<Cfg> {
103 let mut configs = Vec::new();
104
105 for attr in attrs {
106 if let Ok(syn::Meta::List(syn::MetaList { path, nested, .. })) = attr.parse_meta() {
107 if !path.is_ident("cfg") || nested.len() != 1 {
108 continue;
109 }
110
111 if let Some(config) = Cfg::load_single(nested.first().unwrap()) {
112 configs.push(config);
113 }
114 }
115 }
116
117 match configs.len() {
118 0 => None,
119 1 => Some(configs.pop().unwrap()),
120 _ => Some(Cfg::All(configs)),
121 }
122 }
123
124 pub fn load_metadata(dependency: &Dependency) -> Option<Cfg> {
125 let target = dependency.target.as_ref()?;
126 match syn::parse_str::<syn::Meta>(target) {
127 Ok(target) => {
128 // Parsing succeeded using the #[cfg] syntax
129 if let syn::Meta::List(syn::MetaList { path, nested, .. }) = target {
130 if !path.is_ident("cfg") || nested.len() != 1 {
131 return None;
132 }
133 Cfg::load_single(nested.first().unwrap())
134 } else {
135 None
136 }
137 }
138 Err(_) => {
139 // Parsing failed using #[cfg], this may be a literal target
140 // name
141 Cfg::load_single(&syn::NestedMeta::Lit(syn::Lit::Str(syn::LitStr::new(
142 target,
143 proc_macro2::Span::call_site(),
144 ))))
145 }
146 }
147 }
148
149 fn load_single(item: &syn::NestedMeta) -> Option<Cfg> {
150 Some(match *item {
151 syn::NestedMeta::Meta(syn::Meta::Path(ref path)) => {
152 Cfg::Boolean(format!("{}", path.segments.first().unwrap().ident))
153 }
154 syn::NestedMeta::Meta(syn::Meta::NameValue(syn::MetaNameValue {
155 ref path,
156 lit: syn::Lit::Str(ref value),
157 ..
158 })) => Cfg::Named(
159 format!("{}", path.segments.first().unwrap().ident),
160 value.value(),
161 ),
162 syn::NestedMeta::Meta(syn::Meta::List(syn::MetaList {
163 ref path,
164 ref nested,
165 ..
166 })) => {
167 if path.is_ident("any") {
168 Cfg::Any(Cfg::load_list(nested.iter())?)
169 } else if path.is_ident("all") {
170 Cfg::All(Cfg::load_list(nested.iter())?)
171 } else if path.is_ident("not") {
172 if nested.len() != 1 {
173 return None;
174 }
175
176 Cfg::Not(Box::new(Cfg::load_single(&nested[0])?))
177 } else {
178 return None;
179 }
180 }
181 _ => return None,
182 })
183 }
184
185 fn load_list<'a, I: Iterator<Item = &'a syn::NestedMeta>>(attrs: I) -> Option<Vec<Cfg>> {
186 let mut configs = Vec::new();
187
188 for attr in attrs {
189 configs.push(Cfg::load_single(attr)?);
190 }
191
192 if configs.is_empty() {
193 None
194 } else {
195 Some(configs)
196 }
197 }
198}
199
200pub trait ToCondition: Sized {
201 fn to_condition(&self, config: &Config) -> Option<Condition>;
202}
203
204impl ToCondition for Option<Cfg> {
205 fn to_condition(&self, config: &Config) -> Option<Condition> {
206 self.as_ref()?.to_condition(config)
207 }
208}
209
210impl ToCondition for Cfg {
211 fn to_condition(&self, config: &Config) -> Option<Condition> {
212 match *self {
213 Cfg::Boolean(ref cfg_name) => {
214 let define = config
215 .defines
216 .iter()
217 .find(|(key, ..)| DefineKey::Boolean(cfg_name) == DefineKey::load(key));
218 if let Some((_, define)) = define {
219 Some(Condition::Define(define.to_owned()))
220 } else {
221 warn!(
222 "Missing `[defines]` entry for `{}` in cbindgen config.",
223 self,
224 );
225 None
226 }
227 }
228 Cfg::Named(ref cfg_name, ref cfg_value) => {
229 let define = config.defines.iter().find(|(key, ..)| {
230 DefineKey::Named(cfg_name, cfg_value) == DefineKey::load(key)
231 });
232 if let Some((_, define)) = define {
233 Some(Condition::Define(define.to_owned()))
234 } else {
235 warn!(
236 "Missing `[defines]` entry for `{}` in cbindgen config.",
237 self,
238 );
239 None
240 }
241 }
242 Cfg::Any(ref children) => {
243 let conditions: Vec<_> = children
244 .iter()
245 .filter_map(|x| x.to_condition(config))
246 .collect();
247 match conditions.len() {
248 0 => None,
249 1 => conditions.into_iter().next(),
250 _ => Some(Condition::Any(conditions)),
251 }
252 }
253 Cfg::All(ref children) => {
254 let cfgs: Vec<_> = children
255 .iter()
256 .filter_map(|x| x.to_condition(config))
257 .collect();
258 match cfgs.len() {
259 0 => None,
260 1 => cfgs.into_iter().next(),
261 _ => Some(Condition::All(cfgs)),
262 }
263 }
264 Cfg::Not(ref child) => child
265 .to_condition(config)
266 .map(|cfg| Condition::Not(Box::new(cfg))),
267 }
268 }
269}
270
271#[derive(Debug, Clone)]
272pub enum Condition {
273 Define(String),
274 Any(Vec<Condition>),
275 All(Vec<Condition>),
276 Not(Box<Condition>),
277}
278
279impl Condition {
280 fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
281 match *self {
282 Condition::Define(ref define) => {
283 if config.language == Language::Cython {
284 write!(out, "{}", define);
285 } else {
286 out.write("defined(");
287 write!(out, "{}", define);
288 out.write(")");
289 }
290 }
291 Condition::Any(ref conditions) => {
292 out.write("(");
293 for (i, condition) in conditions.iter().enumerate() {
294 if i != 0 {
295 out.write(if config.language == Language::Cython {
296 " or "
297 } else {
298 " || "
299 });
300 }
301 condition.write(config, out);
302 }
303 out.write(")");
304 }
305 Condition::All(ref conditions) => {
306 out.write("(");
307 for (i, condition) in conditions.iter().enumerate() {
308 if i != 0 {
309 out.write(if config.language == Language::Cython {
310 " and "
311 } else {
312 " && "
313 });
314 }
315 condition.write(config, out);
316 }
317 out.write(")");
318 }
319 Condition::Not(ref condition) => {
320 out.write(if config.language == Language::Cython {
321 "not "
322 } else {
323 "!"
324 });
325 condition.write(config, out);
326 }
327 }
328 }
329}
330
331pub trait ConditionWrite {
332 fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
333 fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>);
334}
335
336impl ConditionWrite for Option<Condition> {
337 fn write_before<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
338 if let Some(ref cfg) = *self {
339 if config.language == Language::Cython {
340 out.write("IF ");
341 cfg.write(config, out);
342 out.open_brace();
343 } else {
344 out.push_set_spaces(0);
345 out.write("#if ");
346 cfg.write(config, out);
347 out.pop_set_spaces();
348 out.new_line();
349 }
350 }
351 }
352
353 fn write_after<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
354 if self.is_some() {
355 if config.language == Language::Cython {
356 out.close_brace(false);
357 } else {
358 out.new_line();
359 out.push_set_spaces(0);
360 out.write("#endif");
361 out.pop_set_spaces();
362 }
363 }
364 }
365}
366