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}
172
173impl Commit {
174 pub fn new(
175 tree: ObjectHash,
176 parents: Vec<ObjectHash>,
177 author: String,
178 message: String,
179 ) -> Self {
180 let timestamp = chrono::Utc::now().timestamp();
181 Commit {
182 tree,
183 parents,
184 author: author.clone(),
185 committer: author,
186 timestamp,
187 message,
188 pq_signature: None, metadata: None,
190 }
191 }
192
193 pub fn sign(&mut self, keypair: &crate::crypto::signatures::PQKeyPair) {
195 let mut commit_data = self.clone();
197 commit_data.pq_signature = None;
198 let data = serde_json::to_vec(&commit_data).expect("Failed to serialize commit");
199
200 self.pq_signature = Some(keypair.sign(&data));
202 }
203
204 pub fn verify_signature(&self) -> Result<(), String> {
206 match &self.pq_signature {
207 Some(sig) => {
208 let mut commit_data = self.clone();
210 commit_data.pq_signature = None;
211 let data = serde_json::to_vec(&commit_data)
212 .map_err(|e| format!("Failed to serialize: {}", e))?;
213
214 sig.verify(&data)
215 }
216 None => Err("Commit is not signed".to_string()),
217 }
218 }
219}
220
221#[derive(Debug, Clone, Serialize, Deserialize)]
223pub struct Tag {
224 pub target: ObjectHash,
226 pub target_type: String,
228 pub tag_name: String,
230 pub tagger: String,
232 pub timestamp: i64,
234 pub message: String,
236 #[serde(skip_serializing_if = "Option::is_none")]
238 pub pq_signature: Option<PQSignature>,
239 #[serde(skip_serializing_if = "Option::is_none")]
241 pub metadata: Option<serde_json::Value>,
242}
243
244impl Tag {
245 pub fn new(
246 target: ObjectHash,
247 target_type: String,
248 tag_name: String,
249 tagger: String,
250 message: String,
251 ) -> Self {
252 Tag {
253 target,
254 target_type,
255 tag_name,
256 tagger,
257 timestamp: chrono::Utc::now().timestamp(),
258 message,
259 pq_signature: None,
260 metadata: None,
261 }
262 }
263
264 pub fn sign(&mut self, keypair: &crate::crypto::signatures::PQKeyPair) {
266 let mut tag_data = self.clone();
267 tag_data.pq_signature = None;
268 let data = serde_json::to_vec(&tag_data).expect("Failed to serialize tag");
269 self.pq_signature = Some(keypair.sign(&data));
270 }
271
272 pub fn verify_signature(&self) -> Result<(), String> {
274 match &self.pq_signature {
275 Some(sig) => {
276 let mut tag_data = self.clone();
277 tag_data.pq_signature = None;
278 let data = serde_json::to_vec(&tag_data)
279 .map_err(|e| format!("Failed to serialize: {}", e))?;
280 sig.verify(&data)
281 }
282 None => Err("Tag is not signed".to_string()),
283 }
284 }
285}
286
287#[cfg(test)]
288mod tests {
289 use super::*;
290
291 #[test]
292 fn test_object_hash() {
293 let data = b"test content";
294 let hash = ObjectHash::from_bytes(data);
295 assert_eq!(hash.as_str().len(), 192); }
299
300 #[test]
301 fn test_blob() {
302 let content = b"Hello, world!".to_vec();
303 let blob = Blob::new(content.clone());
304 assert_eq!(blob.content, content);
305 }
306
307 #[test]
308 fn test_tree() {
309 let mut tree = Tree::new();
310 let hash = ObjectHash::from_hex("abc123".to_string());
311 tree.add_entry(
312 "100644".to_string(),
313 "file.txt".to_string(),
314 hash,
315 "blob".to_string(),
316 );
317 assert_eq!(tree.entries.len(), 1);
318 }
319}