use hashbrown::HashSet;
pub trait EvolveListener: Send + Sync {
fn evolve_progress(
&self,
entity_class_name: &str,
n_read: u64,
n_converted: u64,
) -> bool;
}
#[derive(Default)]
pub struct EvolveConfig {
classes_to_evolve: HashSet<String>,
listener: Option<Box<dyn EvolveListener>>,
}
impl EvolveConfig {
pub fn new() -> Self {
Self { classes_to_evolve: HashSet::new(), listener: None }
}
pub fn add_class_to_evolve(
&mut self,
entity_class: impl Into<String>,
) -> &mut Self {
self.classes_to_evolve.insert(entity_class.into());
self
}
pub fn with_class_to_evolve(
mut self,
entity_class: impl Into<String>,
) -> Self {
self.classes_to_evolve.insert(entity_class.into());
self
}
pub fn classes_to_evolve(&self) -> &HashSet<String> {
&self.classes_to_evolve
}
pub fn should_evolve(&self, class_name: &str) -> bool {
self.classes_to_evolve.is_empty()
|| self.classes_to_evolve.contains(class_name)
}
pub fn set_listener(
&mut self,
listener: impl EvolveListener + 'static,
) -> &mut Self {
self.listener = Some(Box::new(listener));
self
}
pub fn with_listener(
mut self,
listener: impl EvolveListener + 'static,
) -> Self {
self.listener = Some(Box::new(listener));
self
}
pub fn listener(&self) -> Option<&dyn EvolveListener> {
self.listener.as_deref()
}
}
impl std::fmt::Debug for EvolveConfig {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("EvolveConfig")
.field("classes_to_evolve", &self.classes_to_evolve)
.field("listener", &self.listener.is_some())
.finish()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_evolves_all() {
let cfg = EvolveConfig::new();
assert!(cfg.should_evolve("any.Class"));
assert!(cfg.should_evolve("another.Class"));
}
#[test]
fn test_specific_class_filter() {
let cfg = EvolveConfig::new().with_class_to_evolve("my.pkg.Person");
assert!(cfg.should_evolve("my.pkg.Person"));
assert!(!cfg.should_evolve("my.pkg.Other"));
}
#[test]
fn test_add_multiple_classes() {
let mut cfg = EvolveConfig::new();
cfg.add_class_to_evolve("A");
cfg.add_class_to_evolve("B");
assert!(cfg.should_evolve("A"));
assert!(cfg.should_evolve("B"));
assert!(!cfg.should_evolve("C"));
}
#[test]
fn test_classes_to_evolve_returns_set() {
let cfg = EvolveConfig::new().with_class_to_evolve("X");
assert!(cfg.classes_to_evolve().contains("X"));
}
#[test]
fn test_no_listener_by_default() {
let cfg = EvolveConfig::new();
assert!(cfg.listener().is_none());
}
#[test]
fn test_with_listener() {
struct TestListener;
impl EvolveListener for TestListener {
fn evolve_progress(&self, _: &str, _: u64, _: u64) -> bool {
true
}
}
let cfg = EvolveConfig::new().with_listener(TestListener);
assert!(cfg.listener().is_some());
}
#[test]
fn test_listener_called() {
use std::sync::{Arc, Mutex};
let calls = Arc::new(Mutex::new(Vec::<String>::new()));
let calls_clone = calls.clone();
struct CountListener {
calls: Arc<Mutex<Vec<String>>>,
}
impl EvolveListener for CountListener {
fn evolve_progress(
&self,
entity_class_name: &str,
_n_read: u64,
_n_converted: u64,
) -> bool {
self.calls.lock().unwrap().push(entity_class_name.to_string());
true
}
}
let cfg = EvolveConfig::new()
.with_listener(CountListener { calls: calls_clone });
let result = cfg.listener().unwrap().evolve_progress("my.Entity", 1, 1);
assert!(result);
assert_eq!(calls.lock().unwrap().len(), 1);
assert_eq!(calls.lock().unwrap()[0], "my.Entity");
}
#[test]
fn test_debug() {
let cfg = EvolveConfig::new().with_class_to_evolve("X");
let s = format!("{:?}", cfg);
assert!(s.contains("EvolveConfig"));
}
}