use async_trait::async_trait;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::RwLock;
use crate::{Result, Object, OatsError};
use crate::actions::ActionResult;
pub type SystemId = uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
pub enum Priority {
Low = 1,
Normal = 2,
High = 3,
Critical = 4,
}
impl Default for Priority {
fn default() -> Self {
Self::Normal
}
}
#[async_trait]
pub trait System: Send + Sync {
fn name(&self) -> &str;
fn description(&self) -> &str;
async fn initialize(&mut self) -> Result<()> {
Ok(())
}
async fn shutdown(&mut self) -> Result<()> {
Ok(())
}
async fn process(&mut self, objects: Vec<Object>, priority: Priority) -> Result<Vec<ActionResult>>;
fn priority(&self) -> Priority {
Priority::Normal
}
fn is_ready(&self) -> bool {
true
}
fn get_stats(&self) -> SystemStats {
SystemStats::default()
}
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct SystemStats {
pub objects_processed: u64,
pub actions_executed: u64,
pub errors: u64,
pub total_processing_time_ms: u64,
pub last_processed: Option<chrono::DateTime<chrono::Utc>>,
pub avg_processing_time_ms: f64,
pub peak_processing_time_ms: u64,
}
impl SystemStats {
pub fn update_processing_time(&mut self, processing_time_ms: u64) {
self.total_processing_time_ms += processing_time_ms;
self.peak_processing_time_ms = self.peak_processing_time_ms.max(processing_time_ms);
if self.objects_processed > 0 {
self.avg_processing_time_ms = self.total_processing_time_ms as f64 / self.objects_processed as f64;
}
}
pub fn throughput_objects_per_second(&self) -> f64 {
if self.total_processing_time_ms > 0 {
(self.objects_processed as f64 * 1000.0) / self.total_processing_time_ms as f64
} else {
0.0
}
}
pub fn reset(&mut self) {
self.objects_processed = 0;
self.actions_executed = 0;
self.errors = 0;
self.total_processing_time_ms = 0;
self.avg_processing_time_ms = 0.0;
self.peak_processing_time_ms = 0;
self.last_processed = None;
}
pub fn error_rate(&self) -> f64 {
let total = self.objects_processed + self.actions_executed;
if total > 0 {
(self.errors as f64 / total as f64) * 100.0
} else {
0.0
}
}
}
pub struct SystemManager {
systems: HashMap<String, Box<dyn System>>,
object_registry: Arc<RwLock<HashMap<String, Object>>>,
}
impl SystemManager {
pub fn new() -> Self {
Self {
systems: HashMap::new(),
object_registry: Arc::new(RwLock::new(HashMap::with_capacity(100))),
}
}
pub fn with_capacity(expected_objects: usize) -> Self {
Self {
systems: HashMap::new(),
object_registry: Arc::new(RwLock::new(HashMap::with_capacity(expected_objects))),
}
}
pub fn add_system(&mut self, system: Box<dyn System>) {
let name = system.name().to_string();
self.systems.insert(name, system);
}
pub fn remove_system(&mut self, name: &str) -> Option<Box<dyn System>> {
self.systems.remove(name)
}
pub fn get_system(&self, name: &str) -> Option<&Box<dyn System>> {
self.systems.get(name)
}
pub fn systems(&self) -> &HashMap<String, Box<dyn System>> {
&self.systems
}
pub fn system_count(&self) -> usize {
self.systems.len()
}
pub async fn register_object(&self, object: Object) {
let mut registry = self.object_registry.write().await;
registry.insert(object.id.to_string(), object);
}
pub async fn get_object(&self, id: &str) -> Option<Object> {
let registry = self.object_registry.read().await;
registry.get(id).cloned()
}
pub async fn get_all_objects(&self) -> Vec<Object> {
let registry = self.object_registry.read().await;
registry.values().cloned().collect()
}
pub async fn object_count(&self) -> usize {
let registry = self.object_registry.read().await;
registry.len()
}
pub async fn clear_objects(&self) {
let mut registry = self.object_registry.write().await;
registry.clear();
}
pub async fn reserve_objects(&self, additional: usize) {
let mut registry = self.object_registry.write().await;
registry.reserve(additional);
}
pub async fn process_all(&mut self, priority: Priority) -> Result<Vec<ActionResult>> {
let objects = self.get_all_objects().await;
let mut all_results = Vec::new();
let mut system_names: Vec<_> = self.systems.keys().cloned().collect();
system_names.sort_by(|a, b| {
let a_priority = self.systems.get(a).map(|s| s.priority()).unwrap_or(Priority::Normal);
let b_priority = self.systems.get(b).map(|s| s.priority()).unwrap_or(Priority::Normal);
b_priority.cmp(&a_priority)
});
for system_name in system_names {
if let Some(system) = self.systems.get_mut(&system_name) {
if system.is_ready() {
match system.process(objects.clone(), priority).await {
Ok(results) => all_results.extend(results),
Err(e) => {
let error_result = ActionResult::failure(format!("System error: {}", e));
all_results.push(error_result);
}
}
}
}
}
Ok(all_results)
}
pub async fn process_with_system(
&mut self,
system_name: &str,
objects: Vec<Object>,
priority: Priority,
) -> Result<Vec<ActionResult>> {
let system = self
.systems
.get_mut(system_name)
.ok_or_else(|| OatsError::system_error(format!("System '{}' not found", system_name)))?;
if !system.is_ready() {
return Err(OatsError::system_error("System is not ready"));
}
system.process(objects, priority).await
}
pub async fn initialize_all(&mut self) -> Result<()> {
for (name, system) in &mut self.systems {
if let Err(e) = system.initialize().await {
return Err(OatsError::system_error(format!(
"Failed to initialize system '{}': {}",
name, e
)));
}
}
Ok(())
}
pub async fn shutdown_all(&mut self) -> Result<()> {
for (name, system) in &mut self.systems {
if let Err(e) = system.shutdown().await {
return Err(OatsError::system_error(format!(
"Failed to shutdown system '{}': {}",
name, e
)));
}
}
Ok(())
}
pub fn get_all_stats(&self) -> HashMap<String, SystemStats> {
self.systems
.iter()
.map(|(name, system)| (name.clone(), system.get_stats()))
.collect()
}
}
impl Default for SystemManager {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_priority_ordering() {
assert!(Priority::Critical > Priority::High);
assert!(Priority::High > Priority::Normal);
assert!(Priority::Normal > Priority::Low);
}
#[test]
fn test_system_stats() {
let stats = SystemStats::default();
assert_eq!(stats.objects_processed, 0);
assert_eq!(stats.actions_executed, 0);
assert_eq!(stats.errors, 0);
}
}