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