Skip to content

Commit 3428b99

Browse files
committed
Add nullable attribute on properties macro
1 parent 7612a98 commit 3428b99

File tree

3 files changed

+40
-13
lines changed

3 files changed

+40
-13
lines changed

glib-macros/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,7 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream {
872872
/// `PropertySet` and `PropertySetNested` if possible.
873873
///
874874
/// The type `Option<T>` is supported as a property only if `Option<T>` implements `ToValueOptional`.
875+
/// Optional types also require the `nullable` attribute.
875876
/// If your type doesn't support `PropertySet`, you can't use the generated setter, but you can
876877
/// always define a custom one.
877878
///
@@ -913,7 +914,7 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream {
913914
/// numeric_builder: RefCell<u32>,
914915
/// #[property(get, set, builder('c'))]
915916
/// builder_with_required_param: RefCell<char>,
916-
/// #[property(get, set)]
917+
/// #[property(get, set, nullable)]
917918
/// optional: RefCell<Option<String>>,
918919
/// #[property(get, set)]
919920
/// smart_pointer: Rc<RefCell<String>>,

glib-macros/src/properties.rs

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,9 @@ enum PropAttr {
8686
// Builder(Punctuated(required_params), Optionals(TokenStream))
8787
Builder(Punctuated<syn::Expr, Token![,]>, TokenStream2),
8888

89+
// ident
90+
Nullable,
91+
8992
// ident [= expr]
9093
Get(Option<syn::Expr>),
9194
Set(Option<syn::Expr>),
@@ -151,6 +154,7 @@ impl Parse for PropAttr {
151154
} else {
152155
// attributes with only the identifier name
153156
match &*name_str {
157+
"nullable" => PropAttr::Nullable,
154158
"get" => PropAttr::Get(None),
155159
"set" => PropAttr::Set(None),
156160
"readwrite" | "read_only" | "write_only" => {
@@ -171,6 +175,7 @@ impl Parse for PropAttr {
171175

172176
#[derive(Default)]
173177
struct ReceivedAttrs {
178+
nullable: bool,
174179
get: Option<MaybeCustomFn>,
175180
set: Option<MaybeCustomFn>,
176181
override_class: Option<syn::Type>,
@@ -197,6 +202,7 @@ impl Parse for ReceivedAttrs {
197202
impl ReceivedAttrs {
198203
fn set_from_attr(&mut self, attr: PropAttr) {
199204
match attr {
205+
PropAttr::Nullable => self.nullable = true,
200206
PropAttr::Get(some_fn) => self.get = Some(some_fn.into()),
201207
PropAttr::Set(some_fn) => self.set = Some(some_fn.into()),
202208
PropAttr::Name(lit) => self.name = Some(lit),
@@ -223,6 +229,7 @@ struct PropDesc {
223229
name: syn::LitStr,
224230
override_class: Option<syn::Type>,
225231
override_interface: Option<syn::Type>,
232+
nullable: bool,
226233
get: Option<MaybeCustomFn>,
227234
set: Option<MaybeCustomFn>,
228235
member: Option<syn::Ident>,
@@ -238,6 +245,7 @@ impl PropDesc {
238245
attrs: ReceivedAttrs,
239246
) -> syn::Result<Self> {
240247
let ReceivedAttrs {
248+
nullable,
241249
get,
242250
set,
243251
override_class,
@@ -280,6 +288,7 @@ impl PropDesc {
280288
name,
281289
override_class,
282290
override_interface,
291+
nullable,
283292
get,
284293
set,
285294
member,
@@ -514,8 +523,23 @@ fn expand_wrapper_getset_properties(props: &[PropDesc]) -> TokenStream2 {
514523
let is_construct_only = p.builder_fields.iter().any(|(k, _)| *k == "construct_only");
515524
let setter = (p.set.is_some() && !is_construct_only).then(|| {
516525
let ident = format_ident!("set_{}", ident);
517-
quote!(pub fn #ident<'a>(&self, value: impl std::borrow::Borrow<<<#ty as #crate_ident::Property>::Value as #crate_ident::HasParamSpec>::SetValue>) {
518-
self.set_property_from_value(#name, &::std::convert::From::from(std::borrow::Borrow::borrow(&value)))
526+
let target_ty = quote!(<<#ty as #crate_ident::Property>::Value as #crate_ident::HasParamSpec>::SetValue);
527+
let set_ty = if p.nullable {
528+
quote!(Option<impl std::borrow::Borrow<#target_ty>>)
529+
} else {
530+
quote!(impl std::borrow::Borrow<#target_ty>)
531+
};
532+
let upcasted_borrowed_value = if p.nullable {
533+
quote!(
534+
value.as_ref().map(|v| std::borrow::Borrow::borrow(v))
535+
)
536+
} else {
537+
quote!(
538+
std::borrow::Borrow::borrow(&value)
539+
)
540+
};
541+
quote!(pub fn #ident<'a>(&self, value: #set_ty) {
542+
self.set_property_from_value(#name, &::std::convert::From::from(#upcasted_borrowed_value))
519543
})
520544
});
521545
let span = p.attrs_span;

glib-macros/tests/properties.rs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -137,9 +137,9 @@ mod foo {
137137
boxed: RefCell<SimpleBoxedString>,
138138
#[property(get, set, builder(SimpleEnum::One))]
139139
fenum: RefCell<SimpleEnum>,
140-
#[property(get, set)]
140+
#[property(get, set, nullable)]
141141
object: RefCell<Option<glib::Object>>,
142-
#[property(get, set)]
142+
#[property(get, set, nullable)]
143143
optional: RefCell<Option<String>>,
144144
#[property(get, set)]
145145
smart_pointer: Rc<RefCell<String>>,
@@ -297,11 +297,6 @@ fn props() {
297297
foo::SimpleBoxedString("".into())
298298
);
299299

300-
// optional
301-
assert_eq!(myfoo.property::<Option<String>>("optional"), None,);
302-
303-
myfoo.connect_optional_notify(|_| println!("notified"));
304-
305300
// Test `FooPropertiesExt`
306301
// getters
307302
{
@@ -341,9 +336,6 @@ fn props() {
341336
"setter working".to_string()
342337
);
343338

344-
// object subclass
345-
myfoo.set_object(glib::BoxedAnyObject::new(""));
346-
347339
// custom
348340
myfoo.set_fake_field("fake setter");
349341
assert_eq!(
@@ -366,4 +358,14 @@ fn props() {
366358
let not_overridden: u32 = myfoo.property("not-overridden");
367359
assert_eq!(not_overridden, 42);
368360
}
361+
362+
// optional
363+
myfoo.set_optional(Some("Hello world"));
364+
assert_eq!(myfoo.optional(), Some("Hello world".to_string()));
365+
myfoo.connect_optional_notify(|_| println!("notified"));
366+
367+
// object subclass
368+
let myobj = glib::BoxedAnyObject::new("");
369+
myfoo.set_object(Some(myobj.upcast_ref()));
370+
assert_eq!(myfoo.object(), Some(myobj.upcast()))
369371
}

0 commit comments

Comments
 (0)