gear_objects/
component.rs

1#[allow(unused_imports)]
2use super::*;
3use core::fmt::{self, Debug};
4use fnv::FnvHashMap;
5#[allow(unused_imports)]
6use paste::paste;
7use std::any::Any;
8use std::hash::{Hash, Hasher};
9use std::marker::Unsize;
10use std::ptr::{DynMetadata, Pointee};
11use type_erased_ptr::*;
12
13/// The unit of composition for the gear object model.
14/// A component consists  of one or more objects. Each object implements one or more
15/// traits. Component clients are only allowed to interact with objects via their traits.
16/// Note that publicly released traits should be treated as immutable to foster backward
17/// compatibility.
18pub struct Component {
19    pub id: ComponentId,
20    objects: FnvHashMap<TypeId, Box<dyn Any + Send + Sync>>, // object id => type erased boxed object
21    traits: FnvHashMap<TypeId, TypeErasedPointer>, // trait id => type erased trait pointer
22    repeated: FnvHashMap<TypeId, Vec<TypeErasedPointer>>, // trait id => [type erased trait pointer]
23    refs: FnvHashMap<TypeId, ObjectRefs>, // object id => outstanding trait references on the object
24    empty: Vec<TypeErasedPointer>,
25}
26
27impl Component {
28    /// tag is used by the Debug trait on Component (and ComponentId).
29    pub fn new(tag: &str) -> Component {
30        Component {
31            id: next_component_id(tag),
32            objects: FnvHashMap::default(),
33            traits: FnvHashMap::default(),
34            repeated: FnvHashMap::default(),
35            empty: Vec::new(),
36            refs: FnvHashMap::default(),
37        }
38    }
39
40    // Normally the [`add_traits`]` macro would be used instead of calling this directly.
41    #[doc(hidden)]
42    pub fn add_trait<Trait, Object>(
43        &mut self,
44        object_id: TypeId,
45        trait_id: TypeId,
46        obj_ptr: *mut Object,
47    ) where
48        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
49        Object: Unsize<Trait> + 'static,
50    {
51        let erased = TypeErasedPointer::from_trait::<Object, Trait>(object_id, obj_ptr);
52        let old = self.traits.insert(trait_id, erased);
53        assert!(old.is_none(), "trait was already added to the component");
54    }
55
56    // Normally the [`add_repeated_traits`]` macro would be used instead of calling this directly.
57    #[doc(hidden)]
58    pub fn add_repeated_trait<Trait, Object>(
59        &mut self,
60        object_id: TypeId,
61        trait_id: TypeId,
62        obj_ptr: *mut Object,
63    ) where
64        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
65        Object: Unsize<Trait> + 'static,
66    {
67        let erased = TypeErasedPointer::from_trait::<Object, Trait>(object_id, obj_ptr);
68        let pointers = self.repeated.entry(trait_id).or_insert(vec![]);
69        pointers.push(erased);
70    }
71
72    // Normally the [`add_object`]` macro would be used instead of calling this directly.
73    #[doc(hidden)]
74    pub fn add_object<Object>(&mut self, obj_id: TypeId, obj_ptr: *mut Object)
75    where
76        Object: Send + Sync + 'static,
77    {
78        let erased: Box<dyn Any + Send + Sync> = unsafe { Box::from_raw(obj_ptr) };
79        let old = self.objects.insert(obj_id, erased);
80        assert!(
81            old.is_none(),
82            "object type was already added to the component"
83        );
84
85        self.refs.entry(obj_id).or_insert(ObjectRefs::new());
86    }
87
88    // TODO: May want to support remove_object. Would be kinda slow: probably need to
89    // change traits and repeated so that the value includes the object's type id. One
90    // nice thing is, that if we did do that, Debug and Display could print the traits
91    // associated with the corresponding object.
92
93    // Normally the [`has_trait`]` macro would be used instead of calling this directly.
94    #[doc(hidden)]
95    pub fn has<Trait>(&self, trait_id: TypeId) -> bool
96    where
97        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
98    {
99        self.traits.get(&trait_id).is_some()
100    }
101
102    // Normally the [`find_trait`]` macro would be used instead of calling this directly.
103    #[doc(hidden)]
104    pub fn find<Trait>(&self, trait_id: TypeId) -> Option<RefTrait<Trait>>
105    where
106        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
107    {
108        if let Some(erased) = self.traits.get(&trait_id) {
109            let refs = self.refs.get(&erased.object_id).unwrap();
110            let r = unsafe { erased.to_trait::<Trait>(refs) };
111            Some(r)
112        } else {
113            None
114        }
115    }
116
117    // Normally the [`find_trait_mut`]` macro would be used instead of calling this directly.
118    #[doc(hidden)]
119    pub fn find_mut<Trait>(&self, trait_id: TypeId) -> Option<RefMutTrait<Trait>>
120    where
121        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
122    {
123        if let Some(erased) = self.traits.get(&trait_id) {
124            let refs = self.refs.get(&erased.object_id).unwrap();
125            let r = unsafe { erased.to_trait_mut::<Trait>(refs) };
126            Some(r)
127        } else {
128            None
129        }
130    }
131
132    // Normally the [`find_repeated_trait`]` macro would be used instead of calling this directly.
133    #[doc(hidden)]
134    pub fn find_repeated<Trait>(&self, trait_id: TypeId) -> impl Iterator<Item = RefTrait<Trait>>
135    where
136        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
137    {
138        self.repeated
139            .get(&trait_id)
140            .unwrap_or(&self.empty)
141            .iter()
142            .map(|e| unsafe {
143                let refs = self.refs.get(&e.object_id).unwrap();
144                e.to_trait::<Trait>(refs)
145            })
146    }
147
148    // Normally the [`find_repeated_trait_mut`]` macro would be used instead of calling this directly.
149    #[doc(hidden)]
150    pub fn find_repeated_mut<Trait>(
151        &self,
152        trait_id: TypeId,
153    ) -> impl Iterator<Item = RefMutTrait<Trait>>
154    where
155        Trait: ?Sized + Pointee<Metadata = DynMetadata<Trait>> + 'static,
156    {
157        self.repeated
158            .get(&trait_id)
159            .unwrap_or(&self.empty)
160            .iter()
161            .map(|e| unsafe {
162                let refs = self.refs.get(&e.object_id).unwrap();
163                e.to_trait_mut::<Trait>(refs)
164            })
165    }
166}
167
168/// Use this for all trait and object types used within components.
169///
170/// # Examples
171///
172/// ```
173/// #![feature(lazy_cell)]
174/// use gear_objects::*;
175/// use paste::paste;
176///
177/// trait Fruit {
178///     fn eat(&self) -> String;
179/// }
180/// register_type!(Fruit);
181/// ```
182#[macro_export]
183macro_rules! register_type {
184    ($type:ty) => {
185        paste! {
186            pub fn [<get_ $type:lower _id>]() -> TypeId {
187                unique_type_id!()
188            }
189        }
190    };
191}
192
193// Use the [`add_object`] macro not this one.
194#[doc(hidden)]
195#[macro_export]
196macro_rules! add_traits {
197    ($component:expr, $obj_type:ty, $obj_ptr:expr, $trait1:ty) => {{
198        paste! {
199            $component.add_trait::<dyn $trait1, $obj_type>(
200                [<get_ $obj_type:lower _id>](),
201                [<get_ $trait1:lower _id>](),
202                $obj_ptr);
203        }
204    }};
205
206    ($component:expr, $obj_type:ty, $obj_ptr:expr, $trait1:ty, $($trait2:ty),+) => {{
207        add_traits!($component, $obj_type, $obj_ptr, $trait1);
208        add_traits!($component, $obj_type, $obj_ptr, $($trait2),+)
209    }};
210}
211
212// Use the [`add_object`] macro not this one.
213#[doc(hidden)]
214#[macro_export]
215macro_rules! add_repeated_traits {
216    ($component:expr, $obj_type:ty, $obj_ptr:expr, $trait1:ty) => {{
217        paste! {
218            $component.add_repeated_trait::<dyn $trait1, $obj_type>(
219                [<get_ $obj_type:lower _id>](),
220                [<get_ $trait1:lower _id>](),
221                $obj_ptr);
222        }
223    }};
224
225    ($component:expr, $obj_type:ty, $obj_ptr:expr, $trait1:ty, $($trait2:ty),+) => {{
226        add_repeated_traits!($component, $obj_type, $obj_ptr, $trait1);
227        add_repeated_traits!($component, $obj_type, $obj_ptr, $($trait2),+)
228    }};
229}
230
231/// Use this to add an object along with its associated traits to a component. Note that
232/// repeated traits are listed in a second optional list.
233///
234/// # Examples
235///
236/// ```
237/// #![feature(lazy_cell)]
238/// use gear_objects::*;
239/// use core::fmt;
240/// use paste::paste;
241/// use std::fmt::{Display};
242///
243/// struct Apple {}
244/// register_type!(Apple);
245///
246/// trait Fruit {
247///     fn eat(&self) -> String;
248/// }
249/// register_type!(Fruit);
250///
251/// impl Fruit for Apple {
252///     fn eat(&self) -> String {
253///         "yum!".to_owned()
254///     }
255/// }
256///
257/// impl fmt::Display for Apple {
258///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
259///         write!(f, "Apple")
260///     }
261/// }
262/// register_type!(Display);
263///
264/// let apple = Apple {};
265/// let mut component = Component::new("apple");
266/// add_object!(component, Apple, apple, [Fruit], [Display]);
267/// ```
268#[macro_export]
269macro_rules! add_object {
270    ($component:expr, $obj_type:ty, $object:expr, [$trait1:ty]) => {{   // 1 0
271        paste! {
272            let boxed = Box::new($object);
273            let obj_ptr = Box::into_raw(boxed);
274            add_traits!($component, $obj_type, obj_ptr, $trait1);
275            $component.add_object::<$obj_type>(
276                [<get_ $obj_type:lower _id>](),
277                obj_ptr);
278        }
279    }};
280
281    ($component:expr, $obj_type:ty, $object:expr, [$trait1:ty], [$trait2:ty]) => {{ // 1 1
282        paste! {
283            let boxed = Box::new($object);
284            let obj_ptr = Box::into_raw(boxed);
285            add_traits!($component, $obj_type, obj_ptr, $trait1);
286            add_repeated_traits!($component, $obj_type, obj_ptr, $trait2);
287            $component.add_object::<$obj_type>(
288                [<get_ $obj_type:lower _id>](),
289                obj_ptr);
290        }
291    }};
292
293    ($component:expr, $obj_type:ty, $object:expr, [$trait1:ty], [$trait2:ty, $($trait3:ty),+]) => {{   // 1 +
294        paste! {
295            let boxed = Box::new($object);
296            let obj_ptr = Box::into_raw(boxed);
297            add_traits!($component, $obj_type, obj_ptr, $trait1);
298            add_repeated_traits!($component, $obj_type, obj_ptr, $trait2);
299            add_repeated_traits!($component, $obj_type, obj_ptr, $($trait3),+);
300            $component.add_object::<$obj_type>(
301                [<get_ $obj_type:lower _id>](),
302                obj_ptr);
303        }
304    }};
305
306    ($component:expr, $obj_type:ty, $object:expr, [$trait1:ty, $($trait2:ty),+]) => {{  // + 0
307        paste! {
308            let boxed = Box::new($object);
309            let obj_ptr = Box::into_raw(boxed);
310            add_traits!($component, $obj_type, obj_ptr, $trait1);
311            add_traits!($component, $obj_type, obj_ptr, $($trait2),+);
312            $component.add_object::<$obj_type>(
313                [<get_ $obj_type:lower _id>](),
314                obj_ptr);
315        }
316    }};
317
318    ($component:expr, $obj_type:ty, $object:expr, [$trait1:ty, $($trait2:ty),+], [$trait3:ty]) => {{   // + 1
319        paste! {
320            let boxed = Box::new($object);
321            let obj_ptr = Box::into_raw(boxed);
322            add_traits!($component, $obj_type, obj_ptr, $trait1);
323            add_traits!($component, $obj_type, obj_ptr, $($trait2),+);
324            add_repeated_traits!($component, $obj_type, obj_ptr, $trait3);
325            $component.add_object::<$obj_type>(
326                [<get_ $obj_type:lower _id>](),
327                obj_ptr);
328        }
329    }};
330
331    ($component:expr, $obj_type:ty, $object:expr, [$trait1:ty, $($trait2:ty),+], [$trait3:ty, $($trait4:ty),+]) => {{   // + +
332        paste! {
333            let boxed = Box::new($object);
334            let obj_ptr = Box::into_raw(boxed);
335            add_traits!($component, $obj_type, obj_ptr, $trait1);
336            add_traits!($component, $obj_type, obj_ptr, $($trait2),+);
337            add_repeated_traits!($component, $obj_type, obj_ptr, $trait3);
338            add_repeated_traits!($component, $obj_type, obj_ptr, $($trait4),+);
339            $component.add_object::<$obj_type>(
340                [<get_ $obj_type:lower _id>](),
341                obj_ptr);
342        }
343    }};
344}
345
346#[macro_export]
347macro_rules! has_trait {
348    ($component:expr, $trait:ty) => {{
349        paste! {
350            $component.has::<dyn $trait>([<get_ $trait:lower _id>]())
351        }
352    }};
353}
354
355/// Returns an optional reference to a trait for an object within the component.
356///
357/// # Examples
358///
359/// ```
360/// #![feature(lazy_cell)]
361/// use gear_objects::*;
362/// use paste::paste;
363///
364/// struct Apple {}
365/// register_type!(Apple);
366///
367/// trait Fruit {
368///     fn eat(&self) -> String;
369/// }
370/// register_type!(Fruit);
371///
372/// impl Fruit for Apple {
373///     fn eat(&self) -> String {
374///         "yum!".to_owned()
375///     }
376/// }
377///
378/// let apple = Apple {};
379/// let mut component = Component::new("apple");
380/// add_object!(component, Apple, apple, [Fruit]);
381///
382/// let fruit = find_trait!(component, Fruit);
383/// assert_eq!(fruit.unwrap().eat(), "yum!");
384/// ```
385#[macro_export]
386macro_rules! find_trait {
387    ($component:expr, $trait:ty) => {{
388        paste! {
389            $component.find::<dyn $trait>([<get_ $trait:lower _id>]())
390        }
391    }};
392}
393
394/// The borrowing rules for components are the standard rust rules: mutable references are
395/// exclusive references. But they apply to individual objects within a component so it's
396/// possible to simultaneously get two mutable references to two different objects within
397/// a component but not two mutable references to the same object (this is checked at
398/// runtime).
399#[macro_export]
400macro_rules! find_trait_mut {
401    ($component:expr, $trait:ty) => {{
402        paste! {
403            $component.find_mut::<dyn $trait>([<get_ $trait:lower _id>]())
404        }
405    }};
406}
407
408/// Returns an iterator over a trait that may be implemented by multiple objects within
409/// the component.
410#[macro_export]
411macro_rules! find_repeated_trait {
412    ($component:expr, $trait:ty) => {{
413        paste! {
414            $component.find_repeated::<dyn $trait>([<get_ $trait:lower _id>]())
415        }
416    }};
417}
418
419/// Returns an iterator over a trait that may be implemented by multiple objects within
420/// the component.
421#[macro_export]
422macro_rules! find_repeated_trait_mut {
423    ($component:expr, $trait:ty) => {{
424        paste! {
425            $component.find_repeated_mut::<dyn $trait>([<get_ $trait:lower _id>]())
426        }
427    }};
428}
429
430impl PartialEq for Component {
431    fn eq(&self, other: &Component) -> bool {
432        self.id == other.id
433    }
434}
435
436impl Eq for Component {}
437
438impl Ord for Component {
439    fn cmp(&self, rhs: &Self) -> std::cmp::Ordering {
440        self.id.cmp(&rhs.id)
441    }
442}
443
444impl PartialOrd for Component {
445    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
446        Some(self.cmp(other))
447    }
448}
449
450impl Hash for Component {
451    fn hash<H: Hasher>(&self, state: &mut H) {
452        self.id.hash(state);
453    }
454}
455
456impl Debug for Component {
457    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
458        writeln!(f, "{:?}", self.id)?;
459        for d in find_repeated_trait!(self, Debug) {
460            d.fmt(f)?;
461        }
462        fmt::Result::Ok(())
463    }
464}
465register_type!(Debug);
466
467#[cfg(test)]
468mod tests {
469    use super::*;
470    use std::fmt::Display;
471    use std::sync::atomic::AtomicU8;
472    use std::sync::atomic::Ordering;
473
474    trait Fruit {
475        fn eat(&self) -> String;
476    }
477    register_type!(Fruit);
478
479    trait Ball {
480        fn throw(&self) -> String;
481    }
482    register_type!(Ball);
483
484    struct Apple {}
485    register_type!(Apple);
486
487    impl Fruit for Apple {
488        fn eat(&self) -> String {
489            "yum!".to_owned()
490        }
491    }
492
493    impl Ball for Apple {
494        fn throw(&self) -> String {
495            "splat".to_owned()
496        }
497    }
498
499    impl fmt::Display for Apple {
500        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
501            write!(f, "Apple")
502        }
503    }
504    register_type!(Display);
505
506    trait Ripe {
507        fn ripeness(&self) -> i32;
508        fn ripen(&mut self);
509    }
510    register_type!(Ripe);
511    struct Banana {
512        ripeness: i32,
513    }
514    register_type!(Banana);
515
516    impl Ripe for Banana {
517        fn ripeness(&self) -> i32 {
518            self.ripeness
519        }
520
521        fn ripen(&mut self) {
522            self.ripeness += 1;
523        }
524    }
525
526    impl Fruit for Banana {
527        fn eat(&self) -> String {
528            "mushy".to_owned()
529        }
530    }
531
532    impl fmt::Display for Banana {
533        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
534            write!(f, "Banana")
535        }
536    }
537
538    static DROP_COUNT: AtomicU8 = AtomicU8::new(0);
539
540    struct Football {}
541    register_type!(Football);
542
543    impl Ball for Football {
544        fn throw(&self) -> String {
545            "touchdown".to_owned()
546        }
547    }
548
549    impl Drop for Football {
550        fn drop(&mut self) {
551            DROP_COUNT.fetch_add(1, Ordering::Relaxed);
552        }
553    }
554
555    #[test]
556    fn two_traits() {
557        let apple = Apple {};
558        let mut component = Component::new("apple");
559        add_object!(component, Apple, apple, [Fruit, Ball]);
560
561        let fruit = find_trait!(component, Fruit);
562        assert!(fruit.is_some());
563        assert_eq!(fruit.unwrap().eat(), "yum!");
564
565        let ball = find_trait!(component, Ball);
566        assert!(ball.is_some());
567        assert_eq!(ball.unwrap().throw(), "splat");
568    }
569
570    #[test]
571    fn has() {
572        let apple = Apple {};
573        let mut component = Component::new("apple");
574        add_object!(component, Apple, apple, [Fruit, Ball]);
575
576        assert!(has_trait!(component, Fruit));
577        assert!(!has_trait!(component, Ripe));
578    }
579
580    #[test]
581    fn missing_trait() {
582        let banana = Banana { ripeness: 0 };
583        let mut component = Component::new("banana");
584        add_object!(component, Banana, banana, [Fruit]);
585
586        let fruit = find_trait!(component, Fruit);
587        assert!(fruit.is_some());
588        assert_eq!(fruit.unwrap().eat(), "mushy");
589
590        let ball = find_trait!(component, Ball);
591        assert!(ball.is_none());
592    }
593
594    #[test]
595    fn dropped_object() {
596        assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 0);
597        {
598            let football = Football {};
599            let mut component = Component::new("football");
600            add_object!(component, Football, football, [Ball]);
601
602            let ball = find_trait!(component, Ball);
603            assert!(ball.is_some());
604            assert_eq!(ball.unwrap().throw(), "touchdown");
605        }
606        assert_eq!(DROP_COUNT.load(Ordering::Relaxed), 1);
607    }
608
609    #[test]
610    fn mutable_find() {
611        let banana = Banana { ripeness: 0 };
612        let mut component = Component::new("banana");
613        add_object!(component, Banana, banana, [Fruit, Ripe]);
614
615        {
616            let ripe = find_trait!(component, Ripe).unwrap();
617            assert_eq!(ripe.ripeness(), 0);
618        }
619
620        {
621            let mut ripe = find_trait_mut!(component, Ripe).unwrap();
622            ripe.ripen();
623            ripe.ripen();
624
625            // this will panic
626            // let mut fruit = find_trait_mut!(component, Fruit).unwrap();
627            // fruit.eat();
628        }
629
630        let ripe = find_trait!(component, Ripe).unwrap(); // grab a new ref to appease the borrow checker
631        assert_eq!(ripe.ripeness(), 2);
632    }
633
634    #[test]
635    fn repeated() {
636        let banana = Banana { ripeness: 0 };
637        let apple = Apple {};
638        let mut component = Component::new("banana");
639        add_object!(component, Banana, banana, [Fruit, Ripe], [Display]);
640        add_object!(component, Apple, apple, [Ball], [Display]);
641
642        // display method
643        // fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>;
644
645        let displays: Vec<String> = find_repeated_trait!(component, Display)
646            .map(|t| format!("{}", &(*t)))
647            .collect();
648        assert_eq!(displays.len(), 2);
649        assert!(
650            (displays[0] == "Apple" && displays[1] == "Banana")
651                || (displays[1] == "Apple" && displays[0] == "Banana")
652        );
653    }
654}
655
656#[cfg(test)]
657mod thread_tests {
658    use super::*;
659    use std::{
660        sync::{Arc, RwLock},
661        thread,
662    };
663
664    trait Name {
665        fn get(&self) -> &str;
666        fn get_mut(&mut self) -> &mut String;
667    }
668    register_type!(Name);
669
670    struct Thing {
671        name: String,
672    }
673    register_type!(Thing);
674
675    impl Name for Thing {
676        fn get(&self) -> &str {
677            &self.name
678        }
679
680        fn get_mut(&mut self) -> &mut String {
681            &mut self.name
682        }
683    }
684
685    #[test]
686    fn threading() {
687        let thing = Thing {
688            name: "hello world".to_owned(),
689        };
690        let mut component = Component::new("thing");
691        add_object!(component, Thing, thing, [Name]);
692
693        let gstate = Arc::new(RwLock::new(component));
694
695        let state = gstate.clone();
696        let thread1 = thread::spawn(move || {
697            for _ in 0..100 {
698                {
699                    let component = state.write().unwrap();
700                    let mut name = find_trait_mut!(component, Name).unwrap();
701                    let name = name.get_mut();
702                    if name.len() < 30 {
703                        name.insert(6, '_');
704                    }
705                }
706                thread::yield_now();
707            }
708        });
709
710        let state = gstate.clone();
711        let thread2 = thread::spawn(move || {
712            for _ in 0..100 {
713                {
714                    let component = state.write().unwrap();
715                    let mut name = find_trait_mut!(component, Name).unwrap();
716                    let name = name.get_mut();
717                    if name.len() > 11 {
718                        name.remove(6);
719                    }
720                }
721                thread::yield_now();
722            }
723        });
724
725        thread1.join().unwrap();
726        thread2.join().unwrap();
727
728        let component = &gstate.read().unwrap();
729        let name = find_trait!(component, Name).unwrap();
730        let name = name.get();
731        assert!(name.starts_with("hello"));
732        assert!(name.ends_with("world"));
733    }
734}