rush_ecs_core/
blueprint.rs

1use crate::error::CoreError;
2use anyhow::{bail, Result};
3use borsh::{BorshDeserialize, BorshSerialize};
4use std::{
5    cmp::{Eq, PartialEq},
6    collections::BTreeMap,
7    mem::discriminant,
8};
9
10// TODO: Consider using structs for Region and Entity
11// with Display trait as oposed to Plain-Old-Data String
12pub type Region = String;
13pub type Entity = String;
14pub type Component = String;
15pub type ComponentPair = (Component, ComponentValue);
16pub type ComponentTree = BTreeMap<Component, ComponentValue>;
17pub type ComponentType = String;
18pub type ComponentTypeTree = BTreeMap<Component, ComponentType>;
19pub type BlueprintString = String;
20
21/// Enum defining the supported dataset in the World
22/// and how it maps with Rust data types
23#[derive(Clone, BorshDeserialize, BorshSerialize, Debug)]
24pub enum ComponentValue {
25    String(String),
26    Integer(i64),
27    Float(f64),
28    Boolean(bool),
29}
30
31impl ComponentValue {
32    /// Unwraps Enum to its parameter value
33    ///
34    /// Get String inside Enum parameter. Panics if
35    /// the ComponentValue is not a String
36    pub fn unwrap_string(self) -> String {
37        use ComponentValue::*;
38
39        match self {
40            String(v) => v,
41            _ => panic!("Not a String"),
42        }
43    }
44
45    /// Unwraps Enum to its parameter value
46    ///
47    /// Get f64 inside Enum parameter. Panics if
48    /// the ComponentValue is not an f64
49    pub fn unwrap_float(self) -> f64 {
50        use ComponentValue::*;
51
52        match self {
53            Float(v) => v,
54            _ => panic!("Not a Float"),
55        }
56    }
57
58    /// Unwraps Enum to its parameter value
59    ///
60    /// Get i64 inside Enum parameter. Panics if
61    /// the ComponentValue is not an i64
62    pub fn unwrap_int(self) -> i64 {
63        use ComponentValue::*;
64
65        match self {
66            Integer(v) => v,
67            _ => panic!("Not an Integer"),
68        }
69    }
70
71    /// Unwraps Enum to its parameter value
72    ///
73    /// Get bool inside Enum parameter. Panics if
74    /// the ComponentValue is not an bool
75    pub fn unwrap_bool(self) -> bool {
76        use ComponentValue::*;
77
78        match self {
79            Boolean(v) => v,
80            _ => panic!("Not a Boolean"),
81        }
82    }
83}
84
85impl PartialEq for ComponentValue {
86    fn eq(&self, other: &Self) -> bool {
87        match self {
88            ComponentValue::String(v_self) => {
89                let mut is_equal = false;
90                if let ComponentValue::String(v_other) = other {
91                    is_equal = v_self == v_other;
92                }
93                is_equal
94            }
95
96            ComponentValue::Integer(v_self) => {
97                let mut is_equal = false;
98                if let ComponentValue::Integer(v_other) = other {
99                    is_equal = v_self == v_other;
100                }
101                is_equal
102            }
103
104            ComponentValue::Float(v_self) => {
105                let mut is_equal = false;
106                if let ComponentValue::Float(v_other) = other {
107                    is_equal = v_self == v_other;
108                }
109                is_equal
110            }
111
112            ComponentValue::Boolean(v_self) => {
113                let mut is_equal = false;
114                if let ComponentValue::Boolean(v_other) = other {
115                    is_equal = v_self == v_other;
116                }
117                is_equal
118            }
119        }
120    }
121}
122
123impl Eq for ComponentValue {}
124
125/// Blueprint of a World
126///
127/// Represents a World in programmable data
128#[derive(Clone, Debug, Default, Eq, PartialEq)]
129pub struct Blueprint {
130    /// World's Name
131    pub name: String,
132    /// World's Description
133    pub description: String,
134    // TODO: Fix Regions, posssible remove entity identification
135    /// Region names and Entities that exist in it
136    pub regions: BTreeMap<Region, Vec<Entity>>,
137    /// Entity name and Names of its Components
138    pub entities: BTreeMap<Entity, ComponentTypeTree>,
139    /// Instances of different Entities in different Regions
140    pub instances: BTreeMap<Region, BTreeMap<Entity, Vec<ComponentTree>>>,
141}
142
143impl Blueprint {
144    pub fn new(world_name: String, world_description: String) -> Self {
145        Self {
146            name: world_name,
147            description: world_description,
148            entities: BTreeMap::new(),
149            regions: BTreeMap::new(),
150            instances: BTreeMap::new(),
151        }
152    }
153
154    /// Preloads Region and Entity Keys
155    ///
156    /// The add_instance associated function will fail when preload
157    /// is not called to preload the Region and Entity keys
158    /// needed in the Instances table
159    pub fn preload(&mut self, regions: Vec<Region>, entities: Vec<Entity>) {
160        for region_name in regions.into_iter() {
161            // preload Regions
162            self.instances.insert(region_name.clone(), BTreeMap::new());
163
164            // preload Entities
165            let region_mut = self.instances.get_mut(&region_name).unwrap();
166            for entity_name in entities.clone().into_iter() {
167                region_mut.insert(entity_name, Vec::new());
168            }
169        }
170    }
171
172    pub fn add_entity(&mut self, name: Entity, component_types: ComponentTypeTree) {
173        self.entities.insert(name, component_types);
174    }
175
176    pub fn add_region(&mut self, name: Region, entities: Vec<Entity>) {
177        self.regions.insert(name, entities);
178    }
179
180    pub fn add_instance(
181        &mut self,
182        region: Region,
183        entity: Entity,
184        component_tree: ComponentTree,
185    ) -> Result<()> {
186        // get mutable region
187        let region_mut = match self.instances.get_mut(&region) {
188            // instance exists
189            Some(e) => e,
190            // insert and get, if not exists
191            None => bail!(CoreError::RegionNotFound),
192        };
193
194        // get mutable entity
195        let entity_mut = match region_mut.get_mut(&entity) {
196            // instance exists
197            Some(e) => e,
198            // insert and get, if not exists
199            None => bail!(CoreError::EntityNotFound),
200        };
201
202        // add entity instance to blueprint under region
203        entity_mut.push(component_tree);
204
205        Ok(())
206    }
207
208    // TODO: Add Validations
209    /// Add Instance with default values
210    ///
211    /// Fetches the component tree structure from self.entities
212    /// and creates an Instance with default values according
213    /// to the Entity's ComponentTypeTree
214    ///
215    pub fn add_default_instance(&mut self, region: Region, entity: Entity) -> Result<u64> {
216        // get mutable region
217        let region_mut = match self.instances.get_mut(&region) {
218            // instance exists
219            Some(e) => e,
220            // insert and get, if not exists
221            None => bail!(CoreError::RegionNotFound),
222        };
223
224        // get mutable entity
225        let entity_mut = match region_mut.get_mut(&entity) {
226            // instance exists
227            Some(e) => e,
228            // insert and get, if not exists
229            None => bail!(CoreError::EntityNotFound),
230        };
231
232        // get entity component type tree
233        let component_type_tree = self.entities.get(&entity).unwrap();
234
235        // populate component tree based on type tree
236        let mut component_tree: ComponentTree = BTreeMap::new();
237        for k in component_type_tree.keys() {
238            let v_type = component_type_tree.get(k).unwrap().clone();
239
240            let v: ComponentValue;
241            if v_type == "String" {
242                v = ComponentValue::String(String::default())
243            } else if v_type == "f64" {
244                v = ComponentValue::Float(f64::default())
245            } else if v_type == "i64" {
246                v = ComponentValue::Integer(i64::default())
247            } else if v_type == "bool" {
248                v = ComponentValue::Boolean(bool::default())
249            } else {
250                bail!(CoreError::UnsupportedDataType);
251            }
252
253            // push type default into component tree
254            component_tree.insert(k.to_string(), v);
255        }
256
257        // add instance
258        entity_mut.push(component_tree);
259
260        // return index (nonce) of instance
261        Ok(entity_mut.len() as u64)
262    }
263
264    /// Return Instance with default values
265    ///
266    /// Fetches the component tree structure from self.entities
267    /// and creates an Instance with default values according
268    /// to the Entity's ComponentTypeTree
269    ///
270    pub fn get_default_components(&self, entity: &Entity) -> Result<ComponentTree> {
271        // get entity component type tree
272        let component_type_tree = self.entities.get(entity).unwrap();
273
274        // populate component tree based on type tree
275        let mut component_tree: ComponentTree = BTreeMap::new();
276        for k in component_type_tree.keys() {
277            let v_type = component_type_tree.get(k).unwrap().clone();
278
279            let v: ComponentValue;
280            if v_type == "String" {
281                v = ComponentValue::String(String::default())
282            } else if v_type == "f64" {
283                v = ComponentValue::Float(f64::default())
284            } else if v_type == "i64" {
285                v = ComponentValue::Integer(i64::default())
286            } else if v_type == "bool" {
287                v = ComponentValue::Boolean(bool::default())
288            } else {
289                bail!(CoreError::UnsupportedDataType);
290            }
291
292            // push type default into component tree
293            component_tree.insert(k.to_string(), v);
294        }
295
296        // return component tree
297        Ok(component_tree)
298    }
299
300    pub fn get_instance(
301        &mut self,
302        region: Region,
303        entity: Entity,
304        nonce: u64,
305    ) -> Result<ComponentTree> {
306        // get mutable region
307        let region_mut = match self.instances.get_mut(&region) {
308            // instance exists
309            Some(e) => e,
310            // insert and get, if not exists
311            None => bail!(CoreError::RegionNotFound),
312        };
313
314        // get mutable entity
315        let entity_mut = match region_mut.get_mut(&entity) {
316            // instance exists
317            Some(e) => e,
318            // insert and get, if not exists
319            None => bail!(CoreError::EntityNotFound),
320        };
321
322        // get instance component tree
323        let instance = entity_mut[nonce as usize].clone();
324
325        Ok(instance)
326    }
327
328    pub fn get_component_value(
329        &mut self,
330        region: Region,
331        entity: Entity,
332        nonce: u64,
333        component: Component,
334    ) -> Result<ComponentValue> {
335        let component_tree = match self.get_instance(region, entity, nonce) {
336            Ok(ct) => ct,
337            Err(e) => bail!("{}: {}", CoreError::InstanceNotFound, e),
338        };
339
340        let option_value = component_tree.get(&component);
341
342        match option_value {
343            Some(v) => Ok(v.clone()),
344            None => bail!(CoreError::ComponentNotFound),
345        }
346    }
347
348    pub fn set_component_value(
349        &mut self,
350        region: Region,
351        entity: Entity,
352        nonce: u64,
353        component: Component,
354        value: ComponentValue,
355    ) -> Result<()> {
356        // get mutable region
357        let region_mut = match self.instances.get_mut(&region) {
358            // instance exists
359            Some(e) => e,
360            // insert and get, if not exists
361            None => bail!(CoreError::RegionNotFound),
362        };
363
364        // get mutable entity
365        let entity_mut = match region_mut.get_mut(&entity) {
366            // instance exists
367            Some(e) => e,
368            // insert and get, if not exists
369            None => bail!(CoreError::EntityNotFound),
370        };
371
372        let component_tree = &mut entity_mut[nonce as usize];
373
374        let option_value = component_tree.get_mut(&component);
375
376        if option_value.is_none() {
377            bail!(CoreError::ComponentNotFound);
378        }
379
380        let component_value = option_value.unwrap();
381
382        // ensure they're the same ComponentValue variant
383        if discriminant(&value) != discriminant(component_value) {
384            bail!(CoreError::MismatchedDataType)
385        }
386
387        *component_value = value;
388
389        Ok(())
390    }
391}