use crate::level::LogLevel;
use parking_lot::RwLock;
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::Arc;
use std::time::{Duration, Instant};
#[cfg(feature = "system-monitor")]
use crate::monitor::SystemMonitor;
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub enum ComponentStatus {
Running,
Success,
Failed(String),
Cancelled,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ComponentMetadata {
pub custom: HashMap<String, String>,
pub memory_bytes: Option<u64>,
pub message: Option<String>,
pub level: LogLevel,
}
impl ComponentMetadata {
pub fn new() -> Self {
Self {
custom: HashMap::new(),
memory_bytes: None,
message: None,
level: LogLevel::Info,
}
}
pub fn with_custom(mut self, key: &str, value: &str) -> Self {
self.custom.insert(key.to_string(), value.to_string());
self
}
pub fn with_memory(mut self, bytes: u64) -> Self {
self.memory_bytes = Some(bytes);
self
}
pub fn with_message(mut self, message: &str) -> Self {
self.message = Some(message.to_string());
self
}
pub fn with_level(mut self, level: LogLevel) -> Self {
self.level = level;
self
}
}
impl Default for ComponentMetadata {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct Component {
pub id: String,
pub name: String,
pub parent_id: Option<String>,
pub children: Vec<String>,
pub start_time: Instant,
pub end_time: Option<Instant>,
pub status: ComponentStatus,
pub metadata: ComponentMetadata,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SerializableComponent {
pub id: String,
pub name: String,
pub parent_id: Option<String>,
pub children: Vec<String>,
pub duration_ms: Option<f64>,
pub status: ComponentStatus,
pub metadata: ComponentMetadata,
}
impl Component {
pub fn new(id: String, name: String, parent_id: Option<String>) -> Self {
Self {
id,
name,
parent_id,
children: Vec::new(),
start_time: Instant::now(),
end_time: None,
status: ComponentStatus::Running,
metadata: ComponentMetadata::new(),
}
}
pub fn duration(&self) -> Option<Duration> {
self.end_time.map(|end| end.duration_since(self.start_time))
}
pub fn complete(&mut self, status: ComponentStatus) {
self.end_time = Some(Instant::now());
self.status = status;
}
pub fn is_running(&self) -> bool {
matches!(self.status, ComponentStatus::Running)
}
pub fn to_serializable(&self) -> SerializableComponent {
SerializableComponent {
id: self.id.clone(),
name: self.name.clone(),
parent_id: self.parent_id.clone(),
children: self.children.clone(),
duration_ms: self.duration().map(|d| d.as_secs_f64() * 1000.0),
status: self.status.clone(),
metadata: self.metadata.clone(),
}
}
}
#[derive(Debug)]
pub struct ComponentTracker {
components: RwLock<HashMap<String, Component>>,
current_stack: RwLock<Vec<String>>,
next_id: RwLock<u64>,
}
impl ComponentTracker {
pub fn new() -> Self {
Self {
components: RwLock::new(HashMap::new()),
current_stack: RwLock::new(Vec::new()),
next_id: RwLock::new(0),
}
}
fn generate_id(&self) -> String {
let mut next_id = self.next_id.write();
let id = *next_id;
*next_id += 1;
format!("comp_{}", id)
}
pub fn start_component(&self, name: &str) -> String {
let id = self.generate_id();
let parent_id = self.current_stack.read().last().cloned();
let component = Component::new(id.clone(), name.to_string(), parent_id.clone());
if let Some(parent_id) = &parent_id {
if let Some(parent) = self.components.write().get_mut(parent_id) {
parent.children.push(id.clone());
}
}
self.components.write().insert(id.clone(), component);
self.current_stack.write().push(id.clone());
id
}
pub fn end_component(&self, id: &str, status: ComponentStatus) -> Result<(), String> {
let mut components = self.components.write();
let mut stack = self.current_stack.write();
if let Some(pos) = stack.iter().position(|x| x == id) {
stack.remove(pos);
}
if let Some(component) = components.get_mut(id) {
component.complete(status);
Ok(())
} else {
Err(format!("Component with ID '{}' not found", id))
}
}
pub fn update_metadata(&self, id: &str, metadata: ComponentMetadata) -> Result<(), String> {
let mut components = self.components.write();
if let Some(component) = components.get_mut(id) {
component.metadata = metadata;
Ok(())
} else {
Err(format!("Component with ID '{}' not found", id))
}
}
pub fn get_components(&self) -> HashMap<String, Component> {
self.components.read().clone()
}
pub fn get_root_components(&self) -> Vec<Component> {
self.components
.read()
.values()
.filter(|c| c.parent_id.is_none())
.cloned()
.collect()
}
pub fn get_children(&self, parent_id: &str) -> Vec<Component> {
let components = self.components.read();
if let Some(parent) = components.get(parent_id) {
parent
.children
.iter()
.filter_map(|child_id| components.get(child_id).cloned())
.collect()
} else {
Vec::new()
}
}
pub fn clear(&self) {
self.components.write().clear();
self.current_stack.write().clear();
*self.next_id.write() = 0;
}
}
impl Default for ComponentTracker {
fn default() -> Self {
Self::new()
}
}
pub struct ComponentGuard {
id: String,
tracker: Arc<ComponentTracker>,
#[cfg(feature = "system-monitor")]
system_monitor: Option<Arc<RwLock<SystemMonitor>>>,
start_memory: Option<u64>,
}
impl ComponentGuard {
pub fn new(name: &str, tracker: Arc<ComponentTracker>) -> Self {
let id = tracker.start_component(name);
Self {
id,
tracker,
#[cfg(feature = "system-monitor")]
system_monitor: None,
start_memory: None,
}
}
pub fn new_with_monitor(
name: &str,
tracker: Arc<ComponentTracker>,
monitor: Arc<RwLock<SystemMonitor>>,
) -> Self {
let id = tracker.start_component(name);
let start_memory = {
let mut monitor_guard = monitor.write();
monitor_guard.refresh();
monitor_guard.process_memory()
};
Self {
id,
tracker,
system_monitor: Some(monitor),
start_memory,
}
}
pub fn id(&self) -> &str {
&self.id
}
pub fn update_metadata(&self, metadata: ComponentMetadata) -> Result<(), String> {
self.tracker.update_metadata(&self.id, metadata)
}
pub fn add_metadata(&self, key: &str, value: &str) -> Result<(), String> {
let components = self.tracker.components.read();
if let Some(component) = components.get(&self.id) {
let mut metadata = component.metadata.clone();
metadata.custom.insert(key.to_string(), value.to_string());
drop(components);
self.tracker.update_metadata(&self.id, metadata)
} else {
Err(format!("Component with ID '{}' not found", self.id))
}
}
#[cfg(feature = "system-monitor")]
pub fn update_memory_usage(&self) -> Result<(), String> {
if let Some(monitor) = &self.system_monitor {
let mut monitor_guard = monitor.write();
monitor_guard.refresh();
if let Some(current_memory) = monitor_guard.process_memory() {
let memory_to_store = if let Some(start_mem) = self.start_memory {
if current_memory > start_mem {
current_memory - start_mem
} else {
current_memory
}
} else {
current_memory
};
let metadata = ComponentMetadata::new().with_memory(memory_to_store);
self.tracker.update_metadata(&self.id, metadata)
} else {
Err("Failed to get current memory usage".to_string())
}
} else {
Err("System monitor not available".to_string())
}
}
pub fn complete_success(self) {
let _ = self
.tracker
.end_component(&self.id, ComponentStatus::Success);
std::mem::forget(self);
}
pub fn complete_failure(self, error: &str) {
let _ = self
.tracker
.end_component(&self.id, ComponentStatus::Failed(error.to_string()));
std::mem::forget(self);
}
pub fn complete_cancelled(self) {
let _ = self
.tracker
.end_component(&self.id, ComponentStatus::Cancelled);
std::mem::forget(self);
}
}
impl Drop for ComponentGuard {
fn drop(&mut self) {
#[cfg(feature = "system-monitor")]
if let Some(monitor) = &self.system_monitor {
let mut monitor_guard = monitor.write();
monitor_guard.refresh();
if let Some(current_memory) = monitor_guard.process_memory() {
let memory_to_store = if let Some(start_mem) = self.start_memory {
if current_memory > start_mem {
current_memory - start_mem
} else {
current_memory
}
} else {
current_memory
};
let metadata = ComponentMetadata::new().with_memory(memory_to_store);
let _ = self.tracker.update_metadata(&self.id, metadata);
}
}
let _ = self
.tracker
.end_component(&self.id, ComponentStatus::Success);
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_component_creation() {
let tracker = ComponentTracker::new();
let id = tracker.start_component("test_component");
let components = tracker.get_components();
assert_eq!(components.len(), 1);
assert!(components.contains_key(&id));
let component = &components[&id];
assert_eq!(component.name, "test_component");
assert!(component.is_running());
}
#[test]
fn test_parent_child_relationship() {
let tracker = ComponentTracker::new();
let parent_id = tracker.start_component("parent");
let child_id = tracker.start_component("child");
let components = tracker.get_components();
let parent = &components[&parent_id];
let child = &components[&child_id];
assert_eq!(child.parent_id, Some(parent_id.clone()));
assert!(parent.children.contains(&child_id));
}
#[test]
fn test_component_guard() {
let tracker = Arc::new(ComponentTracker::new());
{
let _guard = ComponentGuard::new("test", tracker.clone());
let components = tracker.get_components();
assert_eq!(components.len(), 1);
}
let components = tracker.get_components();
let component = components.values().next().unwrap();
assert_eq!(component.status, ComponentStatus::Success);
}
#[test]
fn test_metadata_updates() {
let tracker = ComponentTracker::new();
let id = tracker.start_component("test");
let metadata = ComponentMetadata::new()
.with_custom("key", "value")
.with_memory(1024)
.with_message("Test message");
tracker.update_metadata(&id, metadata).unwrap();
let components = tracker.get_components();
let component = &components[&id];
assert_eq!(
component.metadata.custom.get("key"),
Some(&"value".to_string())
);
assert_eq!(component.metadata.memory_bytes, Some(1024));
assert_eq!(component.metadata.message, Some("Test message".to_string()));
}
}