use std::sync::Arc;
use dashmap::DashMap;
use crate::types::{AccountId, AwsRegion};
#[derive(Debug)]
pub struct AccountRegionStore<T: Default + Send + Sync> {
inner: DashMap<(AccountId, AwsRegion), Arc<T>>,
}
impl<T: Default + Send + Sync> AccountRegionStore<T> {
#[must_use]
pub fn new() -> Self {
Self {
inner: DashMap::new(),
}
}
#[must_use]
pub fn get_or_create(&self, account: &AccountId, region: &AwsRegion) -> Arc<T> {
self.inner
.entry((account.clone(), region.clone()))
.or_insert_with(|| Arc::new(T::default()))
.clone()
}
#[must_use]
pub fn get(&self, account: &AccountId, region: &AwsRegion) -> Option<Arc<T>> {
self.inner
.get(&(account.clone(), region.clone()))
.map(|v| v.clone())
}
#[must_use]
pub fn remove(&self, account: &AccountId, region: &AwsRegion) -> Option<Arc<T>> {
self.inner
.remove(&(account.clone(), region.clone()))
.map(|(_, v)| v)
}
pub fn reset(&self) {
self.inner.clear();
}
#[must_use]
pub fn len(&self) -> usize {
self.inner.len()
}
#[must_use]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
}
impl<T: Default + Send + Sync> Default for AccountRegionStore<T> {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[derive(Debug, Default)]
struct TestState {
value: std::sync::atomic::AtomicU64,
}
#[test]
fn test_should_create_state_on_first_access() {
let store = AccountRegionStore::<TestState>::new();
let account = AccountId::default();
let region = AwsRegion::default();
assert!(store.is_empty());
let state = store.get_or_create(&account, ®ion);
assert_eq!(store.len(), 1);
assert_eq!(state.value.load(std::sync::atomic::Ordering::Relaxed), 0);
}
#[test]
fn test_should_return_same_state_on_subsequent_access() {
let store = AccountRegionStore::<TestState>::new();
let account = AccountId::default();
let region = AwsRegion::default();
let state1 = store.get_or_create(&account, ®ion);
state1.value.store(42, std::sync::atomic::Ordering::Relaxed);
let state2 = store.get_or_create(&account, ®ion);
assert_eq!(state2.value.load(std::sync::atomic::Ordering::Relaxed), 42);
}
#[test]
fn test_should_isolate_different_regions() {
let store = AccountRegionStore::<TestState>::new();
let account = AccountId::default();
let us_east = AwsRegion::new("us-east-1");
let eu_west = AwsRegion::new("eu-west-1");
let state_us = store.get_or_create(&account, &us_east);
state_us
.value
.store(1, std::sync::atomic::Ordering::Relaxed);
let state_eu = store.get_or_create(&account, &eu_west);
assert_eq!(state_eu.value.load(std::sync::atomic::Ordering::Relaxed), 0);
assert_eq!(store.len(), 2);
}
#[test]
fn test_should_reset_all_state() {
let store = AccountRegionStore::<TestState>::new();
let _ = store.get_or_create(&AccountId::default(), &AwsRegion::default());
let _ = store.get_or_create(&AccountId::default(), &AwsRegion::new("eu-west-1"));
assert_eq!(store.len(), 2);
store.reset();
assert!(store.is_empty());
}
}