void_core/index/
rebuild.rs1use std::collections::HashMap;
4use std::path::Path;
5
6use camino::Utf8PathBuf;
7use ignore::WalkBuilder;
8
9use crate::crypto::KeyVault;
10use crate::metadata::manifest_tree::TreeManifest;
11use crate::refs;
12
13use crate::store::{FsStore, ObjectStoreExt};
14use void_crypto::EncryptedCommit;
15use crate::support::configure_walker;
16use crate::{Result, VoidError};
17
18use super::entry::index_entry_from_file;
19use super::io::write_index;
20use super::types::IndexEntry;
21
22#[derive(Debug, Clone)]
24pub struct RebuildResult {
25 pub entries_rebuilt: usize,
27 pub from_head: usize,
29 pub from_working_tree: usize,
31}
32
33
34
35pub fn rebuild_index(void_dir: &Path, workspace: &Path, vault: &KeyVault) -> Result<RebuildResult> {
47 let void_dir_utf8 = Utf8PathBuf::try_from(void_dir.to_path_buf())
48 .map_err(|e| VoidError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?;
49 let workspace_utf8 = Utf8PathBuf::try_from(workspace.to_path_buf())
50 .map_err(|e| VoidError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?;
51
52 let head_commit_cid = refs::resolve_head(&void_dir_utf8)?;
54 let mut head_entries: HashMap<String, IndexEntry> = HashMap::new();
55 let mut commit_cid_typed: Option<void_crypto::CommitCid> = None;
56
57 if let Some(ref cid_typed) = head_commit_cid {
58 commit_cid_typed = Some(cid_typed.clone());
59
60 let objects_dir = Utf8PathBuf::try_from(void_dir.join("objects"))
61 .map_err(|e| VoidError::Io(std::io::Error::new(std::io::ErrorKind::InvalidData, e)))?;
62 let store = FsStore::new(objects_dir)?;
63
64 let commit_cid = crate::cid::from_bytes(cid_typed.as_bytes())?;
65 let commit_encrypted: EncryptedCommit = store.get_blob(&commit_cid)?;
66 let (commit_bytes, reader) = crate::crypto::CommitReader::open_with_vault(vault, &commit_encrypted)?;
67 let commit = commit_bytes.parse()?;
68
69 let manifest = TreeManifest::from_commit(&store, &commit, &reader)?
70 .ok_or_else(|| VoidError::IntegrityError {
71 expected: "manifest_cid present on commit".into(),
72 actual: "None".into(),
73 })?;
74
75 for me in manifest.iter() {
76 let me = me?;
77 head_entries.insert(
78 me.path.clone(),
79 IndexEntry {
80 path: me.path.clone(),
81 content_hash: me.content_hash,
82 mtime_secs: 0,
83 mtime_nanos: 0,
84 size: me.length,
85 },
86 );
87 }
88 }
89
90 let void_dir_name = void_dir
92 .file_name()
93 .and_then(|s| s.to_str())
94 .unwrap_or(".void")
95 .to_string();
96
97 let mut working_tree_files: HashMap<String, ()> = HashMap::new();
98 let mut builder = WalkBuilder::new(workspace);
99 let walker = configure_walker(&mut builder)
100 .filter_entry({
101 let void_dir_name = void_dir_name.clone();
102 move |entry| {
103 let name = entry.file_name().to_string_lossy();
104 if name == void_dir_name
105 || name == ".git"
106 || name == "node_modules"
107 || name == ".DS_Store"
108 {
109 return false;
110 }
111 true
112 }
113 })
114 .build();
115
116 for entry in walker.flatten() {
117 if !entry.file_type().map(|t| t.is_file()).unwrap_or(false) {
118 continue;
119 }
120 let path = entry.path().to_path_buf();
121 let rel = match path.strip_prefix(workspace) {
122 Ok(r) => r.to_string_lossy().replace('\\', "/"),
123 Err(_) => continue,
124 };
125 working_tree_files.insert(rel, ());
126 }
127
128 let mut entries = Vec::new();
130 let mut from_head = 0usize;
131 let mut from_working_tree = 0usize;
132
133 for (rel_path, _) in &working_tree_files {
134 if let Some(head_entry) = head_entries.get(rel_path) {
135 let current_entry = match index_entry_from_file(&workspace_utf8, rel_path) {
137 Ok(entry) => entry,
138 Err(_) => continue, };
140
141 if current_entry.content_hash == head_entry.content_hash {
142 entries.push(IndexEntry {
144 path: rel_path.clone(),
145 content_hash: head_entry.content_hash,
146 mtime_secs: current_entry.mtime_secs,
147 mtime_nanos: current_entry.mtime_nanos,
148 size: current_entry.size,
149 });
150 from_head += 1;
151 } else {
152 entries.push(current_entry);
154 from_working_tree += 1;
155 }
156 } else {
157 match index_entry_from_file(&workspace_utf8, rel_path) {
159 Ok(entry) => {
160 entries.push(entry);
161 from_working_tree += 1;
162 }
163 Err(_) => continue, }
165 }
166 }
167
168 let entries_rebuilt = entries.len();
170 write_index(void_dir, vault.index_key()?, commit_cid_typed, entries)?;
171
172 Ok(RebuildResult {
173 entries_rebuilt,
174 from_head,
175 from_working_tree,
176 })
177}