├── .gitignore ├── Cargo.toml ├── README.md └── src ├── dynamic.rs └── lib.rs /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "query_interface" 3 | version = "0.3.5" 4 | authors = ["Diggory Blake "] 5 | description = "Dynamically query a type-erased object for any trait implementation" 6 | repository = "https://github.com/Diggsey/query_interface" 7 | license = "MIT OR Apache-2.0" 8 | 9 | [features] 10 | dynamic = ["lazy_static"] 11 | 12 | [dependencies] 13 | lazy_static = { version = "1.0", optional = true } 14 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # query_interface 2 | Dynamically query a type-erased object for any trait implementation 3 | 4 | [Documentation](https://docs.rs/query_interface/) 5 | 6 | Example: 7 | ```rust 8 | #[macro_use] 9 | extern crate query_interface; 10 | 11 | use query_interface::{Object, ObjectClone}; 12 | use std::fmt::Debug; 13 | 14 | #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] 15 | struct Foo; 16 | 17 | interfaces!(Foo: ObjectClone, Debug, Bar); 18 | 19 | trait Bar { 20 | fn do_something(&self); 21 | } 22 | impl Bar for Foo { 23 | fn do_something(&self) { 24 | println!("I'm a Foo!"); 25 | } 26 | } 27 | 28 | fn main() { 29 | let obj = Box::new(Foo) as Box; 30 | let obj2 = obj.clone(); 31 | println!("{:?}", obj2); 32 | 33 | obj2.query_ref::().unwrap().do_something(); // Prints: "I'm a Foo!" 34 | } 35 | ``` 36 | 37 | In short, this allows you to pass around `Object`s and still have access to any of the (object-safe) traits 38 | implemented for the underlying type. The library also provides some useful object-safe equivalents to standard 39 | traits, including `ObjectClone`, `ObjectPartialEq`, `ObjectPartialOrd`, `ObjectHash`. 40 | 41 | To improve usability, the non-object-safe versions of the traits are implemented directly on the `Object` trait 42 | object, allowing you to easily clone `Object`s and store them in collections. 43 | 44 | You can have your own `Object`-like traits to enforce some additional static requirements by using the `mopo!` 45 | macro: 46 | ```rust 47 | trait CustomObject : Object { 48 | ... 49 | } 50 | mopo!(CustomObject); 51 | 52 | struct Foo; 53 | interfaces!(Foo: CustomObject); 54 | 55 | impl CustomObject for Foo { 56 | ... 57 | } 58 | ``` 59 | 60 | Now you can use `CustomObject` in all the ways you could use `Object`. 61 | 62 | With the "dynamic" feature, you can *at runtime* register additional traits to be queryable on a type. This 63 | allows you to bypass the normal coherence rules: 64 | 65 | ```rust 66 | trait Custom {} 67 | impl Custom for String {} 68 | 69 | fn main() { 70 | dynamic_interfaces! { 71 | String: Custom; 72 | } 73 | } 74 | ``` 75 | -------------------------------------------------------------------------------- /src/dynamic.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | use std::any::TypeId; 3 | use std::sync::{Arc, RwLock}; 4 | use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; 5 | use std::cell::RefCell; 6 | 7 | use VTable; 8 | 9 | type RegistryKey = (TypeId, TypeId); 10 | 11 | #[doc(hidden)] 12 | #[derive(Default, Clone)] 13 | pub struct Registry { 14 | entries: HashMap 15 | } 16 | 17 | impl Registry { 18 | #[doc(hidden)] 19 | pub unsafe fn register(&mut self, vtable: VTable) { 20 | self.entries.insert((TypeId::of::(), TypeId::of::()), vtable); 21 | } 22 | fn find(&self, trait_id: TypeId) -> Option { 23 | self.entries.get(&(TypeId::of::(), trait_id)).cloned() 24 | } 25 | } 26 | 27 | // The global registry can be dynamically updated, so must be protected 28 | // by an RwLock. 29 | #[derive(Default)] 30 | struct GlobalRegistry { 31 | registry: Arc 32 | } 33 | 34 | // But we need read access to be *super* fast, since it's accessed 35 | // on every query, so we store a local reference that doesn't require 36 | // any synchronisation to access. 37 | struct LocalRegistry { 38 | registry: Arc, 39 | version: usize 40 | } 41 | 42 | lazy_static! { 43 | static ref GLOBAL_REGISTRY: RwLock = RwLock::default(); 44 | } 45 | static GLOBAL_REGISTRY_VERSION: AtomicUsize = ATOMIC_USIZE_INIT; 46 | 47 | impl LocalRegistry { 48 | fn obtain() -> Self { 49 | // Obtain read access to global registry 50 | let lock_guard = GLOBAL_REGISTRY.read().unwrap(); 51 | 52 | // Keep a local reference to the registry 53 | LocalRegistry { 54 | registry: lock_guard.registry.clone(), 55 | version: GLOBAL_REGISTRY_VERSION.load(Ordering::Acquire), 56 | } 57 | } 58 | } 59 | 60 | thread_local! { 61 | static LOCAL_REGISTRY: RefCell = RefCell::new(LocalRegistry::obtain()); 62 | } 63 | 64 | // *Fast* read-only access to the registry 65 | fn with_registry R>(f: F) -> R { 66 | LOCAL_REGISTRY.with(|registry| { 67 | let mut registry = registry.borrow_mut(); 68 | 69 | // Check for updates 70 | if GLOBAL_REGISTRY_VERSION.load(Ordering::Acquire) != registry.version { 71 | *registry = LocalRegistry::obtain(); 72 | } 73 | 74 | // Access to registry 75 | f(®istry.registry) 76 | }) 77 | } 78 | 79 | // Slower write access to the registry 80 | #[doc(hidden)] 81 | pub fn with_registry_mut R>(f: F) -> R { 82 | // Obtain write access to global registry 83 | let mut lock_guard = GLOBAL_REGISTRY.write().unwrap(); 84 | 85 | // Allow caller to mutate registry 86 | let result = f(Arc::make_mut(&mut lock_guard.registry)); 87 | 88 | // Notify readers that the registry has changed 89 | GLOBAL_REGISTRY_VERSION.fetch_add(1, Ordering::AcqRel); 90 | 91 | result 92 | } 93 | 94 | #[doc(hidden)] 95 | pub fn find_in_registry(trait_id: TypeId) -> Option { 96 | with_registry(|registry| registry.find::(trait_id)) 97 | } 98 | 99 | #[macro_export] 100 | macro_rules! dynamic_interfaces { 101 | ($($name:ty: $($iface:ty),+;)*) => ( 102 | $crate::dynamic::with_registry_mut(|registry| { unsafe { $( 103 | registry.register::<$name, $name>($crate::VTable::none()); 104 | registry.register::<$name, $crate::Object>(vtable_for!($name as $crate::Object)); 105 | $( 106 | registry.register::<$name, $iface>(vtable_for!($name as $iface)); 107 | )+ 108 | )* } }); 109 | ) 110 | } 111 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! query-interface - dynamically query a type-erased object for any trait implementation 2 | //! 3 | //! ```rust 4 | //! #[macro_use] 5 | //! extern crate query_interface; 6 | //! use query_interface::{Object, ObjectClone}; 7 | //! use std::fmt::Debug; 8 | //! 9 | //! #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug)] 10 | //! struct Foo; 11 | //! 12 | //! interfaces!(Foo: ObjectClone, Debug, Bar); 13 | //! 14 | //! trait Bar { 15 | //! fn do_something(&self); 16 | //! } 17 | //! impl Bar for Foo { 18 | //! fn do_something(&self) { 19 | //! println!("I'm a Foo!"); 20 | //! } 21 | //! } 22 | //! 23 | //! fn main() { 24 | //! let obj = Box::new(Foo) as Box; 25 | //! let obj2 = obj.clone(); 26 | //! println!("{:?}", obj2); 27 | //! 28 | //! obj2.query_ref::().unwrap().do_something(); // Prints: "I'm a Foo!" 29 | //! } 30 | //! ``` 31 | #[cfg(feature = "dynamic")] 32 | #[macro_use] 33 | extern crate lazy_static; 34 | 35 | use std::any::{Any, TypeId}; 36 | use std::cmp::Ordering; 37 | use std::collections::hash_map::DefaultHasher; 38 | use std::fmt::{Debug, Display}; 39 | use std::hash::{Hash, Hasher}; 40 | use std::path::PathBuf; 41 | use std::ptr; 42 | 43 | #[cfg(feature = "dynamic")] 44 | #[macro_use] 45 | pub mod dynamic; 46 | 47 | /// Represents a trait object's vtable pointer. You shouldn't need to use this as a 48 | /// consumer of the crate but it is required for macro expansion. 49 | #[doc(hidden)] 50 | #[repr(C)] 51 | #[derive(Copy, Clone, Debug)] 52 | pub struct VTable(*const ()); 53 | 54 | impl VTable { 55 | pub fn none() -> VTable { 56 | VTable(ptr::null()) 57 | } 58 | } 59 | 60 | unsafe impl Send for VTable {} 61 | unsafe impl Sync for VTable {} 62 | 63 | /// Represents a trait object's layout. You shouldn't need to use this as a 64 | /// consumer of the crate but it is required for macro expansion. 65 | #[doc(hidden)] 66 | #[repr(C)] 67 | #[derive(Copy, Clone, Debug)] 68 | pub struct TraitObject { 69 | pub data: *const (), 70 | pub vtable: VTable, 71 | } 72 | 73 | /// Obtain the vtable for a type/trait pair. You shouldn't need to use this as a 74 | /// consumer of the crate but it is required for macro expansion. 75 | #[doc(hidden)] 76 | #[macro_export] 77 | macro_rules! vtable_for { 78 | ($x:ty as $y:ty) => {{ 79 | let x = ::std::ptr::null::<$x>() as *const $y; 80 | #[allow(unused_unsafe)] 81 | unsafe { 82 | ::std::mem::transmute::<_, $crate::TraitObject>(x).vtable 83 | } 84 | }}; 85 | } 86 | 87 | /// Define a custom Object-like trait. The `query`, `query_ref` and `query_mut` 88 | /// methods will be automatically implemented on this trait object. 89 | /// 90 | /// You may add additional static bounds to your custom trait via the 91 | /// `HasInterface` trait. This example will statically ensure that all 92 | /// types convertible to `MyObject` can be cloned. Your trait must extend 93 | /// `Object`. 94 | /// 95 | /// ```rust 96 | /// # #[macro_use] 97 | /// # extern crate query_interface; 98 | /// # use query_interface::*; 99 | /// trait MyObject: Object + ObjectClone + HasInterface { } 100 | /// mopo!(MyObject); 101 | /// # fn main() {} 102 | /// ``` 103 | #[macro_export] 104 | macro_rules! mopo { 105 | ($name:ty) => { 106 | impl $name { 107 | pub fn query_ref(&self) -> Option<&U> { 108 | if let Some(vtable) = self.query_vtable(::std::any::TypeId::of::()) { 109 | unsafe { 110 | let data = self as *const Self; 111 | let u = $crate::TraitObject { 112 | data: data as *const (), 113 | vtable: vtable, 114 | }; 115 | Some(*::std::mem::transmute::<_, &&U>(&u)) 116 | } 117 | } else { 118 | None 119 | } 120 | } 121 | pub fn query_mut(&mut self) -> Option<&mut U> { 122 | if let Some(vtable) = self.query_vtable(::std::any::TypeId::of::()) { 123 | unsafe { 124 | let data = self as *mut Self; 125 | let mut u = $crate::TraitObject { 126 | data: data as *const (), 127 | vtable: vtable, 128 | }; 129 | Some(*::std::mem::transmute::<_, &mut &mut U>(&mut u)) 130 | } 131 | } else { 132 | None 133 | } 134 | } 135 | pub fn query( 136 | self: Box, 137 | ) -> ::std::result::Result, Box> { 138 | if let Some(vtable) = self.query_vtable(::std::any::TypeId::of::()) { 139 | unsafe { 140 | let data = Box::into_raw(self); 141 | let mut u = $crate::TraitObject { 142 | data: data as *const (), 143 | vtable: vtable, 144 | }; 145 | Ok(Box::from_raw(*::std::mem::transmute::<_, &mut *mut U>( 146 | &mut u, 147 | ))) 148 | } 149 | } else { 150 | Err(self) 151 | } 152 | } 153 | pub fn query_arc( 154 | self_: ::std::sync::Arc, 155 | ) -> ::std::result::Result<::std::sync::Arc, ::std::sync::Arc> { 156 | if let Some(vtable) = self_.query_vtable(::std::any::TypeId::of::()) { 157 | unsafe { 158 | let data = ::std::sync::Arc::into_raw(self_); 159 | let mut u = $crate::TraitObject { 160 | data: data as *const (), 161 | vtable: vtable, 162 | }; 163 | Ok(::std::sync::Arc::from_raw(*::std::mem::transmute::< 164 | _, 165 | &mut *mut U, 166 | >(&mut u))) 167 | } 168 | } else { 169 | Err(self_) 170 | } 171 | } 172 | pub fn query_rc( 173 | self_: ::std::rc::Rc, 174 | ) -> ::std::result::Result<::std::rc::Rc, ::std::rc::Rc> { 175 | if let Some(vtable) = self_.query_vtable(::std::any::TypeId::of::()) { 176 | unsafe { 177 | let data = ::std::rc::Rc::into_raw(self_); 178 | let mut u = $crate::TraitObject { 179 | data: data as *const (), 180 | vtable: vtable, 181 | }; 182 | Ok(::std::rc::Rc::from_raw(*::std::mem::transmute::< 183 | _, 184 | &mut *mut U, 185 | >(&mut u))) 186 | } 187 | } else { 188 | Err(self_) 189 | } 190 | } 191 | pub fn obj_partial_eq(&self, other: &Self) -> bool { 192 | if let Some(x) = self.query_ref::() { 193 | x.obj_eq(other.query_ref().unwrap()) 194 | } else { 195 | (self as *const Self) == (other as *const Self) 196 | } 197 | } 198 | pub fn obj_partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { 199 | if let Some(x) = self.query_ref::() { 200 | x.obj_partial_cmp(other.query_ref().unwrap()) 201 | } else { 202 | None 203 | } 204 | } 205 | } 206 | impl ::std::clone::Clone for Box<$name> { 207 | fn clone(&self) -> Self { 208 | (**self).to_owned() 209 | } 210 | } 211 | impl ::std::borrow::ToOwned for $name { 212 | type Owned = Box<$name>; 213 | fn to_owned(&self) -> Box<$name> { 214 | self.query_ref::() 215 | .expect("Object not clonable!") 216 | .obj_clone() 217 | .query::<$name>() 218 | .unwrap() 219 | } 220 | } 221 | impl ::std::fmt::Debug for $name { 222 | fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { 223 | if let Some(o) = self.query_ref::() { 224 | o.fmt(f) 225 | } else { 226 | writeln!(f, "Object {{ }}") 227 | } 228 | } 229 | } 230 | impl ::std::cmp::PartialEq for $name { 231 | fn eq(&self, other: &Self) -> bool { 232 | // Require `Eq` rather than `PartialEq` as this allows `Object`s to be used as 233 | // key in hash maps 234 | if let Some(x) = self.query_ref::() { 235 | x.obj_eq(other.query_ref().unwrap()) 236 | } else { 237 | // This trivially meets the requirements of `Eq` 238 | (self as *const Self) == (other as *const Self) 239 | } 240 | } 241 | } 242 | impl ::std::cmp::Eq for $name {} 243 | impl ::std::cmp::PartialOrd for $name { 244 | fn partial_cmp(&self, other: &Self) -> Option<::std::cmp::Ordering> { 245 | Some(self.cmp(other)) 246 | } 247 | } 248 | impl ::std::cmp::Ord for $name { 249 | fn cmp(&self, other: &Self) -> ::std::cmp::Ordering { 250 | if let Some(x) = self.query_ref::() { 251 | if let Some(o) = x.obj_cmp(other.query_ref().unwrap()) { 252 | return o; 253 | } 254 | } 255 | Ord::cmp(&(self as *const Self), &(other as *const Self)) 256 | } 257 | } 258 | impl ::std::hash::Hash for $name { 259 | fn hash(&self, state: &mut H) { 260 | if let Some(x) = self.query_ref::() { 261 | x.obj_hash(state) 262 | } else { 263 | state.write_usize(self as *const Self as *const () as usize) 264 | } 265 | } 266 | } 267 | }; 268 | } 269 | 270 | /// This trait is the primary function of the library. `Object` trait objects 271 | /// can be freely queried for any other trait, allowing conversion between 272 | /// trait objects. 273 | pub unsafe trait Object: Any { 274 | /// This is implemented by the `interfaces!` macro, and should never be 275 | /// manually implemented. 276 | #[doc(hidden)] 277 | fn query_vtable(&self, id: TypeId) -> Option; 278 | } 279 | 280 | /// You can use this trait to ensure that a type implements a trait as an 281 | /// interface. This means the type declared the trait in its `interfaces!(...)` 282 | /// list, and guarantees that querying an `Object` of that type for the trait 283 | /// will always succeed. 284 | /// 285 | /// When using `HasInterface` in a generic bound, you should also 286 | /// specify `SomeTrait` as a bound. While `HasInterface` is a more 287 | /// stringent requirement than, and in practice implies `SomeTrait`, the 288 | /// compiler cannot deduce that because it is enforced through macros rather 289 | /// than the type system. 290 | pub unsafe trait HasInterface {} 291 | 292 | mopo!(dyn Object); 293 | 294 | /// This is an object-safe version of `Clone`, which is automatically 295 | /// implemented for all `Clone + Object` types. This is a support trait used to 296 | /// allow `Object` trait objects to be clonable. 297 | pub trait ObjectClone { 298 | fn obj_clone(&self) -> Box; 299 | } 300 | impl ObjectClone for T { 301 | fn obj_clone(&self) -> Box { 302 | Box::new(self.clone()) 303 | } 304 | } 305 | 306 | /// This is an object-safe version of `PartialEq`, which is automatically 307 | /// implemented for all `PartialEq + Object` types. This is a support trait used to 308 | /// allow `Object` trait objects to be comparable in this way. 309 | pub trait ObjectPartialEq { 310 | fn obj_eq(&self, other: &dyn Object) -> bool; 311 | } 312 | impl ObjectPartialEq for T { 313 | fn obj_eq(&self, other: &dyn Object) -> bool { 314 | if let Some(o) = other.query_ref::() { 315 | self == o 316 | } else { 317 | false 318 | } 319 | } 320 | } 321 | 322 | /// This is an object-safe version of `Eq`, which is automatically 323 | /// implemented for all `Eq + Object` types. This is a support trait used to 324 | /// allow `Object` trait objects to be comparable in this way. 325 | pub trait ObjectEq: ObjectPartialEq {} 326 | impl ObjectEq for T {} 327 | 328 | /// This is an object-safe version of `PartialOrd`, which is automatically 329 | /// implemented for all `PartialOrd + Object` types. This is a support trait used to 330 | /// allow `Object` trait objects to be comparable in this way. 331 | pub trait ObjectPartialOrd { 332 | fn obj_partial_cmp(&self, other: &dyn Object) -> Option; 333 | } 334 | impl ObjectPartialOrd for T { 335 | fn obj_partial_cmp(&self, other: &dyn Object) -> Option { 336 | if let Some(o) = other.query_ref::() { 337 | self.partial_cmp(o) 338 | } else { 339 | None 340 | } 341 | } 342 | } 343 | 344 | /// This is an object-safe version of `Ord`, which is automatically 345 | /// implemented for all `Ord + Object` types. This is a support trait used to 346 | /// allow `Object` trait objects to be comparable in this way. 347 | pub trait ObjectOrd { 348 | fn obj_cmp(&self, other: &dyn Object) -> Option; 349 | } 350 | impl ObjectOrd for T { 351 | fn obj_cmp(&self, other: &dyn Object) -> Option { 352 | if let Some(o) = other.query_ref::() { 353 | Some(self.cmp(o)) 354 | } else { 355 | None 356 | } 357 | } 358 | } 359 | 360 | /// This is an object-safe version of `Hash`, which is automatically 361 | /// implemented for all `Hash + Object` types. This is a support trait used to 362 | /// allow `Object` trait objects to be comparable in this way. 363 | /// 364 | /// Note: `Object`s are not guaranteed to hash to the same value as their 365 | /// underlying type. 366 | pub trait ObjectHash { 367 | fn obj_hash(&self, state: &mut dyn Hasher); 368 | } 369 | impl ObjectHash for T { 370 | fn obj_hash(&self, state: &mut dyn Hasher) { 371 | let mut h = DefaultHasher::new(); 372 | self.hash(&mut h); 373 | state.write_u64(h.finish()); 374 | } 375 | } 376 | 377 | /// Allow a set of traits to be dynamically queried from a type when it is 378 | /// stored as an `Object` trait object. 379 | /// 380 | /// Example use: 381 | /// 382 | /// ```rust 383 | /// # #[macro_use] 384 | /// # extern crate query_interface; 385 | /// # use query_interface::*; 386 | /// #[derive(Clone)] 387 | /// struct Foo; 388 | /// interfaces!(Foo: ObjectClone); 389 | /// # fn main() {} 390 | /// ``` 391 | #[macro_export(local_inner_macros)] 392 | macro_rules! interfaces { 393 | (@unbracket $(($($v:tt)*))*) => ($($($v)*)*); 394 | (@inner $imp:tt $cond:tt $name:ty: $($iface:ty),+ {}) => ( 395 | interfaces!(@unbracket $imp ($crate::HasInterface<$name> for $name) $cond ({})); 396 | interfaces!(@unbracket $imp ($crate::HasInterface for $name) $cond ({})); 397 | $(interfaces!(@unbracket $imp ($crate::HasInterface<$iface> for $name) $cond ({}));)* 398 | interfaces!(@unbracket $imp ($crate::Object for $name) $cond ({ 399 | fn query_vtable(&self, id: ::std::any::TypeId) -> Option<$crate::VTable> { 400 | if id == ::std::any::TypeId::of::<$name>() { 401 | Some($crate::VTable::none()) 402 | } else if id == ::std::any::TypeId::of::() { 403 | Some(vtable_for!($name as dyn $crate::Object)) 404 | } else $(if id == ::std::any::TypeId::of::<$iface>() { 405 | Some(vtable_for!($name as $iface)) 406 | } else)* { 407 | // If "dynamic" feature is enabled, fall back to 408 | // looking in the registry 409 | #[cfg(feature = "dynamic")] 410 | { $crate::dynamic::find_in_registry::<$name>(id) } 411 | // No dynamic lookup 412 | #[cfg(not(feature = "dynamic"))] 413 | { None } 414 | } 415 | } 416 | })); 417 | ); 418 | (@imp ($($result:tt)*) $name:ty: $($iface:ty),+ $(where $($cond:tt)*)*) => ( 419 | interfaces!(@inner (unsafe impl<$($result)*>) ($(where $($cond)*)*) $name: $($iface),+ {}); 420 | ); 421 | (@parse < $($rest:tt)*) => ( 422 | interfaces!(@parseArg () $($rest)*); 423 | ); 424 | (@parse $($rest:tt)*) => ( 425 | interfaces!(@imp () $($rest)*); 426 | ); 427 | (@parseArg ($($result:tt)*) $name:ident , $($rest:tt)*) => ( 428 | interfaces!(@parseArg ($($result)* $name ,) $($rest)*); 429 | ); 430 | (@parseArg ($($result:tt)*) $name:ident : $($rest:tt)*) => ( 431 | interfaces!(@parseBound ($($result)* $name : ) $($rest)*); 432 | ); 433 | (@parseArg ($($result:tt)*) $name:ident > $($rest:tt)*) => ( 434 | interfaces!(@imp ($($result)* $name) $($rest)*); 435 | ); 436 | (@parseBound ($($result:tt)*) $bound:tt + $($rest:tt)*) => ( 437 | interfaces!(@parseBound ($($result)* $bound +) $($rest)*); 438 | ); 439 | (@parseBound ($($result:tt)*) $bound:tt , $($rest:tt)*) => ( 440 | interfaces!(@parseArg ($($result)* $bound ,) $($rest)*); 441 | ); 442 | (@parseBound ($($result:tt)*) $bound:tt > $($rest:tt)*) => ( 443 | interfaces!(@imp ($($result)* $bound) $($rest)*); 444 | ); 445 | (< $($rest:tt)*) => ( 446 | interfaces!(@parse < $($rest)*); 447 | ); 448 | ($x:ty: $($rest:tt)*) => ( 449 | interfaces!(@parse $x: $($rest)*); 450 | ); 451 | (@expand2 ($name:ty) ($($rest:tt)*)) => ( 452 | interfaces!($name $($rest)*); 453 | ); 454 | (@expand {$($name:ty),*} $rest:tt) => ( 455 | $( interfaces!(@expand2 ($name) $rest); )* 456 | ); 457 | ({$($name:ty),*} $($rest:tt)*) => ( 458 | interfaces!(@expand {$($name),*} ($($rest)*)); 459 | ); 460 | } 461 | 462 | // Integral types 463 | interfaces!({ 464 | bool, i8, u8, i16, u16, i32, u32, i64, u64, char 465 | }: dyn ObjectClone, dyn Debug, dyn Display, dyn ObjectPartialEq, dyn ObjectPartialOrd, dyn ObjectEq, dyn ObjectOrd, dyn ObjectHash, dyn ToString); 466 | 467 | // Floating point types 468 | interfaces!({ 469 | f32, f64 470 | }: dyn ObjectClone, dyn Debug, dyn Display, dyn ObjectPartialEq, dyn ObjectPartialOrd, dyn ToString); 471 | 472 | // Strings 473 | interfaces!( 474 | String: dyn ObjectClone, 475 | dyn Debug, 476 | dyn Display, 477 | dyn ObjectPartialEq, 478 | dyn ObjectPartialOrd, 479 | dyn ObjectEq, 480 | dyn ObjectOrd, 481 | dyn ObjectHash, 482 | dyn ToString 483 | ); 484 | 485 | // Paths 486 | interfaces!( 487 | PathBuf: dyn ObjectClone, 488 | dyn Debug, 489 | dyn ObjectPartialEq, 490 | dyn ObjectPartialOrd, 491 | dyn ObjectEq, 492 | dyn ObjectOrd, 493 | dyn ObjectHash 494 | ); 495 | 496 | // Vecs 497 | interfaces!({ 498 | Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec, Vec 499 | }: dyn ObjectClone, dyn Debug, dyn ObjectPartialEq, dyn ObjectPartialOrd, dyn ObjectEq, dyn ObjectOrd, dyn ObjectHash); 500 | interfaces!({ 501 | Vec, Vec 502 | }: dyn ObjectClone, dyn Debug, dyn ObjectPartialEq, dyn ObjectPartialOrd); 503 | interfaces!({ 504 | Vec, Vec 505 | }: dyn ObjectClone, dyn Debug, dyn ObjectPartialEq, dyn ObjectPartialOrd, dyn ObjectEq, dyn ObjectOrd, dyn ObjectHash); 506 | 507 | #[cfg(test)] 508 | mod tests { 509 | use std::fmt::Debug; 510 | use std::rc::Rc; 511 | use std::sync::Arc; 512 | 513 | #[derive(Debug, Clone)] 514 | struct Bar; 515 | interfaces!(Bar: dyn Foo, dyn super::ObjectClone, dyn Debug, dyn Custom); 516 | 517 | trait Foo: Debug { 518 | fn test(&self) -> bool { 519 | false 520 | } 521 | } 522 | trait Foo2: Debug {} 523 | impl Foo for Bar { 524 | fn test(&self) -> bool { 525 | true 526 | } 527 | } 528 | impl Foo2 for Bar {} 529 | 530 | #[derive(Debug, Clone)] 531 | struct GenericBar(T); 532 | interfaces!( GenericBar: dyn super::ObjectClone, dyn Debug where T: Clone); 533 | 534 | #[test] 535 | fn test_ref() { 536 | let x = Box::new(Bar) as Box; 537 | let foo: Option<&dyn Foo> = x.query_ref(); 538 | assert!(foo.is_some()); 539 | assert!(foo.unwrap().test()); 540 | let foo2: Option<&dyn Foo2> = x.query_ref(); 541 | assert!(foo2.is_none()); 542 | let bar: Option<&Bar> = x.query_ref(); 543 | assert!(bar.is_some()); 544 | } 545 | 546 | #[test] 547 | fn test_mut() { 548 | let mut x = Box::new(Bar) as Box; 549 | { 550 | let foo = x.query_mut::(); 551 | assert!(foo.is_some()); 552 | assert!(foo.unwrap().test()); 553 | } 554 | { 555 | let foo2 = x.query_mut::(); 556 | assert!(foo2.is_none()); 557 | } 558 | { 559 | let bar = x.query_mut::(); 560 | assert!(bar.is_some()); 561 | } 562 | } 563 | 564 | #[test] 565 | fn test_owned() { 566 | let x = Box::new(Bar) as Box; 567 | let foo: Result, _> = x.clone().query(); 568 | assert!(foo.is_ok()); 569 | assert!(foo.unwrap().test()); 570 | let foo2: Result, _> = x.clone().query(); 571 | assert!(foo2.is_err()); 572 | let bar: Result, _> = x.clone().query(); 573 | assert!(bar.is_ok()); 574 | } 575 | 576 | #[test] 577 | fn test_rc() { 578 | let x = Rc::new(Bar) as Rc; 579 | let foo: Result, _> = super::Object::query_rc(x.clone()); 580 | assert!(foo.is_ok()); 581 | assert!(foo.unwrap().test()); 582 | let foo2: Result, _> = super::Object::query_rc(x.clone()); 583 | assert!(foo2.is_err()); 584 | let bar: Result, _> = super::Object::query_rc(x.clone()); 585 | assert!(bar.is_ok()); 586 | } 587 | 588 | #[test] 589 | fn test_arc() { 590 | let x = Arc::new(Bar) as Arc; 591 | let foo: Result, _> = super::Object::query_arc(x.clone()); 592 | assert!(foo.is_ok()); 593 | assert!(foo.unwrap().test()); 594 | let foo2: Result, _> = super::Object::query_arc(x.clone()); 595 | assert!(foo2.is_err()); 596 | let bar: Result, _> = super::Object::query_arc(x.clone()); 597 | assert!(bar.is_ok()); 598 | } 599 | 600 | trait Custom: super::Object {} 601 | impl Custom for Bar {} 602 | mopo!(dyn Custom); 603 | 604 | #[test] 605 | fn test_derived() { 606 | let x = Box::new(Bar) as Box; 607 | let foo: Result, _> = x.clone().query(); 608 | assert!(foo.is_ok()); 609 | assert!(foo.unwrap().test()); 610 | let foo2: Result, _> = x.clone().query(); 611 | assert!(foo2.is_err()); 612 | let bar: Result, _> = x.clone().query(); 613 | assert!(bar.is_ok()); 614 | } 615 | 616 | trait Dynamic { 617 | fn test(&self) -> u32; 618 | } 619 | impl Dynamic for Bar { 620 | fn test(&self) -> u32 { 621 | 42 622 | } 623 | } 624 | 625 | #[test] 626 | fn test_dynamic() { 627 | let x = Box::new(Bar) as Box; 628 | let dyn1: Option<&dyn Dynamic> = x.query_ref(); 629 | assert!(dyn1.is_none()); 630 | 631 | dynamic_interfaces! { 632 | Bar: Dynamic; 633 | } 634 | 635 | let dyn2: Option<&dyn Dynamic> = x.query_ref(); 636 | assert!(dyn2.unwrap().test() == 42); 637 | } 638 | 639 | #[test] 640 | fn test_primitives() { 641 | Box::new(1) as Box; 642 | Box::new(1f32) as Box; 643 | Box::new("test".to_string()) as Box; 644 | Box::new(vec![1, 2, 3]) as Box; 645 | } 646 | } 647 | --------------------------------------------------------------------------------