1use fnv::FnvHashMap;
2
3use crate::config::{Entity, Position};
4
5/// A structure to keep information for [`Entity`] as a key.
6#[derive(Debug, Default, Clone, PartialEq, Eq)]
7pub struct EntityMap<T> {
8 // we have a global type to allocate in on stack.
9 // because most of the time no changes are made to the [`EntityMap`].
10 global: T,
11 columns: FnvHashMap<usize, T>,
12 rows: FnvHashMap<usize, T>,
13 cells: FnvHashMap<Position, T>,
14}
15
16impl<T> EntityMap<T> {
17 /// Creates an empty [`EntityMap`].
18 pub fn new(global: T) -> Self {
19 Self {
20 global,
21 rows: FnvHashMap::default(),
22 columns: FnvHashMap::default(),
23 cells: FnvHashMap::default(),
24 }
25 }
26
27 /// Get a value for an [`Entity`].
28 pub fn get(&self, entity: Entity) -> &T {
29 if self.rows.is_empty() && self.columns.is_empty() && self.cells.is_empty() {
30 return &self.global;
31 }
32
33 match entity {
34 Entity::Column(col) => self.columns.get(&col).unwrap_or(&self.global),
35 Entity::Row(row) => self.rows.get(&row).unwrap_or(&self.global),
36 Entity::Cell(row, col) => {
37 // todo: optimize;
38 //
39 // Cause we can change rows/columns/cells separately we need to check them separately.
40 // But we often doing this checks in `Grid::fmt` and I believe if we could optimize it it could be beneficial.
41 //
42 // Haven't found a solution for that yet.
43 //
44 // I was wondering if there is a hash function like.
45 // Apparently it doesn't make sense cause we will reset columns/rows on cell insert which is not what we want.
46 //
47 // ```
48 // hash(column, row) == hash(column) == hash(row)
49 // ```
50 //
51 // ref: https://opendsa-server.cs.vt.edu/ODSA/Books/Everything/html/Sparse.html
52 // ref: https://users.rust-lang.org/t/make-hash-return-same-value-whather-the-order-of-element-of-a-tuple/69932/13
53
54 self.cells
55 .get(&(row, col))
56 .or_else(|| self.columns.get(&col))
57 .or_else(|| self.rows.get(&row))
58 .unwrap_or(&self.global)
59 }
60 Entity::Global => &self.global,
61 }
62 }
63
64 /// Removes a value for an [`Entity`].
65 pub fn remove(&mut self, entity: Entity) {
66 match entity {
67 Entity::Global => {
68 self.cells.clear();
69 self.rows.clear();
70 self.columns.clear();
71 }
72 Entity::Column(col) => self.cells.retain(|&(_, c), _| c != col),
73 Entity::Row(row) => self.cells.retain(|&(r, _), _| r != row),
74 Entity::Cell(row, col) => {
75 self.cells.remove(&(row, col));
76 }
77 }
78 }
79}
80
81impl<T: Clone> EntityMap<T> {
82 /// Set a value for an [`Entity`].
83 pub fn insert(&mut self, entity: Entity, value: T) {
84 match entity {
85 Entity::Column(col) => {
86 for &row in self.rows.keys() {
87 self.cells.insert((row, col), value.clone());
88 }
89
90 self.columns.insert(col, value);
91 }
92 Entity::Row(row) => {
93 for &col in self.columns.keys() {
94 self.cells.insert((row, col), value.clone());
95 }
96
97 self.rows.insert(row, value);
98 }
99 Entity::Cell(row, col) => {
100 self.cells.insert((row, col), value);
101 }
102 Entity::Global => {
103 self.remove(Entity::Global);
104 self.global = value
105 }
106 }
107 }
108}
109