// Copyright 2015, 2016 Ethcore (UK) Ltd. // This file is part of Parity. // Parity is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // Parity is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with Parity. If not, see . //! A collection associating pair of keys (row and column) with a single value. use std::default::Default; use std::hash::Hash; use std::collections::HashMap; /// Structure to hold double-indexed values /// /// You can obviously use `HashMap<(Row,Col), Val>`, but this structure gives /// you better access to all `Columns` in Specific `Row`. Namely you can get sub-hashmap /// `HashMap` for specific `Row` pub struct Table where Row: Eq + Hash + Clone, Col: Eq + Hash { map: HashMap>, } impl Default for Table where Row: Eq + Hash + Clone, Col: Eq + Hash { fn default() -> Self { Table::new() } } // There is default but clippy does not detect it? #[cfg_attr(feature="dev", allow(new_without_default))] impl Table where Row: Eq + Hash + Clone, Col: Eq + Hash { /// Creates new Table pub fn new() -> Self { Table { map: HashMap::new(), } } /// Removes all elements from this Table pub fn clear(&mut self) { self.map.clear(); } /// Returns length of the Table (number of (row, col, val) tuples) pub fn len(&self) -> usize { self.map.values().fold(0, |acc, v| acc + v.len()) } /// Check if there is any element in this Table pub fn is_empty(&self) -> bool { self.map.is_empty() || self.map.values().all(|v| v.is_empty()) } /// Get mutable reference for single Table row. pub fn row_mut(&mut self, row: &Row) -> Option<&mut HashMap> { self.map.get_mut(row) } /// Checks if row is defined for that table (note that even if defined it might be empty) pub fn has_row(&self, row: &Row) -> bool { self.map.contains_key(row) } /// Get immutable reference for single row in this Table pub fn row(&self, row: &Row) -> Option<&HashMap> { self.map.get(row) } /// Get element in cell described by `(row, col)` pub fn get(&self, row: &Row, col: &Col) -> Option<&Val> { self.map.get(row).and_then(|r| r.get(col)) } /// Remove value from specific cell /// /// It will remove the row if it's the last value in it pub fn remove(&mut self, row: &Row, col: &Col) -> Option { let (val, is_empty) = { let row_map = self.map.get_mut(row); if let None = row_map { return None; } let mut row_map = row_map.unwrap(); let val = row_map.remove(col); (val, row_map.is_empty()) }; // Clean row if is_empty { self.map.remove(row); } val } /// Remove given row from Table if there are no values defined in it /// /// When using `#row_mut` it may happen that all values from some row are drained. /// Table however will not be aware that row is empty. /// You can use this method to explicitly remove row entry from the Table. pub fn clear_if_empty(&mut self, row: &Row) { let is_empty = self.map.get(row).map_or(false, |m| m.is_empty()); if is_empty { self.map.remove(row); } } /// Inserts new value to specified cell /// /// Returns previous value (if any) pub fn insert(&mut self, row: Row, col: Col, val: Val) -> Option { self.map.entry(row).or_insert_with(HashMap::new).insert(col, val) } } #[cfg(test)] mod test { use super::*; #[test] fn should_create_empty_table() { // when let table : Table = Table::new(); // then assert!(table.is_empty()); assert_eq!(table.len(), 0); } #[test] fn should_insert_elements_and_return_previous_if_any() { // given let mut table = Table::new(); // when let r1 = table.insert(5, 4, true); let r2 = table.insert(10, 4, true); let r3 = table.insert(10, 10, true); let r4 = table.insert(10, 10, false); // then assert!(r1.is_none()); assert!(r2.is_none()); assert!(r3.is_none()); assert!(r4.is_some()); assert!(!table.is_empty()); assert_eq!(r4.unwrap(), true); assert_eq!(table.len(), 3); } #[test] fn should_remove_element() { // given let mut table = Table::new(); table.insert(5, 4, true); assert!(!table.is_empty()); assert_eq!(table.len(), 1); // when let r = table.remove(&5, &4); // then assert!(table.is_empty()); assert_eq!(table.len() ,0); assert_eq!(r.unwrap(), true); } #[test] fn should_return_none_if_trying_to_remove_non_existing_element() { // given let mut table : Table = Table::new(); assert!(table.is_empty()); // when let r = table.remove(&5, &4); // then assert!(r.is_none()); } #[test] fn should_clear_row_if_removing_last_element() { // given let mut table = Table::new(); table.insert(5, 4, true); assert!(table.has_row(&5)); // when let r = table.remove(&5, &4); // then assert!(r.is_some()); assert!(!table.has_row(&5)); } #[test] fn should_return_element_given_row_and_col() { // given let mut table = Table::new(); table.insert(1551, 1234, 123); // when let r1 = table.get(&1551, &1234); let r2 = table.get(&5, &4); // then assert!(r1.is_some()); assert!(r2.is_none()); assert_eq!(r1.unwrap(), &123); } #[test] fn should_clear_table() { // given let mut table = Table::new(); table.insert(1, 1, true); table.insert(1, 2, false); table.insert(2, 2, false); assert_eq!(table.len(), 3); // when table.clear(); // then assert!(table.is_empty()); assert_eq!(table.len(), 0); assert_eq!(table.has_row(&1), false); assert_eq!(table.has_row(&2), false); } #[test] fn should_return_mutable_row() { // given let mut table = Table::new(); table.insert(1, 1, true); table.insert(1, 2, false); table.insert(2, 2, false); // when { let mut row = table.row_mut(&1).unwrap(); row.remove(&1); row.remove(&2); } assert!(table.has_row(&1)); table.clear_if_empty(&1); // then assert!(!table.has_row(&1)); assert_eq!(table.len(), 1); } }