use std::fmt;
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::{
errors::GitError,
hash::ObjectHash,
internal::object::{
ObjectTrait,
integrity::IntegrityHash,
types::{ActorRef, Header, ObjectType},
},
};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum DecisionType {
Commit,
Checkpoint,
Abandon,
Retry,
Rollback,
#[serde(untagged)]
Other(String),
}
impl fmt::Display for DecisionType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DecisionType::Commit => write!(f, "commit"),
DecisionType::Checkpoint => write!(f, "checkpoint"),
DecisionType::Abandon => write!(f, "abandon"),
DecisionType::Retry => write!(f, "retry"),
DecisionType::Rollback => write!(f, "rollback"),
DecisionType::Other(s) => write!(f, "{}", s),
}
}
}
impl From<String> for DecisionType {
fn from(s: String) -> Self {
match s.as_str() {
"commit" => DecisionType::Commit,
"checkpoint" => DecisionType::Checkpoint,
"abandon" => DecisionType::Abandon,
"retry" => DecisionType::Retry,
"rollback" => DecisionType::Rollback,
_ => DecisionType::Other(s),
}
}
}
impl From<&str> for DecisionType {
fn from(s: &str) -> Self {
match s {
"commit" => DecisionType::Commit,
"checkpoint" => DecisionType::Checkpoint,
"abandon" => DecisionType::Abandon,
"retry" => DecisionType::Retry,
"rollback" => DecisionType::Rollback,
_ => DecisionType::Other(s.to_string()),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct Decision {
#[serde(flatten)]
header: Header,
run_id: Uuid,
decision_type: DecisionType,
#[serde(default, skip_serializing_if = "Option::is_none")]
chosen_patchset_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
result_commit_sha: Option<IntegrityHash>,
#[serde(default, skip_serializing_if = "Option::is_none")]
checkpoint_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
rationale: Option<String>,
}
impl Decision {
pub fn new(
created_by: ActorRef,
run_id: Uuid,
decision_type: impl Into<DecisionType>,
) -> Result<Self, String> {
Ok(Self {
header: Header::new(ObjectType::Decision, created_by)?,
run_id,
decision_type: decision_type.into(),
chosen_patchset_id: None,
result_commit_sha: None,
checkpoint_id: None,
rationale: None,
})
}
pub fn header(&self) -> &Header {
&self.header
}
pub fn run_id(&self) -> Uuid {
self.run_id
}
pub fn decision_type(&self) -> &DecisionType {
&self.decision_type
}
pub fn chosen_patchset_id(&self) -> Option<Uuid> {
self.chosen_patchset_id
}
pub fn result_commit_sha(&self) -> Option<&IntegrityHash> {
self.result_commit_sha.as_ref()
}
pub fn checkpoint_id(&self) -> Option<&str> {
self.checkpoint_id.as_deref()
}
pub fn rationale(&self) -> Option<&str> {
self.rationale.as_deref()
}
pub fn set_chosen_patchset_id(&mut self, chosen_patchset_id: Option<Uuid>) {
self.chosen_patchset_id = chosen_patchset_id;
}
pub fn set_result_commit_sha(&mut self, result_commit_sha: Option<IntegrityHash>) {
self.result_commit_sha = result_commit_sha;
}
pub fn set_checkpoint_id(&mut self, checkpoint_id: Option<String>) {
self.checkpoint_id = checkpoint_id;
}
pub fn set_rationale(&mut self, rationale: Option<String>) {
self.rationale = rationale;
}
}
impl fmt::Display for Decision {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Decision: {}", self.header.object_id())
}
}
impl ObjectTrait for Decision {
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::Decision
}
fn get_size(&self) -> usize {
match serde_json::to_vec(self) {
Ok(v) => v.len(),
Err(e) => {
tracing::warn!("failed to compute Decision 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_decision_fields() {
let actor = ActorRef::agent("test-agent").expect("actor");
let run_id = Uuid::from_u128(0x1);
let patchset_id = Uuid::from_u128(0x2);
let expected_hash = IntegrityHash::compute(b"decision-hash");
let mut decision = Decision::new(actor, run_id, "commit").expect("decision");
decision.set_chosen_patchset_id(Some(patchset_id));
decision.set_result_commit_sha(Some(expected_hash));
decision.set_rationale(Some("tests passed".to_string()));
assert_eq!(decision.chosen_patchset_id(), Some(patchset_id));
assert_eq!(decision.result_commit_sha(), Some(&expected_hash));
assert_eq!(decision.rationale(), Some("tests passed"));
assert_eq!(decision.decision_type(), &DecisionType::Commit);
}
}