use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};
pub type ScopeId = u64;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScopeInfo {
pub name: String,
pub parent: Option<String>,
pub depth: usize,
pub variables: Vec<String>,
pub total_memory: usize,
pub peak_memory: usize,
pub allocation_count: usize,
pub lifetime_start: Option<u64>,
pub lifetime_end: Option<u64>,
pub is_active: bool,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ScopeHierarchy {
pub root_scopes: Vec<String>,
pub scope_tree: HashMap<String, Vec<String>>,
pub max_depth: usize,
pub total_scopes: usize,
}
#[derive(Debug)]
pub struct ScopeTracker {
active_scopes: Arc<Mutex<HashMap<ScopeId, ScopeInfo>>>,
scope_stack: Arc<Mutex<HashMap<String, Vec<ScopeId>>>>,
next_scope_id: Arc<Mutex<u64>>,
hierarchy: Arc<Mutex<ScopeHierarchy>>,
}
impl ScopeTracker {
pub fn new() -> Self {
Self {
active_scopes: Arc::new(Mutex::new(HashMap::new())),
scope_stack: Arc::new(Mutex::new(HashMap::new())),
next_scope_id: Arc::new(Mutex::new(1)),
hierarchy: Arc::new(Mutex::new(ScopeHierarchy {
root_scopes: Vec::new(),
scope_tree: HashMap::new(),
max_depth: 0,
total_scopes: 0,
})),
}
}
pub fn enter_scope(&self, name: String) -> ScopeId {
let scope_id = {
let mut id = self
.next_scope_id
.lock()
.expect("Failed to acquire next_scope_id lock");
let id_val = *id;
*id += 1;
id_val
};
let thread_id = format!("{:?}", std::thread::current().id());
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
let (parent, depth) = {
let stack = self
.scope_stack
.lock()
.expect("Failed to acquire scope_stack lock");
if let Some(thread_stack) = stack.get(&thread_id) {
if let Some(&parent_id) = thread_stack.last() {
if let Some(active) = self
.active_scopes
.lock()
.expect("Failed to acquire active_scopes lock")
.get(&parent_id)
{
(Some(active.name.clone()), active.depth + 1)
} else {
(None, 0)
}
} else {
(None, 0)
}
} else {
(None, 0)
}
};
let scope_info = ScopeInfo {
name: name.clone(),
parent: parent.clone(),
depth,
variables: Vec::new(),
total_memory: 0,
peak_memory: 0,
allocation_count: 0,
lifetime_start: Some(timestamp),
lifetime_end: None,
is_active: true,
};
self.active_scopes
.lock()
.expect("Failed to acquire active_scopes lock")
.insert(scope_id, scope_info.clone());
self.scope_stack
.lock()
.expect("Failed to acquire scope_stack lock")
.entry(thread_id)
.or_default()
.push(scope_id);
{
let mut hierarchy = self
.hierarchy
.lock()
.expect("Failed to acquire hierarchy lock");
if let Some(parent_name) = &parent {
hierarchy
.scope_tree
.entry(parent_name.clone())
.or_default()
.push(name.clone());
} else {
if !hierarchy.root_scopes.contains(&name) {
hierarchy.root_scopes.push(name);
}
}
hierarchy.total_scopes += 1;
if depth > hierarchy.max_depth {
hierarchy.max_depth = depth;
}
}
scope_id
}
pub fn exit_scope(&self) -> Option<ScopeId> {
let thread_id = format!("{:?}", std::thread::current().id());
let timestamp = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_nanos() as u64;
let scope_id = {
let mut stack = self
.scope_stack
.lock()
.expect("Failed to acquire scope_stack lock");
if let Some(thread_stack) = stack.get_mut(&thread_id) {
thread_stack.pop()
} else {
None
}
};
if let Some(scope_id) = scope_id {
if let Some(scope) = self
.active_scopes
.lock()
.expect("Failed to acquire active_scopes lock")
.get_mut(&scope_id)
{
scope.lifetime_end = Some(timestamp);
scope.is_active = false;
}
Some(scope_id)
} else {
None
}
}
pub fn get_scope(&self, scope_id: ScopeId) -> Option<ScopeInfo> {
self.active_scopes
.lock()
.expect("Failed to acquire active_scopes lock")
.get(&scope_id)
.cloned()
}
pub fn get_all_scopes(&self) -> Vec<(ScopeId, ScopeInfo)> {
self.active_scopes
.lock()
.expect("Failed to acquire active_scopes lock")
.iter()
.map(|(k, v)| (*k, v.clone()))
.collect()
}
pub fn get_hierarchy(&self) -> ScopeHierarchy {
self.hierarchy
.lock()
.expect("Failed to acquire hierarchy lock")
.clone()
}
pub fn clear(&self) {
self.active_scopes
.lock()
.expect("Failed to acquire active_scopes lock")
.clear();
self.scope_stack
.lock()
.expect("Failed to acquire scope_stack lock")
.clear();
*self
.next_scope_id
.lock()
.expect("Failed to acquire next_scope_id lock") = 1;
}
}
impl Default for ScopeTracker {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_scope_tracker_creation() {
let tracker = ScopeTracker::new();
assert_eq!(tracker.get_all_scopes().len(), 0);
}
#[test]
fn test_enter_exit_scope() {
let tracker = ScopeTracker::new();
let scope_id = tracker.enter_scope("test_scope".to_string());
assert!(scope_id > 0);
let scope = tracker.get_scope(scope_id);
assert!(scope.is_some());
assert!(scope.unwrap().is_active);
let exited = tracker.exit_scope();
assert!(exited.is_some());
let scope = tracker.get_scope(scope_id);
assert!(scope.is_some());
assert!(!scope.unwrap().is_active);
}
#[test]
fn test_nested_scopes() {
let tracker = ScopeTracker::new();
let outer_id = tracker.enter_scope("outer".to_string());
let inner_id = tracker.enter_scope("inner".to_string());
let outer = tracker.get_scope(outer_id).unwrap();
let inner = tracker.get_scope(inner_id).unwrap();
assert_eq!(inner.depth, outer.depth + 1);
assert_eq!(inner.parent, Some("outer".to_string()));
tracker.exit_scope();
tracker.exit_scope();
}
#[test]
fn test_hierarchy() {
let tracker = ScopeTracker::new();
tracker.enter_scope("root".to_string());
tracker.enter_scope("child1".to_string());
tracker.exit_scope();
tracker.enter_scope("child2".to_string());
tracker.exit_scope();
tracker.exit_scope();
let hierarchy = tracker.get_hierarchy();
assert_eq!(hierarchy.root_scopes.len(), 1);
assert_eq!(hierarchy.total_scopes, 3);
}
}