Skip to content

Commit

Permalink
Add entry API (#12)
Browse files Browse the repository at this point in the history
* impl get_mut

* Add quasi-standard "Entry API"
  • Loading branch information
JayKickliter authored Sep 16, 2022
1 parent 1b6eafb commit d8bd069
Show file tree
Hide file tree
Showing 5 changed files with 328 additions and 22 deletions.
157 changes: 157 additions & 0 deletions src/entry.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
//! `HexMap`'s Entry API.

use crate::{compaction::Compactor, h3ron::H3Cell, HexMap};

/// A view into a single entry in a map, which may either be vacant or
/// occupied.
///
/// This enum is constructed from the [entry][HexMap::entry] method on
/// [HexMap][HexMap].
pub enum Entry<'a, V, C> {
/// An occupied entry.
Occupied(OccupiedEntry<'a, V>),
/// A vacant entry.
Vacant(VacantEntry<'a, V, C>),
}

/// A view into an occupied entry in a `HexMap`. It is part of the
/// [`Entry`] enum.
pub struct OccupiedEntry<'a, V> {
pub(crate) hex: H3Cell,
pub(crate) value: &'a mut V,
}

/// A view into a vacant entry in a `HexMap`. It is part of the
/// [`Entry`] enum.
pub struct VacantEntry<'a, V, C> {
pub(crate) hex: H3Cell,
pub(crate) map: &'a mut HexMap<V, C>,
}

impl<'a, V, C> Entry<'a, V, C>
where
C: Compactor<V>,
{
/// Provides in-place mutable access to an occupied entry before
/// any potential inserts into the map.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12)
/// .and_modify(|v| *v = "Paris")
/// .or_insert("France");
/// assert_eq!(map[eiffel_tower_res12], "France");
///
/// map.entry(eiffel_tower_res12)
/// .and_modify(|v| *v = "Paris")
/// .or_insert("France");
/// assert_eq!(map[eiffel_tower_res12], "Paris");
/// ```
pub fn and_modify<F>(self, f: F) -> Self
where
F: FnOnce(&mut V),
{
match self {
Entry::Occupied(OccupiedEntry { hex, value }) => {
f(value);
Entry::Occupied(OccupiedEntry { hex, value })
}
Entry::Vacant(_) => self,
}
}

/// Ensures a value is in the entry by inserting the default if
/// empty, and returns a mutable reference to the value in the
/// entry.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12)
/// .or_insert("Paris");
/// assert_eq!(map[eiffel_tower_res12], "Paris");
/// ```
pub fn or_insert(self, default: V) -> &'a mut V {
match self {
Entry::Occupied(OccupiedEntry { hex: _hex, value }) => value,
Entry::Vacant(VacantEntry { hex, map }) => {
map.insert(hex, default);
// We just inserted; unwrap is fine.
map.get_mut(hex).unwrap()
}
}
}

/// Ensures a value is in the entry by inserting the result of the
/// default function if empty, and returns a mutable reference to
/// the value in the entry.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12)
/// .or_insert_with(|| "Paris");
/// assert_eq!(map[eiffel_tower_res12], "Paris");
/// ```
pub fn or_insert_with<F>(self, default: F) -> &'a mut V
where
F: FnOnce() -> V,
{
match self {
Entry::Occupied(OccupiedEntry { hex: _hex, value }) => value,
Entry::Vacant(VacantEntry { hex, map }) => {
map.insert(hex, default());
// We just inserted; unwrap is fine.
map.get_mut(hex).unwrap()
}
}
}
}

impl<'a, V, C> Entry<'a, V, C>
where
V: Default,
C: Compactor<V>,
{
/// Ensures a value is in the entry by inserting the default value
/// if empty, and returns a mutable reference to the value in the
/// entry.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map: HexMap<Option<&str>> = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.entry(eiffel_tower_res12).or_default();
/// assert_eq!(map[eiffel_tower_res12], None);
/// ```
pub fn or_default(self) -> &'a mut V {
match self {
Entry::Occupied(OccupiedEntry { hex: _hex, value }) => value,
Entry::Vacant(VacantEntry { hex, map }) => {
map.insert(hex, Default::default());
// We just inserted; unwrap is fine.
map.get_mut(hex).unwrap()
}
}
}
}
165 changes: 150 additions & 15 deletions src/hexmap.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
//! A HexMap is a structure for mapping geographical regions to values.

pub use crate::entry::{Entry, OccupiedEntry, VacantEntry};
use crate::{
compaction::{Compactor, NullCompactor},
digits::{base, Digits},
Expand Down Expand Up @@ -63,7 +66,7 @@ use std::{cmp::PartialEq, iter::FromIterator};
feature = "serde-support",
derive(serde::Serialize, serde::Deserialize)
)]
pub struct HexMap<V, C> {
pub struct HexMap<V, C = NullCompactor> {
/// All h3 0 base cell indices in the tree
nodes: Box<[Option<Node<V>>]>,
/// User-provided compator. Defaults to the null compactor.
Expand All @@ -87,6 +90,22 @@ impl<V> HexMap<V, NullCompactor> {
}

impl<V, C: Compactor<V>> HexMap<V, C> {
/// Adds a hexagon/value pair to the set.
pub fn insert(&mut self, hex: H3Cell, value: V) {
let base_cell = base(hex);
let digits = Digits::new(hex);
match self.nodes[base_cell as usize].as_mut() {
Some(node) => node.insert(0_u8, digits, value, &mut self.compactor),
None => {
let mut node = Node::new();
node.insert(0_u8, digits, value, &mut self.compactor);
self.nodes[base_cell as usize] = Some(node);
}
}
}
}

impl<V, C> HexMap<V, C> {
/// Constructs a new, empty `HexMap` with the provided [compactor][crate::compaction].
///
/// Incurs a single heap allocation to store all 122 resolution-0
Expand Down Expand Up @@ -128,20 +147,6 @@ impl<V, C: Compactor<V>> HexMap<V, C> {
self.len() == 0
}

/// Adds a hexagon/value pair to the set.
pub fn insert(&mut self, hex: H3Cell, value: V) {
let base_cell = base(hex);
let digits = Digits::new(hex);
match self.nodes[base_cell as usize].as_mut() {
Some(node) => node.insert(0_u8, digits, value, &mut self.compactor),
None => {
let mut node = Node::new();
node.insert(0_u8, digits, value, &mut self.compactor);
self.nodes[base_cell as usize] = Some(node);
}
}
}

/// Returns `true` if the set fully contains `hex`.
///
/// This method will return `true` if any of the following are
Expand Down Expand Up @@ -176,6 +181,30 @@ impl<V, C: Compactor<V>> HexMap<V, C> {
None => None,
}
}

/// Returns a reference to the value corresponding to the given
/// hex or one of its parents.
pub fn get_mut(&mut self, hex: H3Cell) -> Option<&mut V> {
let base_cell = base(hex);
match self.nodes[base_cell as usize].as_mut() {
Some(node) => {
let digits = Digits::new(hex);
node.get_mut(digits)
}
None => None,
}
}

/// Gets the entry in the map for the corresponding cell.
pub fn entry(&'_ mut self, hex: H3Cell) -> Entry<'_, V, C> {
if self.get(hex).is_none() {
return Entry::Vacant(VacantEntry { hex, map: self });
}
Entry::Occupied(OccupiedEntry {
hex,
value: self.get_mut(hex).unwrap(),
})
}
}

impl<V: PartialEq> Default for HexMap<V, NullCompactor> {
Expand Down Expand Up @@ -225,3 +254,109 @@ impl<'a, V: Copy + 'a, C: Compactor<V>> Extend<(&'a H3Cell, &'a V)> for HexMap<V
}
}
}

impl<V, C> std::ops::Index<H3Cell> for HexMap<V, C> {
type Output = V;

/// Returns a reference to the value corresponding to the supplied
/// key.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.insert(eiffel_tower_res12, "France");
/// assert_eq!(map[eiffel_tower_res12], "France");
/// ```
///
/// # Panics
///
/// Panics if the cell is not present in the `HexMap`.
fn index(&self, cell: H3Cell) -> &V {
self.get(cell).expect("no entry found for cell")
}
}

impl<V, C> std::ops::Index<&H3Cell> for HexMap<V, C> {
type Output = V;

/// Returns a reference to the value corresponding to the supplied
/// key.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.insert(eiffel_tower_res12, "France");
/// assert_eq!(map[&eiffel_tower_res12], "France");
/// ```
///
/// # Panics
///
/// Panics if the cell is not present in the `HexMap`.
fn index(&self, cell: &H3Cell) -> &V {
self.get(*cell).expect("no entry found for cell")
}
}

impl<V, C> std::ops::IndexMut<H3Cell> for HexMap<V, C> {
/// Returns a reference to the value corresponding to the supplied
/// key.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.insert(eiffel_tower_res12, "France");
/// assert_eq!(map[eiffel_tower_res12], "France");
///
/// map[eiffel_tower_res12] = "Paris";
/// assert_eq!(map[eiffel_tower_res12], "Paris");
/// ```
///
/// # Panics
///
/// Panics if the cell is not present in the `HexMap`.
fn index_mut(&mut self, cell: H3Cell) -> &mut V {
self.get_mut(cell).expect("no entry found for cell")
}
}

impl<V, C> std::ops::IndexMut<&H3Cell> for HexMap<V, C> {
/// Returns a reference to the value corresponding to the supplied
/// key.
///
/// # Examples
///
/// ```
/// use hextree::{h3ron::{H3Cell, Index}, HexMap};
///
/// let mut map = HexMap::new();
/// let eiffel_tower_res12 = H3Cell::new(0x8c1fb46741ae9ff);
///
/// map.insert(eiffel_tower_res12, "France");
/// assert_eq!(map[&eiffel_tower_res12], "France");
///
/// map[&eiffel_tower_res12] = "Paris";
/// assert_eq!(map[&eiffel_tower_res12], "Paris");
/// ```
///
/// # Panics
///
/// Panics if the cell is not present in the `HexMap`.
fn index_mut(&mut self, cell: &H3Cell) -> &mut V {
self.get_mut(*cell).expect("no entry found for cell")
}
}
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
#![deny(missing_docs)]
#![deny(rustdoc::broken_intra_doc_links)]
#![deny(unsafe_code, missing_docs, rustdoc::broken_intra_doc_links)]

//! hextree provides tree structures that represent geographic regions with H3 cells.
//!
Expand Down Expand Up @@ -44,7 +43,8 @@

pub mod compaction;
mod digits;
mod hexmap;
mod entry;
pub mod hexmap;
mod hexset;
mod node;

Expand Down
Loading

0 comments on commit d8bd069

Please sign in to comment.