use std::{iter::{zip, Zip}, slice::Iter, vec::IntoIter};
use deki_macros::{ForceDefault, imp};
#[derive(ForceDefault)]
pub struct StackMap<K: PartialEq, V> {
keys: Vec<K>,
values: Vec<V>,
}
#[imp(StackMap<K:PartialEq,V:Default>)]
pub fn entry(&mut self,key:K) -> &mut V {
match self.key_idx(&key) {
None => {
self.keys.push(key);
self.values.push(V::default());
self.values.last_mut().unwrap()
}
Some(id) => &mut self.values[id],
}
}
impl<K: PartialEq, V> StackMap<K, V> {
pub fn key_idx(&self, key: &K) -> Option<usize> {
self.keys.iter().enumerate().find_map(|(id, k)| if key == k { Some(id) } else { None })
}
pub fn keys(&self) -> &[K] {
&self.keys
}
pub fn values(&self) -> &[V] {
&self.values
}
pub fn iter(&self) -> Zip<Iter<'_, K>, Iter<'_, V>> {
zip(self.keys.iter(), self.values.iter())
}
#[allow(clippy::should_implement_trait)]
pub fn into_iter(self) -> Zip<IntoIter<K>, IntoIter<V>> {
zip(self.keys, self.values)
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
pub fn len(&self) -> usize {
self.keys.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn stackmap_entry_does_not_dedup() {
let mut map: StackMap<String, i32> = StackMap::default();
map.entry("a".into());
map.entry("a".into());
assert_eq!(map.key_idx(&"a".into()), Some(0));
}
#[test]
fn stackmap_keys_keep_insertion_order() {
let mut map: StackMap<String, i32> = StackMap::default();
map.entry("z".into());
map.entry("a".into());
map.entry("m".into());
let keys: Vec<String> = map.keys().to_vec();
assert_eq!(keys, vec![String::from("z"), String::from("a"), String::from("m")]);
}
#[test]
fn stackmap_len_nonempty() {
let mut map: StackMap<i32, String> = StackMap::default();
for i in 0..5 {
map.entry(i);
}
assert_eq!(map.len(), 5);
}
#[test]
fn stackmap_keys_returns_correct_slice() {
let mut map: StackMap<String, i32> = StackMap::default();
map.entry("b".into());
map.entry("a".into());
assert_eq!(map.keys(), &["b", "a"]);
}
#[test]
fn stackmap_values_returns_correct_slice() {
let mut map: StackMap<String, i32> = StackMap::default();
map.entry("x".into());
*map.entry("x".into()) = 42;
assert_eq!(map.values(), &[42]);
}
}