swamp_script_core_extra/
map2.rs

1use seq_map::SeqMap;
2use std::fmt::Debug;
3use std::fmt::Display;
4use std::hash::Hash;
5
6pub fn print_grid<R, C, V>(map: &Map2<R, C, V>)
7where
8    R: Display + Eq + Hash + Clone,
9    C: Display + Eq + Hash + Clone,
10    V: Debug + Clone,
11{
12    let rows: Vec<_> = map.rows.keys().collect();
13    let cols: Vec<_> = map.columns.keys().collect();
14
15    // Print header: leave space for row labels.
16    print!("{:>10} ", ""); // empty top-left cell
17    for col in &cols {
18        print!("{:>10} ", col);
19    }
20    println!();
21
22    for row in rows {
23        print!("{:>10} ", row);
24        for col in &cols {
25            if let Some(value) = map.get(col, row) {
26                print!("{:?} ", value);
27            } else {
28                print!("{:>10} ", "");
29            }
30        }
31        println!();
32    }
33}
34
35#[derive(Debug, Clone)]
36pub struct Map2<R: Eq + Hash, C: Eq + Hash, V> {
37    rows: SeqMap<R, SeqMap<C, V>>,
38    columns: SeqMap<C, SeqMap<R, V>>,
39}
40
41impl<R, C, V> Map2<R, C, V>
42where
43    R: Eq + Hash + Clone,
44    C: Eq + Hash + Clone,
45    V: Clone,
46{
47    #[must_use]
48    pub fn new() -> Self {
49        Self {
50            rows: SeqMap::new(),
51            columns: SeqMap::new(),
52        }
53    }
54
55    pub fn rows(&self) -> &SeqMap<R, SeqMap<C, V>> {
56        &self.rows
57    }
58
59    pub fn columns(&self) -> &SeqMap<C, SeqMap<R, V>> {
60        &self.columns
61    }
62
63    /// Returns a reference to the value at the given row and column.
64    pub fn get(&self, col: &C, row: &R) -> Option<&V> {
65        self.rows.get(row).and_then(|row_map| row_map.get(col))
66    }
67
68    /// Gets a reference to the row (i.e. all columns for that row).
69    pub fn get_row(&self, row: &R) -> Option<&SeqMap<C, V>> {
70        self.rows.get(row)
71    }
72
73    /// Gets a reference to the column (i.e. all rows for that column).
74    pub fn get_column(&self, col: &C) -> Option<&SeqMap<R, V>> {
75        self.columns.get(col)
76    }
77
78    pub fn has(&self, col: &C, row: &R) -> bool {
79        self.rows
80            .get(row)
81            .map_or(false, |row_map| row_map.contains_key(col))
82    }
83
84    /// Inserts a value into the map at the given row and column.
85    /// If there was an existing value at that position, it is returned.
86    pub fn insert(&mut self, col: C, row: R, value: V) {
87        // Insert into the rows map.
88        if self.rows.contains_key(&row) {
89            self.rows
90                .get_mut(&row)
91                .unwrap()
92                .insert(col.clone(), value.clone())
93                .unwrap();
94        } else {
95            let mut row_map = SeqMap::new();
96            row_map.insert(col.clone(), value.clone()).unwrap();
97            self.rows.insert(row.clone(), row_map).unwrap();
98        };
99
100        // Insert into the columns map.
101        if self.columns.contains_key(&col) {
102            self.columns
103                .get_mut(&col)
104                .unwrap()
105                .insert(row, value)
106                .unwrap();
107        } else {
108            let mut col_map = SeqMap::new();
109            col_map.insert(row, value).unwrap();
110            self.columns.insert(col, col_map).unwrap();
111        }
112    }
113
114    /// Removes the value at the given row and column.
115    /// Returns the removed value, if it existed.
116    pub fn remove(&mut self, col: &C, row: &R) -> Option<V> {
117        // Remove from the rows map.
118        let removed = if let Some(row_map) = self.rows.get_mut(row) {
119            let removed = row_map.remove(col);
120            // Clean up if the row becomes empty.
121            if row_map.is_empty() {
122                self.rows.remove(row);
123            }
124            removed
125        } else {
126            None
127        };
128
129        // Remove from the columns map.
130        if let Some(col_map) = self.columns.get_mut(col) {
131            col_map.remove(row);
132            // Clean up if the column becomes empty.
133            if col_map.is_empty() {
134                self.columns.remove(col);
135            }
136        }
137        removed
138    }
139}