use crate::config::Config;
use crate::core::config::CoreConfig;
use crate::metrics::MetricsCollector;
use anyhow::Result;
use std::any::Any;
use std::collections::HashMap;
use std::sync::Arc;
use std::time::Instant;
use uuid::Uuid;
pub struct CommandContext {
pub config: Arc<Config>,
pub core_config: Option<Arc<CoreConfig>>,
pub args: HashMap<String, serde_json::Value>,
state: HashMap<String, Box<dyn Any + Send + Sync>>,
pub execution_id: Uuid,
pub start_time: Instant,
pub metrics: Arc<MetricsCollector>,
pub json_output: bool,
pub verbosity: u8,
}
impl CommandContext {
pub fn new(config: Config) -> Self {
let (metrics_collector, processor) = MetricsCollector::new();
processor.start();
Self {
config: Arc::new(config),
core_config: None,
args: HashMap::new(),
state: HashMap::new(),
execution_id: Uuid::new_v4(),
start_time: Instant::now(),
metrics: Arc::new(metrics_collector),
json_output: false,
verbosity: 0,
}
}
pub fn with_configs(config: Config, core_config: CoreConfig) -> Self {
let (metrics_collector, processor) = MetricsCollector::new();
processor.start();
Self {
config: Arc::new(config),
core_config: Some(Arc::new(core_config)),
args: HashMap::new(),
state: HashMap::new(),
execution_id: Uuid::new_v4(),
start_time: Instant::now(),
metrics: Arc::new(metrics_collector),
json_output: false,
verbosity: 0,
}
}
pub fn set_arg(&mut self, key: impl Into<String>, value: serde_json::Value) {
self.args.insert(key.into(), value);
}
pub fn get_arg(&self, key: &str) -> Option<&serde_json::Value> {
self.args.get(key)
}
pub fn get_arg_as<T: serde::de::DeserializeOwned>(&self, key: &str) -> Result<T> {
let value = self
.args
.get(key)
.ok_or_else(|| anyhow::anyhow!("Argument '{}' not found", key))?;
serde_json::from_value(value.clone())
.map_err(|e| anyhow::anyhow!("Failed to deserialize argument '{}': {}", key, e))
}
pub fn set_state<T: Any + Send + Sync>(&mut self, key: impl Into<String>, value: T) {
self.state.insert(key.into(), Box::new(value));
}
pub fn get_state<T: Any + Send + Sync>(&self, key: &str) -> Option<&T> {
self.state.get(key).and_then(|v| v.downcast_ref::<T>())
}
pub fn get_state_mut<T: Any + Send + Sync>(&mut self, key: &str) -> Option<&mut T> {
self.state.get_mut(key).and_then(|v| v.downcast_mut::<T>())
}
pub fn elapsed(&self) -> std::time::Duration {
self.start_time.elapsed()
}
pub fn set_json_output(&mut self, enabled: bool) {
self.json_output = enabled;
}
pub fn set_verbosity(&mut self, level: u8) {
self.verbosity = level;
}
pub fn is_verbose(&self) -> bool {
self.verbosity >= 1
}
pub fn is_debug(&self) -> bool {
self.verbosity >= 2
}
}
#[cfg(test)]
impl CommandContext {
pub fn mock() -> Self {
let (metrics_collector, processor) = MetricsCollector::new();
processor.start();
Self {
config: Arc::new(Config::default()),
core_config: None,
args: HashMap::new(),
state: HashMap::new(),
execution_id: Uuid::new_v4(),
start_time: Instant::now(),
metrics: Arc::new(metrics_collector),
json_output: false,
verbosity: 0,
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[tokio::test]
async fn test_context_creation() {
let ctx = CommandContext::mock();
assert!(!ctx.json_output);
assert_eq!(ctx.verbosity, 0);
}
#[tokio::test]
async fn test_arg_storage() {
let mut ctx = CommandContext::mock();
ctx.set_arg("model", json!("llama-2-7b"));
ctx.set_arg("count", json!(5));
assert_eq!(ctx.get_arg("model"), Some(&json!("llama-2-7b")));
assert_eq!(ctx.get_arg("count"), Some(&json!(5)));
assert_eq!(ctx.get_arg("missing"), None);
}
#[tokio::test]
async fn test_typed_arg_retrieval() {
let mut ctx = CommandContext::mock();
ctx.set_arg("count", json!(42));
ctx.set_arg("name", json!("test"));
let count: i32 = ctx.get_arg_as("count").unwrap();
assert_eq!(count, 42);
let name: String = ctx.get_arg_as("name").unwrap();
assert_eq!(name, "test");
}
#[tokio::test]
async fn test_state_storage() {
let mut ctx = CommandContext::mock();
ctx.set_state("counter", 42_i32);
ctx.set_state("name", "test".to_string());
assert_eq!(ctx.get_state::<i32>("counter"), Some(&42));
assert_eq!(ctx.get_state::<String>("name"), Some(&"test".to_string()));
}
#[tokio::test]
async fn test_verbosity() {
let mut ctx = CommandContext::mock();
assert!(!ctx.is_verbose());
assert!(!ctx.is_debug());
ctx.set_verbosity(1);
assert!(ctx.is_verbose());
assert!(!ctx.is_debug());
ctx.set_verbosity(2);
assert!(ctx.is_verbose());
assert!(ctx.is_debug());
}
#[tokio::test]
async fn test_elapsed_time() {
let ctx = CommandContext::mock();
tokio::time::sleep(std::time::Duration::from_millis(10)).await;
let elapsed = ctx.elapsed();
assert!(elapsed.as_millis() >= 10);
}
}