use std::sync::Arc;
use crate::dictionary::DictEntry;
use crate::error::{DictError, Result};
use crate::lazy_entries::LazyEntries;
pub trait EntryStore: Send + Sync {
fn get(&self, index: u32) -> Result<Arc<DictEntry>>;
fn get_entries_at(&self, first_index: u32, surface: &str) -> Result<Vec<Arc<DictEntry>>>;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
}
pub struct EagerStore {
entries: Vec<Arc<DictEntry>>,
}
impl EagerStore {
#[must_use]
pub fn new(entries: Vec<DictEntry>) -> Self {
Self {
entries: entries.into_iter().map(Arc::new).collect(),
}
}
#[must_use]
pub const fn from_arc_vec(entries: Vec<Arc<DictEntry>>) -> Self {
Self { entries }
}
#[cfg(test)]
#[must_use]
pub fn entries(&self) -> &[Arc<DictEntry>] {
&self.entries
}
}
impl EntryStore for EagerStore {
fn get(&self, index: u32) -> Result<Arc<DictEntry>> {
self.entries.get(index as usize).cloned().ok_or_else(|| {
DictError::Format(format!(
"entry index out of bounds: {} >= {}",
index,
self.entries.len()
))
})
}
fn get_entries_at(&self, first_index: u32, surface: &str) -> Result<Vec<Arc<DictEntry>>> {
let start = first_index as usize;
let results: Vec<Arc<DictEntry>> = self
.entries
.get(start..)
.unwrap_or(&[])
.iter()
.take_while(|e| e.surface == surface)
.cloned()
.collect();
Ok(results)
}
fn len(&self) -> usize {
self.entries.len()
}
}
pub struct LazyStore {
lazy_entries: LazyEntries,
}
impl LazyStore {
#[must_use]
pub const fn new(lazy_entries: LazyEntries) -> Self {
Self { lazy_entries }
}
#[must_use]
pub fn cached_count(&self) -> usize {
self.lazy_entries.cached_count()
}
pub fn set_cache_size(&self, size: usize) {
self.lazy_entries.set_cache_size(size);
}
pub fn clear_cache(&self) {
self.lazy_entries.clear_cache();
}
}
impl EntryStore for LazyStore {
fn get(&self, index: u32) -> Result<Arc<DictEntry>> {
self.lazy_entries.get(index)
}
fn get_entries_at(&self, first_index: u32, surface: &str) -> Result<Vec<Arc<DictEntry>>> {
self.lazy_entries.get_entries_at(first_index, surface)
}
fn len(&self) -> usize {
self.lazy_entries.len()
}
}
#[cfg(test)]
mod tests {
use super::*;
fn sample_entries() -> Vec<DictEntry> {
vec![
DictEntry::new("가", 1, 1, 100, "NNG"),
DictEntry::new("가", 2, 2, 50, "JKS"),
DictEntry::new("나", 3, 3, 200, "NP"),
]
}
#[test]
fn test_eager_store_get() {
let store = EagerStore::new(sample_entries());
let entry = store.get(0).expect("should get entry 0");
assert_eq!(entry.surface, "가");
assert_eq!(entry.left_id, 1);
let entry = store.get(1).expect("should get entry 1");
assert_eq!(entry.surface, "가");
assert_eq!(entry.left_id, 2);
assert!(store.get(100).is_err());
}
#[test]
fn test_eager_store_get_entries_at() {
let store = EagerStore::new(sample_entries());
let entries = store.get_entries_at(0, "가").expect("should get entries");
assert_eq!(entries.len(), 2);
assert_eq!(entries[0].feature, "NNG");
assert_eq!(entries[1].feature, "JKS");
let entries = store.get_entries_at(2, "나").expect("should get entries");
assert_eq!(entries.len(), 1);
assert_eq!(entries[0].surface, "나");
let entries = store.get_entries_at(0, "다").expect("should get entries");
assert!(entries.is_empty());
}
#[test]
fn test_eager_store_len() {
let store = EagerStore::new(sample_entries());
assert_eq!(store.len(), 3);
assert!(!store.is_empty());
let empty_store = EagerStore::new(Vec::new());
assert_eq!(empty_store.len(), 0);
assert!(empty_store.is_empty());
}
#[test]
fn test_lazy_store_roundtrip() {
use tempfile::tempdir;
let entries = sample_entries();
let dir = tempdir().expect("create temp dir");
let path = dir.path().join("entries.bin");
LazyEntries::save_entries(&entries, &path).expect("save");
let lazy = LazyEntries::from_file(&path).expect("load");
let store = LazyStore::new(lazy);
assert_eq!(store.len(), 3);
let entry = store.get(0).expect("get 0");
assert_eq!(entry.surface, "가");
let entries = store.get_entries_at(0, "가").expect("get_entries_at");
assert_eq!(entries.len(), 2);
}
#[test]
fn test_lazy_store_cache() {
use tempfile::tempdir;
let entries = sample_entries();
let dir = tempdir().expect("create temp dir");
let path = dir.path().join("entries.bin");
LazyEntries::save_entries(&entries, &path).expect("save");
let lazy = LazyEntries::from_file(&path).expect("load");
let store = LazyStore::new(lazy);
assert_eq!(store.cached_count(), 0);
let _ = store.get(0).expect("get 0");
assert_eq!(store.cached_count(), 1);
store.clear_cache();
assert_eq!(store.cached_count(), 0);
}
}