Skip to content

Commit 13e3079

Browse files
authored
Merge pull request #1023 from ranfdev/nullable_props
Add nullable attribute on properties macro
2 parents a08e02d + 53f3f38 commit 13e3079

File tree

3 files changed

+41
-13
lines changed

3 files changed

+41
-13
lines changed

glib-macros/src/lib.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -872,6 +872,8 @@ 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: without it, the generated setter on the wrapper type
876+
/// will take `T` instead of `Option<T>`, preventing the user from ever calling the setter with a `None` value.
875877
/// If your type doesn't support `PropertySet`, you can't use the generated setter, but you can
876878
/// always define a custom one.
877879
///
@@ -913,7 +915,7 @@ pub fn cstr_bytes(item: TokenStream) -> TokenStream {
913915
/// numeric_builder: RefCell<u32>,
914916
/// #[property(get, set, builder('c'))]
915917
/// builder_with_required_param: RefCell<char>,
916-
/// #[property(get, set)]
918+
/// #[property(get, set, nullable)]
917919
/// optional: RefCell<Option<String>>,
918920
/// #[property(get, set)]
919921
/// 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
@@ -139,9 +139,9 @@ mod foo {
139139
boxed: RefCell<SimpleBoxedString>,
140140
#[property(get, set, builder(SimpleEnum::One))]
141141
fenum: RefCell<SimpleEnum>,
142-
#[property(get, set)]
142+
#[property(get, set, nullable)]
143143
object: RefCell<Option<glib::Object>>,
144-
#[property(get, set)]
144+
#[property(get, set, nullable)]
145145
optional: RefCell<Option<String>>,
146146
#[property(get, set)]
147147
smart_pointer: Rc<RefCell<String>>,
@@ -305,11 +305,6 @@ fn props() {
305305
foo::SimpleBoxedString("".into())
306306
);
307307

308-
// optional
309-
assert_eq!(myfoo.property::<Option<String>>("optional"), None,);
310-
311-
myfoo.connect_optional_notify(|_| println!("notified"));
312-
313308
// Test `FooPropertiesExt`
314309
// getters
315310
{
@@ -349,9 +344,6 @@ fn props() {
349344
"setter working".to_string()
350345
);
351346

352-
// object subclass
353-
myfoo.set_object(glib::BoxedAnyObject::new(""));
354-
355347
// custom
356348
myfoo.set_fake_field("fake setter");
357349
assert_eq!(
@@ -374,4 +366,14 @@ fn props() {
374366
let not_overridden: u32 = myfoo.property("not-overridden");
375367
assert_eq!(not_overridden, 42);
376368
}
369+
370+
// optional
371+
myfoo.set_optional(Some("Hello world"));
372+
assert_eq!(myfoo.optional(), Some("Hello world".to_string()));
373+
myfoo.connect_optional_notify(|_| println!("notified"));
374+
375+
// object subclass
376+
let myobj = glib::BoxedAnyObject::new("");
377+
myfoo.set_object(Some(myobj.upcast_ref()));
378+
assert_eq!(myfoo.object(), Some(myobj.upcast()))
377379
}

0 commit comments

Comments
 (0)