papergrid/config/spanned/
entity_map.rs

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: Default::default(),
24            columns: Default::default(),
25            cells: Default::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, pos: Position) -> &T {
36        // todo: optimize;
37        //
38        // Cause we can change rows/columns/cells separately we need to check them separately.
39        // But we often doing this checks in `Grid::fmt` and I believe if we could optimize it it could be beneficial.
40        //
41        // Haven't found a solution for that yet.
42        //
43        // I was wondering if there is a hash function like.
44        // Apparently it doesn't make sense cause we will reset columns/rows on cell insert which is not what we want.
45        //
46        // ```
47        // hash(column, row) == hash(column) == hash(row)
48        // ```
49        //
50        // ref: https://opendsa-server.cs.vt.edu/ODSA/Books/Everything/html/Sparse.html
51        // ref: https://users.rust-lang.org/t/make-hash-return-same-value-whather-the-order-of-element-of-a-tuple/69932/13
52
53        self.cells
54            .get(&pos)
55            .or_else(|| self.columns.get(&pos.col))
56            .or_else(|| self.rows.get(&pos.row))
57            .unwrap_or(&self.global)
58    }
59
60    /// Removes a value for an [`Entity`].
61    pub fn remove(&mut self, entity: Entity) {
62        match entity {
63            Entity::Global => {
64                self.cells.clear();
65                self.rows.clear();
66                self.columns.clear();
67            }
68            Entity::Column(col) => self.cells.retain(|pos, _| pos.col != col),
69            Entity::Row(row) => self.cells.retain(|pos, _| pos.row != row),
70            Entity::Cell(row, col) => {
71                self.cells.remove(&Position::new(row, col));
72            }
73        }
74    }
75}
76
77impl<T: Clone> EntityMap<T> {
78    /// Set a value for an [`Entity`].
79    pub fn insert(&mut self, entity: Entity, value: T) {
80        match entity {
81            Entity::Column(col) => {
82                for &row in self.rows.keys() {
83                    self.cells.insert(Position::new(row, col), value.clone());
84                }
85
86                self.columns.insert(col, value);
87            }
88            Entity::Row(row) => {
89                for &col in self.columns.keys() {
90                    self.cells.insert(Position::new(row, col), value.clone());
91                }
92
93                self.rows.insert(row, value);
94            }
95            Entity::Cell(row, col) => {
96                self.cells.insert(Position::new(row, col), value);
97            }
98            Entity::Global => {
99                self.remove(Entity::Global);
100                self.global = value
101            }
102        }
103    }
104}
105
106impl<T> From<EntityMap<T>> for HashMap<Entity, T> {
107    fn from(value: EntityMap<T>) -> Self {
108        let mut m = HashMap::new();
109        m.insert(Entity::Global, value.global);
110
111        for (pos, value) in value.cells {
112            m.insert(Entity::from(pos), value);
113        }
114
115        for (row, value) in value.rows {
116            m.insert(Entity::Row(row), value);
117        }
118
119        for (col, value) in value.columns {
120            m.insert(Entity::Column(col), value);
121        }
122
123        m
124    }
125}
126
127impl<T> AsRef<T> for EntityMap<T> {
128    fn as_ref(&self) -> &T {
129        &self.global
130    }
131}