use crate::data::store::{LocaleProfile, RegistryStore};
use arc_swap::ArcSwap;
use std::sync::Arc;
pub trait IRegistryState: Send + Sync {
fn get_profile(&self, id: &str) -> Option<Arc<LocaleProfile>>;
fn resolve_alias(&self, tag: &str) -> Option<String>;
fn get_version(&self) -> String;
fn get_base_resource_uri(&self) -> String;
}
#[derive(Debug, Clone)]
pub struct RegistryState {
active_store: Arc<ArcSwap<RegistryStore>>,
}
impl Default for RegistryState {
fn default() -> Self {
Self::new()
}
}
impl RegistryState {
pub fn new() -> Self {
let store = RegistryStore::new();
Self { active_store: Arc::new(ArcSwap::from_pointee(store)) }
}
pub fn swap_registry(&self, new_store: RegistryStore) {
self.active_store.store(Arc::new(new_store));
}
}
impl IRegistryState for RegistryState {
fn get_profile(&self, id: &str) -> Option<Arc<LocaleProfile>> {
let store = self.active_store.load();
store.get_profile(id)
}
fn resolve_alias(&self, tag: &str) -> Option<String> {
let store = self.active_store.load();
store.resolve_alias(tag)
}
fn get_version(&self) -> String {
self.active_store.load().metadata.version.clone()
}
fn get_base_resource_uri(&self) -> String {
self.active_store.load().base_resource_uri.to_string()
}
}
#[cfg(test)]
mod tests {
use super::*;
use bistun_core::traits::{Direction, MorphType, NormType, SegType, TransType};
fn create_mock_profile() -> LocaleProfile {
LocaleProfile {
id: "ar-EG".to_string(),
morph: MorphType::TEMPLATIC,
base_seg: SegType::SPACE,
alt_seg: None,
direction: Direction::RTL,
has_bidi: true,
requires_shaping: true,
plurals: vec!["other".to_string()],
unicode_blocks: vec![],
normalization: NormType::NFC,
transliteration: TransType::NONE,
required_resource: None,
}
}
#[test]
fn test_registry_wait_free_hot_swap() {
let state = RegistryState::new();
assert!(state.get_profile("ar-EG").is_none());
let mut new_store = RegistryStore::new();
new_store.insert_stub(create_mock_profile());
new_store.insert_alias("in".to_string(), "id".to_string());
new_store.set_base_resource_uri("https://test.cdn.bistun.io/".to_string());
let worker_state = state.clone();
state.swap_registry(new_store);
let profile = worker_state.get_profile("ar-EG").expect("Swap failed on cloned state");
assert_eq!(profile.direction, Direction::RTL);
assert_eq!(worker_state.resolve_alias("in"), Some("id".to_string()));
assert_eq!(worker_state.get_base_resource_uri(), "https://test.cdn.bistun.io/");
}
}