use crate::*; use async_trait::async_trait; use std::error::Error as StdError; use uuid::Uuid; pub mod stdout; pub mod sqlite; pub mod postgres; pub mod writer; pub mod mock; #[derive(Debug)] pub enum BuildEventLogError { DatabaseError(String), SerializationError(String), ConnectionError(String), QueryError(String), } impl std::fmt::Display for BuildEventLogError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { BuildEventLogError::DatabaseError(msg) => write!(f, "Database error: {}", msg), BuildEventLogError::SerializationError(msg) => write!(f, "Serialization error: {}", msg), BuildEventLogError::ConnectionError(msg) => write!(f, "Connection error: {}", msg), BuildEventLogError::QueryError(msg) => write!(f, "Query error: {}", msg), } } } impl StdError for BuildEventLogError {} pub type Result = std::result::Result; #[derive(Debug, Clone)] pub struct QueryResult { pub columns: Vec, pub rows: Vec>, } // Summary types for list endpoints #[derive(Debug, Clone)] pub struct BuildRequestSummary { pub build_request_id: String, pub status: BuildRequestStatus, pub requested_partitions: Vec, pub created_at: i64, pub updated_at: i64, } #[derive(Debug, Clone)] pub struct PartitionSummary { pub partition_ref: String, pub status: PartitionStatus, pub updated_at: i64, pub build_request_id: Option, } #[derive(Debug, Clone)] pub struct ActivitySummary { pub active_builds_count: u32, pub recent_builds: Vec, pub recent_partitions: Vec, pub total_partitions_count: u32, } #[async_trait] pub trait BuildEventLog: Send + Sync { // Append new event to the log async fn append_event(&self, event: BuildEvent) -> Result<()>; // Query events by build request async fn get_build_request_events( &self, build_request_id: &str, since: Option ) -> Result>; // Query events by partition async fn get_partition_events( &self, partition_ref: &str, since: Option ) -> Result>; // Query events by job run async fn get_job_run_events( &self, job_run_id: &str ) -> Result>; // Query events in time range async fn get_events_in_range( &self, start_time: i64, end_time: i64 ) -> Result>; // Execute raw SQL queries (for dashboard and debugging) async fn execute_query(&self, query: &str) -> Result; // Get latest partition availability status async fn get_latest_partition_status( &self, partition_ref: &str ) -> Result>; // status and timestamp // Check if partition is being built by another request async fn get_active_builds_for_partition( &self, partition_ref: &str ) -> Result>; // build request IDs // Initialize/setup the storage backend async fn initialize(&self) -> Result<()>; // List recent build requests with pagination and filtering async fn list_build_requests( &self, limit: u32, offset: u32, status_filter: Option, ) -> Result<(Vec, u32)>; // List recent partitions with pagination and filtering async fn list_recent_partitions( &self, limit: u32, offset: u32, status_filter: Option, ) -> Result<(Vec, u32)>; // Get aggregated activity summary for dashboard async fn get_activity_summary(&self) -> Result; // Get the build request ID that created an available partition async fn get_build_request_for_available_partition( &self, partition_ref: &str ) -> Result>; // build request ID that made partition available } // Helper function to generate event ID pub fn generate_event_id() -> String { Uuid::new_v4().to_string() } // Helper function to get current timestamp in nanoseconds pub fn current_timestamp_nanos() -> i64 { std::time::SystemTime::now() .duration_since(std::time::UNIX_EPOCH) .unwrap() .as_nanos() as i64 } // Helper function to create build event with metadata pub fn create_build_event( build_request_id: String, event_type: crate::build_event::EventType, ) -> BuildEvent { BuildEvent { event_id: generate_event_id(), timestamp: current_timestamp_nanos(), build_request_id, event_type: Some(event_type), } } // Parse build event log URI and create appropriate implementation pub async fn create_build_event_log(uri: &str) -> Result> { if uri == "stdout" { Ok(Box::new(stdout::StdoutBuildEventLog::new())) } else if uri.starts_with("sqlite://") { let path = &uri[9..]; // Remove "sqlite://" prefix let log = sqlite::SqliteBuildEventLog::new(path).await?; log.initialize().await?; Ok(Box::new(log)) } else if uri.starts_with("postgres://") { let log = postgres::PostgresBuildEventLog::new(uri).await?; log.initialize().await?; Ok(Box::new(log)) } else { Err(BuildEventLogError::ConnectionError( format!("Unsupported build event log URI: {}", uri) )) } }