//! Edit history CRUD operations on `MessageRepo`. //! //! Immutable append-only log. Every message body edit creates one row. use chrono::Utc; use sqlx::Row; use uuid::Uuid; use crate::ImksResult; use crate::models::message_edit::{EditSummary, MessageEdit}; use super::message_repo::MessageRepo; impl MessageRepo { /// Record an edit to a message's body. pub async fn record_edit( &self, message_id: Uuid, edited_by: Uuid, old_body: &str, new_body: &str, ) -> ImksResult { let id = Uuid::now_v7(); let now = Utc::now(); sqlx::query_as::<_, MessageEdit>( r#" INSERT INTO message_edit (id, message_id, edited_by, old_body, new_body, edited_at) VALUES ($1, $2, $3, $4, $5, $6) RETURNING * "#, ) .bind(id) .bind(message_id) .bind(edited_by) .bind(old_body) .bind(new_body) .bind(now) .fetch_one(self.pool()) .await .map_err(Into::into) } /// Get the full edit history for a message, oldest first. pub async fn get_edit_history(&self, message_id: Uuid) -> ImksResult> { sqlx::query_as::<_, MessageEdit>( "SELECT * FROM message_edit WHERE message_id = $1 ORDER BY edited_at ASC", ) .bind(message_id) .fetch_all(self.pool()) .await .map_err(Into::into) } /// Get a summary of edits for a message (count + last editor). pub async fn get_edit_summary(&self, message_id: Uuid) -> ImksResult { let row = sqlx::query( r#" SELECT COUNT(*)::BIGINT AS edit_count, MAX(edited_at) AS last_edited_at, (ARRAY_AGG(edited_by ORDER BY edited_at DESC))[1] AS last_edited_by FROM message_edit WHERE message_id = $1 "#, ) .bind(message_id) .fetch_one(self.pool()) .await?; Ok(EditSummary { edit_count: row.get("edit_count"), last_edited_at: row.get("last_edited_at"), last_edited_by: row.get("last_edited_by"), }) } }