1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
#![no_std]
#![allow(unused_imports)]
//!
//! Downcast trait: A module to support downcasting dyn traits using [core::any].
//! This trait is similar to [intertrait](https://crates.io/crates/intertrait), but does not require
//! to make a hashtable or any fancy linker magic. For certain cases all casting is optimized away.
//!
//! This crate uses transmute (which is generally considered unsafe rust) to pass an unknown type
//!  as a return value from a function, but the value is then transmuted back to the original type.
//!
//! Downcast traits enables callers to convert dyn objects that implement the
//! DowncastTrait trait to any trait that is supported by the struct implementing the trait.
//! The most useful usecase for this is if a class contains a list of objects that implements a
//! trait and want to call functions on a subset which implements another trait too. This is similar
//! to casting to a sub-class in an object oriented language.
//!
//! Example:
//! * A Widget trait is implemented for all widgets in a graphical user interface system.
//! * The widget trait extends the DowncastTrait.
//! * A widget may implement the Container trait if it is possible to add child widgets to the widget.
//! * A container has a list of widgets, and want to call a specific functions on all widgets that
//!   implement container.
//!
//! ```
//! #[macro_use] extern crate downcast_trait;
//! use downcast_trait::DowncastTrait;
//! use core::{any::{Any, TypeId}, mem};
//! trait Widget: DowncastTrait {}
//! trait Container: Widget {
//!     fn enumerate_widget_leaves_recursive(&self) -> Vec<&Box<dyn Widget>>;
//! }
//! struct Window {
//!     sub_widgets: Vec<Box<dyn Widget>>,
//! }
//! impl Widget for Window {}
//! impl Container for Window {
//!     fn enumerate_widget_leaves_recursive(&self) -> Vec<&Box<dyn Widget>> {
//!         let mut result = Vec::<&Box<dyn Widget>>::new();
//!         self.sub_widgets.iter().for_each(|sub_widget| {
//!             if let Some(sub_container) =
//!                 downcast_trait!(dyn Container, sub_widget.as_ref().to_downcast_trait())
//!             {
//!                 result.extend(sub_container.enumerate_widget_leaves_recursive());
//!             } else {
//!                 result.push(sub_widget);
//!             }
//!         });
//!         result
//!     }
//! }
//! impl DowncastTrait for Window {
//!     downcast_trait_impl_convert_to!(dyn Container);
//! }
//! ```
use core::{
    any::{Any, TypeId},
    mem,
};

/// This trait should be implemented by any structs that or traits that should be downcastable
/// to dowcast to one or more traits. The functions required by this trait should be implemented
/// using the [downcast_trait_impl_convert_to](macro.downcast_trait_impl_convert_to.html) macro.
/// ```ignore
/// trait Widget: DowncastTrait {}
/// ```
pub trait DowncastTrait {
    /// # Safety
    /// This function is called by the [downcast_trait](macro.downcast_trait.html) macro and should
    /// not be accessed directly.
    unsafe fn convert_to_trait(&self, trait_id: TypeId) -> Option<&(dyn Any)>;
    /// # Safety
    /// This function is called by the [downcast_trait_mut](macro.downcast_trait_mut.html) macro
    /// and should not be accessed directly.
    unsafe fn convert_to_trait_mut(&mut self, trait_id: TypeId) -> Option<&mut (dyn Any)>;
    /// This macro is used to cast any implementer of this trait to a DowncastTrait
    fn to_downcast_trait(&self) -> &dyn DowncastTrait;
    /// This macro is used to cast any implementer of this trait to a mut DowncastTrait
    fn to_downcast_trait_mut(&mut self) -> &mut dyn DowncastTrait;
}

/// This macro can be used to cast a &dyn DowncastTrait to an implemented trait e.g:
/// ```ignore
/// if let Some(sub_container) =
///     downcast_trait!(dyn Container, sub_widget.as_ref().to_downcast_trait())
/// {
///   //Use downcasted trait
/// }
/// ```
#[macro_export]
macro_rules! downcast_trait {
    ( dyn $type:path, $src:expr) => {{
        fn transmute_helper(src: &dyn DowncastTrait) -> Option<&dyn $type> {
            unsafe {
                src.convert_to_trait(TypeId::of::<dyn $type>())
                    .map(|dst| mem::transmute::<&(dyn Any), &(dyn $type)>(dst))
            }
        }
        transmute_helper($src)
    }};
}

/// This macro can be used to cast a &dyn mut DowncastTrait to an implemented trait e.g:
/// ```ignore
/// if let Some(sub_container) =
///     downcast_trait_mut!(dyn Container, sub_widget.as_ref().to_downcast_trait())
/// {
///   //Use downcasted trait
/// }
/// ```
#[macro_export]
macro_rules! downcast_trait_mut {
    ( dyn $type:path, $src:expr) => {{
        fn transmute_helper(src: &mut dyn DowncastTrait) -> Option<&mut dyn $type> {
            unsafe {
                src.convert_to_trait_mut(TypeId::of::<dyn $type>())
                    .map(|dst| mem::transmute::<&mut (dyn Any), &mut (dyn $type)>(dst))
            }
        }
        transmute_helper($src)
    }};
}
/// This macro can be used by a struct impl, to implement the functions required by the downcas traitt
/// to dowcast to one or more traits.
/// ```ignore
/// impl DowncastTrait for Window {
///     downcast_trait_impl_convert_to!(dyn Container, dyn Scrollable, dyn Clickable);
/// }
/// ```

#[macro_export]
macro_rules! downcast_trait_impl_convert_to
{
    ($(dyn $type:path),+) => {
        unsafe fn convert_to_trait(& self, trait_id: TypeId) -> Option<& (dyn Any)> {
            if false
            {
               None
            }
            $(
            else if trait_id == TypeId::of::<dyn $type>()
            {
                Some(mem::transmute::<& (dyn $type), & dyn Any>(
                    self as & (dyn $type)
                ))
            }
            )*
            else
            {
                None
            }
        }

        unsafe fn convert_to_trait_mut(& mut self, trait_id: TypeId) -> Option<& mut (dyn Any)> {
            if false
            {
               None
            }
            $(
            else if trait_id == TypeId::of::<dyn $type>()
            {
                Some(mem::transmute::<& mut (dyn $type), & mut dyn Any>(
                    self as & mut (dyn $type)
                ))
            }
            )*
            else
            {
                None
            }
        }

        fn to_downcast_trait(& self) -> & dyn DowncastTrait
        {
            self
        }

        fn to_downcast_trait_mut(& mut self) -> & mut dyn DowncastTrait
        {
            self
        }
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    trait Downcasted {
        fn get_number(&self) -> u32;
    }
    trait Downcasted2 {
        fn get_number(&self) -> u32;
    }
    struct Downcastable {
        val: u32,
    }
    impl Downcasted for Downcastable {
        fn get_number(&self) -> u32 {
            self.val + 123
        }
    }
    impl Downcasted2 for Downcastable {
        fn get_number(&self) -> u32 {
            self.val + 456
        }
    }
    impl DowncastTrait for Downcastable {
        downcast_trait_impl_convert_to!(dyn Downcasted, dyn Downcasted2);
    }

    #[test]
    fn exploration() {
        let mut tst = Downcastable { val: 0 };
        let ts: &mut dyn DowncastTrait = tst.to_downcast_trait_mut();
        let downcasted_maybe = downcast_trait!(dyn Downcasted, ts);
        if let Some(downcasted) = downcasted_maybe {
            assert_eq!(downcasted.get_number(), 123);
        }
        let downcasted_maybe2 = downcast_trait!(dyn Downcasted2, ts);
        if let Some(downcasted2) = downcasted_maybe2 {
            assert_eq!(downcasted2.get_number(), 456);
        }

        let mut downcasted_maybemut = downcast_trait_mut!(dyn Downcasted2, ts);
        match &mut downcasted_maybemut {
            Some(downcasted_mut) => {
                assert_eq!(downcasted_mut.get_number(), 456);
            }
            None => assert!(false),
        }
    }
}