git_internal/internal/object/
decision.rs1use std::fmt;
15
16use serde::{Deserialize, Serialize};
17use uuid::Uuid;
18
19use crate::{
20 errors::GitError,
21 hash::ObjectHash,
22 internal::object::{
23 ObjectTrait,
24 integrity::IntegrityHash,
25 types::{ActorRef, Header, ObjectType},
26 },
27};
28
29#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
31#[serde(rename_all = "snake_case")]
32pub enum DecisionType {
33 Commit,
35 Checkpoint,
37 Abandon,
39 Retry,
41 Rollback,
43 #[serde(untagged)]
44 Other(String),
45}
46
47impl fmt::Display for DecisionType {
48 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
49 match self {
50 DecisionType::Commit => write!(f, "commit"),
51 DecisionType::Checkpoint => write!(f, "checkpoint"),
52 DecisionType::Abandon => write!(f, "abandon"),
53 DecisionType::Retry => write!(f, "retry"),
54 DecisionType::Rollback => write!(f, "rollback"),
55 DecisionType::Other(s) => write!(f, "{}", s),
56 }
57 }
58}
59
60impl From<String> for DecisionType {
61 fn from(s: String) -> Self {
62 match s.as_str() {
63 "commit" => DecisionType::Commit,
64 "checkpoint" => DecisionType::Checkpoint,
65 "abandon" => DecisionType::Abandon,
66 "retry" => DecisionType::Retry,
67 "rollback" => DecisionType::Rollback,
68 _ => DecisionType::Other(s),
69 }
70 }
71}
72
73impl From<&str> for DecisionType {
74 fn from(s: &str) -> Self {
75 match s {
76 "commit" => DecisionType::Commit,
77 "checkpoint" => DecisionType::Checkpoint,
78 "abandon" => DecisionType::Abandon,
79 "retry" => DecisionType::Retry,
80 "rollback" => DecisionType::Rollback,
81 _ => DecisionType::Other(s.to_string()),
82 }
83 }
84}
85
86#[derive(Debug, Clone, Serialize, Deserialize)]
89pub struct Decision {
90 #[serde(flatten)]
91 header: Header,
92 run_id: Uuid,
93 decision_type: DecisionType,
94 chosen_patchset_id: Option<Uuid>,
95 result_commit_sha: Option<IntegrityHash>,
96 checkpoint_id: Option<String>,
97 rationale: Option<String>,
98}
99
100impl Decision {
101 pub fn new(
103 repo_id: Uuid,
104 created_by: ActorRef,
105 run_id: Uuid,
106 decision_type: impl Into<DecisionType>,
107 ) -> Result<Self, String> {
108 Ok(Self {
109 header: Header::new(ObjectType::Decision, repo_id, created_by)?,
110 run_id,
111 decision_type: decision_type.into(),
112 chosen_patchset_id: None,
113 result_commit_sha: None,
114 checkpoint_id: None,
115 rationale: None,
116 })
117 }
118
119 pub fn header(&self) -> &Header {
120 &self.header
121 }
122
123 pub fn run_id(&self) -> Uuid {
124 self.run_id
125 }
126
127 pub fn decision_type(&self) -> &DecisionType {
128 &self.decision_type
129 }
130
131 pub fn chosen_patchset_id(&self) -> Option<Uuid> {
132 self.chosen_patchset_id
133 }
134
135 pub fn result_commit_sha(&self) -> Option<&IntegrityHash> {
136 self.result_commit_sha.as_ref()
137 }
138
139 pub fn checkpoint_id(&self) -> Option<&str> {
140 self.checkpoint_id.as_deref()
141 }
142
143 pub fn rationale(&self) -> Option<&str> {
144 self.rationale.as_deref()
145 }
146
147 pub fn set_chosen_patchset_id(&mut self, chosen_patchset_id: Option<Uuid>) {
148 self.chosen_patchset_id = chosen_patchset_id;
149 }
150
151 pub fn set_result_commit_sha(&mut self, result_commit_sha: Option<IntegrityHash>) {
152 self.result_commit_sha = result_commit_sha;
153 }
154
155 pub fn set_checkpoint_id(&mut self, checkpoint_id: Option<String>) {
156 self.checkpoint_id = checkpoint_id;
157 }
158
159 pub fn set_rationale(&mut self, rationale: Option<String>) {
160 self.rationale = rationale;
161 }
162}
163
164impl fmt::Display for Decision {
165 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
166 write!(f, "Decision: {}", self.header.object_id())
167 }
168}
169
170impl ObjectTrait for Decision {
171 fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result<Self, GitError>
172 where
173 Self: Sized,
174 {
175 serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
176 }
177
178 fn get_type(&self) -> ObjectType {
179 ObjectType::Decision
180 }
181
182 fn get_size(&self) -> usize {
183 serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0)
184 }
185
186 fn to_data(&self) -> Result<Vec<u8>, GitError> {
187 serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
188 }
189}
190
191#[cfg(test)]
192mod tests {
193 use super::*;
194
195 #[test]
196 fn test_decision_fields() {
197 let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
198 let actor = ActorRef::agent("test-agent").expect("actor");
199 let run_id = Uuid::from_u128(0x1);
200 let patchset_id = Uuid::from_u128(0x2);
201 let expected_hash = IntegrityHash::compute(b"decision-hash");
202
203 let mut decision = Decision::new(repo_id, actor, run_id, "commit").expect("decision");
204 decision.set_chosen_patchset_id(Some(patchset_id));
205 decision.set_result_commit_sha(Some(expected_hash));
206 decision.set_rationale(Some("tests passed".to_string()));
207
208 assert_eq!(decision.chosen_patchset_id(), Some(patchset_id));
209 assert_eq!(decision.result_commit_sha(), Some(&expected_hash));
210 assert_eq!(decision.rationale(), Some("tests passed"));
211 assert_eq!(decision.decision_type(), &DecisionType::Commit);
212 }
213}