1use std::path::Path;
5
6use objects::{
7 object::{ContentHash, FileChangeSet, diff_trees},
8 store::ObjectStore,
9 worktree::WorktreeStatus,
10};
11
12use super::{diff_helpers::TreeBlobContentLoader, diff_options::SemanticDiffOptions};
13use crate::{
14 cache::SemanticParseCache,
15 diff::{
16 diff_engine::SemanticEngine,
17 diff_types::{SemanticCheckOnlyResult, SemanticDiffResult, SemanticSummaryResult},
18 },
19};
20
21pub fn semantic_check_only<S: ObjectStore + ?Sized>(
23 store: &S,
24 from_tree_hash: &ContentHash,
25 to_tree_hash: &ContentHash,
26 options: &SemanticDiffOptions,
27) -> Result<SemanticCheckOnlyResult, anyhow::Error> {
28 semantic_check_only_with_cache(
29 store,
30 from_tree_hash,
31 to_tree_hash,
32 options,
33 SemanticParseCache::shared(),
34 )
35}
36
37pub fn semantic_check_only_with_cache<S: ObjectStore + ?Sized>(
39 store: &S,
40 from_tree_hash: &ContentHash,
41 to_tree_hash: &ContentHash,
42 options: &SemanticDiffOptions,
43 cache: &SemanticParseCache,
44) -> Result<SemanticCheckOnlyResult, anyhow::Error> {
45 let file_changes = diff_trees(store, from_tree_hash, to_tree_hash)?;
46 let old_loader = TreeBlobContentLoader::new(store, *from_tree_hash);
47 let new_loader = TreeBlobContentLoader::new(store, *to_tree_hash);
48 SemanticEngine::new(
49 file_changes,
50 |path| old_loader.load_content(path),
51 |path| new_loader.load_content(path),
52 options,
53 cache,
54 )
55 .check_only()
56}
57
58pub fn semantic_check_only_worktree<S: ObjectStore + ?Sized>(
60 store: &S,
61 from_tree_hash: &ContentHash,
62 worktree_root: &Path,
63 status: &WorktreeStatus,
64 options: &SemanticDiffOptions,
65) -> Result<SemanticCheckOnlyResult, anyhow::Error> {
66 semantic_check_only_worktree_with_cache(
67 store,
68 from_tree_hash,
69 worktree_root,
70 status,
71 options,
72 SemanticParseCache::shared(),
73 )
74}
75
76pub fn semantic_check_only_worktree_with_cache<S: ObjectStore + ?Sized>(
78 store: &S,
79 from_tree_hash: &ContentHash,
80 worktree_root: &Path,
81 status: &WorktreeStatus,
82 options: &SemanticDiffOptions,
83 cache: &SemanticParseCache,
84) -> Result<SemanticCheckOnlyResult, anyhow::Error> {
85 let old_loader = TreeBlobContentLoader::new(store, *from_tree_hash);
86 SemanticEngine::new(
87 file_changes_from_status(status),
88 |path| old_loader.load_content(path),
89 |path| load_worktree_blob_content(worktree_root, path),
90 options,
91 cache,
92 )
93 .check_only()
94}
95
96pub fn semantic_diff_summary<S: ObjectStore + ?Sized>(
98 store: &S,
99 from_tree_hash: &ContentHash,
100 to_tree_hash: &ContentHash,
101 options: &SemanticDiffOptions,
102) -> Result<SemanticSummaryResult, anyhow::Error> {
103 semantic_diff_summary_with_cache(
104 store,
105 from_tree_hash,
106 to_tree_hash,
107 options,
108 SemanticParseCache::shared(),
109 )
110}
111
112pub fn semantic_diff_summary_with_cache<S: ObjectStore + ?Sized>(
114 store: &S,
115 from_tree_hash: &ContentHash,
116 to_tree_hash: &ContentHash,
117 options: &SemanticDiffOptions,
118 cache: &SemanticParseCache,
119) -> Result<SemanticSummaryResult, anyhow::Error> {
120 let file_changes = diff_trees(store, from_tree_hash, to_tree_hash)?;
121 let old_loader = TreeBlobContentLoader::new(store, *from_tree_hash);
122 let new_loader = TreeBlobContentLoader::new(store, *to_tree_hash);
123 SemanticEngine::new(
124 file_changes,
125 |path| old_loader.load_content(path),
126 |path| new_loader.load_content(path),
127 options,
128 cache,
129 )
130 .summary()
131}
132
133pub fn semantic_diff_summary_worktree<S: ObjectStore + ?Sized>(
135 store: &S,
136 from_tree_hash: &ContentHash,
137 worktree_root: &Path,
138 status: &WorktreeStatus,
139 options: &SemanticDiffOptions,
140) -> Result<SemanticSummaryResult, anyhow::Error> {
141 semantic_diff_summary_worktree_with_cache(
142 store,
143 from_tree_hash,
144 worktree_root,
145 status,
146 options,
147 SemanticParseCache::shared(),
148 )
149}
150
151pub fn semantic_diff_summary_worktree_with_cache<S: ObjectStore + ?Sized>(
153 store: &S,
154 from_tree_hash: &ContentHash,
155 worktree_root: &Path,
156 status: &WorktreeStatus,
157 options: &SemanticDiffOptions,
158 cache: &SemanticParseCache,
159) -> Result<SemanticSummaryResult, anyhow::Error> {
160 let old_loader = TreeBlobContentLoader::new(store, *from_tree_hash);
161 SemanticEngine::new(
162 file_changes_from_status(status),
163 |path| old_loader.load_content(path),
164 |path| load_worktree_blob_content(worktree_root, path),
165 options,
166 cache,
167 )
168 .summary()
169}
170
171pub fn semantic_diff<S: ObjectStore + ?Sized>(
173 store: &S,
174 from_tree_hash: &ContentHash,
175 to_tree_hash: &ContentHash,
176 options: &SemanticDiffOptions,
177) -> Result<SemanticDiffResult, anyhow::Error> {
178 semantic_diff_with_cache(
179 store,
180 from_tree_hash,
181 to_tree_hash,
182 options,
183 SemanticParseCache::shared(),
184 )
185}
186
187pub fn semantic_diff_with_cache<S: ObjectStore + ?Sized>(
189 store: &S,
190 from_tree_hash: &ContentHash,
191 to_tree_hash: &ContentHash,
192 options: &SemanticDiffOptions,
193 cache: &SemanticParseCache,
194) -> Result<SemanticDiffResult, anyhow::Error> {
195 let file_changes = diff_trees(store, from_tree_hash, to_tree_hash)?;
196 let old_loader = TreeBlobContentLoader::new(store, *from_tree_hash);
197 let new_loader = TreeBlobContentLoader::new(store, *to_tree_hash);
198 SemanticEngine::new(
199 file_changes,
200 |path| old_loader.load_content(path),
201 |path| new_loader.load_content(path),
202 options,
203 cache,
204 )
205 .full()
206}
207
208pub fn semantic_diff_worktree<S: ObjectStore + ?Sized>(
210 store: &S,
211 from_tree_hash: &ContentHash,
212 worktree_root: &Path,
213 status: &WorktreeStatus,
214 options: &SemanticDiffOptions,
215) -> Result<SemanticDiffResult, anyhow::Error> {
216 semantic_diff_worktree_with_cache(
217 store,
218 from_tree_hash,
219 worktree_root,
220 status,
221 options,
222 SemanticParseCache::shared(),
223 )
224}
225
226pub fn semantic_diff_worktree_with_cache<S: ObjectStore + ?Sized>(
228 store: &S,
229 from_tree_hash: &ContentHash,
230 worktree_root: &Path,
231 status: &WorktreeStatus,
232 options: &SemanticDiffOptions,
233 cache: &SemanticParseCache,
234) -> Result<SemanticDiffResult, anyhow::Error> {
235 let old_loader = TreeBlobContentLoader::new(store, *from_tree_hash);
236 SemanticEngine::new(
237 file_changes_from_status(status),
238 |path| old_loader.load_content(path),
239 |path| load_worktree_blob_content(worktree_root, path),
240 options,
241 cache,
242 )
243 .full()
244}
245
246fn file_changes_from_status(status: &WorktreeStatus) -> FileChangeSet {
247 let mut file_changes = FileChangeSet::with_capacity(status.change_count());
248 for path in &status.deleted {
249 file_changes.push_deleted(path.display().to_string());
250 }
251 for path in &status.added {
252 file_changes.push_added(path.display().to_string());
253 }
254 for path in &status.modified {
255 file_changes.push_modified(path.display().to_string());
256 }
257 file_changes
258}
259
260fn load_worktree_blob_content(
261 worktree_root: &Path,
262 path: &Path,
263) -> Result<Option<String>, anyhow::Error> {
264 let worktree_path = worktree_root.join(path);
265 match std::fs::read_to_string(&worktree_path) {
266 Ok(content) => Ok(Some(content)),
267 Err(err) if err.kind() == std::io::ErrorKind::NotFound => Ok(None),
268 Err(err) => Err(err.into()),
269 }
270}