use guts_auth::WebhookEvent;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RealtimeEvent {
#[serde(rename = "type")]
pub event_type: String,
pub channel: String,
pub event: EventKind,
pub data: serde_json::Value,
pub timestamp: u64,
pub event_id: String,
}
impl RealtimeEvent {
pub fn new(channel: String, event: EventKind, data: serde_json::Value) -> Self {
Self {
event_type: "event".to_string(),
channel,
event,
data,
timestamp: Self::now(),
event_id: uuid::Uuid::new_v4().to_string(),
}
}
fn now() -> u64 {
std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_secs()
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EventKind {
Push,
BranchCreated,
BranchDeleted,
TagCreated,
TagDeleted,
PrOpened,
PrClosed,
PrMerged,
PrUpdated,
PrReopened,
PrReview,
PrComment,
IssueOpened,
IssueClosed,
IssueReopened,
IssueUpdated,
IssueComment,
LabelAdded,
LabelRemoved,
RepoCreated,
RepoUpdated,
CollaboratorAdded,
CollaboratorRemoved,
}
impl EventKind {
pub fn from_webhook(event: WebhookEvent) -> Self {
match event {
WebhookEvent::Push => EventKind::Push,
WebhookEvent::PullRequest => EventKind::PrOpened,
WebhookEvent::PullRequestReview => EventKind::PrReview,
WebhookEvent::PullRequestComment => EventKind::PrComment,
WebhookEvent::Issue => EventKind::IssueOpened,
WebhookEvent::IssueComment => EventKind::IssueComment,
WebhookEvent::Create => EventKind::BranchCreated,
WebhookEvent::Delete => EventKind::BranchDeleted,
WebhookEvent::Fork => EventKind::RepoCreated,
WebhookEvent::Star => EventKind::RepoUpdated,
}
}
pub fn all() -> Vec<EventKind> {
vec![
EventKind::Push,
EventKind::BranchCreated,
EventKind::BranchDeleted,
EventKind::TagCreated,
EventKind::TagDeleted,
EventKind::PrOpened,
EventKind::PrClosed,
EventKind::PrMerged,
EventKind::PrUpdated,
EventKind::PrReopened,
EventKind::PrReview,
EventKind::PrComment,
EventKind::IssueOpened,
EventKind::IssueClosed,
EventKind::IssueReopened,
EventKind::IssueUpdated,
EventKind::IssueComment,
EventKind::LabelAdded,
EventKind::LabelRemoved,
EventKind::RepoCreated,
EventKind::RepoUpdated,
EventKind::CollaboratorAdded,
EventKind::CollaboratorRemoved,
]
}
}
impl std::fmt::Display for EventKind {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
EventKind::Push => write!(f, "push"),
EventKind::BranchCreated => write!(f, "branch.created"),
EventKind::BranchDeleted => write!(f, "branch.deleted"),
EventKind::TagCreated => write!(f, "tag.created"),
EventKind::TagDeleted => write!(f, "tag.deleted"),
EventKind::PrOpened => write!(f, "pr.opened"),
EventKind::PrClosed => write!(f, "pr.closed"),
EventKind::PrMerged => write!(f, "pr.merged"),
EventKind::PrUpdated => write!(f, "pr.updated"),
EventKind::PrReopened => write!(f, "pr.reopened"),
EventKind::PrReview => write!(f, "pr.review"),
EventKind::PrComment => write!(f, "pr.comment"),
EventKind::IssueOpened => write!(f, "issue.opened"),
EventKind::IssueClosed => write!(f, "issue.closed"),
EventKind::IssueReopened => write!(f, "issue.reopened"),
EventKind::IssueUpdated => write!(f, "issue.updated"),
EventKind::IssueComment => write!(f, "issue.comment"),
EventKind::LabelAdded => write!(f, "label.added"),
EventKind::LabelRemoved => write!(f, "label.removed"),
EventKind::RepoCreated => write!(f, "repo.created"),
EventKind::RepoUpdated => write!(f, "repo.updated"),
EventKind::CollaboratorAdded => write!(f, "collaborator.added"),
EventKind::CollaboratorRemoved => write!(f, "collaborator.removed"),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PushEventData {
#[serde(rename = "ref")]
pub ref_name: String,
pub before: String,
pub after: String,
pub pusher: String,
pub commit_count: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PullRequestEventData {
pub number: u64,
pub title: String,
pub author: String,
pub source_branch: String,
pub target_branch: String,
pub state: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct IssueEventData {
pub number: u64,
pub title: String,
pub author: String,
pub state: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CommentEventData {
pub id: u64,
pub parent_number: u64,
pub author: String,
pub body_preview: String,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ReviewEventData {
pub id: u64,
pub pr_number: u64,
pub reviewer: String,
pub state: String,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_event_creation() {
let data = serde_json::json!({
"ref": "refs/heads/main",
"before": "abc123",
"after": "def456"
});
let event = RealtimeEvent::new("repo:alice/myrepo".to_string(), EventKind::Push, data);
assert_eq!(event.event_type, "event");
assert_eq!(event.channel, "repo:alice/myrepo");
assert_eq!(event.event, EventKind::Push);
assert!(!event.event_id.is_empty());
}
#[test]
fn test_event_kind_display() {
assert_eq!(EventKind::Push.to_string(), "push");
assert_eq!(EventKind::PrOpened.to_string(), "pr.opened");
assert_eq!(EventKind::IssueComment.to_string(), "issue.comment");
}
#[test]
fn test_event_serialization() {
let data = serde_json::json!({"test": "value"});
let event = RealtimeEvent::new("repo:test/repo".to_string(), EventKind::Push, data);
let json = serde_json::to_string(&event).unwrap();
assert!(json.contains("\"type\":\"event\""));
assert!(json.contains("\"channel\":\"repo:test/repo\""));
}
}