1use std::borrow::Cow;
6use std::collections::HashMap;
7use std::io::Write;
8
9use syn::{self, UnOp};
10
11use crate::bindgen::config::Config;
12use crate::bindgen::dependencies::Dependencies;
13use crate::bindgen::ir::{
14 AnnotationSet, Cfg, ConditionWrite, Documentation, GenericParams, Item, ItemContainer, Path,
15 Struct, ToCondition, Type,
16};
17use crate::bindgen::library::Library;
18use crate::bindgen::writer::SourceWriter;
19use crate::bindgen::Bindings;
20
21#[derive(Debug, Clone)]
22pub enum Literal {
23 Expr(String),
24 Path(String),
25 PostfixUnaryOp {
26 op: &'static str,
27 value: Box<Literal>,
28 },
29 BinOp {
30 left: Box<Literal>,
31 op: &'static str,
32 right: Box<Literal>,
33 },
34 Struct {
35 path: Path,
36 export_name: String,
37 fields: HashMap<String, Literal>,
38 },
39}
40
41impl Literal {
42 fn replace_self_with(&mut self, self_ty: &Path) {
43 match *self {
44 Literal::PostfixUnaryOp { .. }
45 | Literal::BinOp { .. }
46 | Literal::Expr(..)
47 | Literal::Path(..) => {}
48 Literal::Struct {
49 ref mut path,
50 ref mut export_name,
51 ref mut fields,
52 } => {
53 if path.replace_self_with(self_ty) {
54 *export_name = self_ty.name().to_owned();
55 }
56 for (ref _name, ref mut expr) in fields {
57 expr.replace_self_with(self_ty);
58 }
59 }
60 }
61 }
62
63 fn is_valid(&self, bindings: &Bindings) -> bool {
64 match *self {
65 Literal::Expr(..) => true,
66 Literal::Path(..) => true,
67 Literal::PostfixUnaryOp { ref value, .. } => value.is_valid(bindings),
68 Literal::BinOp {
69 ref left,
70 ref right,
71 ..
72 } => left.is_valid(bindings) && right.is_valid(bindings),
73 Literal::Struct { ref path, .. } => bindings.struct_exists(path),
74 }
75 }
76}
77
78impl Literal {
79 pub fn rename_for_config(&mut self, config: &Config) {
80 match self {
81 Literal::Struct {
82 ref mut export_name,
83 fields,
84 ..
85 } => {
86 config.export.rename(export_name);
87 for (_, lit) in fields {
88 lit.rename_for_config(config);
89 }
90 }
91 Literal::Path(ref mut name) => {
92 config.export.rename(name);
93 }
94 Literal::PostfixUnaryOp { ref mut value, .. } => {
95 value.rename_for_config(config);
96 }
97 Literal::BinOp {
98 ref mut left,
99 ref mut right,
100 ..
101 } => {
102 left.rename_for_config(config);
103 right.rename_for_config(config);
104 }
105 Literal::Expr(_) => {}
106 }
107 }
108
109 pub fn load(expr: &syn::Expr) -> Result<Literal, String> {
111 match *expr {
112 syn::Expr::Binary(ref bin_expr) => {
114 let l = Self::load(&bin_expr.left)?;
115 let r = Self::load(&bin_expr.right)?;
116 let op = match bin_expr.op {
117 syn::BinOp::Add(..) => "+",
118 syn::BinOp::Sub(..) => "-",
119 syn::BinOp::Mul(..) => "*",
120 syn::BinOp::Div(..) => "div",
121 syn::BinOp::Rem(..) => "mod",
122 syn::BinOp::And(..) => "and",
123 syn::BinOp::Or(..) => "or",
124 syn::BinOp::BitXor(..) => "xor",
125 syn::BinOp::BitAnd(..) => "and",
126 syn::BinOp::BitOr(..) => "or",
127 syn::BinOp::Shl(..) => "shl",
128 syn::BinOp::Shr(..) => "shr",
129 syn::BinOp::Eq(..) => "==",
130 syn::BinOp::Lt(..) => "<",
131 syn::BinOp::Le(..) => "<=",
132 syn::BinOp::Ne(..) => "!=",
133 syn::BinOp::Ge(..) => ">=",
134 syn::BinOp::Gt(..) => ">",
135 syn::BinOp::AddEq(..) => "+=",
136 syn::BinOp::SubEq(..) => "-=",
137 syn::BinOp::MulEq(..) => "*=",
138 syn::BinOp::DivEq(..) => "/=",
139 syn::BinOp::RemEq(..) => "%=",
140 syn::BinOp::BitXorEq(..) => "^=",
141 syn::BinOp::BitAndEq(..) => "&=",
142 syn::BinOp::BitOrEq(..) => "|=",
143 syn::BinOp::ShlEq(..) => ">>=",
144 syn::BinOp::ShrEq(..) => "<<=",
145 };
146 Ok(Literal::BinOp {
147 left: Box::new(l),
148 op,
149 right: Box::new(r),
150 })
151 }
152
153 syn::Expr::Lit(syn::ExprLit { ref lit, .. }) => {
155 match lit {
156 syn::Lit::Str(ref value) => {
157 Ok(Literal::Expr(format!("u8\"{}\"", value.value())))
158 }
159 syn::Lit::Byte(ref value) => Ok(Literal::Expr(format!("{}", value.value()))),
160 syn::Lit::Char(ref value) => Ok(Literal::Expr(match value.value() as u32 {
161 0..=255 => format!("'{}'", value.value().escape_default()),
162 other_code => format!(r"L'\u{:X}'", other_code),
163 })),
164 syn::Lit::Int(ref value) => {
165 Ok(Literal::Expr(value.base10_digits().to_string()))
166 }
167 syn::Lit::Float(ref value) => {
168 Ok(Literal::Expr(value.base10_digits().to_string()))
169 }
170 syn::Lit::Bool(ref value) => Ok(Literal::Expr(format!("{}", value.value))),
171 _ => Err(format!("Unsupported literal expression. {:?}", *lit)),
173 }
174 }
175
176 syn::Expr::Struct(syn::ExprStruct {
177 ref path,
178 ref fields,
179 ..
180 }) => {
181 let struct_name = path.segments[0].ident.to_string();
182 let mut field_map = HashMap::<String, Literal>::default();
183 for field in fields {
184 let ident = match field.member {
185 syn::Member::Named(ref name) => name.to_string(),
186 syn::Member::Unnamed(ref index) => format!("F{}", index.index),
187 };
188 let key = ident.to_string();
189 let value = Literal::load(&field.expr)?;
190 field_map.insert(key, value);
191 }
192 Ok(Literal::Struct {
193 path: Path::new(struct_name.clone()),
194 export_name: struct_name,
195 fields: field_map,
196 })
197 }
198
199 syn::Expr::Unary(syn::ExprUnary {
200 ref op, ref expr, ..
201 }) => match *op {
202 UnOp::Neg(_) => {
203 let val = Self::load(expr)?;
204 Ok(Literal::PostfixUnaryOp {
205 op: "-",
206 value: Box::new(val),
207 })
208 }
209 _ => Err(format!("Unsupported Unary expression. {:?}", *op)),
210 },
211
212 syn::Expr::Path(syn::ExprPath {
214 path: syn::Path { ref segments, .. },
215 ..
216 }) => {
217 if segments.len() == 1 {
219 Ok(Literal::Path(format!("{}", segments.last().unwrap().ident)))
220 } else {
221 Err(format!("Unsupported path expression. {:?}", *segments))
222 }
223 }
224
225 syn::Expr::Paren(syn::ExprParen { ref expr, .. }) => Self::load(expr),
226
227 _ => Err(format!("Unsupported expression. {:?}", *expr)),
228 }
229 }
230
231 fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
232 match self {
233 Literal::Expr(v) => write!(out, "{}", v),
234 Literal::Path(v) => write!(out, "{}", v),
235 Literal::PostfixUnaryOp { op, ref value } => {
236 write!(out, "{}", op);
237 value.write(config, out);
238 }
239 Literal::BinOp {
240 ref left,
241 op,
242 ref right,
243 } => {
244 write!(out, "(");
245 left.write(config, out);
246 write!(out, " {} ", op);
247 right.write(config, out);
248 write!(out, ")");
249 }
250 Literal::Struct {
251 export_name,
252 fields,
253 path,
254 } => {
255 write!(out, "{}(", export_name);
256
257 let mut is_first_field = true;
258 let ordered_fields = out.bindings().struct_field_names(path);
260 for ordered_key in ordered_fields.iter() {
261 if let Some(ref lit) = fields.get(ordered_key) {
262 if !is_first_field {
263 write!(out, ", ");
264 } else {
265 is_first_field = false;
266 }
267 write!(out, "{}: ", ordered_key);
268
269 lit.write(config, out);
270 }
271 }
272 write!(out, ")");
273 }
274 }
275 }
276}
277
278#[derive(Debug, Clone)]
279pub struct Constant {
280 pub path: Path,
281 pub export_name: String,
282 pub ty: Type,
283 pub value: Literal,
284 pub cfg: Option<Cfg>,
285 pub annotations: AnnotationSet,
286 pub documentation: Documentation,
287 pub associated_to: Option<Path>,
288}
289
290fn can_handle(ty: &Type, expr: &syn::Expr) -> bool {
291 if ty.is_primitive_or_ptr_primitive() {
292 return true;
293 }
294 match *expr {
295 syn::Expr::Struct(_) => true,
296 _ => false,
297 }
298}
299
300impl Constant {
301 pub fn load(
302 path: Path,
303 mod_cfg: Option<&Cfg>,
304 ty: &syn::Type,
305 expr: &syn::Expr,
306 attrs: &[syn::Attribute],
307 associated_to: Option<Path>,
308 ) -> Result<Constant, String> {
309 let ty = Type::load(ty)?;
310 let mut ty = match ty {
311 Some(ty) => ty,
312 None => {
313 return Err("Cannot have a zero sized const definition.".to_owned());
314 }
315 };
316
317 if !can_handle(&ty, expr) {
318 return Err("Unhandled const definition".to_owned());
319 }
320
321 let mut lit = Literal::load(&expr)?;
322
323 if let Some(ref associated_to) = associated_to {
324 ty.replace_self_with(associated_to);
325 lit.replace_self_with(associated_to);
326 }
327
328 Ok(Constant::new(
329 path,
330 ty,
331 lit,
332 Cfg::append(mod_cfg, Cfg::load(attrs)),
333 AnnotationSet::load(attrs)?,
334 Documentation::load(attrs),
335 associated_to,
336 ))
337 }
338
339 pub fn new(
340 path: Path,
341 ty: Type,
342 value: Literal,
343 cfg: Option<Cfg>,
344 annotations: AnnotationSet,
345 documentation: Documentation,
346 associated_to: Option<Path>,
347 ) -> Self {
348 let export_name = path.name().to_owned();
349 Self {
350 path,
351 export_name,
352 ty,
353 value,
354 cfg,
355 annotations,
356 documentation,
357 associated_to,
358 }
359 }
360}
361
362impl Item for Constant {
363 fn path(&self) -> &Path {
364 &self.path
365 }
366
367 fn add_dependencies(&self, library: &Library, out: &mut Dependencies) {
368 self.ty.add_dependencies(library, out);
369 }
370
371 fn export_name(&self) -> &str {
372 &self.export_name
373 }
374
375 fn cfg(&self) -> Option<&Cfg> {
376 self.cfg.as_ref()
377 }
378
379 fn annotations(&self) -> &AnnotationSet {
380 &self.annotations
381 }
382
383 fn annotations_mut(&mut self) -> &mut AnnotationSet {
384 &mut self.annotations
385 }
386
387 fn container(&self) -> ItemContainer {
388 ItemContainer::Constant(self.clone())
389 }
390
391 fn rename_for_config(&mut self, config: &Config) {
392 if self.associated_to.is_none() {
393 config.export.rename(&mut self.export_name);
394 }
395 self.value.rename_for_config(config);
396 self.ty.rename_for_config(config, &GenericParams::default()); }
398}
399
400impl Constant {
401 pub fn write<F: Write>(
402 &self,
403 config: &Config,
404 out: &mut SourceWriter<F>,
405 associated_to_struct: Option<&Struct>,
406 ) {
407 if let Some(assoc) = associated_to_struct {
408 if assoc.is_generic() {
409 return; }
411 }
412
413 if !self.value.is_valid(out.bindings()) {
414 return;
415 }
416
417 let associated_to_transparent = associated_to_struct.map_or(false, |s| s.is_transparent);
418
419 let in_body = associated_to_struct.is_some() && false && !associated_to_transparent;
420
421 let condition = (&self.cfg).to_condition(config);
422 condition.write_before(config, out);
423
424 let name = if in_body {
425 Cow::Owned(format!(
426 "{}::{}",
427 associated_to_struct.unwrap().export_name(),
428 self.export_name(),
429 ))
430 } else if self.associated_to.is_none() {
431 Cow::Borrowed(self.export_name())
432 } else {
433 let associated_name = match associated_to_struct {
434 Some(s) => Cow::Borrowed(s.export_name()),
435 None => {
436 let mut name = self.associated_to.as_ref().unwrap().name().to_owned();
437 config.export.rename(&mut name);
438 Cow::Owned(name)
439 }
440 };
441
442 Cow::Owned(format!("{}_{}", associated_name, self.export_name()))
443 };
444
445 let value = match self.value {
446 Literal::Struct {
447 ref fields,
448 ref path,
449 ..
450 } if out.bindings().struct_is_transparent(path) => &fields.iter().next().unwrap().1,
451 _ => &self.value,
452 };
453
454 write!(out, "const {}* = ", name);
455 value.write(config, out);
456 condition.write_after(config, out);
457 }
458}