cxx_qt_gen/generator/naming/
property.rs

1// SPDX-FileCopyrightText: 2022 Klarälvdalens Datakonsult AB, a KDAB Group company <info@kdab.com>
2// SPDX-FileContributor: Andrew Hayzen <andrew.hayzen@kdab.com>
3//
4// SPDX-License-Identifier: MIT OR Apache-2.0
5use crate::{
6    naming::Name,
7    parser::property::{FlagState, ParsedQProperty},
8};
9use quote::format_ident;
10use syn::Result;
11
12use crate::generator::structuring::StructuredQObject;
13use core::ops::Deref;
14
15#[derive(Debug)]
16pub enum NameState {
17    Auto(Name),
18    Custom(Name),
19}
20
21impl Deref for NameState {
22    type Target = Name;
23
24    fn deref(&self) -> &Self::Target {
25        match self {
26            Self::Auto(name) => name,
27            Self::Custom(name) => name,
28        }
29    }
30}
31
32impl NameState {
33    pub fn from_flag_with_auto_fn(
34        state: &FlagState,
35        auto_fn: impl Fn() -> Name,
36        structured_qobject: &StructuredQObject,
37        signal: bool,
38    ) -> Result<Self> {
39        let lookup_fn = if signal {
40            StructuredQObject::signal_lookup
41        } else {
42            StructuredQObject::method_lookup
43        };
44        Ok(match state {
45            FlagState::Auto => Self::Auto(auto_fn()),
46            FlagState::Custom(ident) => Self::Custom(lookup_fn(structured_qobject, ident)?),
47        })
48    }
49}
50
51/// Names for parts of a Q_PROPERTY
52pub struct QPropertyNames {
53    pub name: Name,
54    pub getter: NameState,
55    pub setter: Option<NameState>,
56    pub notify: Option<NameState>,
57    pub reset: Option<Name>,
58}
59
60impl QPropertyNames {
61    pub(crate) fn try_from_property(
62        property: &ParsedQProperty,
63        structured_qobject: &StructuredQObject,
64    ) -> Result<Self> {
65        let property_name = &property.name;
66
67        // Cache flags as they are accessed multiple times
68        let flags = &property.flags;
69
70        let getter = NameState::from_flag_with_auto_fn(
71            &flags.read,
72            || getter_name_from_property(property_name),
73            structured_qobject,
74            false,
75        )?;
76
77        let setter = flags
78            .write
79            .clone()
80            .map(|setter| {
81                NameState::from_flag_with_auto_fn(
82                    &setter,
83                    || setter_name_from_property(property_name),
84                    structured_qobject,
85                    false,
86                )
87            })
88            .transpose()?;
89
90        let notify = flags
91            .notify
92            .clone()
93            .map(|notify| {
94                NameState::from_flag_with_auto_fn(
95                    &notify,
96                    || notify_name_from_property(property_name),
97                    structured_qobject,
98                    true,
99                )
100            })
101            .transpose()?;
102
103        let reset = flags
104            .reset
105            .as_ref()
106            .map(|ident| structured_qobject.method_lookup(ident))
107            .transpose()?;
108
109        Ok(Self {
110            getter,
111            setter,
112            notify,
113            reset,
114            name: property_name.clone(),
115        })
116    }
117}
118
119fn capitalise_first(str: String) -> String {
120    let mut out = "".to_string();
121    if let Some(first) = str.chars().next() {
122        out.push(first.to_ascii_uppercase());
123        out.push_str(&str[1..]);
124    }
125    out
126}
127
128/// For a given property name generate the getter name
129fn getter_name_from_property(name: &Name) -> Name {
130    name.clone()
131        .with_cxx_name(format!("get{}", capitalise_first(name.cxx_unqualified())))
132}
133
134/// For a given property name generate the setter name
135fn setter_name_from_property(name: &Name) -> Name {
136    name.clone()
137        .with_rust_name(format_ident!("set_{}", name.rust_unqualified()))
138        .with_cxx_name(format!("set{}", capitalise_first(name.cxx_unqualified())))
139}
140
141/// For a given property name generate the notify signal name
142fn notify_name_from_property(name: &Name) -> Name {
143    name.clone()
144        .with_rust_name(format_ident!("{}_changed", name.rust_unqualified()))
145        .with_cxx_name(format!("{}Changed", name.cxx_unqualified()))
146}
147
148#[cfg(test)]
149pub mod tests {
150    use syn::parse_quote;
151
152    use super::*;
153    use crate::parser::property::QPropertyFlags;
154    use crate::parser::qobject::ParsedQObject;
155
156    pub fn create_i32_qpropertyname() -> QPropertyNames {
157        let property = ParsedQProperty {
158            name: Name::mock_name_with_cxx("my_property", "myProperty"),
159            ty: parse_quote! { i32 },
160            flags: QPropertyFlags::default(),
161        };
162
163        let obj = ParsedQObject::mock();
164
165        let structured_qobject = StructuredQObject::mock(&obj);
166        QPropertyNames::try_from_property(&property, &structured_qobject)
167            .expect("Failed to create QPropertyNames")
168    }
169
170    #[test]
171    fn test_parsed_property() {
172        let names = create_i32_qpropertyname();
173        assert_eq!(names.name.cxx_unqualified(), "myProperty");
174        assert_eq!(names.name.rust_unqualified(), "my_property");
175        assert_eq!(names.getter.cxx_unqualified(), "getMyProperty");
176        assert_eq!(names.getter.rust_unqualified(), "my_property");
177        assert_eq!(
178            names.setter.as_ref().unwrap().cxx_unqualified(),
179            "setMyProperty"
180        );
181        assert_eq!(
182            names.setter.as_ref().unwrap().rust_unqualified(),
183            "set_my_property"
184        );
185        assert_eq!(
186            names.notify.as_ref().unwrap().cxx_unqualified(),
187            "myPropertyChanged"
188        );
189        assert_eq!(
190            names.notify.as_ref().unwrap().rust_unqualified(),
191            "my_property_changed"
192        );
193    }
194
195    #[test]
196    fn test_capitalise_first() {
197        assert_eq!(capitalise_first("abc".to_owned()), "Abc".to_owned());
198        assert_eq!(capitalise_first("".to_string()), "".to_owned());
199        assert_eq!(capitalise_first("a".to_owned()), "A".to_owned());
200    }
201}