git_internal/internal/object/
intent_event.rs1use std::fmt;
30
31use serde::{Deserialize, Serialize};
32use uuid::Uuid;
33
34use crate::{
35 errors::GitError,
36 hash::ObjectHash,
37 internal::object::{
38 ObjectTrait,
39 integrity::IntegrityHash,
40 types::{ActorRef, Header, ObjectType},
41 },
42};
43
44#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
45#[serde(rename_all = "snake_case")]
46pub enum IntentEventKind {
47 Analyzed,
49 Completed,
51 Cancelled,
53 #[serde(untagged)]
56 Other(String),
57}
58
59#[derive(Debug, Clone, Serialize, Deserialize)]
61#[serde(deny_unknown_fields)]
62pub struct IntentEvent {
63 #[serde(flatten)]
66 header: Header,
67 intent_id: Uuid,
69 kind: IntentEventKind,
71 #[serde(default, skip_serializing_if = "Option::is_none")]
73 reason: Option<String>,
74 #[serde(default, skip_serializing_if = "Option::is_none")]
76 result_commit: Option<IntegrityHash>,
77 #[serde(default, skip_serializing_if = "Option::is_none")]
84 next_intent_id: Option<Uuid>,
85}
86
87impl IntentEvent {
88 pub fn new(
90 created_by: ActorRef,
91 intent_id: Uuid,
92 kind: IntentEventKind,
93 ) -> Result<Self, String> {
94 Ok(Self {
95 header: Header::new(ObjectType::IntentEvent, created_by)?,
96 intent_id,
97 kind,
98 reason: None,
99 result_commit: None,
100 next_intent_id: None,
101 })
102 }
103
104 pub fn header(&self) -> &Header {
106 &self.header
107 }
108
109 pub fn intent_id(&self) -> Uuid {
111 self.intent_id
112 }
113
114 pub fn kind(&self) -> &IntentEventKind {
116 &self.kind
117 }
118
119 pub fn reason(&self) -> Option<&str> {
121 self.reason.as_deref()
122 }
123
124 pub fn result_commit(&self) -> Option<&IntegrityHash> {
126 self.result_commit.as_ref()
127 }
128
129 pub fn next_intent_id(&self) -> Option<Uuid> {
131 self.next_intent_id
132 }
133
134 pub fn set_reason(&mut self, reason: Option<String>) {
136 self.reason = reason;
137 }
138
139 pub fn set_result_commit(&mut self, result_commit: Option<IntegrityHash>) {
141 self.result_commit = result_commit;
142 }
143
144 pub fn set_next_intent_id(&mut self, next_intent_id: Option<Uuid>) {
146 self.next_intent_id = next_intent_id;
147 }
148}
149
150impl fmt::Display for IntentEvent {
151 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
152 write!(f, "IntentEvent: {}", self.header.object_id())
153 }
154}
155
156impl ObjectTrait for IntentEvent {
157 fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result<Self, GitError>
158 where
159 Self: Sized,
160 {
161 serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
162 }
163
164 fn get_type(&self) -> ObjectType {
165 ObjectType::IntentEvent
166 }
167
168 fn get_size(&self) -> usize {
169 match serde_json::to_vec(self) {
170 Ok(v) => v.len(),
171 Err(e) => {
172 tracing::warn!("failed to compute IntentEvent size: {}", e);
173 0
174 }
175 }
176 }
177
178 fn to_data(&self) -> Result<Vec<u8>, GitError> {
179 serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
180 }
181}
182
183#[cfg(test)]
184mod tests {
185 use super::*;
186
187 #[test]
193 fn test_intent_event_fields() {
194 let actor = ActorRef::agent("planner").expect("actor");
198 let mut event = IntentEvent::new(actor, Uuid::from_u128(0x1), IntentEventKind::Completed)
199 .expect("event");
200 let hash = IntegrityHash::compute(b"commit");
201 let next_intent_id = Uuid::from_u128(0x2);
202 event.set_reason(Some("done".to_string()));
203 event.set_result_commit(Some(hash));
204 event.set_next_intent_id(Some(next_intent_id));
205
206 assert_eq!(event.kind(), &IntentEventKind::Completed);
207 assert_eq!(event.reason(), Some("done"));
208 assert_eq!(event.result_commit(), Some(&hash));
209 assert_eq!(event.next_intent_id(), Some(next_intent_id));
210 }
211
212 #[test]
213 fn test_intent_event_kind_accepts_unknown_string() {
214 let kind: IntentEventKind =
218 serde_json::from_str("\"waiting_for_human_review\"").expect("kind");
219
220 assert_eq!(
221 kind,
222 IntentEventKind::Other("waiting_for_human_review".to_string())
223 );
224 }
225}