//! Message edit history — maps to `message_edit` table. //! //! Immutable append-only log of every edit to a message. //! Used for audit trails, "edited" indicators with hover-to-see-original, //! and compliance / moderation review. use chrono::{DateTime, Utc}; use serde::{Deserialize, Serialize}; use uuid::Uuid; /// One edit record. Stored every time a message body is modified. #[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow)] pub struct MessageEdit { pub id: Uuid, pub message_id: Uuid, /// Who made the edit (usually the author; can be a moderator). pub edited_by: Uuid, /// Body content before the edit. pub old_body: String, /// Body content after the edit. pub new_body: String, pub edited_at: DateTime, } /// Lightweight summary for the "edited" tooltip (no full body). #[derive(Debug, Clone, Serialize, Deserialize)] pub struct EditSummary { pub edit_count: i64, pub last_edited_at: Option>, pub last_edited_by: Option, } #[cfg(test)] mod tests { use super::*; #[test] fn test_message_edit_serialize() { let edit = MessageEdit { id: Uuid::now_v7(), message_id: Uuid::now_v7(), edited_by: Uuid::now_v7(), old_body: "before edit".to_string(), new_body: "after edit".to_string(), edited_at: Utc::now(), }; let json = serde_json::to_value(&edit).unwrap(); assert_eq!(json["old_body"], "before edit"); assert_eq!(json["new_body"], "after edit"); } #[test] fn test_edit_summary_serialize() { let summary = EditSummary { edit_count: 3, last_edited_at: Some(Utc::now()), last_edited_by: Some(Uuid::now_v7()), }; let json = serde_json::to_value(&summary).unwrap(); assert_eq!(json["edit_count"], 3); } #[test] fn test_edit_summary_no_edits() { let summary = EditSummary { edit_count: 0, last_edited_at: None, last_edited_by: None, }; let json = serde_json::to_value(&summary).unwrap(); assert_eq!(json["edit_count"], 0); assert!(json["last_edited_at"].is_null()); } }