rusty_beads/types/
event.rs

1//! Event type definitions for audit trail.
2
3use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use std::fmt;
6use std::str::FromStr;
7
8/// An audit event recording a change to an issue.
9#[derive(Debug, Clone, Serialize, Deserialize)]
10pub struct Event {
11    /// Unique event ID.
12    pub id: i64,
13    /// Associated issue ID.
14    pub issue_id: String,
15    /// Type of change.
16    pub event_type: EventType,
17    /// Who made the change.
18    pub actor: String,
19    /// Previous value (if applicable).
20    #[serde(skip_serializing_if = "Option::is_none")]
21    pub old_value: Option<String>,
22    /// New value (if applicable).
23    #[serde(skip_serializing_if = "Option::is_none")]
24    pub new_value: Option<String>,
25    /// Explanatory note.
26    #[serde(skip_serializing_if = "Option::is_none")]
27    pub comment: Option<String>,
28    /// When the event occurred.
29    pub created_at: DateTime<Utc>,
30}
31
32impl Event {
33    /// Create a new event.
34    pub fn new(
35        id: i64,
36        issue_id: impl Into<String>,
37        event_type: EventType,
38        actor: impl Into<String>,
39    ) -> Self {
40        Self {
41            id,
42            issue_id: issue_id.into(),
43            event_type,
44            actor: actor.into(),
45            old_value: None,
46            new_value: None,
47            comment: None,
48            created_at: Utc::now(),
49        }
50    }
51
52    /// Set old and new values.
53    pub fn with_change(
54        mut self,
55        old_value: Option<String>,
56        new_value: Option<String>,
57    ) -> Self {
58        self.old_value = old_value;
59        self.new_value = new_value;
60        self
61    }
62
63    /// Set a comment.
64    pub fn with_comment(mut self, comment: impl Into<String>) -> Self {
65        self.comment = Some(comment.into());
66        self
67    }
68}
69
70/// The type of audit event.
71#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
72#[serde(rename_all = "snake_case")]
73pub enum EventType {
74    /// Issue created.
75    Created,
76    /// General update.
77    Updated,
78    /// Status transition.
79    StatusChanged,
80    /// Comment added.
81    Commented,
82    /// Issue closed.
83    Closed,
84    /// Issue reopened.
85    Reopened,
86    /// Dependency created.
87    DependencyAdded,
88    /// Dependency removed.
89    DependencyRemoved,
90    /// Label added.
91    LabelAdded,
92    /// Label removed.
93    LabelRemoved,
94    /// Issue compacted.
95    Compacted,
96}
97
98impl EventType {
99    /// Returns the string representation for database storage.
100    pub fn as_str(&self) -> &'static str {
101        match self {
102            EventType::Created => "created",
103            EventType::Updated => "updated",
104            EventType::StatusChanged => "status_changed",
105            EventType::Commented => "commented",
106            EventType::Closed => "closed",
107            EventType::Reopened => "reopened",
108            EventType::DependencyAdded => "dependency_added",
109            EventType::DependencyRemoved => "dependency_removed",
110            EventType::LabelAdded => "label_added",
111            EventType::LabelRemoved => "label_removed",
112            EventType::Compacted => "compacted",
113        }
114    }
115}
116
117impl fmt::Display for EventType {
118    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
119        write!(f, "{}", self.as_str())
120    }
121}
122
123impl FromStr for EventType {
124    type Err = String;
125
126    fn from_str(s: &str) -> Result<Self, Self::Err> {
127        match s.to_lowercase().as_str() {
128            "created" => Ok(EventType::Created),
129            "updated" => Ok(EventType::Updated),
130            "status_changed" => Ok(EventType::StatusChanged),
131            "commented" => Ok(EventType::Commented),
132            "closed" => Ok(EventType::Closed),
133            "reopened" => Ok(EventType::Reopened),
134            "dependency_added" => Ok(EventType::DependencyAdded),
135            "dependency_removed" => Ok(EventType::DependencyRemoved),
136            "label_added" => Ok(EventType::LabelAdded),
137            "label_removed" => Ok(EventType::LabelRemoved),
138            "compacted" => Ok(EventType::Compacted),
139            _ => Err(format!("unknown event type: {}", s)),
140        }
141    }
142}