use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum EventType {
ProblemCreated,
ProblemSolved,
ProblemDissolved,
ProblemReopened,
SolutionCreated,
SolutionSubmitted,
SolutionApproved,
SolutionWithdrawn,
CritiqueRaised,
CritiqueAddressed,
CritiqueDismissed,
CritiqueValidated,
CritiqueReplied,
MilestoneCreated,
MilestoneCompleted,
GithubIssueCreated,
GithubIssueImported,
GithubIssueClosed,
GithubPrCreated,
GithubPrMerged,
GithubReviewImported,
}
impl EventType {
pub const fn as_str(&self) -> &'static str {
match self {
Self::ProblemCreated => "problem_created",
Self::ProblemSolved => "problem_solved",
Self::ProblemDissolved => "problem_dissolved",
Self::ProblemReopened => "problem_reopened",
Self::SolutionCreated => "solution_created",
Self::SolutionSubmitted => "solution_submitted",
Self::SolutionApproved => "solution_approved",
Self::SolutionWithdrawn => "solution_withdrawn",
Self::CritiqueRaised => "critique_raised",
Self::CritiqueAddressed => "critique_addressed",
Self::CritiqueDismissed => "critique_dismissed",
Self::CritiqueValidated => "critique_validated",
Self::CritiqueReplied => "critique_replied",
Self::MilestoneCreated => "milestone_created",
Self::MilestoneCompleted => "milestone_completed",
Self::GithubIssueCreated => "github_issue_created",
Self::GithubIssueImported => "github_issue_imported",
Self::GithubIssueClosed => "github_issue_closed",
Self::GithubPrCreated => "github_pr_created",
Self::GithubPrMerged => "github_pr_merged",
Self::GithubReviewImported => "github_review_imported",
}
}
}
impl std::fmt::Display for EventType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Event {
pub when: DateTime<Utc>,
#[serde(rename = "type")]
pub event_type: EventType,
pub entity: String,
pub by: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub rationale: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub refs: Vec<String>,
#[serde(flatten)]
pub extra: EventExtra,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct EventExtra {
#[serde(skip_serializing_if = "Option::is_none")]
pub target: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub severity: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub problem: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub supersedes: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub github_number: Option<u64>,
}
impl Event {
pub fn new(event_type: EventType, entity: impl Into<String>, by: impl Into<String>) -> Self {
Self {
when: Utc::now(),
event_type,
entity: entity.into(),
by: by.into(),
rationale: None,
refs: Vec::new(),
extra: EventExtra::default(),
}
}
pub fn with_rationale(mut self, rationale: impl Into<String>) -> Self {
self.rationale = Some(rationale.into());
self
}
pub fn with_refs(mut self, refs: Vec<String>) -> Self {
self.refs = refs;
self
}
pub fn with_timestamp(mut self, when: DateTime<Utc>) -> Self {
self.when = when;
self
}
pub fn with_extra(mut self, extra: EventExtra) -> Self {
self.extra = extra;
self
}
pub fn to_json_line(&self) -> Result<String, serde_json::Error> {
serde_json::to_string(self)
}
pub fn to_commit_suffix(&self) -> Result<String, serde_json::Error> {
Ok(format!("jjj: {}", self.to_json_line()?))
}
}