Skip to main content

furmint_registry/
lib.rs

1//! # Component registry
2//! This crate provides required trait, struct and a macro to store and decode components from scenes
3#![warn(missing_docs)]
4
5pub extern crate ron;
6
7use ron::Value;
8use specs::{Entity, LazyUpdate, World};
9use std::collections::HashMap;
10
11/// All components must have a factory implementing this trait to be able created from a scene
12/// config.
13///
14/// You might want to use the [`impl_component_factory`] macro.
15pub trait ComponentFactory: Send + Sync {
16    /// Build a component from `value` and insert it into `entity` of `world`
17    fn insert(&self, world: &mut World, entity: Entity, value: &Value);
18    /// Build a component from `value` and lazily insert it into `entity` of `world`
19    fn insert_lazy(&self, lazy: &LazyUpdate, entity: Entity, value: &Value);
20    /// Component name
21    fn name() -> &'static str
22    where
23        Self: Sized;
24}
25
26/// Registry of component name - component factory
27pub struct ComponentRegistry {
28    map: HashMap<String, Box<dyn ComponentFactory>>,
29}
30
31impl ComponentRegistry {
32    /// Creates a new instance of [`ComponentRegistry`]
33    pub fn new() -> Self {
34        Self {
35            map: HashMap::new(),
36        }
37    }
38
39    /// Registers a component factory
40    pub fn register(&mut self, name: &str, f: Box<dyn ComponentFactory>) {
41        self.map.insert(name.to_string(), f);
42    }
43
44    /// Fetches a component factory by name
45    pub fn get(&self, name: &str) -> Option<&dyn ComponentFactory> {
46        self.map.get(name).map(|v| &**v)
47    }
48}
49
50impl Default for ComponentRegistry {
51    fn default() -> Self {
52        Self::new()
53    }
54}
55
56#[macro_export]
57/// Create and implement a component factory for a component.
58///
59/// # Arguments
60/// * `factory` - arbitrary factory name (tho component name + Factory is recommended)
61/// * `component` - the component itself
62///
63/// # Example
64/// ```ignore
65/// impl_component_factory!(SpriteFactory, Sprite);
66/// ```
67macro_rules! impl_component_factory {
68    ($factory:ident, $component:path, $name:expr) => {
69        #[derive(Debug, Default)]
70        pub struct $factory;
71
72        impl $crate::ComponentFactory for $factory {
73            fn insert(&self, world: &mut specs::World, entity: specs::Entity, value: &ron::Value) {
74                match ron::Value::into_rust::<$component>(value.clone()) {
75                    Ok(component) => {
76                        if let Err(error) = world
77                            .write_storage::<$component>()
78                            .insert(entity, component)
79                        {
80                            log::error!(
81                                "failed to insert component {} for entity {:?}: {}",
82                                stringify!($component),
83                                entity,
84                                error
85                            );
86                        }
87                    }
88
89                    Err(error) => {
90                        log::error!(
91                            "failed to construct component {} for entity {:?}: {}",
92                            stringify!($component),
93                            entity,
94                            error
95                        );
96                    }
97                }
98            }
99
100            fn insert_lazy(
101                &self,
102                lazy: &specs::LazyUpdate,
103                entity: specs::Entity,
104                value: &ron::Value,
105            ) {
106                match ron::Value::into_rust::<$component>(value.clone()) {
107                    Ok(component) => {
108                        lazy.insert(entity, component);
109                    }
110
111                    Err(error) => {
112                        log::error!(
113                            "failed to construct component {} for entity {:?}: {}",
114                            stringify!($component),
115                            entity,
116                            error
117                        );
118                    }
119                }
120            }
121
122            fn name() -> &'static str {
123                $name
124            }
125        }
126    };
127}