use crate::*;
use squote::{format_ident, quote, Literal, TokenStream};
#[derive(Debug)]
pub struct Enum {
pub name: TypeName,
pub fields: Vec<(&'static str, EnumConstant)>,
pub underlying_type: winmd::ElementType,
pub signature: String,
}
#[derive(Copy, Clone, PartialEq, PartialOrd, Eq, Ord, Debug)]
pub enum EnumConstant {
U32(u32),
I32(i32),
}
impl EnumConstant {
fn next(&self) -> Self {
match self {
Self::U32(value) => Self::U32(value + 1),
Self::I32(value) => Self::I32(value + 1),
}
}
}
impl Enum {
pub fn from_type_name(name: TypeName) -> Self {
let signature = if name.def.is_winrt() {
name.enum_signature()
} else {
String::new()
};
let mut fields = Vec::new();
let mut underlying_type = None;
for field in name.def.fields() {
if field.flags().literal() {
if let Some(constant) = field.constant() {
let mut value = constant.value();
let value = match constant.value_type() {
winmd::ElementType::I32 => EnumConstant::I32(value.read_i32()),
winmd::ElementType::U32 => EnumConstant::U32(value.read_u32()),
_ => panic!("Enum::from_type_def"),
};
fields.push((field.name(), value));
} else if fields.is_empty() {
fields.push((field.name(), EnumConstant::I32(0)));
} else {
fields.push((field.name(), fields.last().unwrap().1.next()));
}
} else {
let blob = &mut field.sig();
blob.read_unsigned();
blob.read_modifiers();
blob.read_expected(0x1D);
blob.read_modifiers();
underlying_type = Some(winmd::ElementType::from_blob(blob));
}
}
Self {
name,
fields,
underlying_type: underlying_type.expect("Enum.from_type_name"),
signature,
}
}
pub fn gen(&self) -> TokenStream {
let name = self.name.gen();
let (underlying_type, bitwise) = match self.underlying_type {
winmd::ElementType::I32 => (format_ident!("i32"), TokenStream::new()),
winmd::ElementType::U32 => (
format_ident!("u32"),
quote! {
impl ::std::ops::BitOr for #name {
type Output = Self;
fn bitor(self, rhs: Self) -> Self {
Self(self.0 | rhs.0)
}
}
impl ::std::ops::BitAnd for #name {
type Output = Self;
fn bitand(self, rhs: Self) -> Self {
Self(self.0 & rhs.0)
}
}
},
),
_ => panic!("Unexpected enum underlying type: {}", name),
};
let fields = self.fields.iter().map(|(name, value)| {
let name = format_ident(&name);
let value = match value {
EnumConstant::U32(value) => quote! { #value },
EnumConstant::I32(value) => quote! { #value },
};
quote! {
pub const #name: Self = Self(#value);
}
});
let runtime_type = if self.signature.is_empty() {
TokenStream::new()
} else {
let signature = Literal::byte_string(&self.signature.as_bytes());
quote! {
unsafe impl ::windows::RuntimeType for #name {
type DefaultType = Self;
const SIGNATURE: ::windows::ConstBuffer = ::windows::ConstBuffer::from_slice(#signature);
}
}
};
quote! {
#[allow(non_camel_case_types)]
#[repr(transparent)]
pub struct #name(pub #underlying_type);
impl ::std::convert::From<#underlying_type> for #name {
fn from(value: #underlying_type) -> Self {
Self(value)
}
}
impl ::std::clone::Clone for #name {
fn clone(&self) -> Self {
Self(self.0)
}
}
impl ::std::default::Default for #name {
fn default() -> Self {
Self(0)
}
}
impl ::std::fmt::Debug for #name {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
write!(f, "{:?}", self.0)
}
}
impl ::std::cmp::PartialEq for #name {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}
impl ::std::cmp::Eq for #name {}
impl ::std::marker::Copy for #name {}
impl #name {
#![allow(non_upper_case_globals)]
#(#fields)*
}
unsafe impl ::windows::Abi for #name {
type Abi = Self;
}
#runtime_type
#bitwise
}
}
}