Skip to main content

qtbridge_gen/qt_derive/
qmodelitem.rs

1use proc_macro::TokenStream;
2use quote::{quote, format_ident};
3use syn::DeriveInput;
4use std::collections::HashMap;
5
6pub fn try_derive_qmodelitem(input: TokenStream) -> syn::Result<TokenStream> {
7    let input: DeriveInput = syn::parse(input)?;
8    let name = &input.ident;
9
10    let s = match &input.data {
11        syn::Data::Struct(st) => st,
12        _ => return Err(syn::Error::new_spanned(&input, "QModelItem only supports structs")),
13    };
14
15    let fields = s.fields.iter().collect::<Vec<_>>();
16
17    let (members, field_names): (Vec<_>, Vec<_>) = fields.iter().enumerate()
18    .map(|(i, f)|
19        match f.ident.as_ref() {
20            Some(ident) => (quote! { self.#ident }, ident.to_string()),
21            None => {
22                let index = syn::Index::from(i);
23                (quote! { self.#index }, format!("_{}", i))
24            },
25        })
26    .unzip();
27
28    let len = fields.len();
29    let max_elements = 15;
30
31    if len > max_elements {
32        return Err(syn::Error::new_spanned(&s.fields, "QModelItem supports only up to 15 fields"));
33    }
34
35    let field_types = fields.iter().map(|f| &f.ty).collect::<Vec<_>>();
36
37    // see  Qt::ItemDataRole
38    let default_roles: HashMap<&str, i32> = HashMap::from([
39        ("display", 0x0),
40        ("decoration", 0x1),
41        ("edit", 0x2),
42        ("toolTip", 0x3),
43        ("statusTip", 0x4),
44        ("whatsThis", 0x5),
45    ]);
46
47    let mut prop_mapping = Vec::new();
48
49    // Do some mapping so the default types go to the respective index
50    // if they are present. This is important due to the current implementation
51    // of some default delegates.
52    prop_mapping.resize(field_names.len(), max_elements);
53    for (i, f) in field_names.iter().enumerate() {
54        if let Some(&predef) = default_roles.get(f.as_str()) {
55            prop_mapping[predef as usize] = i;
56        }
57    }
58    let mut j = 0;
59    for (i, f) in field_names.iter().enumerate() {
60        if !default_roles.contains_key(f.as_str()){
61            while prop_mapping[j] < max_elements {
62                j += 1;
63            }
64            prop_mapping[j] = i;
65            j += 1;
66        }
67    }
68
69    let mut typdefs = Vec::new();
70    let mut getters = Vec::new();
71    let mut setters = Vec::new();
72
73    for (i, member) in members.iter().enumerate() {
74        let getter_fn_ident = format_ident!("get{}", prop_mapping[i]);
75        let setter_fn_ident = format_ident!("set{}", prop_mapping[i]);
76        let type_ident = format_ident!("T{}", prop_mapping[i]);
77        let ty = &field_types[i];
78
79        typdefs.push(quote! { type #type_ident = #ty; });
80        getters.push(quote! { fn #getter_fn_ident(&self) -> Option<&#ty> { Some(&#member) } });
81        setters.push(quote! { fn #setter_fn_ident(&mut self, value: #ty) { #member = value; } });
82    }
83
84    for i in len..15 {
85        let type_ident = format_ident!("T{}", i);
86        typdefs.push(quote! { type #type_ident = (); });
87    }
88
89    let mut role_tokens = Vec::new();
90
91    for (i,f) in field_names.iter().enumerate() {
92        let idx = prop_mapping[i] as i32;
93
94        role_tokens.push(quote! {
95            (#idx, #f.into()),
96        });
97    }
98
99    Ok(quote! {
100        impl QModelItem for #name {
101
102            #(#typdefs)*
103
104            #(#getters)*
105
106            #(#setters)*
107
108            fn role_names() -> HashMap<i32, String> {
109                let roles = [
110                #(#role_tokens)*
111                ];
112                roles.iter().cloned().collect()
113            }
114        }
115    }.into())
116}