Skip to main content

git_semantic/index/
mod.rs

1mod builder;
2mod error;
3mod storage;
4
5pub use builder::IndexBuilder;
6pub use error::IndexError;
7pub use storage::IndexStorage;
8
9use chrono::{DateTime, Utc};
10use serde::{Deserialize, Serialize};
11
12use crate::git::CommitInfo;
13
14#[derive(Debug, Clone, Serialize, Deserialize)]
15pub struct IndexEntry {
16    pub commit: CommitInfo,
17    pub embedding: Vec<f32>, // Serializable version of ndarray
18}
19
20#[derive(Debug, Clone, Serialize, Deserialize)]
21pub struct IndexMetadata {
22    pub created_at: DateTime<Utc>,
23    pub updated_at: DateTime<Utc>,
24    pub total_commits: usize,
25    pub include_diffs: bool,
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
29pub struct SemanticIndex {
30    pub entries: Vec<IndexEntry>,
31    pub model_version: String,
32    pub last_commit: String,
33    pub metadata: IndexMetadata,
34}
35
36impl SemanticIndex {
37    pub fn new(model_version: String, last_commit: String, include_diffs: bool) -> Self {
38        let now = Utc::now();
39        Self {
40            entries: Vec::new(),
41            model_version,
42            last_commit,
43            metadata: IndexMetadata {
44                created_at: now,
45                updated_at: now,
46                total_commits: 0,
47                include_diffs,
48            },
49        }
50    }
51}
52
53#[cfg(test)]
54mod tests {
55    use super::*;
56    use crate::git::CommitInfo;
57
58    #[test]
59    fn test_semantic_index_new() {
60        let index =
61            SemanticIndex::new("bge-small-en-v1.5".to_string(), "abc1234".to_string(), true);
62        assert_eq!(index.model_version, "bge-small-en-v1.5");
63        assert_eq!(index.last_commit, "abc1234");
64        assert!(index.entries.is_empty());
65        assert_eq!(index.metadata.total_commits, 0);
66        assert!(index.metadata.include_diffs);
67    }
68
69    #[test]
70    fn test_semantic_index_quick_mode() {
71        let index = SemanticIndex::new(
72            "bge-small-en-v1.5".to_string(),
73            "abc1234".to_string(),
74            false,
75        );
76        assert!(!index.metadata.include_diffs);
77    }
78
79    #[test]
80    fn test_semantic_index_timestamps_are_set() {
81        let before = Utc::now();
82        let index = SemanticIndex::new("model".to_string(), "hash".to_string(), true);
83        let after = Utc::now();
84        assert!(index.metadata.created_at >= before && index.metadata.created_at <= after);
85        assert!(index.metadata.updated_at >= before && index.metadata.updated_at <= after);
86    }
87
88    #[test]
89    fn test_index_entry_serialization_roundtrip() {
90        let entry = IndexEntry {
91            commit: CommitInfo {
92                hash: "abc1234".to_string(),
93                author: "Alice".to_string(),
94                date: chrono::DateTime::parse_from_rfc3339("2024-06-15T12:00:00Z")
95                    .unwrap()
96                    .with_timezone(&Utc),
97                message: "test commit".to_string(),
98                diff_summary: "+added line".to_string(),
99            },
100            embedding: vec![0.1, 0.2, 0.3],
101        };
102
103        let mut index = SemanticIndex::new("model".to_string(), "abc1234".to_string(), true);
104        index.entries.push(entry);
105        index.metadata.total_commits = 1;
106
107        let serialized = bincode::serialize(&index).unwrap();
108        let deserialized: SemanticIndex = bincode::deserialize(&serialized).unwrap();
109
110        assert_eq!(deserialized.entries.len(), 1);
111        assert_eq!(deserialized.entries[0].commit.hash, "abc1234");
112        assert_eq!(deserialized.entries[0].commit.author, "Alice");
113        assert_eq!(deserialized.entries[0].embedding, vec![0.1, 0.2, 0.3]);
114        assert_eq!(deserialized.model_version, "model");
115        assert_eq!(deserialized.metadata.total_commits, 1);
116        assert!(deserialized.metadata.include_diffs);
117    }
118}