Skip to main content

git_semantic/index/
builder.rs

1use chrono::Utc;
2use tracing::debug;
3
4use crate::embedding::ModelManager;
5use crate::git::CommitInfo;
6
7use super::{IndexEntry, IndexError, SemanticIndex};
8
9pub struct IndexBuilder {
10    entries: Vec<IndexEntry>,
11    model_manager: ModelManager,
12    model_version: String,
13    last_commit: Option<String>,
14    include_diffs: bool,
15    created_at: Option<chrono::DateTime<Utc>>,
16}
17
18impl IndexBuilder {
19    pub fn new(mut model_manager: ModelManager, include_diffs: bool) -> Result<Self, IndexError> {
20        model_manager.init()?;
21        let model_version = model_manager.model_version();
22
23        Ok(Self {
24            entries: Vec::new(),
25            model_manager,
26            model_version,
27            last_commit: None,
28            include_diffs,
29            created_at: None,
30        })
31    }
32
33    pub fn from_existing(
34        index: SemanticIndex,
35        mut model_manager: ModelManager,
36    ) -> Result<Self, IndexError> {
37        model_manager.init()?;
38        let model_version = model_manager.model_version();
39        let include_diffs = index.metadata.include_diffs;
40        let created_at = Some(index.metadata.created_at);
41
42        Ok(Self {
43            entries: index.entries,
44            model_manager,
45            model_version,
46            last_commit: Some(index.last_commit),
47            include_diffs,
48            created_at,
49        })
50    }
51
52    /// Set the newest indexed commit hash (should be HEAD or newest new commit).
53    pub fn set_last_commit(&mut self, hash: String) {
54        self.last_commit = Some(hash);
55    }
56
57    pub fn add_commit(&mut self, commit: CommitInfo) -> Result<(), IndexError> {
58        if self.entries.iter().any(|e| e.commit.hash == commit.hash) {
59            debug!("Commit {} already indexed, skipping", &commit.hash[..7]);
60            return Ok(());
61        }
62
63        debug!("Adding commit: {}", &commit.hash[..7]);
64
65        let text = commit.to_text(self.include_diffs);
66        let embedding_array = self.model_manager.encode_text(&text)?;
67        let embedding = embedding_array.to_vec();
68
69        let entry = IndexEntry { commit, embedding };
70        self.entries.push(entry);
71
72        Ok(())
73    }
74
75    pub fn build(self) -> SemanticIndex {
76        let last_commit = self.last_commit.unwrap_or_else(|| "unknown".to_string());
77
78        let mut index = SemanticIndex::new(self.model_version, last_commit, self.include_diffs);
79        index.entries = self.entries;
80        index.metadata.total_commits = index.entries.len();
81        index.metadata.updated_at = Utc::now();
82        if let Some(created_at) = self.created_at {
83            index.metadata.created_at = created_at;
84        }
85
86        index
87    }
88}