Skip to content

Commit 0e1b6dc

Browse files
committed
Add #[derive(QEnum)]
see rust-lang/rust#8730
1 parent 7603e50 commit 0e1b6dc

File tree

4 files changed

+201
-22
lines changed

4 files changed

+201
-22
lines changed

qmetaobject/src/lib.rs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,22 @@ pub trait QGadget {
409409
Self: Sized;
410410
}
411411

412+
/// Trait that is implemented by the QEnum custom derive macro
413+
///
414+
/// Do not implement this trait yourself, use `#[derive(QEnum)]`.
415+
pub trait QEnum {
416+
/// Returns a pointer to a meta object
417+
fn static_meta_object() -> *const QMetaObject
418+
where
419+
Self: Sized;
420+
421+
fn from_raw_value(raw: i32) -> Option<Self>
422+
where
423+
Self: Sized;
424+
425+
fn to_raw_value(&self) -> i32;
426+
}
427+
412428
#[doc(hidden)]
413429
#[no_mangle]
414430
pub unsafe extern "C" fn RustObject_metaObject(p: *mut RefCell<QObject>) -> *const QMetaObject {
@@ -445,7 +461,8 @@ pub struct QMetaObject {
445461
pub superdata: *const QMetaObject,
446462
pub string_data: *const u8,
447463
pub data: *const u32,
448-
pub static_metacall: extern "C" fn(o: *mut c_void, c: u32, idx: u32, a: *const *mut c_void),
464+
pub static_metacall:
465+
Option<extern "C" fn(o: *mut c_void, c: u32, idx: u32, a: *const *mut c_void)>,
449466
pub r: *const c_void,
450467
pub e: *const c_void,
451468
}

qmetaobject/tests/tests.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -606,3 +606,10 @@ fn panic_when_moved_setter() {
606606
do_test(my_obj, "Item { function doTest() { _obj.prop_y = 45; } }");
607607
}
608608
*/
609+
610+
#[derive(QEnum)]
611+
#[repr(u8)]
612+
enum MyEnum {
613+
None = 0,
614+
First = 1,
615+
}

qmetaobject_impl/src/lib.rs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ pub fn qgadget_impl(input: TokenStream) -> TokenStream {
7070
qobject_impl::generate(input, false)
7171
}
7272

73+
/// Implementation of #[derive(QEnum)]
74+
#[proc_macro_derive(QEnum, attributes(QMetaObjectCrate))]
75+
pub fn qenum_impl(input: TokenStream) -> TokenStream {
76+
qobject_impl::generate_enum(input)
77+
}
78+
7379
/// Implementation of the qmetaobject::qrc! macro
7480
#[proc_macro]
7581
pub fn qrc_internal(input: TokenStream) -> TokenStream {

qmetaobject_impl/src/qobject_impl.rs

Lines changed: 170 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -110,9 +110,15 @@ struct MetaProperty {
110110
alias: Option<syn::Ident>,
111111
}
112112

113+
#[derive(Clone)]
114+
struct MetaEnum {
115+
name: syn::Ident,
116+
variants: Vec<syn::Ident>,
117+
}
118+
113119
#[derive(Default)]
114120
struct MetaObject {
115-
int_data: Vec<u32>,
121+
int_data: Vec<proc_macro2::TokenStream>,
116122
string_data: Vec<String>,
117123
}
118124
impl MetaObject {
@@ -144,58 +150,75 @@ impl MetaObject {
144150
result
145151
}
146152

153+
fn push_int(&mut self, i: u32) {
154+
self.int_data.push(quote!(#i));
155+
}
156+
157+
fn extend_from_int_slice(&mut self, slice: &[u32]) {
158+
for i in slice {
159+
self.int_data.push(quote!(#i));
160+
}
161+
}
162+
147163
fn compute_int_data(
148164
&mut self,
149165
class_name: String,
150166
properties: &[MetaProperty],
151167
methods: &[MetaMethod],
168+
enums: &[MetaEnum],
152169
signal_count: usize,
153170
) {
154-
let has_notify = properties.iter().any(|p| p.notify_signal.is_some());
155-
171+
let has_notify = properties.iter().any(|p| p.notify_signal.is_some());
156172
self.add_string(class_name);
157173
self.add_string("".to_owned());
158174

159175
let mut offset = 14;
160176
let property_offset = offset + methods.len() as u32 * 5;
161-
//...
177+
let enum_offset = property_offset + properties.len() as u32 * (if has_notify { 4 } else { 3 });
162178

163-
self.int_data.extend_from_slice(&[
179+
self.extend_from_int_slice(&[
164180
7, // revision
165181
0, // classname
166182
0,
167183
0, // class info count and offset
168184
methods.len() as u32,
169-
offset, // method count and offset
185+
if methods.len() == 0 { 0 } else { offset }, // method count and offset
170186
properties.len() as u32,
171-
property_offset, // properties count and offset
172-
0,
173-
0, // enum count and offset
187+
if properties.len() == 0 { 0 } else { property_offset }, // properties count and offset
188+
enums.len() as u32,
189+
if enums.len() == 0 { 0 } else { enum_offset }, // enum count and offset
174190
0,
175191
0, // constructor count and offset
176192
0x4, // flags (PropertyAccessInStaticMetaCall)
177193
signal_count as u32, // signalCount
178194
]);
179195

180-
offset = property_offset + properties.len() as u32 * (if has_notify { 4 } else { 3 });
196+
offset = enum_offset + enums.len() as u32 * 5;
181197

182198
for m in methods {
183199
let n = self.add_string(m.name.to_string());
184-
self.int_data
185-
.extend_from_slice(&[n, m.args.len() as u32, offset, 1, m.flags]);
200+
self.extend_from_int_slice(&[n, m.args.len() as u32, offset, 1, m.flags]);
186201
offset += 1 + 2 * m.args.len() as u32;
187202
}
188203

189204
for p in properties {
190205
let n = self.add_string(p.alias.as_ref().unwrap_or(&p.name).to_string());
191206
let type_id = self.add_type(p.typ.clone());
192-
self.int_data.extend_from_slice(&[n, type_id, p.flags]);
207+
self.extend_from_int_slice(&[n, type_id, p.flags]);
208+
}
209+
210+
for e in enums {
211+
let n = self.add_string(e.name.to_string());
212+
// name, alias, flag, count, data offset
213+
self.extend_from_int_slice(&[n, n, 0x2, e.variants.len() as u32, offset]);
214+
offset += 2 * e.variants.len() as u32;
193215
}
216+
194217
if has_notify {
195218
for p in properties {
196219
match p.notify_signal {
197-
None => self.int_data.push(0 as u32),
198-
Some(ref signal) => self.int_data.push(
220+
None => self.push_int(0 as u32),
221+
Some(ref signal) => self.push_int(
199222
methods
200223
.iter()
201224
.position(|x| x.name == *signal && (x.flags & 0x4) != 0)
@@ -208,16 +231,26 @@ impl MetaObject {
208231
for m in methods {
209232
// return type
210233
let ret_type = self.add_type(m.ret_type.clone());
211-
self.int_data.push(ret_type);
234+
self.push_int(ret_type);
212235
// types
213236
for a in m.args.iter() {
214237
let ty = self.add_type(a.typ.clone());
215-
self.int_data.push(ty);
238+
self.push_int(ty);
216239
}
217240
// names
218241
for a in m.args.iter() {
219242
let n = self.add_string(a.name.clone().into_token_stream().to_string());
220-
self.int_data.push(n);
243+
self.push_int(n);
244+
}
245+
}
246+
247+
for e in enums {
248+
for v in &e.variants {
249+
let n = self.add_string(v.to_string());
250+
// name, value
251+
self.push_int(n);
252+
let e_name = &e.name;
253+
self.int_data.push(quote!{ #e_name::#v as u32 });
221254
}
222255
}
223256
}
@@ -487,7 +520,7 @@ pub fn generate(input: TokenStream, is_qobject: bool) -> TokenStream {
487520
let methods = methods2;
488521

489522
let mut mo: MetaObject = Default::default();
490-
mo.compute_int_data(name.to_string(), &properties, &methods, signals.len());
523+
mo.compute_int_data(name.to_string(), &properties, &methods, &[], signals.len());
491524

492525
let str_data32 = mo.build_string_data(32);
493526
let str_data64 = mo.build_string_data(64);
@@ -710,7 +743,7 @@ pub fn generate(input: TokenStream, is_qobject: bool) -> TokenStream {
710743
superdata: #base_meta_object,
711744
string_data: STRING_DATA.as_ptr(),
712745
data: INT_DATA.as_ptr(),
713-
static_metacall: static_metacall,
746+
static_metacall: Some(static_metacall),
714747
r: ::std::ptr::null(),
715748
e: ::std::ptr::null(),
716749
};};
@@ -739,7 +772,7 @@ pub fn generate(input: TokenStream, is_qobject: bool) -> TokenStream {
739772
superdata: #base_meta_object,
740773
string_data: STRING_DATA.as_ptr(),
741774
data: INT_DATA.as_ptr(),
742-
static_metacall: static_metacall #turbo_generics,
775+
static_metacall: Some(static_metacall #turbo_generics),
743776
r: ::std::ptr::null(),
744777
e: ::std::ptr::null(),
745778
}));
@@ -885,3 +918,119 @@ pub fn generate(input: TokenStream, is_qobject: bool) -> TokenStream {
885918
}
886919
body.into()
887920
}
921+
922+
fn is_valid_repr_attribute(attribute: &syn::Attribute) -> bool {
923+
match attribute.parse_meta() {
924+
Ok(syn::Meta::List(list)) => {
925+
if list.ident.to_string() == "repr" && list.nested.len() == 1 {
926+
match &list.nested[0] {
927+
syn::NestedMeta::Meta(syn::Meta::Word(word)) => {
928+
let acceptables = vec!["u8", "u16", "u32", "i8", "i16", "i32"];
929+
let word = word.to_string();
930+
acceptables.iter().any(|x| *x == word.to_string())
931+
}
932+
_ => false,
933+
}
934+
} else {
935+
false
936+
}
937+
}
938+
_ => false
939+
}
940+
}
941+
942+
pub fn generate_enum(input: TokenStream) -> TokenStream {
943+
let ast = parse_macro_input!(input as syn::DeriveInput);
944+
945+
let name = &ast.ident;
946+
947+
let mut is_repr_explicit = false;
948+
for attr in &ast.attrs {
949+
is_repr_explicit |= is_valid_repr_attribute(attr);
950+
}
951+
if !is_repr_explicit {
952+
panic!("#[derive(QEnum)] only support enum with explicit #[repr(*)], possible integer type are u8, u16, u32, i8, i16, i32.")
953+
}
954+
955+
let crate_ = super::get_crate(&ast);
956+
let mut meta_enum = MetaEnum {
957+
name: name.clone(),
958+
variants: Vec::new()
959+
};
960+
961+
let mut from_raw_blocks = Vec::new();
962+
let mut to_raw_blocks = Vec::new();
963+
964+
if let syn::Data::Enum(ref data) = ast.data {
965+
for variant in data.variants.iter() {
966+
match &variant.fields {
967+
syn::Fields::Unit => {}
968+
// TODO report error with span
969+
_ => panic!("#[derive(QEnum)] only support field-less enum")
970+
}
971+
972+
let var_name = &variant.ident;
973+
meta_enum.variants.push(var_name.clone());
974+
975+
from_raw_blocks.push(quote!{
976+
if raw == #name::#var_name as i32 {
977+
Some(#name::#var_name)
978+
} else
979+
});
980+
981+
to_raw_blocks.push(quote!{
982+
#name::#var_name => #name::#var_name as i32,
983+
});
984+
}
985+
} else {
986+
panic!("#[derive(QEnum)] is only defined for enums, not for structs!");
987+
}
988+
989+
let enums = vec![meta_enum];
990+
let mut mo: MetaObject = Default::default();
991+
mo.compute_int_data(name.to_string(), &[], &[], &enums, 0);
992+
let str_data32 = mo.build_string_data(32);
993+
let str_data64 = mo.build_string_data(64);
994+
let int_data = mo.int_data;
995+
996+
let mo = if ast.generics.params.is_empty() {
997+
quote! {
998+
qmetaobject_lazy_static! { static ref MO: #crate_::QMetaObject = #crate_::QMetaObject {
999+
superdata: ::std::ptr::null(),
1000+
string_data: STRING_DATA.as_ptr(),
1001+
data: INT_DATA.as_ptr(),
1002+
static_metacall: None,
1003+
r: ::std::ptr::null(),
1004+
e: ::std::ptr::null(),
1005+
};};
1006+
return &*MO;
1007+
}
1008+
} else {
1009+
panic!("#[derive(QEnum)] is only defined for C enums, doesn't support generics");
1010+
};
1011+
1012+
let body = quote!{
1013+
impl QEnum for #name {
1014+
fn from_raw_value(raw :i32) -> Option<Self> {
1015+
#(#from_raw_blocks)*
1016+
{ None }
1017+
}
1018+
1019+
fn to_raw_value(&self) -> i32 {
1020+
match self {
1021+
#(#to_raw_blocks)*
1022+
}
1023+
}
1024+
1025+
fn static_meta_object()->*const #crate_::QMetaObject {
1026+
#[cfg(target_pointer_width = "64")]
1027+
static STRING_DATA : &'static [u8] = & [ #(#str_data64),* ];
1028+
#[cfg(target_pointer_width = "32")]
1029+
static STRING_DATA : &'static [u8] = & [ #(#str_data32),* ];
1030+
static INT_DATA : &'static [u32] = & [ #(#int_data),* ];
1031+
#mo
1032+
}
1033+
}
1034+
};
1035+
body.into()
1036+
}

0 commit comments

Comments
 (0)