cloacina 0.6.1

A Rust library for resilient task execution and orchestration.
Documentation
/*
 *  Copyright 2025-2026 Colliery Software
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */

//! Execution Event Model
//!
//! This module defines domain structures and types for tracking execution events.
//! Execution events provide a complete audit trail of task and workflow state
//! transitions for debugging, compliance, and replay capability.
//!
//! These are API-level types; backend-specific models handle database storage.

use crate::database::universal_types::{UniversalTimestamp, UniversalUuid};
use serde::{Deserialize, Serialize};

/// Represents an execution event record (domain type).
///
/// Execution events are append-only records tracking all state transitions
/// for tasks and workflows. Each event captures the transition type, associated
/// context, and ordering information for replay.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExecutionEvent {
    /// Unique identifier for this event
    pub id: UniversalUuid,
    /// The workflow execution this event belongs to
    pub workflow_execution_id: UniversalUuid,
    /// The task execution this event relates to (None for workflow-level events)
    pub task_execution_id: Option<UniversalUuid>,
    /// The type of event (e.g., "task_created", "task_completed")
    pub event_type: String,
    /// JSON-encoded additional data for the event
    pub event_data: Option<String>,
    /// Worker ID that generated this event (for distributed tracing)
    pub worker_id: Option<String>,
    /// When this event was created
    pub created_at: UniversalTimestamp,
    /// Monotonically increasing sequence number for ordering
    pub sequence_num: i64,
}

/// Structure for creating new execution event records (domain type).
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NewExecutionEvent {
    /// The workflow execution this event belongs to
    pub workflow_execution_id: UniversalUuid,
    /// The task execution this event relates to (None for workflow-level events)
    pub task_execution_id: Option<UniversalUuid>,
    /// The type of event
    pub event_type: String,
    /// JSON-encoded additional data for the event
    pub event_data: Option<String>,
    /// Worker ID that generated this event
    pub worker_id: Option<String>,
}

impl NewExecutionEvent {
    /// Creates a new execution event for a workflow-level transition.
    pub fn workflow_event(
        workflow_execution_id: UniversalUuid,
        event_type: ExecutionEventType,
        event_data: Option<String>,
        worker_id: Option<String>,
    ) -> Self {
        Self {
            workflow_execution_id,
            task_execution_id: None,
            event_type: event_type.as_str().to_string(),
            event_data,
            worker_id,
        }
    }

    /// Creates a new execution event for a task-level transition.
    pub fn task_event(
        workflow_execution_id: UniversalUuid,
        task_execution_id: UniversalUuid,
        event_type: ExecutionEventType,
        event_data: Option<String>,
        worker_id: Option<String>,
    ) -> Self {
        Self {
            workflow_execution_id,
            task_execution_id: Some(task_execution_id),
            event_type: event_type.as_str().to_string(),
            event_data,
            worker_id,
        }
    }
}

/// Enumeration of execution event types in the system.
///
/// These cover the full lifecycle of tasks and workflows, providing
/// complete observability into execution state transitions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ExecutionEventType {
    // Task lifecycle events
    /// A new task execution record was created
    TaskCreated,
    /// Task transitioned to Ready status (eligible for claiming)
    TaskMarkedReady,
    /// Task was claimed by a worker
    TaskClaimed,
    /// Task execution started
    TaskStarted,
    /// Task was deferred (waiting for external condition)
    TaskDeferred,
    /// Deferred task resumed execution
    TaskResumed,
    /// Task completed successfully
    TaskCompleted,
    /// Task failed with an error
    TaskFailed,
    /// Task scheduled for retry after failure
    TaskRetryScheduled,
    /// Task was skipped (trigger rules not met)
    TaskSkipped,
    /// Task was abandoned (exceeded max retries or manually cancelled)
    TaskAbandoned,
    /// Task was reset by recovery process
    TaskReset,

    // Workflow lifecycle events
    /// Workflow execution started
    WorkflowStarted,
    /// Workflow execution completed successfully
    WorkflowCompleted,
    /// Workflow execution failed
    WorkflowFailed,
    /// Workflow was paused
    WorkflowPaused,
    /// Paused workflow was resumed
    WorkflowResumed,
}

impl ExecutionEventType {
    /// Returns the string representation of the event type.
    pub fn as_str(&self) -> &'static str {
        match self {
            // Task events
            ExecutionEventType::TaskCreated => "task_created",
            ExecutionEventType::TaskMarkedReady => "task_marked_ready",
            ExecutionEventType::TaskClaimed => "task_claimed",
            ExecutionEventType::TaskStarted => "task_started",
            ExecutionEventType::TaskDeferred => "task_deferred",
            ExecutionEventType::TaskResumed => "task_resumed",
            ExecutionEventType::TaskCompleted => "task_completed",
            ExecutionEventType::TaskFailed => "task_failed",
            ExecutionEventType::TaskRetryScheduled => "task_retry_scheduled",
            ExecutionEventType::TaskSkipped => "task_skipped",
            ExecutionEventType::TaskAbandoned => "task_abandoned",
            ExecutionEventType::TaskReset => "task_reset",
            // Workflow events
            ExecutionEventType::WorkflowStarted => "workflow_started",
            ExecutionEventType::WorkflowCompleted => "workflow_completed",
            ExecutionEventType::WorkflowFailed => "workflow_failed",
            ExecutionEventType::WorkflowPaused => "workflow_paused",
            ExecutionEventType::WorkflowResumed => "workflow_resumed",
        }
    }

    /// Parses an event type from its string representation.
    #[allow(clippy::should_implement_trait)]
    pub fn from_str(s: &str) -> Option<Self> {
        match s {
            "task_created" => Some(ExecutionEventType::TaskCreated),
            "task_marked_ready" => Some(ExecutionEventType::TaskMarkedReady),
            "task_claimed" => Some(ExecutionEventType::TaskClaimed),
            "task_started" => Some(ExecutionEventType::TaskStarted),
            "task_deferred" => Some(ExecutionEventType::TaskDeferred),
            "task_resumed" => Some(ExecutionEventType::TaskResumed),
            "task_completed" => Some(ExecutionEventType::TaskCompleted),
            "task_failed" => Some(ExecutionEventType::TaskFailed),
            "task_retry_scheduled" => Some(ExecutionEventType::TaskRetryScheduled),
            "task_skipped" => Some(ExecutionEventType::TaskSkipped),
            "task_abandoned" => Some(ExecutionEventType::TaskAbandoned),
            "task_reset" => Some(ExecutionEventType::TaskReset),
            "workflow_started" | "pipeline_started" => Some(ExecutionEventType::WorkflowStarted),
            "workflow_completed" | "pipeline_completed" => {
                Some(ExecutionEventType::WorkflowCompleted)
            }
            "workflow_failed" | "pipeline_failed" => Some(ExecutionEventType::WorkflowFailed),
            "workflow_paused" | "pipeline_paused" => Some(ExecutionEventType::WorkflowPaused),
            "workflow_resumed" | "pipeline_resumed" => Some(ExecutionEventType::WorkflowResumed),
            _ => None,
        }
    }

    /// Returns true if this is a task-level event.
    pub fn is_task_event(&self) -> bool {
        matches!(
            self,
            ExecutionEventType::TaskCreated
                | ExecutionEventType::TaskMarkedReady
                | ExecutionEventType::TaskClaimed
                | ExecutionEventType::TaskStarted
                | ExecutionEventType::TaskDeferred
                | ExecutionEventType::TaskResumed
                | ExecutionEventType::TaskCompleted
                | ExecutionEventType::TaskFailed
                | ExecutionEventType::TaskRetryScheduled
                | ExecutionEventType::TaskSkipped
                | ExecutionEventType::TaskAbandoned
                | ExecutionEventType::TaskReset
        )
    }

    /// Returns true if this is a workflow-level event.
    pub fn is_workflow_event(&self) -> bool {
        matches!(
            self,
            ExecutionEventType::WorkflowStarted
                | ExecutionEventType::WorkflowCompleted
                | ExecutionEventType::WorkflowFailed
                | ExecutionEventType::WorkflowPaused
                | ExecutionEventType::WorkflowResumed
        )
    }
}

impl From<ExecutionEventType> for String {
    fn from(event_type: ExecutionEventType) -> Self {
        event_type.as_str().to_string()
    }
}

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