From 3c41d6deaa2f4c7c7d726c1f124998f52ba31762 Mon Sep 17 00:00:00 2001 From: Jason Francis Date: Sat, 12 Mar 2022 11:08:54 -0500 Subject: [PATCH 1/2] glib: Allow object wrapper to override AsRef, IsA, and casting behavior --- glib/src/object.rs | 225 ++++++++++++++++++++++++++++++++++++-------- glib/src/wrapper.rs | 66 +++++++++---- 2 files changed, 235 insertions(+), 56 deletions(-) diff --git a/glib/src/object.rs b/glib/src/object.rs index 07e6ccf3dbef..212004a09d9d 100644 --- a/glib/src/object.rs +++ b/glib/src/object.rs @@ -59,11 +59,28 @@ pub unsafe trait ObjectType: // rustdoc-stripper-ignore-next /// type of the FFI Class structure. type GlibClassType: 'static; + // rustdoc-stripper-ignore-next + /// type checker for casting + type CastChecker: ObjectCastChecker; fn as_object_ref(&self) -> &ObjectRef; fn as_ptr(&self) -> *mut Self::GlibType; } +pub trait ObjectCastChecker { + fn check(obj: &U) -> bool; +} + +// rustdoc-stripper-ignore-next +/// Generic cast checker for object types. +pub struct GenericObjectCastChecker(std::marker::PhantomData); + +impl ObjectCastChecker for GenericObjectCastChecker { + fn check(obj: &U) -> bool { + obj.type_().is_a(T::static_type()) + } +} + // rustdoc-stripper-ignore-next /// Declares the "is a" relationship. /// @@ -686,7 +703,7 @@ unsafe impl Sync for TypedObjectRef {} /// ObjectType implementations for Object types. See `wrapper!`. #[macro_export] macro_rules! glib_object_wrapper { - (@generic_impl [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $parent_type:ty, $ffi_name:ty, $ffi_class_name:ty, @type_ $get_type_expr:expr) => { + (@generic_impl [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $parent_type:ty, $ffi_name:ty, $ffi_class_name:ty, @type_ $get_type_expr:expr, @default_casts $default_casts:ident, @checkers $cast_checker:ty, $value_checker:ty) => { $(#[$attr])* #[repr(transparent)] $visibility struct $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? { @@ -776,6 +793,7 @@ macro_rules! glib_object_wrapper { unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::object::ObjectType for $name $(<$($generic),+>)? { type GlibType = $ffi_name; type GlibClassType = $ffi_class_name; + type CastChecker = $cast_checker; fn as_object_ref(&self) -> &$crate::object::ObjectRef { &self.inner @@ -793,15 +811,7 @@ macro_rules! glib_object_wrapper { } } - #[doc(hidden)] - impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? AsRef for $name $(<$($generic),+>)? { - fn as_ref(&self) -> &Self { - self - } - } - - #[doc(hidden)] - unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::object::IsA for $name $(<$($generic),+>)? { } + $crate::glib_object_wrapper!(@default_casts $default_casts $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?); #[doc(hidden)] impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::subclass::types::FromObject for $name $(<$($generic),+>)? { @@ -1069,7 +1079,7 @@ macro_rules! glib_object_wrapper { #[doc(hidden)] unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for $name $(<$($generic),+>)? { - type Checker = $crate::object::ObjectValueTypeChecker; + type Checker = $value_checker; unsafe fn from_value(value: &'a $crate::Value) -> Self { let ptr = $crate::gobject_ffi::g_value_dup_object($crate::translate::ToGlibPtr::to_glib_none(value).0); @@ -1081,7 +1091,7 @@ macro_rules! glib_object_wrapper { #[doc(hidden)] unsafe impl<'a $(, $($generic $(: $bound $(+ $bound2)*)?),+)?> $crate::value::FromValue<'a> for &'a $name $(<$($generic),+>)? { - type Checker = $crate::object::ObjectValueTypeChecker; + type Checker = $value_checker; unsafe fn from_value(value: &'a $crate::Value) -> Self { assert_eq!(std::mem::size_of::(), std::mem::size_of::<$crate::ffi::gpointer>()); @@ -1140,6 +1150,12 @@ macro_rules! glib_object_wrapper { } }; + (@default_casts false $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?) => { }; + + (@default_casts true $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?) => { + $crate::glib_object_wrapper!(@munch_impls $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, Self); + }; + (@munch_impls $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, ) => { }; (@munch_impls $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $super_name:path) => { @@ -1158,6 +1174,24 @@ macro_rules! glib_object_wrapper { $crate::glib_object_wrapper!(@munch_impls $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $($implements)*); }; + (@munch_impls_generic) => { }; + + (@munch_impls_generic <$($impl_generic:ident $(: $impl_bound:tt $(+ $impl_bound2:tt)*)?),+> $impl_trait:ident $(<$($trait_generic:ident $(: $trait_bound:tt $(+ $trait_bound2:tt)*)?),+>)? for $impl_type:ident $(<$($type_generic:ident $(: $type_bound:tt $(+ $type_bound2:tt)*)?),+>)?) => { + unsafe impl <$($impl_generic $(: $impl_bound $(+ $impl_bound2)*)?),+> $crate::object::IsA<$impl_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)?> for $impl_type $(<$($type_generic $(: $type_bound $(+ $type_bound2)*)?),+>)? { } + + #[doc(hidden)] + impl <$($impl_generic $(: $impl_bound $(+ $impl_bound2)*)?),+> AsRef<$impl_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)?> for $impl_type $(<$($type_generic $(: $type_bound $(+ $type_bound2)*)?),+>)? { + fn as_ref(&self) -> &$impl_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)? { + $crate::object::Cast::upcast_ref(self) + } + } + }; + + (@munch_impls_generic <$($impl_generic:ident $(: $impl_bound:tt $(+ $impl_bound2:tt)*)?),+> $impl_trait:ident $(<$($trait_generic:ident $(: $trait_bound:tt $(+ $trait_bound2:tt)*)?),+>)? for $impl_type:ident $(<$($type_generic:ident $(: $type_bound:tt $(+ $type_bound2:tt)*)?),+>)?, $($implements:tt)*) => { + $crate::glib_object_wrapper!(@munch_impls_generic <$($impl_generic $(: $impl_bound $(+ $impl_bound2)*)?),+> $impl_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)? for $impl_type $(<$($type_generic $(: $type_bound $(+ $type_bound2)*)?),+>)?); + $crate::glib_object_wrapper!(@munch_impls_generic $($implements)*); + }; + // If there is no parent class, i.e. only glib::Object (@munch_first_impl $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, ) => { $crate::glib_object_wrapper!(@munch_impls $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, ); @@ -1185,34 +1219,90 @@ macro_rules! glib_object_wrapper { // This case is only for glib::Object itself below. All other cases have glib::Object in its // parent class list - (@object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $parent_type:ty, $ffi_name:ty, @ffi_class $ffi_class_name:ty, @type_ $get_type_expr:expr) => { + ( + @object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, $parent_type:ty, $ffi_name:ty, + @ffi_class $ffi_class_name:ty, + @type_ $get_type_expr:expr + ) => { $crate::glib_object_wrapper!( - @generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $impl_type, $parent_type, $ffi_name, $ffi_class_name, - @type_ $get_type_expr); + @generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, $parent_type, $ffi_name, $ffi_class_name, + @type_ $get_type_expr, + @default_casts true, + @checkers $crate::object::GenericObjectCastChecker, $crate::object::ObjectValueTypeChecker + ); #[doc(hidden)] unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::object::IsClass for $name $(<$($generic),+>)? { } }; - (@object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $parent_type:ty, $ffi_name:ty, - @type_ $get_type_expr:expr, @extends [$($extends:tt)*], @implements [$($implements:tt)*]) => { + ( + @object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, $parent_type:ty, $ffi_name:ty, + @type_ $get_type_expr:expr, + @extends [$($extends:tt)*], + @implements [$($implements:tt)*], + @implements_generic [$($implements_generic:tt)*] + ) => { $crate::glib_object_wrapper!( - @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $impl_type, $parent_type, $ffi_name, @ffi_class std::os::raw::c_void, - @type_ $get_type_expr, @extends [$($extends)*], @implements [$($implements)*] + @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, $parent_type, $ffi_name, + @ffi_class std::os::raw::c_void, + @type_ $get_type_expr, + @extends [$($extends)*], + @implements [$($implements)*], + @implements_generic [$($implements_generic)*] ); }; - (@object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $parent_type:ty, $ffi_name:ty, @ffi_class $ffi_class_name:ty, - @type_ $get_type_expr:expr, @extends [$($extends:tt)*], @implements [$($implements:tt)*]) => { + ( + @object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, $parent_type:ty, $ffi_name:ty, + @ffi_class $ffi_class_name:ty, + @type_ $get_type_expr:expr, + @extends [$($extends:tt)*], + @implements [$($implements:tt)*], + @implements_generic [$($implements_generic:tt)*] + ) => { $crate::glib_object_wrapper!( - @generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $impl_type, $parent_type, $ffi_name, $ffi_class_name, - @type_ $get_type_expr + @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, $parent_type, $ffi_name, + @ffi_class $ffi_class_name, + @type_ $get_type_expr, + @default_casts true, + @checkers $crate::object::GenericObjectCastChecker, $crate::object::ObjectValueTypeChecker, + @extends [$($extends)*], + @implements [$($implements)*], + @implements_generic [$($implements_generic)*] + ); + }; + + ( + @object [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, $parent_type:ty, $ffi_name:ty, + @ffi_class $ffi_class_name:ty, + @type_ $get_type_expr:expr, + @default_casts $default_casts:ident, + @checkers $cast_checker:ty, $value_checker:ty, + @extends [$($extends:tt)*], + @implements [$($implements:tt)*], + @implements_generic [$($implements_generic:tt)*] + ) => { + $crate::glib_object_wrapper!( + @generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, $parent_type, $ffi_name, $ffi_class_name, + @type_ $get_type_expr, + @default_casts $default_casts, + @checkers $cast_checker, $value_checker ); $crate::glib_object_wrapper!(@munch_first_impl $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $($extends)*); $crate::glib_object_wrapper!(@munch_impls $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $($implements)*); + $crate::glib_object_wrapper!(@munch_impls_generic $($implements_generic)*); + #[doc(hidden)] impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? AsRef<$crate::object::Object> for $name $(<$($generic),+>)? { fn as_ref(&self) -> &$crate::object::Object { @@ -1229,15 +1319,21 @@ macro_rules! glib_object_wrapper { // FIXME: Workaround for `glib::Object` not being `Send+Sync` but subclasses of it being both // if the impl struct is. - (@object_subclass [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $subclass:ty, - @extends [], @implements [$($implements:tt)*]) => { + ( + @object_subclass [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $subclass:ty, + @extends [], + @implements [$($implements:tt)*] + ) => { $crate::glib_object_wrapper!( @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $subclass, (), <$subclass as $crate::subclass::types::ObjectSubclass>::Instance, @ffi_class <$subclass as $crate::subclass::types::ObjectSubclass>::Class, @type_ $crate::translate::IntoGlib::into_glib(<$subclass as $crate::subclass::types::ObjectSubclassType>::type_()), - @extends [], @implements [$($implements)*] + @extends [], + @implements [$($implements)*], + @implements_generic [] ); unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::object::ObjectSubclassIs for $name $(<$($generic),+>)? { @@ -1245,15 +1341,21 @@ macro_rules! glib_object_wrapper { } }; - (@object_subclass [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $subclass:ty, - @extends [$($extends:tt)+], @implements [$($implements:tt)*]) => { + ( + @object_subclass [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $subclass:ty, + @extends [$($extends:tt)+], + @implements [$($implements:tt)*] + ) => { $crate::glib_object_wrapper!( @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $subclass, <$subclass as $crate::subclass::types::ObjectSubclass>::ParentType, <$subclass as $crate::subclass::types::ObjectSubclass>::Instance, @ffi_class <$subclass as $crate::subclass::types::ObjectSubclass>::Class, @type_ $crate::translate::IntoGlib::into_glib(<$subclass as $crate::subclass::types::ObjectSubclassType>::type_()), - @extends [$($extends)*], @implements [$($implements)*] + @extends [$($extends)*], + @implements [$($implements)*], + @implements_generic [] ); unsafe impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? $crate::object::ObjectSubclassIs for $name $(<$($generic),+>)? { @@ -1261,22 +1363,65 @@ macro_rules! glib_object_wrapper { } }; - (@interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $ffi_name:ty, - @type_ $get_type_expr:expr, @requires [$($requires:tt)*]) => { + ( + @interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, $ffi_name:ty, + @type_ $get_type_expr:expr, + @requires [$($requires:tt)*], + @requires_generic [$($requires_generic:tt)*] + ) => { $crate::glib_object_wrapper!( - @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $impl_type, $ffi_name, @ffi_class std::os::raw::c_void, - @type_ $get_type_expr, @requires [$($requires)*] + @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, $ffi_name, + @ffi_class std::os::raw::c_void, + @type_ $get_type_expr, + @requires [$($requires)*], + @requires_generic [$($requires_generic:tt)*] ); }; - (@interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, $impl_type:ty, $ffi_name:ty, @ffi_class $ffi_class_name:ty, - @type_ $get_type_expr:expr, @requires [$($requires:tt)*]) => { + ( + @interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, $ffi_name:ty, + @ffi_class $ffi_class_name:ty, + @type_ $get_type_expr:expr, + @requires [$($requires:tt)*], + @requires_generic [$($requires_generic:tt)*] + ) => { $crate::glib_object_wrapper!( - @generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $impl_type, (), $ffi_name, $ffi_class_name, - @type_ $get_type_expr + @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, $ffi_name, + @ffi_class $ffi_class_name, + @type_ $get_type_expr, + @default_casts true, + @checkers $crate::object::GenericObjectCastChecker, $crate::object::ObjectValueTypeChecker, + @requires [$($requires)*], + @requires_generic [$($requires_generic:tt)*] + ); + }; + + ( + @interface [$($attr:meta)*] $visibility:vis $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)?, + $impl_type:ty, + $ffi_name:ty, + @ffi_class $ffi_class_name:ty, + @type_ $get_type_expr:expr, + @default_casts $default_casts:ident, + @checkers $cast_checker:ty, $value_checker:ty, + @requires [$($requires:tt)*], + @requires_generic [$($requires_generic:tt)*] + ) => { + $crate::glib_object_wrapper!( + @generic_impl [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $impl_type, (), $ffi_name, $ffi_class_name, + @type_ $get_type_expr, + @default_casts $default_casts, + @checkers $cast_checker, $value_checker ); $crate::glib_object_wrapper!(@munch_impls $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $($requires)*); + $crate::glib_object_wrapper!(@munch_impls_generic $($requires_generic)*); + #[doc(hidden)] impl $(<$($generic $(: $bound $(+ $bound2)*)?),+>)? AsRef<$crate::object::Object> for $name $(<$($generic),+>)? { fn as_ref(&self) -> &$crate::object::Object { @@ -1495,7 +1640,7 @@ impl Drop for PropertyNotificationFreezeGuard { pub trait ObjectExt: ObjectType { // rustdoc-stripper-ignore-next /// Returns `true` if the object is an instance of (can be cast to) `T`. - fn is(&self) -> bool; + fn is(&self) -> bool; // rustdoc-stripper-ignore-next /// Returns the type of the object. @@ -2219,8 +2364,8 @@ pub trait ObjectExt: ObjectType { } impl ObjectExt for T { - fn is(&self) -> bool { - self.type_().is_a(U::static_type()) + fn is(&self) -> bool { + U::CastChecker::check(self) } fn type_(&self) -> Type { diff --git a/glib/src/wrapper.rs b/glib/src/wrapper.rs index 1ad0610cf28c..9f4512a41177 100644 --- a/glib/src/wrapper.rs +++ b/glib/src/wrapper.rs @@ -356,46 +356,66 @@ macro_rules! wrapper { // Object, no parents ( $(#[$attr:meta])* - $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Object<$ffi_name:ty $(, $ffi_class_name:ty)?>) $(@implements $($implements:path),+)?; + $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? + (Object<$ffi_name:ty $(, $ffi_class_name:ty)?>) + $(@implements $($implements:path),+)? + $(, @implements_generic $(<$($impl_generic:ident $(: $impl_bound:tt $(+ $impl_bound2:tt)*)?),+> $impl_trait:ident $(<$($trait_generic:ident $(: $trait_bound:tt $(+ $trait_bound2:tt)*)?),+>)? for $impl_type:ident $(<$($type_generic:ident $(: $type_bound:tt $(+ $type_bound2:tt)*)?),+>)?),+)? + $(, @default_casts $default_casts:ident + , @checkers $cast_checker:ty, $value_checker:ty $(,)?)?; match fn { type_ => || $get_type_expr:expr, } ) => { $crate::glib_object_wrapper!( - @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, (), $ffi_name, + @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + *mut std::os::raw::c_void, (), $ffi_name, $( @ffi_class $ffi_class_name ,)? @type_ $get_type_expr, + $( @default_casts $default_casts, + @checkers $cast_checker, $value_checker, )? @extends [], - @implements [$($($implements),+)?] + @implements [$($($implements),+)?], + @implements_generic [$($(<$($impl_generic $(: $impl_bound $(+ $impl_bound2)*)?),+> $impl_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)? for $impl_type $(<$($type_generic $(: $type_bound $(+ $type_bound2)*)?),+>)?),+)?] ); }; // Object, parents ( $(#[$attr:meta])* - $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Object<$ffi_name:ty $(, $ffi_class_name:ty)?>) @extends $($extends:path),+ $(, @implements $($implements:path),+)?; + $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Object<$ffi_name:ty $(, $ffi_class_name:ty)?>) + @extends $($extends:path),+ + $(, @implements $($implements:path),+)? + $(, @implements_generic $(<$($impl_generic:ident $(: $impl_bound:tt $(+ $impl_bound2:tt)*)?),+> $impl_trait:ident $(<$($trait_generic:ident $(: $trait_bound:tt $(+ $trait_bound2:tt)*)?),+>)? for $impl_type:ident $(<$($type_generic:ident $(: $type_bound:tt $(+ $type_bound2:tt)*)?),+>)?),+)? + $(, @default_casts $default_casts:ident + , @checkers $cast_checker:ty, $value_checker:ty $(,)?)?; match fn { type_ => || $get_type_expr:expr, } ) => { $crate::glib_object_wrapper!( - @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, (), $ffi_name, + @object [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + *mut std::os::raw::c_void, (), $ffi_name, $( @ffi_class $ffi_class_name ,)? @type_ $get_type_expr, + $( @default_casts $default_casts, + @checkers $cast_checker, $value_checker, )? @extends [$($extends),+], - @implements [$($($implements),+)?] + @implements [$($($implements),+)?], + @implements_generic [$($(<$($impl_generic $(: $impl_bound $(+ $impl_bound2)*)?),+> $impl_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)? for $impl_type $(<$($type_generic $(: $type_bound $(+ $type_bound2)*)?),+>)?),+)?] ); }; // ObjectSubclass, no parents ( $(#[$attr:meta])* - $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectSubclass<$subclass:ty>) $(@implements $($implements:path),+)?; + $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectSubclass<$subclass:ty>) + $(@implements $($implements:path),+)?; ) => { $crate::glib_object_wrapper!( - @object_subclass [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $subclass, + @object_subclass [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $subclass, @extends [], @implements [$($($implements),+)?] ); @@ -404,10 +424,13 @@ macro_rules! wrapper { // ObjectSubclass, parents ( $(#[$attr:meta])* - $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectSubclass<$subclass:ty>) @extends $($extends:path),+ $(, @implements $($implements:path),+)?; + $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectSubclass<$subclass:ty>) + @extends $($extends:path),+ + $(, @implements $($implements:path),+)?; ) => { $crate::glib_object_wrapper!( - @object_subclass [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, $subclass, + @object_subclass [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + $subclass, @extends [$($extends),+], @implements [$($($implements),+)?] ); @@ -416,30 +439,41 @@ macro_rules! wrapper { // Interface ( $(#[$attr:meta])* - $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Interface<$ffi_name:ty $(, $ffi_class_name:ty)?>) $(@requires $($requires:path),+)?; + $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (Interface<$ffi_name:ty $(, $ffi_class_name:ty)?>) + $(@requires $($requires:path),+)? + $(, @requires_generic $(<$($requires_generic:ident $(: $requires_bound:tt $(+ $requires_bound2:tt)*)?),+> $requires_trait:ident $(<$($trait_generic:ident $(: $trait_bound:tt $(+ $trait_bound2:tt)*)?),+>)? for $requires_type:ident $(<$($type_generic:ident $(: $type_bound:tt $(+ $type_bound2:tt)*)?),+>)?),+)? + $(, @default_casts $default_casts:ident + , @checkers $cast_checker:ty, $value_checker:ty $(,)?)?; match fn { type_ => || $get_type_expr:expr, } ) => { $crate::glib_object_wrapper!( - @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, $ffi_name, + @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + *mut std::os::raw::c_void, $ffi_name, $( @ffi_class $ffi_class_name ,)? @type_ $get_type_expr, - @requires [$( $($requires),+ )?] + $( @default_casts $default_casts, + @checkers $cast_checker, $value_checker, )? + @requires [$( $($requires),+ )?], + @requires_generic [$($(<$($requires_generic $(: $requires_bound $(+ $requires_bound2)*)?),+> $requires_trait $(<$($trait_generic $(: $trait_bound $(+ $trait_bound2)*)?),+>)? for $requires_type $(<$($type_generic $(: $type_bound $(+ $type_bound2)*)?),+>)?),+)?] ); }; // ObjectInterface ( $(#[$attr:meta])* - $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectInterface<$iface_name:ty>) $(@requires $($requires:path),+)?; + $visibility:vis struct $name:ident $(<$($generic:ident $(: $bound:tt $(+ $bound2:tt)*)?),+>)? (ObjectInterface<$iface_name:ty>) + $(@requires $($requires:path),+)?; ) => { $crate::glib_object_wrapper!( - @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, *mut std::os::raw::c_void, std::os::raw::c_void, + @interface [$($attr)*] $visibility $name $(<$($generic $(: $bound $(+ $bound2)*)?),+>)?, + *mut std::os::raw::c_void, std::os::raw::c_void, @ffi_class $iface_name, @type_ $crate::translate::IntoGlib::into_glib(<$iface_name as $crate::subclass::interface::ObjectInterfaceType>::type_()), - @requires [$( $($requires),+ )?] + @requires [$( $($requires),+ )?], + @requires_generic [] ); }; } From 2ca49d51e3ec52a5e4c642c2edf5b785bbf256c4 Mon Sep 17 00:00:00 2001 From: Jason Francis Date: Mon, 14 Mar 2022 09:33:53 -0400 Subject: [PATCH 2/2] gio: Add type parameter to ListModel and ListStore --- gio/Gir.toml | 30 ---- gio/src/auto/list_model.rs | 121 ---------------- gio/src/auto/list_store.rs | 138 ------------------ gio/src/auto/mod.rs | 8 - gio/src/lib.rs | 2 + gio/src/list_model.rs | 203 ++++++++++++++++++++++++-- gio/src/list_store.rs | 258 +++++++++++++++++++++++++++++---- gio/src/prelude.rs | 2 +- gio/src/subclass/list_model.rs | 49 ++++--- 9 files changed, 455 insertions(+), 356 deletions(-) delete mode 100644 gio/src/auto/list_model.rs delete mode 100644 gio/src/auto/list_store.rs diff --git a/gio/Gir.toml b/gio/Gir.toml index 9091dbe386e7..faadde9328db 100644 --- a/gio/Gir.toml +++ b/gio/Gir.toml @@ -788,36 +788,6 @@ manual_traits = ["IOStreamExtManual"] #readonly manual = true -[[object]] -name = "Gio.ListModel" -status = "generate" -manual_traits = ["ListModelExtManual"] - # Can get removed when gir shadow annotations are implemented (gtk-rs/gir#1112) - [[object.function]] - name = "get_item" - ignore = true - [[object.function]] - name = "get_object" - rename = "item" - -[[object]] -name = "Gio.ListStore" -status = "generate" -generate_builder = true - [[object.function]] - name = "insert_sorted" - manual = true - [[object.function]] - name = "sort" - manual = true - [[object.function]] - name = "splice" - # More generic arguments - manual = true - [[object.function]] - name = "find_with_equal_func" - ignore = true # See https://gitlab.gnome.org/GNOME/glib/-/issues/2447 - [[object]] name = "Gio.MemoryInputStream" status = "generate" diff --git a/gio/src/auto/list_model.rs b/gio/src/auto/list_model.rs deleted file mode 100644 index fa95dab152ea..000000000000 --- a/gio/src/auto/list_model.rs +++ /dev/null @@ -1,121 +0,0 @@ -// This file was generated by gir (https://github.com/gtk-rs/gir) -// from gir-files (https://github.com/gtk-rs/gir-files) -// DO NOT EDIT - -use glib::object::Cast; -use glib::object::IsA; -use glib::signal::connect_raw; -use glib::signal::SignalHandlerId; -use glib::translate::*; -use std::boxed::Box as Box_; -use std::fmt; -use std::mem::transmute; - -glib::wrapper! { - #[doc(alias = "GListModel")] - pub struct ListModel(Interface); - - match fn { - type_ => || ffi::g_list_model_get_type(), - } -} - -impl ListModel { - pub const NONE: Option<&'static ListModel> = None; -} - -pub trait ListModelExt: 'static { - #[doc(alias = "g_list_model_get_item_type")] - #[doc(alias = "get_item_type")] - fn item_type(&self) -> glib::types::Type; - - #[doc(alias = "g_list_model_get_n_items")] - #[doc(alias = "get_n_items")] - fn n_items(&self) -> u32; - - #[doc(alias = "g_list_model_get_object")] - #[doc(alias = "get_object")] - fn item(&self, position: u32) -> Option; - - #[doc(alias = "g_list_model_items_changed")] - fn items_changed(&self, position: u32, removed: u32, added: u32); - - #[doc(alias = "items-changed")] - fn connect_items_changed(&self, f: F) - -> SignalHandlerId; -} - -impl> ListModelExt for O { - fn item_type(&self) -> glib::types::Type { - unsafe { - from_glib(ffi::g_list_model_get_item_type( - self.as_ref().to_glib_none().0, - )) - } - } - - fn n_items(&self) -> u32 { - unsafe { ffi::g_list_model_get_n_items(self.as_ref().to_glib_none().0) } - } - - fn item(&self, position: u32) -> Option { - unsafe { - from_glib_full(ffi::g_list_model_get_object( - self.as_ref().to_glib_none().0, - position, - )) - } - } - - fn items_changed(&self, position: u32, removed: u32, added: u32) { - unsafe { - ffi::g_list_model_items_changed( - self.as_ref().to_glib_none().0, - position, - removed, - added, - ); - } - } - - fn connect_items_changed( - &self, - f: F, - ) -> SignalHandlerId { - unsafe extern "C" fn items_changed_trampoline< - P: IsA, - F: Fn(&P, u32, u32, u32) + 'static, - >( - this: *mut ffi::GListModel, - position: libc::c_uint, - removed: libc::c_uint, - added: libc::c_uint, - f: glib::ffi::gpointer, - ) { - let f: &F = &*(f as *const F); - f( - ListModel::from_glib_borrow(this).unsafe_cast_ref(), - position, - removed, - added, - ) - } - unsafe { - let f: Box_ = Box_::new(f); - connect_raw( - self.as_ptr() as *mut _, - b"items-changed\0".as_ptr() as *const _, - Some(transmute::<_, unsafe extern "C" fn()>( - items_changed_trampoline:: as *const (), - )), - Box_::into_raw(f), - ) - } - } -} - -impl fmt::Display for ListModel { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("ListModel") - } -} diff --git a/gio/src/auto/list_store.rs b/gio/src/auto/list_store.rs deleted file mode 100644 index a85a30fb112c..000000000000 --- a/gio/src/auto/list_store.rs +++ /dev/null @@ -1,138 +0,0 @@ -// This file was generated by gir (https://github.com/gtk-rs/gir) -// from gir-files (https://github.com/gtk-rs/gir-files) -// DO NOT EDIT - -use crate::ListModel; -use glib::object::Cast; -use glib::object::IsA; -use glib::translate::*; -use glib::StaticType; -use glib::ToValue; -use std::fmt; -#[cfg(any(feature = "v2_64", feature = "dox"))] -#[cfg_attr(feature = "dox", doc(cfg(feature = "v2_64")))] -use std::mem; - -glib::wrapper! { - #[doc(alias = "GListStore")] - pub struct ListStore(Object) @implements ListModel; - - match fn { - type_ => || ffi::g_list_store_get_type(), - } -} - -impl ListStore { - #[doc(alias = "g_list_store_new")] - pub fn new(item_type: glib::types::Type) -> ListStore { - unsafe { from_glib_full(ffi::g_list_store_new(item_type.into_glib())) } - } - - // rustdoc-stripper-ignore-next - /// Creates a new builder-pattern struct instance to construct [`ListStore`] objects. - /// - /// This method returns an instance of [`ListStoreBuilder`](crate::builders::ListStoreBuilder) which can be used to create [`ListStore`] objects. - pub fn builder() -> ListStoreBuilder { - ListStoreBuilder::default() - } - - #[doc(alias = "g_list_store_append")] - pub fn append(&self, item: &impl IsA) { - unsafe { - ffi::g_list_store_append(self.to_glib_none().0, item.as_ref().to_glib_none().0); - } - } - - #[cfg(any(feature = "v2_64", feature = "dox"))] - #[cfg_attr(feature = "dox", doc(cfg(feature = "v2_64")))] - #[doc(alias = "g_list_store_find")] - pub fn find(&self, item: &impl IsA) -> Option { - unsafe { - let mut position = mem::MaybeUninit::uninit(); - let ret = from_glib(ffi::g_list_store_find( - self.to_glib_none().0, - item.as_ref().to_glib_none().0, - position.as_mut_ptr(), - )); - let position = position.assume_init(); - if ret { - Some(position) - } else { - None - } - } - } - - #[doc(alias = "g_list_store_insert")] - pub fn insert(&self, position: u32, item: &impl IsA) { - unsafe { - ffi::g_list_store_insert( - self.to_glib_none().0, - position, - item.as_ref().to_glib_none().0, - ); - } - } - - #[doc(alias = "g_list_store_remove")] - pub fn remove(&self, position: u32) { - unsafe { - ffi::g_list_store_remove(self.to_glib_none().0, position); - } - } - - #[doc(alias = "g_list_store_remove_all")] - pub fn remove_all(&self) { - unsafe { - ffi::g_list_store_remove_all(self.to_glib_none().0); - } - } -} - -impl Default for ListStore { - fn default() -> Self { - glib::object::Object::new::(&[]) - .expect("Can't construct ListStore object with default parameters") - } -} - -#[derive(Clone, Default)] -// rustdoc-stripper-ignore-next -/// A [builder-pattern] type to construct [`ListStore`] objects. -/// -/// [builder-pattern]: https://doc.rust-lang.org/1.0.0/style/ownership/builders.html -#[must_use = "The builder must be built to be used"] -pub struct ListStoreBuilder { - item_type: Option, -} - -impl ListStoreBuilder { - // rustdoc-stripper-ignore-next - /// Create a new [`ListStoreBuilder`]. - pub fn new() -> Self { - Self::default() - } - - // rustdoc-stripper-ignore-next - /// Build the [`ListStore`]. - #[must_use = "Building the object from the builder is usually expensive and is not expected to have side effects"] - pub fn build(self) -> ListStore { - let mut properties: Vec<(&str, &dyn ToValue)> = vec![]; - if let Some(ref item_type) = self.item_type { - properties.push(("item-type", item_type)); - } - glib::Object::new::(&properties) - .expect("Failed to create an instance of ListStore") - } - - pub fn item_type(mut self, item_type: glib::types::Type) -> Self { - self.item_type = Some(item_type); - self - } -} - -impl fmt::Display for ListStore { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str("ListStore") - } -} diff --git a/gio/src/auto/mod.rs b/gio/src/auto/mod.rs index 76b3ee08e775..be19e4fc6827 100644 --- a/gio/src/auto/mod.rs +++ b/gio/src/auto/mod.rs @@ -179,12 +179,6 @@ pub use self::initable::Initable; mod input_stream; pub use self::input_stream::InputStream; -mod list_model; -pub use self::list_model::ListModel; - -mod list_store; -pub use self::list_store::ListStore; - mod loadable_icon; pub use self::loadable_icon::LoadableIcon; @@ -723,7 +717,6 @@ pub mod traits { pub use super::initable::InitableExt; pub use super::input_stream::InputStreamExt; pub use super::io_stream::IOStreamExt; - pub use super::list_model::ListModelExt; pub use super::loadable_icon::LoadableIconExt; pub use super::memory_input_stream::MemoryInputStreamExt; #[cfg(any(feature = "v2_64", feature = "dox"))] @@ -799,5 +792,4 @@ pub mod builders { pub use super::converter_output_stream::ConverterOutputStreamBuilder; pub use super::data_input_stream::DataInputStreamBuilder; pub use super::data_output_stream::DataOutputStreamBuilder; - pub use super::list_store::ListStoreBuilder; } diff --git a/gio/src/lib.rs b/gio/src/lib.rs index 4b01c1b5091e..5ee1b2632e75 100644 --- a/gio/src/lib.rs +++ b/gio/src/lib.rs @@ -50,7 +50,9 @@ mod initable; mod input_stream; pub use crate::input_stream::{InputStreamAsyncBufRead, InputStreamRead}; mod list_model; +pub use list_model::ListModel; mod list_store; +pub use list_store::ListStore; #[cfg(test)] mod memory_input_stream; #[cfg(test)] diff --git a/gio/src/list_model.rs b/gio/src/list_model.rs index 8f86bb1d15a3..08ea13aa6418 100644 --- a/gio/src/list_model.rs +++ b/gio/src/list_model.rs @@ -1,33 +1,214 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::prelude::*; -use crate::ListModel; +use glib::object::Cast; +use glib::object::IsA; +use glib::signal::connect_raw; +use glib::signal::SignalHandlerId; +use glib::translate::*; +use glib::Object; +use std::boxed::Box as Box_; +use std::mem::transmute; + +glib::wrapper! { + pub struct ListModel)>(Interface), + @requires_generic + ) + (IsA), Sub: (IsA) + (IsA)> ListModel for ListModel, + @default_casts false, + @checkers ListModelCastChecker, ListModelValueChecker; + + match fn { + type_ => || ffi::g_list_model_get_type(), + } +} + +#[doc(hidden)] +pub struct ListModelCastChecker(std::marker::PhantomData); + +impl> glib::object::ObjectCastChecker> for ListModelCastChecker { + fn check(obj: &U) -> bool { + if glib::object::GenericObjectCastChecker::>::check(obj) { + let item_type: glib::Type = + unsafe { from_glib(ffi::g_list_model_get_item_type(obj.as_ptr() as *mut _)) }; + if item_type.is_a(T::static_type()) { + return true; + } + } + false + } +} + +#[doc(hidden)] +pub struct ListModelValueChecker(std::marker::PhantomData); + +unsafe impl> glib::value::ValueTypeChecker for ListModelValueChecker { + type Error = glib::value::ValueTypeMismatchOrNoneError; + + fn check(value: &glib::Value) -> Result<(), Self::Error> { + glib::object::ObjectValueTypeChecker::>::check(value)?; + let model: &ListModel = unsafe { glib::value::FromValue::from_value(value) }; + let model_type = model.item_type(); + let expected = T::static_type(); + if !model_type.is_a(expected) { + return Err(glib::value::ValueTypeMismatchError::new(model_type, expected).into()); + } + + Ok(()) + } +} + +impl> ListModel { + pub const NONE: Option<&'static ListModel> = None; +} + +impl> std::fmt::Display for ListModel { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("ListModel") + } +} + +pub trait ListModelExt: 'static { + #[doc(alias = "g_list_model_get_item_type")] + #[doc(alias = "get_item_type")] + fn item_type(&self) -> glib::types::Type; + + #[doc(alias = "g_list_model_get_n_items")] + #[doc(alias = "get_n_items")] + fn n_items(&self) -> u32; + + #[doc(alias = "g_list_model_get_object")] + #[doc(alias = "get_object")] + fn object(&self, position: u32) -> Option; + + #[doc(alias = "g_list_model_get_item")] + #[doc(alias = "get_item")] + fn item>(&self, position: u32) -> Option + where + Self: IsA>; + + #[doc(alias = "g_list_model_items_changed")] + fn items_changed(&self, position: u32, removed: u32, added: u32); + + #[doc(alias = "items-changed")] + fn connect_items_changed(&self, f: F) + -> SignalHandlerId; -pub trait ListModelExtManual: Sized { // rustdoc-stripper-ignore-next /// Get an immutable snapshot of the container inside the `ListModel`. /// Any modification done to the returned container `Vec` will not be /// reflected on the `ListModel`. - fn snapshot(&self) -> Vec; + fn snapshot>(&self) -> Vec + where + Self: IsA>; } -impl> ListModelExtManual for T { - fn snapshot(&self) -> Vec { - let mut res = Vec::with_capacity(self.n_items() as usize); - for i in 0..self.n_items() { +impl>> ListModelExt for O { + #[inline] + fn item_type(&self) -> glib::types::Type { + unsafe { + from_glib(ffi::g_list_model_get_item_type( + self.as_ref().to_glib_none().0, + )) + } + } + + #[inline] + fn n_items(&self) -> u32 { + unsafe { ffi::g_list_model_get_n_items(self.as_ref().to_glib_none().0) } + } + + #[inline] + fn object(&self, position: u32) -> Option { + unsafe { + from_glib_full(ffi::g_list_model_get_object( + self.upcast_ref::>().to_glib_none().0, + position, + )) + } + } + + fn item>(&self, position: u32) -> Option + where + Self: IsA>, + { + self.object(position).map(|o| { + o.downcast().unwrap_or_else(|o| { + panic!( + "List model type mismatch. Actual {:?}, requested {:?}", + o.type_(), + U::static_type() + ); + }) + }) + } + + #[inline] + fn items_changed(&self, position: u32, removed: u32, added: u32) { + unsafe { + ffi::g_list_model_items_changed( + self.as_ref().to_glib_none().0, + position, + removed, + added, + ); + } + } + + fn connect_items_changed( + &self, + f: F, + ) -> SignalHandlerId { + unsafe extern "C" fn items_changed_trampoline< + P: IsA>, + F: Fn(&P, u32, u32, u32) + 'static, + >( + this: *mut ffi::GListModel, + position: libc::c_uint, + removed: libc::c_uint, + added: libc::c_uint, + f: glib::ffi::gpointer, + ) { + let f: &F = &*(f as *const F); + f( + ListModel::::from_glib_borrow(this).unsafe_cast_ref(), + position, + removed, + added, + ) + } + unsafe { + let f: Box_ = Box_::new(f); + connect_raw( + self.as_ptr() as *mut _, + b"items-changed\0".as_ptr() as *const _, + Some(transmute::<_, unsafe extern "C" fn()>( + items_changed_trampoline:: as *const (), + )), + Box_::into_raw(f), + ) + } + } + + fn snapshot>(&self) -> Vec + where + Self: IsA>, + { + let count = self.n_items(); + let mut res = Vec::with_capacity(count as usize); + for i in 0..count { res.push(self.item(i).unwrap()) } res } } -impl std::iter::IntoIterator for ListModel { - type Item = glib::Object; - type IntoIter = std::vec::IntoIter; +impl + IsA> std::iter::IntoIterator for ListModel { + type Item = T; + type IntoIter = std::vec::IntoIter; // rustdoc-stripper-ignore-next /// Returns an iterator with the elements returned by `ListModel::snapshot` fn into_iter(self) -> Self::IntoIter { - self.snapshot().into_iter() + self.snapshot::().into_iter() } } diff --git a/gio/src/list_store.rs b/gio/src/list_store.rs index f2eb2946b152..87654885698b 100644 --- a/gio/src/list_store.rs +++ b/gio/src/list_store.rs @@ -1,27 +1,137 @@ // Take a look at the license at the top of the repository in the LICENSE file. -use crate::auto::traits::ListModelExt; -use crate::ListStore; +use crate::prelude::ListModelExt; +use crate::ListModel; use glib::translate::*; -use glib::{IsA, Object}; +use glib::{Cast, IsA, Object}; use std::cmp::Ordering; +#[cfg(any(feature = "v2_64", feature = "dox"))] +#[cfg_attr(feature = "dox", doc(cfg(feature = "v2_64")))] +use std::mem; + +glib::wrapper! { + pub struct ListStore)>(Object), + @implements_generic + ) + (IsA), Sub: (IsA) + (IsA)> ListModel for ListStore, + @default_casts true, + @checkers ListStoreCastChecker, ListStoreValueChecker; + + match fn { + type_ => || ffi::g_list_store_get_type(), + } +} + +#[doc(hidden)] +pub struct ListStoreCastChecker(std::marker::PhantomData); + +impl> glib::object::ObjectCastChecker> for ListStoreCastChecker { + fn check(obj: &U) -> bool { + if glib::object::GenericObjectCastChecker::>::check(obj) { + let item_type: glib::Type = + unsafe { from_glib(ffi::g_list_model_get_item_type(obj.as_ptr() as *mut _)) }; + if item_type == T::static_type() { + return true; + } + } + false + } +} + +#[doc(hidden)] +pub struct ListStoreValueChecker(std::marker::PhantomData); + +unsafe impl> glib::value::ValueTypeChecker for ListStoreValueChecker { + type Error = glib::value::ValueTypeMismatchOrNoneError; + + fn check(value: &glib::Value) -> Result<(), Self::Error> { + glib::object::ObjectValueTypeChecker::>::check(value)?; + let store: &ListStore = unsafe { glib::value::FromValue::from_value(value) }; + let store_type = store.item_type(); + let expected = T::static_type(); + if store_type != expected { + return Err(glib::value::ValueTypeMismatchError::new(store_type, expected).into()); + } + + Ok(()) + } +} + +impl> ListStore { + #[doc(alias = "g_list_store_new")] + pub fn new() -> Self { + unsafe { from_glib_full(ffi::g_list_store_new(T::static_type().into_glib())) } + } + + #[doc(alias = "g_list_store_append")] + pub fn append(&self, item: &(impl IsA + IsA)) { + unsafe { + ffi::g_list_store_append( + self.to_glib_none().0, + item.upcast_ref::().to_glib_none().0, + ); + } + } + + #[cfg(any(feature = "v2_64", feature = "dox"))] + #[cfg_attr(feature = "dox", doc(cfg(feature = "v2_64")))] + #[doc(alias = "g_list_store_find")] + pub fn find(&self, item: &(impl IsA + IsA)) -> Option { + unsafe { + let mut position = mem::MaybeUninit::uninit(); + let ret = from_glib(ffi::g_list_store_find( + self.to_glib_none().0, + item.upcast_ref::().to_glib_none().0, + position.as_mut_ptr(), + )); + let position = position.assume_init(); + if ret { + Some(position) + } else { + None + } + } + } + + #[doc(alias = "g_list_store_insert")] + pub fn insert(&self, position: u32, item: &(impl IsA + IsA)) { + unsafe { + ffi::g_list_store_insert( + self.to_glib_none().0, + position, + item.upcast_ref::().to_glib_none().0, + ); + } + } + + #[doc(alias = "g_list_store_remove")] + pub fn remove(&self, position: u32) { + unsafe { + ffi::g_list_store_remove(self.to_glib_none().0, position); + } + } + + #[doc(alias = "g_list_store_remove_all")] + pub fn remove_all(&self) { + unsafe { + ffi::g_list_store_remove_all(self.to_glib_none().0); + } + } -impl ListStore { #[doc(alias = "g_list_store_insert_sorted")] - pub fn insert_sorted, F: FnMut(&Object, &Object) -> Ordering>( + pub fn insert_sorted + IsA, F: FnMut(&T, &T) -> Ordering>( &self, item: &P, compare_func: F, ) -> u32 { unsafe { let mut func = compare_func; - let func_obj: &mut (dyn FnMut(&Object, &Object) -> Ordering) = &mut func; - let func_ptr = &func_obj as *const &mut (dyn FnMut(&Object, &Object) -> Ordering) - as glib::ffi::gpointer; + let func_obj: &mut (dyn FnMut(&T, &T) -> Ordering) = &mut func; + let func_ptr = + &func_obj as *const &mut (dyn FnMut(&T, &T) -> Ordering) as glib::ffi::gpointer; ffi::g_list_store_insert_sorted( self.to_glib_none().0, - item.as_ref().to_glib_none().0, + item.upcast_ref::().to_glib_none().0, Some(compare_func_trampoline), func_ptr, ) @@ -29,12 +139,12 @@ impl ListStore { } #[doc(alias = "g_list_store_sort")] - pub fn sort Ordering>(&self, compare_func: F) { + pub fn sort Ordering>(&self, compare_func: F) { unsafe { let mut func = compare_func; - let func_obj: &mut (dyn FnMut(&Object, &Object) -> Ordering) = &mut func; - let func_ptr = &func_obj as *const &mut (dyn FnMut(&Object, &Object) -> Ordering) - as glib::ffi::gpointer; + let func_obj: &mut (dyn FnMut(&T, &T) -> Ordering) = &mut func; + let func_ptr = + &func_obj as *const &mut (dyn FnMut(&T, &T) -> Ordering) as glib::ffi::gpointer; ffi::g_list_store_sort( self.to_glib_none().0, @@ -45,7 +155,7 @@ impl ListStore { } #[doc(alias = "g_list_store_splice")] - pub fn splice(&self, position: u32, n_removals: u32, additions: &[impl IsA]) { + pub fn splice(&self, position: u32, n_removals: u32, additions: &[impl IsA + IsA]) { let n_additions = additions.len() as u32; unsafe { let additions = additions.as_ptr() as *mut *mut glib::gobject_ffi::GObject; @@ -62,7 +172,7 @@ impl ListStore { // rustdoc-stripper-ignore-next /// Appends all elements in a slice to the `ListStore`. - pub fn extend_from_slice(&self, additions: &[impl IsA]) { + pub fn extend_from_slice(&self, additions: &[impl IsA + IsA]) { self.splice(self.n_items() - 1, 0, additions) } } @@ -80,8 +190,20 @@ unsafe extern "C" fn compare_func_trampoline( (*func)(&a, &b).into_glib() } -impl> std::iter::Extend for ListStore { - fn extend>(&mut self, iter: T) { +impl> Default for ListStore { + fn default() -> Self { + Self::new() + } +} + +impl> std::fmt::Display for ListStore { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.write_str("ListStore") + } +} + +impl + IsA, A: AsRef> std::iter::Extend for ListStore { + fn extend>(&mut self, iter: I) { let additions = iter .into_iter() .map(|o| o.as_ref().clone()) @@ -93,28 +215,106 @@ impl> std::iter::Extend for ListStore { #[cfg(test)] mod tests { use crate::prelude::*; + use crate::Application; + use crate::File; + use crate::ListModel; use crate::ListStore; + use glib::Object; + use glib::StaticType; + use glib::ToValue; + use std::path::PathBuf; + + #[test] + fn type_checking() { + let store = ListStore::::new(); + let f = File::for_path("/"); + store.append(&f); + let _: Object = store.item(0).unwrap(); + let file = store.item(0); + assert_eq!(file.as_ref(), Some(&f)); + assert_eq!(file.unwrap().path().unwrap(), PathBuf::from("/")); + + assert!(store.dynamic_cast_ref::>().is_none()); + + let object = store.upcast_ref::(); + object.downcast_ref::>().unwrap(); + object.downcast_ref::>().unwrap(); + assert!(object.downcast_ref::>().is_none()); + assert!(object.downcast_ref::>().is_none()); + assert!(object.downcast_ref::>().is_none()); + + let typed_model = store.upcast_ref::>(); + assert_eq!(typed_model.item_type(), File::static_type()); + typed_model.downcast_ref::>().unwrap(); + + let object_model = store.upcast_ref::>(); + object_model.downcast_ref::>().unwrap(); + object_model.downcast_ref::>().unwrap(); + assert!(object_model.downcast_ref::>().is_none()); + + let value = store.to_value(); + value.get::>().unwrap(); + value.get::>>().unwrap().unwrap(); + value.get::>().unwrap(); + value.get::>().unwrap(); + value.get::>>().unwrap().unwrap(); + value.get::>>().unwrap().unwrap(); + assert!(value.get::>().is_err()); + assert!(value.get::>().is_err()); + assert!(value.get::>().is_err()); + + let none = None::>; + let none_value = none.to_value(); + assert!(none_value.get::>().is_err()); + assert!(none_value.get::>().is_err()); + assert!(none_value.get::>().is_err()); + assert!(none_value.get::>().is_err()); + assert!(none_value + .get::>>() + .unwrap() + .is_none()); + assert!(none_value + .get::>>() + .unwrap() + .is_none()); + assert!(none_value + .get::>>() + .unwrap() + .is_none()); + assert!(none_value + .get::>>() + .unwrap() + .is_none()); + assert!(none_value + .get::>>() + .unwrap() + .is_none()); + assert!(none_value + .get::>>() + .unwrap() + .is_none()); + } #[test] fn splice() { - let item0 = ListStore::new(ListStore::static_type()); - let item1 = ListStore::new(ListStore::static_type()); - let list = ListStore::new(ListStore::static_type()); + let item0 = ListStore::::new(); + let item1 = ListStore::::new(); + let list = ListStore::>::new(); list.splice(0, 0, &[item0.clone(), item1.clone()]); - assert_eq!(list.item(0), Some(item0.upcast())); - assert_eq!(list.item(1), Some(item1.upcast())); + assert_eq!(list.item(0), Some(item0)); + assert_eq!(list.item(1), Some(item1)); } #[test] fn extend() { - let item0 = ListStore::new(ListStore::static_type()); - let item1 = ListStore::new(ListStore::static_type()); - let mut list = ListStore::new(ListStore::static_type()); + let item0 = ListStore::::new(); + let item1 = ListStore::::new(); + let mut list = ListStore::>::new(); list.extend(&[&item0, &item1]); - assert_eq!(list.item(0).as_ref(), Some(item0.upcast_ref())); - assert_eq!(list.item(1).as_ref(), Some(item1.upcast_ref())); + assert_eq!(list.item(0).as_ref(), Some(&item0)); + assert_eq!(list.item(1).as_ref(), Some(&item1)); list.extend(&[item0.clone(), item1.clone()]); - assert_eq!(list.item(2).as_ref(), Some(item0.upcast_ref())); - assert_eq!(list.item(3).as_ref(), Some(item1.upcast_ref())); + assert_eq!(list.item(2).as_ref(), Some(&item0)); + assert_eq!(list.item(3).as_ref(), Some(&item1)); } } diff --git a/gio/src/prelude.rs b/gio/src/prelude.rs index a59cfacca4db..f8b925b516bb 100644 --- a/gio/src/prelude.rs +++ b/gio/src/prelude.rs @@ -26,7 +26,7 @@ pub use crate::inet_address::InetAddressExtManual; pub use crate::initable::InitableError; pub use crate::input_stream::InputStreamExtManual; pub use crate::io_stream::IOStreamExtManual; -pub use crate::list_model::ListModelExtManual; +pub use crate::list_model::ListModelExt; pub use crate::output_stream::OutputStreamExtManual; pub use crate::pollable_input_stream::PollableInputStreamExtManual; pub use crate::pollable_output_stream::PollableOutputStreamExtManual; diff --git a/gio/src/subclass/list_model.rs b/gio/src/subclass/list_model.rs index 4ab34ef3f53f..5e3eae68274e 100644 --- a/gio/src/subclass/list_model.rs +++ b/gio/src/subclass/list_model.rs @@ -3,7 +3,7 @@ use crate::ListModel; use glib::subclass::prelude::*; use glib::translate::*; -use glib::{Cast, IsA, ObjectExt}; +use glib::{Cast, IsA, Object, ObjectExt}; use once_cell::sync::Lazy; pub trait ListModelImpl: ObjectImpl { @@ -12,26 +12,31 @@ pub trait ListModelImpl: ObjectImpl { #[doc(alias = "get_n_items")] fn n_items(&self, list_model: &Self::Type) -> u32; #[doc(alias = "get_item")] - fn item(&self, list_model: &Self::Type, position: u32) -> Option; + fn item(&self, list_model: &Self::Type, position: u32) -> Option; } pub trait ListModelImplExt: ObjectSubclass { fn parent_item_type(&self, list_model: &Self::Type) -> glib::Type; fn parent_n_items(&self, list_model: &Self::Type) -> u32; - fn parent_item(&self, list_model: &Self::Type, position: u32) -> Option; + fn parent_item(&self, list_model: &Self::Type, position: u32) -> Option; } impl ListModelImplExt for T { fn parent_item_type(&self, list_model: &Self::Type) -> glib::Type { unsafe { let type_data = Self::type_data(); - let parent_iface = type_data.as_ref().parent_interface::() + let parent_iface = type_data.as_ref().parent_interface::>() as *const ffi::GListModelInterface; let func = (*parent_iface) .get_item_type .expect("no parent \"item_type\" implementation"); - let ret = func(list_model.unsafe_cast_ref::().to_glib_none().0); + let ret = func( + list_model + .unsafe_cast_ref::>() + .to_glib_none() + .0, + ); from_glib(ret) } } @@ -39,27 +44,35 @@ impl ListModelImplExt for T { fn parent_n_items(&self, list_model: &Self::Type) -> u32 { unsafe { let type_data = Self::type_data(); - let parent_iface = type_data.as_ref().parent_interface::() + let parent_iface = type_data.as_ref().parent_interface::>() as *const ffi::GListModelInterface; let func = (*parent_iface) .get_n_items .expect("no parent \"n_items\" implementation"); - func(list_model.unsafe_cast_ref::().to_glib_none().0) + func( + list_model + .unsafe_cast_ref::>() + .to_glib_none() + .0, + ) } } - fn parent_item(&self, list_model: &Self::Type, position: u32) -> Option { + fn parent_item(&self, list_model: &Self::Type, position: u32) -> Option { unsafe { let type_data = Self::type_data(); - let parent_iface = type_data.as_ref().parent_interface::() + let parent_iface = type_data.as_ref().parent_interface::>() as *const ffi::GListModelInterface; let func = (*parent_iface) .get_item .expect("no parent \"get_item\" implementation"); let ret = func( - list_model.unsafe_cast_ref::().to_glib_none().0, + list_model + .unsafe_cast_ref::>() + .to_glib_none() + .0, position, ); from_glib_full(ret) @@ -67,9 +80,9 @@ impl ListModelImplExt for T { } } -unsafe impl IsImplementable for ListModel +unsafe impl> IsImplementable for ListModel where - ::Type: IsA, + ::Type: IsA, { fn interface_init(iface: &mut glib::Interface) { let iface = iface.as_mut(); @@ -87,11 +100,11 @@ unsafe extern "C" fn list_model_get_item_type( list_model: *mut ffi::GListModel, ) -> glib::ffi::GType where - ::Type: IsA, + ::Type: IsA, { let instance = &*(list_model as *mut T::Instance); let imp = instance.imp(); - let wrap = from_glib_borrow::<_, ListModel>(list_model); + let wrap = from_glib_borrow::<_, ListModel>(list_model); let type_ = imp.item_type(wrap.unsafe_cast_ref()).into_glib(); @@ -115,12 +128,12 @@ unsafe extern "C" fn list_model_get_n_items( list_model: *mut ffi::GListModel, ) -> u32 where - ::Type: IsA, + ::Type: IsA, { let instance = &*(list_model as *mut T::Instance); let imp = instance.imp(); - imp.n_items(from_glib_borrow::<_, ListModel>(list_model).unsafe_cast_ref()) + imp.n_items(from_glib_borrow::<_, ListModel>(list_model).unsafe_cast_ref()) } unsafe extern "C" fn list_model_get_item( @@ -128,11 +141,11 @@ unsafe extern "C" fn list_model_get_item( position: u32, ) -> *mut glib::gobject_ffi::GObject where - ::Type: IsA, + ::Type: IsA, { let instance = &*(list_model as *mut T::Instance); let imp = instance.imp(); - let wrap = from_glib_borrow::<_, ListModel>(list_model); + let wrap = from_glib_borrow::<_, ListModel>(list_model); let item = imp.item(wrap.unsafe_cast_ref(), position);