use std::collections::HashMap;
use std::fmt;
use std::sync::{Arc, Mutex};
use std::time::{Duration, SystemTime};
#[derive(Debug)]
pub struct MinimalExecutionEngine {
config: MinimalEngineConfig,
active_tasks: Arc<Mutex<HashMap<String, MinimalTask>>>,
state: Arc<Mutex<EngineState>>,
stats: Arc<Mutex<MinimalStatistics>>,
}
#[derive(Debug, Clone)]
pub struct MinimalEngineConfig {
pub max_concurrent_tasks: usize,
pub default_timeout: Duration,
pub enable_monitoring: bool,
pub enable_fault_tolerance: bool,
}
#[derive(Debug, Clone)]
pub struct MinimalTask {
pub id: String,
pub task_type: String,
pub parameters: HashMap<String, String>,
pub priority: u8,
pub created_at: SystemTime,
pub status: TaskStatus,
pub progress: f64,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TaskStatus {
Pending,
Running,
Completed,
Failed,
Cancelled,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EngineState {
Stopped,
Starting,
Running,
Stopping,
Error,
}
#[derive(Debug, Clone)]
pub struct MinimalStatistics {
pub tasks_processed: u64,
pub active_task_count: usize,
pub success_rate: f64,
pub avg_processing_time: Duration,
pub uptime: Duration,
pub last_updated: SystemTime,
}
#[derive(Debug, Clone)]
pub struct TaskResult {
pub task_id: String,
pub success: bool,
pub data: HashMap<String, String>,
pub execution_time: Duration,
pub error: Option<String>,
}
#[derive(Debug, Clone)]
pub enum MinimalEngineError {
TaskNotFound(String),
EngineNotRunning,
ExecutionFailed(String),
ConfigError(String),
SystemError(String),
}
impl fmt::Display for MinimalEngineError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
MinimalEngineError::TaskNotFound(id) => write!(f, "Task not found: {}", id),
MinimalEngineError::EngineNotRunning => write!(f, "Engine not running"),
MinimalEngineError::ExecutionFailed(msg) => write!(f, "Task execution failed: {}", msg),
MinimalEngineError::ConfigError(msg) => write!(f, "Configuration error: {}", msg),
MinimalEngineError::SystemError(msg) => write!(f, "System error: {}", msg),
}
}
}
impl std::error::Error for MinimalEngineError {}
impl MinimalExecutionEngine {
pub fn new(config: MinimalEngineConfig) -> Self {
Self {
config,
active_tasks: Arc::new(Mutex::new(HashMap::new())),
state: Arc::new(Mutex::new(EngineState::Stopped)),
stats: Arc::new(Mutex::new(MinimalStatistics::new())),
}
}
pub fn start(&self) -> Result<(), MinimalEngineError> {
let mut state = self.state.lock().expect("lock should not be poisoned");
match *state {
EngineState::Running => return Ok(()), EngineState::Starting => {
return Err(MinimalEngineError::SystemError(
"Engine is already starting".to_string(),
))
}
_ => {}
}
*state = EngineState::Starting;
std::thread::sleep(Duration::from_millis(100));
*state = EngineState::Running;
{
let mut stats = self.stats.lock().expect("lock should not be poisoned");
stats.last_updated = SystemTime::now();
}
Ok(())
}
pub fn stop(&self) -> Result<(), MinimalEngineError> {
let mut state = self.state.lock().expect("lock should not be poisoned");
match *state {
EngineState::Stopped => return Ok(()), EngineState::Stopping => {
return Err(MinimalEngineError::SystemError(
"Engine is already stopping".to_string(),
))
}
_ => {}
}
*state = EngineState::Stopping;
{
let mut tasks = self.active_tasks.lock().expect("lock should not be poisoned");
for task in tasks.values_mut() {
if task.status == TaskStatus::Running || task.status == TaskStatus::Pending {
task.status = TaskStatus::Cancelled;
}
}
}
*state = EngineState::Stopped;
Ok(())
}
pub fn submit_task(&self, mut task: MinimalTask) -> Result<String, MinimalEngineError> {
{
let state = self.state.lock().expect("lock should not be poisoned");
if *state != EngineState::Running {
return Err(MinimalEngineError::EngineNotRunning);
}
}
{
let tasks = self.active_tasks.lock().expect("lock should not be poisoned");
if tasks.len() >= self.config.max_concurrent_tasks {
return Err(MinimalEngineError::SystemError(
"Maximum concurrent tasks reached".to_string(),
));
}
}
task.status = TaskStatus::Pending;
task.progress = 0.0;
let task_id = task.id.clone();
{
let mut tasks = self.active_tasks.lock().expect("lock should not be poisoned");
tasks.insert(task_id.clone(), task);
}
Ok(task_id)
}
pub fn execute_task(&self, task_id: &str) -> Result<TaskResult, MinimalEngineError> {
let task = {
let mut tasks = self.active_tasks.lock().expect("lock should not be poisoned");
match tasks.get_mut(task_id) {
Some(task) => {
if task.status != TaskStatus::Pending {
return Err(MinimalEngineError::ExecutionFailed(
"Task is not in pending state".to_string(),
));
}
task.status = TaskStatus::Running;
task.progress = 0.1;
task.clone()
}
None => return Err(MinimalEngineError::TaskNotFound(task_id.to_string())),
}
};
let start_time = std::time::Instant::now();
let result = self.simulate_task_execution(&task);
let execution_time = start_time.elapsed();
{
let mut tasks = self.active_tasks.lock().expect("lock should not be poisoned");
if let Some(active_task) = tasks.get_mut(task_id) {
active_task.status = if result.success {
TaskStatus::Completed
} else {
TaskStatus::Failed
};
active_task.progress = 1.0;
}
}
{
let mut stats = self.stats.lock().expect("lock should not be poisoned");
stats.tasks_processed += 1;
stats.last_updated = SystemTime::now();
let total_completed = stats.tasks_processed;
let success_count = if result.success { 1 } else { 0 };
stats.success_rate = (stats.success_rate * (total_completed - 1) as f64
+ success_count as f64)
/ total_completed as f64;
stats.avg_processing_time = Duration::from_nanos(
((stats.avg_processing_time.as_nanos() * (total_completed - 1) as u128
+ execution_time.as_nanos())
/ total_completed as u128) as u64,
);
}
Ok(TaskResult {
task_id: task_id.to_string(),
success: result.success,
data: result.data,
execution_time,
error: result.error,
})
}
pub fn get_state(&self) -> EngineState {
*self.state.lock().expect("lock should not be poisoned")
}
pub fn get_statistics(&self) -> MinimalStatistics {
let mut stats = self.stats.lock().expect("lock should not be poisoned");
stats.active_task_count = self.active_tasks.lock().expect("lock should not be poisoned").len();
stats.clone()
}
pub fn get_active_tasks(&self) -> Vec<MinimalTask> {
self.active_tasks
.lock()
.expect("active_tasks lock should not be poisoned")
.values()
.cloned()
.collect()
}
pub fn get_task(&self, task_id: &str) -> Option<MinimalTask> {
self.active_tasks.lock().expect("lock should not be poisoned").get(task_id).cloned()
}
pub fn cancel_task(&self, task_id: &str) -> Result<(), MinimalEngineError> {
let mut tasks = self.active_tasks.lock().expect("lock should not be poisoned");
match tasks.get_mut(task_id) {
Some(task) => {
if task.status == TaskStatus::Pending || task.status == TaskStatus::Running {
task.status = TaskStatus::Cancelled;
Ok(())
} else {
Err(MinimalEngineError::ExecutionFailed(
"Task cannot be cancelled in current state".to_string(),
))
}
}
None => Err(MinimalEngineError::TaskNotFound(task_id.to_string())),
}
}
fn simulate_task_execution(&self, task: &MinimalTask) -> TaskResult {
match task.task_type.as_str() {
"optimization" => self.simulate_optimization_task(task),
"training" => self.simulate_training_task(task),
"inference" => self.simulate_inference_task(task),
_ => self.simulate_generic_task(task),
}
}
fn simulate_optimization_task(&self, task: &MinimalTask) -> TaskResult {
std::thread::sleep(Duration::from_millis(50));
let mut data = HashMap::new();
data.insert("loss".to_string(), "0.0123".to_string());
data.insert("accuracy".to_string(), "0.987".to_string());
data.insert("iterations".to_string(), "100".to_string());
TaskResult {
task_id: task.id.clone(),
success: true,
data,
execution_time: Duration::from_millis(50),
error: None,
}
}
fn simulate_training_task(&self, task: &MinimalTask) -> TaskResult {
std::thread::sleep(Duration::from_millis(100));
let mut data = HashMap::new();
data.insert("epochs".to_string(), "10".to_string());
data.insert("final_loss".to_string(), "0.0456".to_string());
data.insert("validation_accuracy".to_string(), "0.923".to_string());
TaskResult {
task_id: task.id.clone(),
success: true,
data,
execution_time: Duration::from_millis(100),
error: None,
}
}
fn simulate_inference_task(&self, task: &MinimalTask) -> TaskResult {
std::thread::sleep(Duration::from_millis(20));
let mut data = HashMap::new();
data.insert(
"predictions".to_string(),
"[[0.1, 0.9], [0.8, 0.2]]".to_string(),
);
data.insert("latency_ms".to_string(), "15".to_string());
TaskResult {
task_id: task.id.clone(),
success: true,
data,
execution_time: Duration::from_millis(20),
error: None,
}
}
fn simulate_generic_task(&self, task: &MinimalTask) -> TaskResult {
std::thread::sleep(Duration::from_millis(30));
let mut data = HashMap::new();
data.insert("result".to_string(), "completed".to_string());
data.insert("processing_time".to_string(), "30ms".to_string());
TaskResult {
task_id: task.id.clone(),
success: true,
data,
execution_time: Duration::from_millis(30),
error: None,
}
}
}
impl Default for MinimalEngineConfig {
fn default() -> Self {
Self {
max_concurrent_tasks: 10,
default_timeout: Duration::from_secs(300),
enable_monitoring: true,
enable_fault_tolerance: true,
}
}
}
impl MinimalStatistics {
fn new() -> Self {
Self {
tasks_processed: 0,
active_task_count: 0,
success_rate: 1.0,
avg_processing_time: Duration::from_secs(0),
uptime: Duration::from_secs(0),
last_updated: SystemTime::now(),
}
}
}
impl MinimalTask {
pub fn new(id: String, task_type: String) -> Self {
Self {
id,
task_type,
parameters: HashMap::new(),
priority: 5, created_at: SystemTime::now(),
status: TaskStatus::Pending,
progress: 0.0,
}
}
pub fn with_parameter(mut self, key: String, value: String) -> Self {
self.parameters.insert(key, value);
self
}
pub fn with_priority(mut self, priority: u8) -> Self {
self.priority = priority;
self
}
}
impl MinimalTask {
pub fn optimization(id: String) -> Self {
Self::new(id, "optimization".to_string())
}
pub fn training(id: String) -> Self {
Self::new(id, "training".to_string())
}
pub fn inference(id: String) -> Self {
Self::new(id, "inference".to_string())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_engine_lifecycle() {
let config = MinimalEngineConfig::default();
let engine = MinimalExecutionEngine::new(config);
assert!(engine.start().is_ok());
assert_eq!(engine.get_state(), EngineState::Running);
assert!(engine.stop().is_ok());
assert_eq!(engine.get_state(), EngineState::Stopped);
}
#[test]
fn test_task_submission() {
let config = MinimalEngineConfig::default();
let engine = MinimalExecutionEngine::new(config);
engine.start().expect("start operation should succeed");
let task = MinimalTask::optimization("test_task_001".to_string())
.with_parameter("learning_rate".to_string(), "0.01".to_string())
.with_priority(1);
let task_id = engine.submit_task(task).expect("task submission should succeed");
assert_eq!(task_id, "test_task_001");
let retrieved_task = engine.get_task(&task_id).expect("task retrieval should succeed");
assert_eq!(retrieved_task.task_type, "optimization");
assert_eq!(retrieved_task.status, TaskStatus::Pending);
}
#[test]
fn test_task_execution() {
let config = MinimalEngineConfig::default();
let engine = MinimalExecutionEngine::new(config);
engine.start().expect("start operation should succeed");
let task = MinimalTask::optimization("test_execution_001".to_string());
let task_id = engine.submit_task(task).expect("task submission should succeed");
let result = engine.execute_task(&task_id).expect("task execution should succeed");
assert!(result.success);
assert_eq!(result.task_id, "test_execution_001");
assert!(result.data.contains_key("loss"));
assert!(result.execution_time > Duration::from_secs(0));
}
#[test]
fn test_task_cancellation() {
let config = MinimalEngineConfig::default();
let engine = MinimalExecutionEngine::new(config);
engine.start().expect("start operation should succeed");
let task = MinimalTask::training("test_cancel_001".to_string());
let task_id = engine.submit_task(task).expect("task submission should succeed");
assert!(engine.cancel_task(&task_id).is_ok());
let cancelled_task = engine.get_task(&task_id).expect("task retrieval should succeed");
assert_eq!(cancelled_task.status, TaskStatus::Cancelled);
}
#[test]
fn test_statistics_tracking() {
let config = MinimalEngineConfig::default();
let engine = MinimalExecutionEngine::new(config);
engine.start().expect("start operation should succeed");
let initial_stats = engine.get_statistics();
assert_eq!(initial_stats.tasks_processed, 0);
let task = MinimalTask::inference("test_stats_001".to_string());
let task_id = engine.submit_task(task).expect("task submission should succeed");
engine.execute_task(&task_id).expect("task execution should succeed");
let updated_stats = engine.get_statistics();
assert_eq!(updated_stats.tasks_processed, 1);
assert!(updated_stats.success_rate > 0.0);
assert!(updated_stats.avg_processing_time > Duration::from_secs(0));
}
#[test]
fn test_concurrent_task_limit() {
let config = MinimalEngineConfig {
max_concurrent_tasks: 2,
..Default::default()
};
let engine = MinimalExecutionEngine::new(config);
engine.start().expect("start operation should succeed");
let task1 = MinimalTask::optimization("task_001".to_string());
let task2 = MinimalTask::optimization("task_002".to_string());
assert!(engine.submit_task(task1).is_ok());
assert!(engine.submit_task(task2).is_ok());
let task3 = MinimalTask::optimization("task_003".to_string());
assert!(engine.submit_task(task3).is_err());
}
#[test]
fn test_different_task_types() {
let config = MinimalEngineConfig::default();
let engine = MinimalExecutionEngine::new(config);
engine.start().expect("start operation should succeed");
let opt_task = MinimalTask::optimization("opt_001".to_string());
let opt_id = engine.submit_task(opt_task).expect("task submission should succeed");
let opt_result = engine.execute_task(&opt_id).expect("task execution should succeed");
assert!(opt_result.data.contains_key("loss"));
let train_task = MinimalTask::training("train_001".to_string());
let train_id = engine.submit_task(train_task).expect("task submission should succeed");
let train_result = engine.execute_task(&train_id).expect("task execution should succeed");
assert!(train_result.data.contains_key("epochs"));
let infer_task = MinimalTask::inference("infer_001".to_string());
let infer_id = engine.submit_task(infer_task).expect("task submission should succeed");
let infer_result = engine.execute_task(&infer_id).expect("task execution should succeed");
assert!(infer_result.data.contains_key("predictions"));
}
}