1717//! input structs.
1818
1919use std:: collections:: HashSet ;
20+ use std:: hash:: Hash ;
2021use std:: path:: PathBuf ;
2122
2223use aether_url:: UrlId ;
@@ -178,7 +179,7 @@ impl<DB: Db + DbInputs> DbScan for DB {
178179 } ;
179180
180181 let orphan = self . orphan_root ( ) ;
181- let Some ( orphan_files) = with_removed ( orphan. files ( self ) , file) else {
182+ let Some ( orphan_files) = with_cow_remove ( orphan. files ( self ) , file) else {
182183 // A workspace or library root holds it, nothing to do.
183184 return ;
184185 } ;
@@ -187,7 +188,7 @@ impl<DB: Db + DbInputs> DbScan for DB {
187188 orphan. set_files ( self ) . to ( orphan_files) ;
188189
189190 let stale = self . stale_root ( ) ;
190- if let Some ( stale_files) = with_appended ( stale. files ( self ) , file) {
191+ if let Some ( stale_files) = with_cow_insert ( stale. files ( self ) , file) {
191192 stale. set_files ( self ) . to ( stale_files) ;
192193 }
193194 }
@@ -384,36 +385,37 @@ pub(crate) fn upsert_root_file<DB: Db + DbInputs>(
384385/// entry. Also used by [`watch::remove_watched_file`] when a file
385386/// disappears from disk.
386387pub ( crate ) fn remove_from_pkg_files < DB : Db + DbInputs > ( db : & mut DB , pkg : Package , file : File ) {
387- if let Some ( files) = with_removed ( pkg. files ( db) , file) {
388+ if let Some ( files) = with_cow_filter ( pkg. files ( db) , file) {
388389 pkg. set_files ( db) . to ( files) ;
389390 return ;
390391 }
391- if let Some ( scripts) = with_removed ( pkg. scripts ( db) , file) {
392+ if let Some ( scripts) = with_cow_filter ( pkg. scripts ( db) , file) {
392393 pkg. set_scripts ( db) . to ( scripts) ;
393394 }
394395}
395396
396397pub ( crate ) fn remove_from_orphan < DB : Db + DbInputs > ( db : & mut DB , file : File ) {
397398 let orphan = db. orphan_root ( ) ;
398- if let Some ( files) = with_removed ( orphan. files ( db) , file) {
399+ if let Some ( files) = with_cow_remove ( orphan. files ( db) , file) {
399400 orphan. set_files ( db) . to ( files) ;
400401 }
401402}
402403
403404fn add_to_orphan_files < DB : Db + DbInputs > ( db : & mut DB , file : File ) {
404405 let orphan = db. orphan_root ( ) ;
405- if let Some ( files) = with_appended ( orphan. files ( db) , file) {
406+ if let Some ( files) = with_cow_insert ( orphan. files ( db) , file) {
406407 orphan. set_files ( db) . to ( files) ;
407408 }
408409}
409410
410- /// The container with `file` appended, or `None` if it's already there.
411+ /// The ordered container with `file` appended, or `None` if it's already there.
411412///
412413/// `None` means nothing would change, so the caller skips the salsa write and
413- /// the clone. This keeps the "clone only when the field actually changes"
414- /// rule in one place, shared by every container update on `Root` / `Package` /
415- /// `OrphanRoot`.
416- pub ( crate ) fn with_appended < T : Clone + PartialEq > ( files : & [ T ] , file : T ) -> Option < Vec < T > > {
414+ /// the clone. This keeps the "clone only when the field actually changes" rule
415+ /// in one place, shared by the ordered container updates on `Root` and
416+ /// `Package`. See [`with_inserted`] / [`with_discarded`] for the unordered
417+ /// `OrphanRoot` / `StaleRoot` sets.
418+ pub ( crate ) fn with_cow_push < T : Clone + PartialEq > ( files : & [ T ] , file : T ) -> Option < Vec < T > > {
417419 if files. contains ( & file) {
418420 return None ;
419421 }
@@ -422,11 +424,40 @@ pub(crate) fn with_appended<T: Clone + PartialEq>(files: &[T], file: T) -> Optio
422424 Some ( updated)
423425}
424426
425- /// The container with `file` removed, or `None` if it wasn't there. `None`
426- /// means nothing would change, see [`with_file_appended `].
427- pub ( crate ) fn with_removed < T : Clone + PartialEq > ( files : & [ T ] , file : T ) -> Option < Vec < T > > {
427+ /// The ordered container with `file` removed, or `None` if it wasn't there.
428+ /// `None` means nothing would change, see [`with_appended `].
429+ pub ( crate ) fn with_cow_filter < T : Clone + PartialEq > ( files : & [ T ] , file : T ) -> Option < Vec < T > > {
428430 if !files. contains ( & file) {
429431 return None ;
430432 }
431433 Some ( files. iter ( ) . filter ( |f| * * f != file) . cloned ( ) . collect ( ) )
432434}
435+
436+ /// The set with `item` inserted, or `None` if it's already present. The
437+ /// unordered counterpart of [`with_appended`], used for the `OrphanRoot` /
438+ /// `StaleRoot` sets where membership is all that matters.
439+ pub ( crate ) fn with_cow_insert < T : Clone + Eq + Hash > (
440+ set : & HashSet < T > ,
441+ item : T ,
442+ ) -> Option < HashSet < T > > {
443+ if set. contains ( & item) {
444+ return None ;
445+ }
446+ let mut updated = set. clone ( ) ;
447+ updated. insert ( item) ;
448+ Some ( updated)
449+ }
450+
451+ /// The set with `item` removed, or `None` if it wasn't present. The unordered
452+ /// counterpart of [`with_removed`].
453+ pub ( crate ) fn with_cow_remove < T : Clone + Eq + Hash > (
454+ set : & HashSet < T > ,
455+ item : T ,
456+ ) -> Option < HashSet < T > > {
457+ if !set. contains ( & item) {
458+ return None ;
459+ }
460+ let mut updated = set. clone ( ) ;
461+ updated. remove ( & item) ;
462+ Some ( updated)
463+ }
0 commit comments