righvalor 0.1.0

RighValor: AI Infrastructure and Applications Framework for the Far Edge
/// # Task Domain Types
///
/// Core task-related types that are shared across the RighValor framework.
/// These types define the computational task model used by Master, Worker, and API layers.
use serde::{Deserialize, Serialize};
use utoipa::ToSchema;

use crate::service::ValorServiceId;

/// Task execution priority levels
#[derive(
    Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Default, Serialize, Deserialize, ToSchema,
)]
#[serde(rename_all = "snake_case")]
pub enum ValorTaskPriority {
    Low = 0,
    #[default]
    Normal = 1,
    High = 2,
    Critical = 3,
}

/// Task execution status
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ValorTaskStatus {
    /// Task is waiting to be assigned
    Pending,
    /// Task has been assigned to a worker
    Assigned,
    /// Task is currently being executed
    Running,
    /// Task completed successfully
    Completed,
    /// Task execution failed
    Failed,
    /// Task was cancelled
    Cancelled,
}

/// Task ID - Strong typed identifier for tasks
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, ToSchema)]
pub struct ValorTaskId(pub String);

impl ValorTaskId {
    pub fn new(id: String) -> Self {
        Self(id)
    }

    pub fn generate() -> Self {
        Self(uuid::Uuid::new_v4().to_string())
    }
}

impl std::fmt::Display for ValorTaskId {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{}", self.0)
    }
}

impl From<String> for ValorTaskId {
    fn from(id: String) -> Self {
        Self(id)
    }
}

impl AsRef<str> for ValorTaskId {
    fn as_ref(&self) -> &str {
        &self.0
    }
}

/// Task types - Simplified to use only service_id and optional method
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ValorTaskType {
    /// Execute a specific Valor service by ID
    ExecuteService {
        /// Simple string ID of the service (e.g., "righ.device_classification")
        service_id: ValorServiceId,
        /// Optional service method name
        method: Option<String>,
    },
}

/// Input data for task execution
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ValorTaskInput {
    /// Raw bytes input
    Binary(Vec<u8>),
    /// JSON input
    Json(serde_json::Value),
    /// Text input
    Text(String),
    /// File path input (for local files)
    FilePath(String),
    /// Empty input
    Empty,
}

/// Output data from task execution
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ValorTaskOutput {
    /// Raw bytes output
    Binary(Vec<u8>),
    /// JSON output
    Json(serde_json::Value),
    /// Text output
    Text(String),
    /// File path output (for generated files)
    FilePath(String),
}

/// Structured task error types
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum ValorTaskError {
    /// Task execution failed
    ExecutionFailed {
        /// Error code
        code: TaskErrorCode,
        /// Detailed error message
        message: String,
        /// Optional error context
        context: Option<String>,
    },
    /// Task timeout
    Timeout {
        /// Timeout duration in milliseconds
        timeout_ms: u64,
    },
    /// Resource unavailable
    ResourceUnavailable {
        /// Resource type
        resource_type: String,
        /// Details
        details: String,
    },
    /// Service unavailable
    ServiceUnavailable {
        /// Service name
        service_name: String,
        /// Error reason
        reason: String,
    },
    /// Unknown error (backward compatibility)
    Unknown(String),
}

/// Task error codes
#[derive(Debug, Clone, Copy, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub enum TaskErrorCode {
    /// General execution error
    ExecutionError = 1000,
    /// Invalid input data
    InvalidInput = 1001,
    /// Runtime error
    RuntimeError = 1002,
    /// Network error
    NetworkError = 1003,
    /// Permission error
    PermissionError = 1004,
    /// Configuration error
    ConfigurationError = 1005,
}

impl std::fmt::Display for ValorTaskError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ValorTaskError::ExecutionFailed {
                code,
                message,
                context,
            } => {
                write!(f, "Execution failed ({}): {}", *code as u32, message)?;
                if let Some(ctx) = context {
                    write!(f, " (context: {ctx})")?;
                }
                Ok(())
            }
            ValorTaskError::Timeout { timeout_ms } => {
                write!(f, "Task timeout after {timeout_ms}ms")
            }
            ValorTaskError::ResourceUnavailable {
                resource_type,
                details,
            } => write!(f, "Resource unavailable {resource_type}: {details}"),
            ValorTaskError::ServiceUnavailable {
                service_name,
                reason,
            } => write!(f, "Service '{service_name}' unavailable: {reason}"),
            ValorTaskError::Unknown(msg) => write!(f, "Unknown error: {msg}"),
        }
    }
}

impl std::error::Error for ValorTaskError {}

// Provide conversion from String for backward compatibility
impl From<String> for ValorTaskError {
    fn from(msg: String) -> Self {
        ValorTaskError::Unknown(msg)
    }
}

impl From<&str> for ValorTaskError {
    fn from(msg: &str) -> Self {
        ValorTaskError::Unknown(msg.to_string())
    }
}

/// Represents a computational task that can be assigned by the Valor Master to workers.
///
/// Tasks encapsulate the computational work to be performed, including input data,
/// processing requirements, and execution parameters.
#[derive(Debug, Clone, Serialize, Deserialize, ToSchema)]
#[serde(rename_all = "snake_case")]
pub struct ValorTask {
    /// Unique task identifier (strong typed)
    pub task_id: ValorTaskId,
    /// Task type defines what kind of computation to perform
    pub task_type: ValorTaskType,
    /// Task priority for scheduling
    pub priority: ValorTaskPriority,
    /// Optional task timeout in milliseconds
    #[serde(default)]
    pub timeout_ms: Option<u64>,
    /// Current assignment attempt count
    #[serde(default)]
    pub attempt: u32,
    /// Current task status
    pub status: ValorTaskStatus,
    /// Worker ID if assigned (stored as string for API compatibility)
    pub assigned_worker: Option<String>,
    /// Task creation timestamp (ms since epoch)
    pub created_at: u64,
    /// Task assignment timestamp
    pub assigned_at: Option<u64>,
    /// Task completion timestamp
    pub completed_at: Option<u64>,
    /// Task input data
    pub input: ValorTaskInput,
    /// Task output data (after completion)
    pub output: Option<ValorTaskOutput>,
    /// Structured error information if failed
    pub error: Option<ValorTaskError>,
}

/// Type alias for backward compatibility with Master-specific naming
pub type ValorMasterTask = ValorTask;