use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::time::Duration;
use thiserror::Error;
use super::types::{AnalyzerEvent, AnalysisReport, AnalyzerStats};
pub type AnalyzerResult<T> = Result<T, AnalyzerError>;
#[derive(Debug, Error, Clone, Serialize, Deserialize)]
pub enum AnalyzerError {
#[error("Invalid input: {0}")]
InvalidInput(String),
#[error("Insufficient data: {0}")]
InsufficientData(String),
#[error("Configuration error: {0}")]
ConfigurationError(String),
#[error("Resource exhausted: {0}")]
ResourceExhausted(String),
#[error("Internal error: {0}")]
InternalError(String),
#[error("Invalid state: {0}")]
InvalidState(String),
#[error("Timeout: {0}")]
Timeout(String),
#[error("Analyzer is not running")]
NotRunning,
#[error("Invalid state transition from {from} to {to}")]
InvalidStateTransition {
from: String,
to: String,
},
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
pub enum AnalyzerState {
Initialized,
Starting,
Running,
Draining,
Stopped,
Failed,
}
impl AnalyzerState {
pub fn can_accept_events(&self) -> bool {
matches!(self, AnalyzerState::Running)
}
pub fn is_terminal(&self) -> bool {
matches!(self, AnalyzerState::Stopped | AnalyzerState::Failed)
}
pub fn as_str(&self) -> &'static str {
match self {
AnalyzerState::Initialized => "Initialized",
AnalyzerState::Starting => "Starting",
AnalyzerState::Running => "Running",
AnalyzerState::Draining => "Draining",
AnalyzerState::Stopped => "Stopped",
AnalyzerState::Failed => "Failed",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AnalyzerConfig {
pub id: String,
pub enabled: bool,
pub event_filter: Vec<String>,
pub window_duration: Duration,
pub slide_interval: Duration,
pub sample_rate: f64,
pub max_buffer_size: usize,
pub max_memory_bytes: usize,
pub analysis_interval: Duration,
pub enable_tracing: bool,
}
impl Default for AnalyzerConfig {
fn default() -> Self {
Self {
id: String::new(),
enabled: true,
event_filter: Vec::new(),
window_duration: Duration::from_secs(300), slide_interval: Duration::from_secs(60), sample_rate: 1.0, max_buffer_size: 10_000,
max_memory_bytes: 100 * 1024 * 1024, analysis_interval: Duration::from_secs(60), enable_tracing: false,
}
}
}
#[async_trait]
pub trait Analyzer: Send + Sync {
fn name(&self) -> &str;
fn config(&self) -> &AnalyzerConfig;
fn state(&self) -> AnalyzerState;
async fn start(&mut self) -> AnalyzerResult<()>;
async fn stop(&mut self) -> AnalyzerResult<()>;
async fn process_event(&mut self, event: AnalyzerEvent) -> AnalyzerResult<()>;
async fn generate_report(&self) -> AnalyzerResult<AnalysisReport>;
fn get_stats(&self) -> AnalyzerStats;
async fn reset(&mut self) -> AnalyzerResult<()>;
async fn health_check(&self) -> AnalyzerResult<()>;
}
pub struct AnalyzerConfigBuilder {
config: AnalyzerConfig,
}
impl AnalyzerConfigBuilder {
pub fn new(id: impl Into<String>) -> Self {
Self {
config: AnalyzerConfig {
id: id.into(),
..Default::default()
},
}
}
pub fn enabled(mut self, enabled: bool) -> Self {
self.config.enabled = enabled;
self
}
pub fn event_filter(mut self, filter: Vec<String>) -> Self {
self.config.event_filter = filter;
self
}
pub fn window_duration(mut self, duration: Duration) -> Self {
self.config.window_duration = duration;
self
}
pub fn slide_interval(mut self, interval: Duration) -> Self {
self.config.slide_interval = interval;
self
}
pub fn sample_rate(mut self, rate: f64) -> Self {
self.config.sample_rate = rate.clamp(0.0, 1.0);
self
}
pub fn max_buffer_size(mut self, size: usize) -> Self {
self.config.max_buffer_size = size;
self
}
pub fn max_memory_bytes(mut self, bytes: usize) -> Self {
self.config.max_memory_bytes = bytes;
self
}
pub fn analysis_interval(mut self, interval: Duration) -> Self {
self.config.analysis_interval = interval;
self
}
pub fn enable_tracing(mut self, enabled: bool) -> Self {
self.config.enable_tracing = enabled;
self
}
pub fn build(self) -> AnalyzerConfig {
self.config
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_analyzer_state_can_accept_events() {
assert!(!AnalyzerState::Initialized.can_accept_events());
assert!(!AnalyzerState::Starting.can_accept_events());
assert!(AnalyzerState::Running.can_accept_events());
assert!(!AnalyzerState::Draining.can_accept_events());
assert!(!AnalyzerState::Stopped.can_accept_events());
assert!(!AnalyzerState::Failed.can_accept_events());
}
#[test]
fn test_analyzer_state_is_terminal() {
assert!(!AnalyzerState::Initialized.is_terminal());
assert!(!AnalyzerState::Starting.is_terminal());
assert!(!AnalyzerState::Running.is_terminal());
assert!(!AnalyzerState::Draining.is_terminal());
assert!(AnalyzerState::Stopped.is_terminal());
assert!(AnalyzerState::Failed.is_terminal());
}
#[test]
fn test_analyzer_config_builder() {
let config = AnalyzerConfigBuilder::new("test-analyzer")
.enabled(true)
.window_duration(Duration::from_secs(600))
.sample_rate(0.5)
.max_buffer_size(5000)
.build();
assert_eq!(config.id, "test-analyzer");
assert!(config.enabled);
assert_eq!(config.window_duration, Duration::from_secs(600));
assert_eq!(config.sample_rate, 0.5);
assert_eq!(config.max_buffer_size, 5000);
}
#[test]
fn test_sample_rate_clamping() {
let config = AnalyzerConfigBuilder::new("test")
.sample_rate(1.5) .build();
assert_eq!(config.sample_rate, 1.0);
let config = AnalyzerConfigBuilder::new("test")
.sample_rate(-0.5) .build();
assert_eq!(config.sample_rate, 0.0);
}
}