Skip to content

Commit

Permalink
Add CastNone trait (#843)
Browse files Browse the repository at this point in the history
* Add CastNone trait

Closes #842

* Add CastNone to prelude, add dynamic_cast

* Add example for CastNone

* Describe type using turbofish op
  • Loading branch information
ranfdev authored and bilelmoussaoui committed Dec 5, 2022
1 parent b1a4f62 commit 78d3651
Show file tree
Hide file tree
Showing 3 changed files with 76 additions and 3 deletions.
4 changes: 2 additions & 2 deletions glib/src/lib.rs
Expand Up @@ -29,8 +29,8 @@ pub use self::bytes::Bytes;
pub use self::closure::{Closure, RustClosure};
pub use self::error::{BoolError, Error};
pub use self::object::{
BorrowedObject, Cast, Class, InitiallyUnowned, Interface, IsA, Object, ObjectExt, ObjectType,
SendWeakRef, WeakRef,
BorrowedObject, Cast, CastNone, Class, InitiallyUnowned, Interface, IsA, Object, ObjectExt,
ObjectType, SendWeakRef, WeakRef,
};
pub use self::signal::{
signal_handler_block, signal_handler_disconnect, signal_handler_unblock,
Expand Down
73 changes: 73 additions & 0 deletions glib/src/object.rs
Expand Up @@ -288,6 +288,79 @@ pub trait Cast: ObjectType {

impl<T: ObjectType> Cast for T {}

// rustdoc-stripper-ignore-next
/// Convenience trait mirroring `Cast`, implemented on `Option<Object>` types.
///
/// # Warning
/// Inveitably this trait will discard informations about a downcast failure:
/// you don't know if the object was not of the expected type, or if it was `None`.
/// If you need to handle the downcast error, use `Cast` over a `glib::Object`.
///
/// # Example
/// ```ignore
/// let widget: Option<Widget> = list_item.child();
///
/// // Without using `CastNone`
/// let label = widget.unwrap().downcast::<gtk::Label>().unwrap();
///
/// // Using `CastNone` we can avoid the first `unwrap()` call
/// let label = widget.and_downcast::<gtk::Label>().unwrap();
/// ````
pub trait CastNone: Sized {
type Inner;
fn and_downcast<T: ObjectType>(self) -> Option<T>
where
Self::Inner: CanDowncast<T>;
fn and_downcast_ref<T: ObjectType>(&self) -> Option<&T>
where
Self::Inner: CanDowncast<T>;
fn and_upcast<T: ObjectType>(self) -> Option<T>
where
Self::Inner: IsA<T>;
fn and_upcast_ref<T: ObjectType>(&self) -> Option<&T>
where
Self::Inner: IsA<T>;
fn and_dynamic_cast<T: ObjectType>(self) -> Result<T, Self>;
fn and_dynamic_cast_ref<T: ObjectType>(&self) -> Option<&T>;
}
impl<I: ObjectType + Sized> CastNone for Option<I> {
type Inner = I;

fn and_downcast<T: ObjectType>(self) -> Option<T>
where
Self::Inner: CanDowncast<T>,
{
self.and_then(|i| i.downcast().ok())
}

fn and_downcast_ref<T: ObjectType>(&self) -> Option<&T>
where
Self::Inner: CanDowncast<T>,
{
self.as_ref().and_then(|i| i.downcast_ref())
}
fn and_upcast<T: ObjectType>(self) -> Option<T>
where
Self::Inner: IsA<T>,
{
self.map(|i| i.upcast())
}

fn and_upcast_ref<T: ObjectType>(&self) -> Option<&T>
where
Self::Inner: IsA<T>,
{
self.as_ref().map(|i| i.upcast_ref())
}
fn and_dynamic_cast<T: ObjectType>(self) -> Result<T, Self> {
self.ok_or(None)
.and_then(|i| i.dynamic_cast().map_err(|e| Some(e)))
}
fn and_dynamic_cast_ref<T: ObjectType>(&self) -> Option<&T> {
self.as_ref().and_then(|i| i.dynamic_cast_ref())
}
}

// rustdoc-stripper-ignore-next
/// Marker trait for the statically known possibility of downcasting from `Self` to `T`.
pub trait CanDowncast<T> {}
Expand Down
2 changes: 1 addition & 1 deletion glib/src/prelude.rs
Expand Up @@ -5,6 +5,6 @@

pub use crate::param_spec::ParamSpecBuilderExt;
pub use crate::{
Cast, Continue, IsA, ObjectExt, ObjectType, ParamSpecType, StaticType, StaticTypeExt,
Cast, CastNone, Continue, IsA, ObjectExt, ObjectType, ParamSpecType, StaticType, StaticTypeExt,
StaticVariantType, ToSendValue, ToValue, ToVariant,
};

0 comments on commit 78d3651

Please sign in to comment.