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 materialized: true,
86 },
87 );
88 }
89 }
90
91 let void_dir_name = void_dir
93 .file_name()
94 .and_then(|s| s.to_str())
95 .unwrap_or(".void")
96 .to_string();
97
98 let mut working_tree_files: HashMap<String, ()> = HashMap::new();
99 let mut builder = WalkBuilder::new(workspace);
100 let walker = configure_walker(&mut builder)
101 .filter_entry({
102 let void_dir_name = void_dir_name.clone();
103 move |entry| {
104 let name = entry.file_name().to_string_lossy();
105 if name == void_dir_name
106 || name == ".git"
107 || name == "node_modules"
108 || name == ".DS_Store"
109 {
110 return false;
111 }
112 true
113 }
114 })
115 .build();
116
117 for entry in walker.flatten() {
118 if !entry.file_type().map(|t| t.is_file()).unwrap_or(false) {
119 continue;
120 }
121 let path = entry.path().to_path_buf();
122 let rel = match path.strip_prefix(workspace) {
123 Ok(r) => r.to_string_lossy().replace('\\', "/"),
124 Err(_) => continue,
125 };
126 working_tree_files.insert(rel, ());
127 }
128
129 let mut entries = Vec::new();
131 let mut from_head = 0usize;
132 let mut from_working_tree = 0usize;
133
134 for (rel_path, _) in &working_tree_files {
135 if let Some(head_entry) = head_entries.get(rel_path) {
136 let current_entry = match index_entry_from_file(&workspace_utf8, rel_path) {
138 Ok(entry) => entry,
139 Err(_) => continue, };
141
142 if current_entry.content_hash == head_entry.content_hash {
143 entries.push(IndexEntry {
145 path: rel_path.clone(),
146 content_hash: head_entry.content_hash,
147 mtime_secs: current_entry.mtime_secs,
148 mtime_nanos: current_entry.mtime_nanos,
149 size: current_entry.size,
150 materialized: true,
151 });
152 from_head += 1;
153 } else {
154 entries.push(current_entry);
156 from_working_tree += 1;
157 }
158 } else {
159 match index_entry_from_file(&workspace_utf8, rel_path) {
161 Ok(entry) => {
162 entries.push(entry);
163 from_working_tree += 1;
164 }
165 Err(_) => continue, }
167 }
168 }
169
170 let entries_rebuilt = entries.len();
172 write_index(void_dir, vault.index_key()?, commit_cid_typed, entries)?;
173
174 Ok(RebuildResult {
175 entries_rebuilt,
176 from_head,
177 from_working_tree,
178 })
179}