use std::io::Write;
use syn;
use bindgen::annotation::*;
use bindgen::config::{Config, Language};
use bindgen::rename::*;
use bindgen::utilities::*;
use bindgen::writer::*;
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum Repr {
None,
C,
U8,
U16,
U32,
}
#[derive(Debug, Clone)]
pub struct Enum {
pub name: String,
pub repr: Repr,
pub annotations: AnnotationSet,
pub values: Vec<(String, u64)>,
}
impl Enum {
pub fn load(name: String,
repr: Repr,
annotations: AnnotationSet,
variants: &Vec<syn::Variant>) -> Result<Enum, String>
{
if repr != Repr::U32 &&
repr != Repr::U16 &&
repr != Repr::U8 {
return if repr == Repr::C {
Err(format!("repr(C) is not FFI safe for enums"))
} else {
Err(format!("enum not marked with a repr(u32) or repr(u16) or repr(u8)"))
};
}
let mut values = Vec::new();
let mut current = 0;
for variant in variants {
match variant.data {
syn::VariantData::Unit => {
match variant.discriminant {
Some(syn::ConstExpr::Lit(syn::Lit::Int(i, _))) => {
current = i;
}
Some(_) => {
return Err(format!("unsupported discriminant"));
}
None => { }
}
values.push((variant.ident.to_string(), current));
current = current + 1;
}
_ => {
return Err(format!("unsupported variant"));
}
}
}
if let Some(variants) = annotations.list("enum-trailing-values") {
for variant in variants {
values.push((variant, current));
current = current + 1;
}
}
Ok(Enum {
name: name,
repr: repr,
annotations: annotations,
values: values,
})
}
pub fn rename_fields(&mut self, config: &Config) {
let rules = [self.annotations.parse_atom::<RenameRule>("rename-all"),
config.enumeration.rename_variants];
if let Some(r) = find_first_some(&rules) {
self.values = self.values.iter()
.map(|x| (r.apply_to_pascal_case(&x.0,
IdentifierType::EnumVariant(self)),
x.1.clone()))
.collect();
}
}
}
impl Source for Enum {
fn write<F: Write>(&self, config: &Config, out: &mut SourceWriter<F>) {
let size = match self.repr {
Repr::U32 => "uint32_t",
Repr::U16 => "uint16_t",
Repr::U8 => "uint8_t",
_ => unreachable!(),
};
if config.language == Language::C {
out.write(&format!("enum {}", self.name));
} else {
out.write(&format!("enum class {} : {}", self.name, size));
}
out.open_brace();
for (i, value) in self.values.iter().enumerate() {
if i != 0 {
out.new_line()
}
out.write(&format!("{} = {},", value.0, value.1));
}
if config.enumeration.add_sentinel(&self.annotations) {
out.new_line();
out.new_line();
out.write("Sentinel /* this must be last for serialization purposes. */");
}
out.close_brace(true);
if config.language == Language::C {
out.new_line();
out.write(&format!("typedef {} {};", size, self.name));
}
}
}