1use super::{
23 FileSpan, InvalidSymbolId, RegistrationError, RenameError, SymbolId, SymbolKind, SymbolPath,
24 SymbolRegistry, Visibility,
25};
26use serde::Serialize;
27
28#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
36pub enum RegistryUpdate {
37 Add {
39 path: SymbolPath,
41 kind: SymbolKind,
43 span: FileSpan,
45 },
46
47 Remove {
49 id: SymbolId,
51 },
52
53 Rename {
55 id: SymbolId,
57 new_path: SymbolPath,
59 },
60
61 UpdateSpan {
63 id: SymbolId,
65 new_span: FileSpan,
67 },
68
69 UpdateVisibility {
71 id: SymbolId,
73 new_visibility: Visibility,
75 },
76
77 UpdateKind {
79 id: SymbolId,
81 new_kind: SymbolKind,
83 },
84}
85
86impl RegistryUpdate {
87 pub fn target_id(&self) -> Option<SymbolId> {
91 match self {
92 RegistryUpdate::Add { .. } => None,
93 RegistryUpdate::Remove { id }
94 | RegistryUpdate::Rename { id, .. }
95 | RegistryUpdate::UpdateSpan { id, .. }
96 | RegistryUpdate::UpdateVisibility { id, .. }
97 | RegistryUpdate::UpdateKind { id, .. } => Some(*id),
98 }
99 }
100
101 pub fn is_destructive(&self) -> bool {
106 matches!(
107 self,
108 RegistryUpdate::Remove { .. } | RegistryUpdate::Rename { .. }
109 )
110 }
111}
112
113#[derive(Debug, Clone, Default)]
115pub struct RegistryUpdateBatch {
116 updates: Vec<RegistryUpdate>,
117}
118
119impl RegistryUpdateBatch {
120 pub fn new() -> Self {
122 Self::default()
123 }
124
125 pub fn with_capacity(capacity: usize) -> Self {
127 Self {
128 updates: Vec::with_capacity(capacity),
129 }
130 }
131
132 pub fn push(&mut self, update: RegistryUpdate) {
134 self.updates.push(update);
135 }
136
137 pub fn add_symbol(&mut self, path: SymbolPath, kind: SymbolKind, span: FileSpan) {
139 self.push(RegistryUpdate::Add { path, kind, span });
140 }
141
142 pub fn remove_symbol(&mut self, id: SymbolId) {
144 self.push(RegistryUpdate::Remove { id });
145 }
146
147 pub fn rename_symbol(&mut self, id: SymbolId, new_path: SymbolPath) {
149 self.push(RegistryUpdate::Rename { id, new_path });
150 }
151
152 pub fn update_span(&mut self, id: SymbolId, new_span: FileSpan) {
154 self.push(RegistryUpdate::UpdateSpan { id, new_span });
155 }
156
157 pub fn updates(&self) -> &[RegistryUpdate] {
159 &self.updates
160 }
161
162 pub fn into_updates(self) -> Vec<RegistryUpdate> {
164 self.updates
165 }
166
167 pub fn is_empty(&self) -> bool {
169 self.updates.is_empty()
170 }
171
172 pub fn len(&self) -> usize {
174 self.updates.len()
175 }
176
177 pub fn apply(self, registry: &mut SymbolRegistry) -> Result<usize, ApplyError> {
187 let mut applied = 0;
188
189 for update in self.updates {
190 update.apply(registry)?;
191 applied += 1;
192 }
193
194 Ok(applied)
195 }
196}
197
198#[derive(Debug, thiserror::Error)]
200pub enum ApplyError {
201 #[error("registration failed: {0}")]
203 Registration(#[from] RegistrationError),
204
205 #[error("invalid symbol id: {0}")]
207 InvalidId(#[from] InvalidSymbolId),
208
209 #[error("rename failed: {0}")]
211 Rename(#[from] RenameError),
212}
213
214impl RegistryUpdate {
215 pub fn apply(self, registry: &mut SymbolRegistry) -> Result<(), ApplyError> {
217 match self {
218 RegistryUpdate::Add { path, kind, span } => {
219 let id = registry.register(path, kind)?;
220 registry.set_span(id, span)?;
221 }
222 RegistryUpdate::Remove { id } => {
223 let _ = registry.remove(id);
226 }
227 RegistryUpdate::Rename { id, new_path } => {
228 registry.rename(id, new_path)?;
229 }
230 RegistryUpdate::UpdateSpan { id, new_span } => {
231 registry.set_span(id, new_span)?;
232 }
233 RegistryUpdate::UpdateVisibility { id, new_visibility } => {
234 registry.set_visibility(id, new_visibility)?;
235 }
236 RegistryUpdate::UpdateKind { id, new_kind } => {
237 registry.set_kind(id, new_kind)?;
238 }
239 }
240 Ok(())
241 }
242}
243
244impl IntoIterator for RegistryUpdateBatch {
245 type Item = RegistryUpdate;
246 type IntoIter = std::vec::IntoIter<RegistryUpdate>;
247
248 fn into_iter(self) -> Self::IntoIter {
249 self.updates.into_iter()
250 }
251}
252
253impl<'a> IntoIterator for &'a RegistryUpdateBatch {
254 type Item = &'a RegistryUpdate;
255 type IntoIter = std::slice::Iter<'a, RegistryUpdate>;
256
257 fn into_iter(self) -> Self::IntoIter {
258 self.updates.iter()
259 }
260}
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use ryo_symbol::WorkspaceFilePath;
266
267 fn test_span() -> FileSpan {
269 FileSpan::new(
270 WorkspaceFilePath::new_for_test("test/file.rs", "/", "test"),
271 0,
272 10,
273 )
274 }
275
276 #[test]
277 fn test_target_id() {
278 use slotmap::KeyData;
279
280 let id = SymbolId::from(KeyData::from_ffi(1));
281 let path = SymbolPath::parse("foo::bar").unwrap();
282
283 let add = RegistryUpdate::Add {
285 path: path.clone(),
286 kind: SymbolKind::Function,
287 span: test_span(),
288 };
289 assert!(add.target_id().is_none());
290
291 let remove = RegistryUpdate::Remove { id };
293 assert_eq!(remove.target_id(), Some(id));
294
295 let rename = RegistryUpdate::Rename { id, new_path: path };
297 assert_eq!(rename.target_id(), Some(id));
298 }
299
300 #[test]
301 fn test_is_destructive() {
302 use slotmap::KeyData;
303
304 let id = SymbolId::from(KeyData::from_ffi(1));
305 let path = SymbolPath::parse("foo::bar").unwrap();
306
307 assert!(!RegistryUpdate::Add {
308 path: path.clone(),
309 kind: SymbolKind::Function,
310 span: test_span(),
311 }
312 .is_destructive());
313
314 assert!(RegistryUpdate::Remove { id }.is_destructive());
315 assert!(RegistryUpdate::Rename { id, new_path: path }.is_destructive());
316
317 assert!(!RegistryUpdate::UpdateSpan {
318 id,
319 new_span: test_span()
320 }
321 .is_destructive());
322 }
323
324 #[test]
325 fn test_batch_builder() {
326 use slotmap::KeyData;
327
328 let id = SymbolId::from(KeyData::from_ffi(1));
329 let path = SymbolPath::parse("foo::bar").unwrap();
330
331 let mut batch = RegistryUpdateBatch::new();
332 assert!(batch.is_empty());
333
334 batch.add_symbol(path.clone(), SymbolKind::Function, test_span());
335 batch.remove_symbol(id);
336
337 assert_eq!(batch.len(), 2);
338 assert!(!batch.is_empty());
339 }
340
341 #[test]
342 fn test_batch_apply_add() {
343 let mut registry = SymbolRegistry::new();
344 let path = SymbolPath::parse("test::NewSymbol").unwrap();
345
346 let mut batch = RegistryUpdateBatch::new();
347 batch.add_symbol(path.clone(), SymbolKind::Function, test_span());
348
349 let applied = batch.apply(&mut registry).unwrap();
350 assert_eq!(applied, 1);
351
352 let id = registry.lookup(&path).expect("symbol should exist");
354 assert_eq!(registry.kind(id), Some(SymbolKind::Function));
355 assert!(registry.span(id).is_some());
356 }
357
358 #[test]
359 fn test_batch_apply_remove() {
360 let mut registry = SymbolRegistry::new();
361 let path = SymbolPath::parse("test::ToRemove").unwrap();
362 let id = registry.register(path.clone(), SymbolKind::Struct).unwrap();
363
364 let mut batch = RegistryUpdateBatch::new();
365 batch.remove_symbol(id);
366
367 let applied = batch.apply(&mut registry).unwrap();
368 assert_eq!(applied, 1);
369
370 assert!(registry.lookup(&path).is_none());
372 assert!(!registry.contains(id));
373 }
374
375 #[test]
376 fn test_batch_apply_rename() {
377 let mut registry = SymbolRegistry::new();
378 let old_path = SymbolPath::parse("test::OldName").unwrap();
379 let new_path = SymbolPath::parse("test::NewName").unwrap();
380 let id = registry
381 .register(old_path.clone(), SymbolKind::Struct)
382 .unwrap();
383
384 let mut batch = RegistryUpdateBatch::new();
385 batch.rename_symbol(id, new_path.clone());
386
387 let applied = batch.apply(&mut registry).unwrap();
388 assert_eq!(applied, 1);
389
390 assert!(registry.lookup(&old_path).is_none());
392 assert_eq!(registry.lookup(&new_path), Some(id));
393 }
394
395 #[test]
396 fn test_batch_apply_multiple() {
397 use super::Visibility;
398
399 let mut registry = SymbolRegistry::new();
400 let path1 = SymbolPath::parse("test::First").unwrap();
401 let path2 = SymbolPath::parse("test::Second").unwrap();
402
403 let id1 = registry
404 .register(path1.clone(), SymbolKind::Struct)
405 .unwrap();
406
407 let mut batch = RegistryUpdateBatch::new();
408 batch.add_symbol(path2.clone(), SymbolKind::Function, test_span());
409 batch.push(RegistryUpdate::UpdateVisibility {
410 id: id1,
411 new_visibility: Visibility::Public,
412 });
413
414 let applied = batch.apply(&mut registry).unwrap();
415 assert_eq!(applied, 2);
416
417 assert!(registry.lookup(&path2).is_some());
419 assert_eq!(registry.visibility(id1), Some(&Visibility::Public));
420 }
421
422 #[test]
423 fn test_batch_apply_empty() {
424 let mut registry = SymbolRegistry::new();
425 let batch = RegistryUpdateBatch::new();
426
427 let applied = batch.apply(&mut registry).unwrap();
428 assert_eq!(applied, 0);
429 }
430}