use std::fmt;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
errors::GitError,
hash::ObjectHash,
internal::object::{
ObjectTrait,
types::{ActorRef, Header, ObjectType},
},
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RunEventKind {
Created,
Patching,
Validating,
Completed,
Failed,
Checkpointed,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RunEvent {
#[serde(flatten)]
header: Header,
run_id: Uuid,
kind: RunEventKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
reason: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
error: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
metrics: Option<serde_json::Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
patchset_id: Option<Uuid>,
}
impl RunEvent {
pub fn new(created_by: ActorRef, run_id: Uuid, kind: RunEventKind) -> Result<Self, String> {
Ok(Self {
header: Header::new(ObjectType::RunEvent, created_by)?,
run_id,
kind,
reason: None,
error: None,
metrics: None,
patchset_id: None,
})
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn run_id(&self) -> Uuid {
self.run_id
}
pub fn kind(&self) -> &RunEventKind {
&self.kind
}
pub fn reason(&self) -> Option<&str> {
self.reason.as_deref()
}
pub fn error(&self) -> Option<&str> {
self.error.as_deref()
}
pub fn metrics(&self) -> Option<&serde_json::Value> {
self.metrics.as_ref()
}
pub fn patchset_id(&self) -> Option<Uuid> {
self.patchset_id
}
pub fn set_reason(&mut self, reason: Option<String>) {
self.reason = reason;
}
pub fn set_error(&mut self, error: Option<String>) {
self.error = error;
}
pub fn set_metrics(&mut self, metrics: Option<serde_json::Value>) {
self.metrics = metrics;
}
pub fn set_patchset_id(&mut self, patchset_id: Option<Uuid>) {
self.patchset_id = patchset_id;
}
}
impl fmt::Display for RunEvent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "RunEvent: {}", self.header.object_id())
}
}
impl ObjectTrait for RunEvent {
fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result<Self, GitError>
where
Self: Sized,
{
serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
}
fn get_type(&self) -> ObjectType {
ObjectType::RunEvent
}
fn get_size(&self) -> usize {
match serde_json::to_vec(self) {
Ok(v) => v.len(),
Err(e) => {
tracing::warn!("failed to compute RunEvent size: {}", e);
0
}
}
}
fn to_data(&self) -> Result<Vec<u8>, GitError> {
serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_run_event_fields() {
let actor = ActorRef::agent("planner").expect("actor");
let mut event =
RunEvent::new(actor, Uuid::from_u128(0x1), RunEventKind::Failed).expect("event");
let patchset_id = Uuid::from_u128(0x2);
event.set_reason(Some("validation failed".to_string()));
event.set_error(Some("cargo test failed".to_string()));
event.set_metrics(Some(serde_json::json!({"duration_ms": 1200})));
event.set_patchset_id(Some(patchset_id));
assert_eq!(event.kind(), &RunEventKind::Failed);
assert_eq!(event.reason(), Some("validation failed"));
assert_eq!(event.error(), Some("cargo test failed"));
assert_eq!(event.patchset_id(), Some(patchset_id));
}
}