1use crate::crypto::signatures::PQSignature;
2use blake3;
3use serde::{Deserialize, Serialize};
4use sha3::{Digest, Sha3_512};
5use std::fmt;
6
7#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
11pub struct ObjectHash(pub String);
12
13impl ObjectHash {
14 pub fn from_bytes(data: &[u8]) -> Self {
17 let mut sha3_hasher = Sha3_512::new();
19 sha3_hasher.update(data);
20 let sha3_result = sha3_hasher.finalize();
21
22 let blake3_result = blake3::hash(data);
24
25 let combined = format!(
27 "{}{}",
28 hex::encode(sha3_result),
29 hex::encode(blake3_result.as_bytes())
30 );
31 ObjectHash(combined)
32 }
33
34 pub fn as_str(&self) -> &str {
36 &self.0
37 }
38
39 pub fn from_hex(hex: String) -> Self {
41 ObjectHash(hex)
42 }
43
44 pub fn short(&self) -> String {
46 self.0.chars().take(16).collect()
47 }
48
49 pub fn len(&self) -> usize {
51 self.0.len()
52 }
53
54 pub fn is_empty(&self) -> bool {
55 self.0.is_empty()
56 }
57}
58
59impl fmt::Display for ObjectHash {
60 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61 write!(f, "{}", self.0)
62 }
63}
64
65#[derive(Debug, Clone, Serialize, Deserialize)]
67pub enum Object {
68 Blob(Blob),
70 Tree(Tree),
72 Commit(Commit),
74 Tag(Tag),
76}
77
78impl Object {
79 pub fn type_name(&self) -> &str {
81 match self {
82 Object::Blob(_) => "blob",
83 Object::Tree(_) => "tree",
84 Object::Commit(_) => "commit",
85 Object::Tag(_) => "tag",
86 }
87 }
88
89 pub fn to_bytes(&self) -> Vec<u8> {
91 serde_json::to_vec(self).expect("Failed to serialize object")
92 }
93
94 pub fn from_bytes(bytes: &[u8]) -> Result<Self, String> {
96 serde_json::from_slice(bytes).map_err(|e| format!("Failed to deserialize object: {}", e))
97 }
98
99 pub fn hash(&self) -> ObjectHash {
101 ObjectHash::from_bytes(&self.to_bytes())
102 }
103}
104
105#[derive(Debug, Clone, Serialize, Deserialize)]
107pub struct Blob {
108 pub content: Vec<u8>,
109}
110
111impl Blob {
112 pub fn new(content: Vec<u8>) -> Self {
113 Blob { content }
114 }
115}
116
117#[derive(Debug, Clone, Serialize, Deserialize)]
119pub struct TreeEntry {
120 pub mode: String,
121 pub name: String,
122 pub hash: ObjectHash,
123 pub object_type: String,
124}
125
126#[derive(Debug, Clone, Serialize, Deserialize)]
128pub struct Tree {
129 pub entries: Vec<TreeEntry>,
130}
131
132impl Default for Tree {
133 fn default() -> Self {
134 Self::new()
135 }
136}
137
138impl Tree {
139 pub fn new() -> Self {
140 Tree {
141 entries: Vec::new(),
142 }
143 }
144
145 pub fn add_entry(&mut self, mode: String, name: String, hash: ObjectHash, object_type: String) {
146 self.entries.push(TreeEntry {
147 mode,
148 name,
149 hash,
150 object_type,
151 });
152 }
153}
154
155#[derive(Debug, Clone, Serialize, Deserialize)]
157pub struct Commit {
158 pub tree: ObjectHash,
159 pub parents: Vec<ObjectHash>,
160 pub author: String,
161 pub committer: String,
162 pub timestamp: i64,
163 pub message: String,
164 #[serde(skip_serializing_if = "Option::is_none")]
167 pub pq_signature: Option<PQSignature>,
168 #[serde(skip_serializing_if = "Option::is_none")]
170 pub metadata: Option<serde_json::Value>,
171 #[serde(default, skip_serializing_if = "Option::is_none")]
176 pub timezone: Option<String>,
177}
178
179impl Commit {
180 pub fn new(
181 tree: ObjectHash,
182 parents: Vec<ObjectHash>,
183 author: String,
184 message: String,
185 ) -> Self {
186 let timestamp = chrono::Utc::now().timestamp();
187 Commit {
188 tree,
189 parents,
190 author: author.clone(),
191 committer: author,
192 timestamp,
193 message,
194 pq_signature: None, metadata: None,
196 timezone: None, }
198 }
199
200 pub fn sign(&mut self, keypair: &crate::crypto::signatures::PQKeyPair) {
202 let mut commit_data = self.clone();
204 commit_data.pq_signature = None;
205 let data = serde_json::to_vec(&commit_data).expect("Failed to serialize commit");
206
207 self.pq_signature = Some(keypair.sign(&data));
209 }
210
211 pub fn verify_signature(&self) -> Result<(), String> {
213 match &self.pq_signature {
214 Some(sig) => {
215 let mut commit_data = self.clone();
217 commit_data.pq_signature = None;
218 let data = serde_json::to_vec(&commit_data)
219 .map_err(|e| format!("Failed to serialize: {}", e))?;
220
221 sig.verify(&data)
222 }
223 None => Err("Commit is not signed".to_string()),
224 }
225 }
226}
227
228#[derive(Debug, Clone, Serialize, Deserialize)]
230pub struct Tag {
231 pub target: ObjectHash,
233 pub target_type: String,
235 pub tag_name: String,
237 pub tagger: String,
239 pub timestamp: i64,
241 pub message: String,
243 #[serde(skip_serializing_if = "Option::is_none")]
245 pub pq_signature: Option<PQSignature>,
246 #[serde(skip_serializing_if = "Option::is_none")]
248 pub metadata: Option<serde_json::Value>,
249 #[serde(default, skip_serializing_if = "Option::is_none")]
252 pub timezone: Option<String>,
253}
254
255impl Tag {
256 pub fn new(
257 target: ObjectHash,
258 target_type: String,
259 tag_name: String,
260 tagger: String,
261 message: String,
262 ) -> Self {
263 Tag {
264 target,
265 target_type,
266 tag_name,
267 tagger,
268 timestamp: chrono::Utc::now().timestamp(),
269 message,
270 pq_signature: None,
271 metadata: None,
272 timezone: None, }
274 }
275
276 pub fn sign(&mut self, keypair: &crate::crypto::signatures::PQKeyPair) {
278 let mut tag_data = self.clone();
279 tag_data.pq_signature = None;
280 let data = serde_json::to_vec(&tag_data).expect("Failed to serialize tag");
281 self.pq_signature = Some(keypair.sign(&data));
282 }
283
284 pub fn verify_signature(&self) -> Result<(), String> {
286 match &self.pq_signature {
287 Some(sig) => {
288 let mut tag_data = self.clone();
289 tag_data.pq_signature = None;
290 let data = serde_json::to_vec(&tag_data)
291 .map_err(|e| format!("Failed to serialize: {}", e))?;
292 sig.verify(&data)
293 }
294 None => Err("Tag is not signed".to_string()),
295 }
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_object_hash() {
305 let data = b"test content";
306 let hash = ObjectHash::from_bytes(data);
307 assert_eq!(hash.as_str().len(), 192); }
311
312 #[test]
313 fn test_blob() {
314 let content = b"Hello, world!".to_vec();
315 let blob = Blob::new(content.clone());
316 assert_eq!(blob.content, content);
317 }
318
319 #[test]
320 fn test_tree() {
321 let mut tree = Tree::new();
322 let hash = ObjectHash::from_hex("abc123".to_string());
323 tree.add_entry(
324 "100644".to_string(),
325 "file.txt".to_string(),
326 hash,
327 "blob".to_string(),
328 );
329 assert_eq!(tree.entries.len(), 1);
330 }
331}