git_internal/internal/object/
context.rs1use std::fmt::Display;
17
18use serde::{Deserialize, Serialize};
19use uuid::Uuid;
20
21use crate::{
22 errors::GitError,
23 hash::ObjectHash,
24 internal::object::{
25 ObjectTrait,
26 integrity::IntegrityHash,
27 types::{ActorRef, Header, ObjectType},
28 },
29};
30
31#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
33#[serde(rename_all = "snake_case")]
34pub enum SelectionStrategy {
35 Explicit,
37 Heuristic,
39}
40
41#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
43#[serde(rename_all = "snake_case")]
44pub enum ContextItemKind {
45 File,
47}
48
49#[derive(Debug, Clone, Serialize, Deserialize)]
51pub struct ContextItem {
52 pub kind: ContextItemKind,
53 pub path: String,
54 pub content_id: IntegrityHash,
55}
56
57impl ContextItem {
58 pub fn new(
59 kind: ContextItemKind,
60 path: impl Into<String>,
61 content_id: IntegrityHash,
62 ) -> Result<Self, String> {
63 let path = path.into();
64 if path.trim().is_empty() {
65 return Err("path cannot be empty".to_string());
66 }
67 Ok(Self {
68 kind,
69 path,
70 content_id,
71 })
72 }
73}
74
75#[derive(Debug, Clone, Serialize, Deserialize)]
78pub struct ContextSnapshot {
79 #[serde(flatten)]
80 header: Header,
81 base_commit_sha: IntegrityHash,
82 selection_strategy: SelectionStrategy,
83 #[serde(default)]
84 items: Vec<ContextItem>,
85 summary: Option<String>,
86}
87
88impl ContextSnapshot {
89 pub fn new(
90 repo_id: Uuid,
91 created_by: ActorRef,
92 base_commit_sha: impl AsRef<str>,
93 selection_strategy: SelectionStrategy,
94 ) -> Result<Self, String> {
95 let base_commit_sha = base_commit_sha.as_ref().parse()?;
96 Ok(Self {
97 header: Header::new(ObjectType::ContextSnapshot, repo_id, created_by)?,
98 base_commit_sha,
99 selection_strategy,
100 items: Vec::new(),
101 summary: None,
102 })
103 }
104
105 pub fn header(&self) -> &Header {
106 &self.header
107 }
108
109 pub fn base_commit_sha(&self) -> &IntegrityHash {
110 &self.base_commit_sha
111 }
112
113 pub fn selection_strategy(&self) -> &SelectionStrategy {
114 &self.selection_strategy
115 }
116
117 pub fn items(&self) -> &[ContextItem] {
118 &self.items
119 }
120
121 pub fn summary(&self) -> Option<&str> {
122 self.summary.as_deref()
123 }
124
125 pub fn add_item(&mut self, item: ContextItem) {
126 self.items.push(item);
127 }
128
129 pub fn set_summary(&mut self, summary: Option<String>) {
130 self.summary = summary;
131 }
132}
133
134impl Display for ContextSnapshot {
135 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
136 write!(f, "ContextSnapshot: {}", self.header.object_id())
137 }
138}
139
140impl ObjectTrait for ContextSnapshot {
141 fn from_bytes(data: &[u8], _hash: ObjectHash) -> Result<Self, GitError>
142 where
143 Self: Sized,
144 {
145 serde_json::from_slice(data).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
146 }
147
148 fn get_type(&self) -> ObjectType {
149 ObjectType::ContextSnapshot
150 }
151
152 fn get_size(&self) -> usize {
153 serde_json::to_vec(self).map(|v| v.len()).unwrap_or(0)
154 }
155
156 fn to_data(&self) -> Result<Vec<u8>, GitError> {
157 serde_json::to_vec(self).map_err(|e| GitError::InvalidObjectInfo(e.to_string()))
158 }
159}
160
161#[cfg(test)]
162mod tests {
163 use super::*;
164
165 #[test]
166 fn test_context_snapshot_accessors_and_mutators() {
167 let repo_id = Uuid::from_u128(0x0123456789abcdef0123456789abcdef);
168 let actor = ActorRef::agent("coder").expect("actor");
169 let mut snapshot = ContextSnapshot::new(
170 repo_id,
171 actor,
172 "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
173 SelectionStrategy::Heuristic,
174 )
175 .expect("snapshot");
176
177 assert_eq!(snapshot.selection_strategy(), &SelectionStrategy::Heuristic);
178 assert!(snapshot.items().is_empty());
179 assert!(snapshot.summary().is_none());
180
181 let item = ContextItem::new(
182 ContextItemKind::File,
183 "src/main.rs",
184 "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"
185 .parse()
186 .expect("hash"),
187 )
188 .expect("item");
189 snapshot.add_item(item);
190 snapshot.set_summary(Some("selected by relevance".to_string()));
191
192 assert_eq!(snapshot.items().len(), 1);
193 assert_eq!(snapshot.summary(), Some("selected by relevance"));
194 assert_eq!(snapshot.base_commit_sha().to_hex().len(), 64);
195 }
196}