use crate::apps::AppConfig;
use std::error::Error;
#[derive(Debug)]
pub struct ReadyContext {
pub config: AppConfig,
}
impl ReadyContext {
pub fn new(config: AppConfig) -> Self {
Self { config }
}
pub fn app_label(&self) -> &str {
&self.config.label
}
pub fn app_name(&self) -> &str {
&self.config.name
}
}
pub trait AppReadyHook: Send + Sync {
fn ready(&self, ctx: &ReadyContext) -> Result<(), Box<dyn Error>>;
}
#[derive(Default)]
pub struct HookRegistry {
hooks: Vec<Box<dyn AppReadyHook>>,
}
impl HookRegistry {
pub fn new() -> Self {
Self { hooks: Vec::new() }
}
pub fn register(&mut self, hook: Box<dyn AppReadyHook>) {
self.hooks.push(hook);
}
pub fn execute(&self, ctx: &ReadyContext) -> Result<(), Box<dyn Error>> {
for hook in &self.hooks {
hook.ready(ctx)?;
}
Ok(())
}
pub fn len(&self) -> usize {
self.hooks.len()
}
pub fn is_empty(&self) -> bool {
self.hooks.is_empty()
}
pub fn clear(&mut self) {
self.hooks.clear();
}
}
#[cfg(test)]
mod tests {
use super::*;
struct TestHook {
executed: std::sync::Arc<std::sync::atomic::AtomicBool>,
}
impl AppReadyHook for TestHook {
fn ready(&self, _ctx: &ReadyContext) -> Result<(), Box<dyn Error>> {
self.executed
.store(true, std::sync::atomic::Ordering::SeqCst);
Ok(())
}
}
struct FailingHook;
impl AppReadyHook for FailingHook {
fn ready(&self, _ctx: &ReadyContext) -> Result<(), Box<dyn Error>> {
Err("Hook failed".into())
}
}
#[test]
fn test_ready_context_creation() {
let config = AppConfig::new("myapp", "myapp");
let ctx = ReadyContext::new(config);
assert_eq!(ctx.app_label(), "myapp");
assert_eq!(ctx.app_name(), "myapp");
}
#[test]
fn test_hook_registry_new() {
let registry = HookRegistry::new();
assert_eq!(registry.len(), 0);
assert!(registry.is_empty());
}
#[test]
fn test_hook_registry_register() {
let mut registry = HookRegistry::new();
let executed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let hook = TestHook {
executed: executed.clone(),
};
registry.register(Box::new(hook));
assert_eq!(registry.len(), 1);
assert!(!registry.is_empty());
}
#[test]
fn test_hook_registry_execute() {
let mut registry = HookRegistry::new();
let executed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let hook = TestHook {
executed: executed.clone(),
};
registry.register(Box::new(hook));
let config = AppConfig::new("testapp", "testapp");
let ctx = ReadyContext::new(config);
let result = registry.execute(&ctx);
assert!(result.is_ok());
assert!(executed.load(std::sync::atomic::Ordering::SeqCst));
}
#[test]
fn test_hook_registry_execute_failure() {
let mut registry = HookRegistry::new();
registry.register(Box::new(FailingHook));
let config = AppConfig::new("testapp", "testapp");
let ctx = ReadyContext::new(config);
let result = registry.execute(&ctx);
assert!(result.is_err());
}
#[test]
fn test_hook_registry_multiple_hooks() {
let mut registry = HookRegistry::new();
let executed1 = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let executed2 = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let hook1 = TestHook {
executed: executed1.clone(),
};
let hook2 = TestHook {
executed: executed2.clone(),
};
registry.register(Box::new(hook1));
registry.register(Box::new(hook2));
assert_eq!(registry.len(), 2);
let config = AppConfig::new("testapp", "testapp");
let ctx = ReadyContext::new(config);
let result = registry.execute(&ctx);
assert!(result.is_ok());
assert!(executed1.load(std::sync::atomic::Ordering::SeqCst));
assert!(executed2.load(std::sync::atomic::Ordering::SeqCst));
}
#[test]
fn test_hook_registry_clear() {
let mut registry = HookRegistry::new();
let executed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
let hook = TestHook {
executed: executed.clone(),
};
registry.register(Box::new(hook));
assert_eq!(registry.len(), 1);
registry.clear();
assert_eq!(registry.len(), 0);
assert!(registry.is_empty());
}
#[test]
fn test_hook_registry_stop_on_first_failure() {
let mut registry = HookRegistry::new();
let executed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));
registry.register(Box::new(FailingHook));
let hook = TestHook {
executed: executed.clone(),
};
registry.register(Box::new(hook));
let config = AppConfig::new("testapp", "testapp");
let ctx = ReadyContext::new(config);
let result = registry.execute(&ctx);
assert!(result.is_err());
assert!(!executed.load(std::sync::atomic::Ordering::SeqCst));
}
#[test]
fn test_ready_context_with_verbose_name() {
let config = AppConfig::new("myapp", "myapp").with_verbose_name("My Application");
let ctx = ReadyContext::new(config);
assert_eq!(ctx.config.verbose_name, Some("My Application".to_string()));
}
}