cloacina 0.11.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.
 */

//! Unified Data Access Layer with runtime backend selection
//!
//! This module provides a unified DAL implementation that works with both
//! PostgreSQL and SQLite backends, selecting the appropriate implementation
//! at runtime based on the database connection type.
//!
//! # Architecture
//!
//! The unified DAL uses Diesel's `MultiConnection` feature to support
//! runtime backend selection. Each DAL operation dispatches to the
//! appropriate backend-specific implementation based on the connection type.
//!
//! # Example
//!
//! ```rust,ignore
//! use cloacina::dal::unified::DAL;
//! use cloacina::database::Database;
//!
//! // Create database with runtime backend detection
//! let db = Database::new("postgres://localhost/mydb", "mydb", 10);
//! let dal = DAL::new(db);
//!
//! // Operations automatically use the correct backend
//! let contexts = dal.context().list().await?;
//! ```

use crate::database::{AnyPool, BackendType, Database};

// Sub-modules for each entity type
#[cfg(feature = "postgres")]
pub mod agent_desired;
#[cfg(feature = "postgres")]
pub mod agent_limits;
#[cfg(feature = "postgres")]
pub mod api_keys;
pub mod checkpoint;
pub mod context;
pub mod delivery_outbox;
pub mod execution_event;
pub mod fleet_agents;
#[cfg(feature = "postgres")]
pub mod local_accounts;
pub mod login_throttle;
pub mod models;
#[cfg(feature = "postgres")]
pub mod oidc_login_flows;
#[cfg(feature = "postgres")]
pub mod oidc_sessions;
pub mod reactor_owner_addresses;
pub mod reactor_subscriptions;
pub mod recovery_event;
pub mod schedule;
pub mod schedule_execution;
pub mod task_execution;
pub mod task_execution_metadata;
pub mod workflow_execution;
pub mod workflow_packages;
pub mod workflow_registry_storage;
pub mod ws_tickets;

// Re-export DAL components
#[cfg(feature = "postgres")]
pub use agent_desired::AgentDesiredDAL;
#[cfg(feature = "postgres")]
pub use agent_limits::AgentLimitsDAL;
#[cfg(feature = "postgres")]
pub use api_keys::{ApiKeyDAL, ApiKeyInfo};
pub use checkpoint::CheckpointDAL;
pub use context::ContextDAL;
pub use delivery_outbox::DeliveryOutboxDAL;
pub use execution_event::ExecutionEventDAL;
pub use fleet_agents::{FleetAgent, FleetAgentDAL, FleetAgentRegistration};
#[cfg(feature = "postgres")]
pub use local_accounts::{LocalAccount, LocalAccountDAL, LoginOutcome};
pub use login_throttle::{LoginThrottleDAL, ThrottlePolicy, ThrottleState};
#[cfg(feature = "postgres")]
pub use oidc_login_flows::OidcLoginFlowDAL;
#[cfg(feature = "postgres")]
pub use oidc_sessions::{OidcSessionDAL, RefreshSession};
pub use reactor_subscriptions::{
    compile_predicate, lint_predicate_variables, truncate_predicate_text, ReactorFiring,
    ReactorSubscription, ReactorSubscriptionsDAL, PREDICATE_VARIABLES,
};
pub use recovery_event::RecoveryEventDAL;
pub use schedule::ScheduleDAL;
pub use schedule_execution::{
    RecoveryClaimResult, RecoveryHeartbeatResult, ScheduleExecutionDAL, ScheduleExecutionStats,
};
pub use task_execution::{RetryStats, TaskExecutionDAL};
pub use task_execution_metadata::TaskExecutionMetadataDAL;
pub use workflow_execution::WorkflowExecutionDAL;
pub use workflow_packages::WorkflowPackagesDAL;
pub use workflow_registry_storage::UnifiedRegistryStorage;
pub use ws_tickets::{WsTicketAuth, WsTicketDAL};

/// Helper macro for dispatching operations based on backend type.
///
/// This macro simplifies writing code that needs to execute different
/// implementations based on the database backend.
///
/// The unified Data Access Layer struct.
///
/// This struct provides access to all database operations through a single
/// interface that works with both PostgreSQL and SQLite backends.
///
/// # Thread Safety
///
/// The `DAL` struct is `Clone` and can be safely shared between threads.
/// Each clone references the same underlying database connection pool.
#[derive(Clone, Debug)]
pub struct DAL {
    /// The database instance with connection pool
    pub database: Database,
}

impl DAL {
    /// Creates a new unified DAL instance.
    ///
    /// # Arguments
    ///
    /// * `database` - A Database instance configured for either PostgreSQL or SQLite
    ///
    /// # Returns
    ///
    /// A new DAL instance ready for database operations.
    pub fn new(database: Database) -> Self {
        DAL { database }
    }

    /// Returns the backend type for this DAL instance.
    pub fn backend(&self) -> BackendType {
        self.database.backend()
    }

    /// Returns a reference to the underlying database.
    pub fn database(&self) -> &Database {
        &self.database
    }

    /// Returns the connection pool.
    pub fn pool(&self) -> AnyPool {
        self.database.pool()
    }

    /// Returns an agent-capacity-limits DAL (Postgres only). CLOACI-T-0808.
    #[cfg(feature = "postgres")]
    pub fn agent_limits(&self) -> AgentLimitsDAL<'_> {
        AgentLimitsDAL::new(self)
    }

    /// Returns an agent-desired-count DAL (Postgres only). CLOACI-T-0809.
    #[cfg(feature = "postgres")]
    pub fn agent_desired(&self) -> AgentDesiredDAL<'_> {
        AgentDesiredDAL::new(self)
    }

    /// Returns an API key DAL (Postgres only).
    #[cfg(feature = "postgres")]
    pub fn api_keys(&self) -> ApiKeyDAL<'_> {
        ApiKeyDAL::new(self)
    }

    /// Returns an OIDC refresh-session DAL (Postgres only). CLOACI-T-0793.
    #[cfg(feature = "postgres")]
    pub fn oidc_sessions(&self) -> OidcSessionDAL<'_> {
        OidcSessionDAL::new(self)
    }

    /// Returns a local-accounts DAL (Postgres only). CLOACI-T-0795.
    #[cfg(feature = "postgres")]
    pub fn local_accounts(&self) -> LocalAccountDAL<'_> {
        LocalAccountDAL::new(self)
    }

    /// Returns the login brute-force throttle DAL. CLOACI-T-0923.
    /// Unified (both backends) so its semantics are testable without Postgres.
    pub fn login_throttle(&self) -> LoginThrottleDAL<'_> {
        LoginThrottleDAL::new(self)
    }

    /// Returns the reactor owner-address DAL (CLOACI-T-0851 / A-0012
    /// Amendment 3). A routing hint only — the advisory lock is the sole
    /// ownership authority. Unified (both backends) so the takeover-race
    /// semantics are testable without Postgres.
    pub fn reactor_owner_addresses(&self) -> reactor_owner_addresses::ReactorOwnerAddressesDAL<'_> {
        reactor_owner_addresses::ReactorOwnerAddressesDAL::new(self)
    }

    /// Returns an OIDC login-flow-state DAL (Postgres only). CLOACI-T-0801.
    #[cfg(feature = "postgres")]
    pub fn oidc_login_flows(&self) -> OidcLoginFlowDAL<'_> {
        OidcLoginFlowDAL::new(self)
    }

    /// Returns a checkpoint DAL for computation graph state persistence.
    pub fn checkpoint(&self) -> CheckpointDAL<'_> {
        CheckpointDAL::new(self)
    }

    /// Returns a context DAL for context operations.
    pub fn context(&self) -> ContextDAL<'_> {
        ContextDAL::new(self)
    }

    /// Returns a workflow execution DAL for workflow execution operations.
    pub fn workflow_execution(&self) -> WorkflowExecutionDAL<'_> {
        WorkflowExecutionDAL::new(self)
    }

    /// Returns a task execution DAL for task operations.
    pub fn task_execution(&self) -> TaskExecutionDAL<'_> {
        TaskExecutionDAL::new(self)
    }

    /// Returns a task execution metadata DAL for metadata operations.
    pub fn task_execution_metadata(&self) -> TaskExecutionMetadataDAL<'_> {
        TaskExecutionMetadataDAL::new(self)
    }

    /// Returns a delivery outbox DAL for the interservice communication
    /// substrate (durable, ack-tracked, recipient-addressed push delivery).
    pub fn delivery_outbox(&self) -> DeliveryOutboxDAL<'_> {
        DeliveryOutboxDAL::new(self)
    }

    /// Returns a fleet-agent roster DAL (register/heartbeat state persisted
    /// for multi-replica selection, capacity views and reclaim). CLOACI-T-0916.
    pub fn fleet_agents(&self) -> FleetAgentDAL<'_> {
        FleetAgentDAL::new(self)
    }

    /// Returns a single-use WebSocket-ticket DAL (DB-backed so a ticket
    /// minted on one replica redeems on any other). CLOACI-T-0916.
    pub fn ws_tickets(&self) -> WsTicketDAL<'_> {
        WsTicketDAL::new(self)
    }

    /// Returns a recovery event DAL for recovery operations.
    pub fn recovery_event(&self) -> RecoveryEventDAL<'_> {
        RecoveryEventDAL::new(self)
    }

    /// Returns an execution event DAL for execution event operations.
    pub fn execution_event(&self) -> ExecutionEventDAL<'_> {
        ExecutionEventDAL::new(self)
    }

    /// Returns a unified schedule DAL for schedule operations.
    pub fn schedule(&self) -> ScheduleDAL<'_> {
        ScheduleDAL::new(self)
    }

    /// Returns a unified schedule execution DAL for schedule execution operations.
    pub fn schedule_execution(&self) -> ScheduleExecutionDAL<'_> {
        ScheduleExecutionDAL::new(self)
    }

    /// Returns a workflow packages DAL for package operations.
    pub fn workflow_packages(&self) -> WorkflowPackagesDAL<'_> {
        WorkflowPackagesDAL::new(self)
    }

    /// Returns a reactor subscriptions DAL for reactor-triggered workflow
    /// fan-out (CLOACI-I-0100 / T-0598).
    pub fn reactor_subscriptions(&self) -> ReactorSubscriptionsDAL<'_> {
        ReactorSubscriptionsDAL::new(self)
    }

    /// Creates a workflow registry implementation with the given storage backend.
    ///
    /// # Arguments
    ///
    /// * `storage` - A storage backend implementing `RegistryStorage`
    ///
    /// # Panics
    ///
    /// Panics if the workflow registry cannot be created.
    /// Use [`try_workflow_registry`](Self::try_workflow_registry) for fallible construction.
    pub fn workflow_registry<S: crate::registry::traits::RegistryStorage + 'static>(
        &self,
        storage: S,
    ) -> crate::registry::workflow_registry::WorkflowRegistryImpl<S> {
        self.try_workflow_registry(storage)
            .expect("Failed to create workflow registry")
    }

    /// Creates a workflow registry implementation with the given storage backend.
    ///
    /// This is the fallible version of [`workflow_registry`](Self::workflow_registry).
    ///
    /// # Arguments
    ///
    /// * `storage` - A storage backend implementing `RegistryStorage`
    ///
    /// # Errors
    ///
    /// Returns an error if the workflow registry cannot be initialized.
    pub fn try_workflow_registry<S: crate::registry::traits::RegistryStorage + 'static>(
        &self,
        storage: S,
    ) -> Result<
        crate::registry::workflow_registry::WorkflowRegistryImpl<S>,
        crate::registry::error::RegistryError,
    > {
        crate::registry::workflow_registry::WorkflowRegistryImpl::new(
            storage,
            self.database.clone(),
        )
    }
}