parcel_resolver/
invalidations.rs

1use std::{
2  cell::{Cell, RefCell},
3  collections::HashSet,
4  hash::BuildHasherDefault,
5  sync::Arc,
6};
7
8use rustc_hash::FxHasher;
9
10use crate::{
11  cache::{CachedPath, IdentityHasher},
12  ResolverError,
13};
14
15/// Files that should invalidate the cache when they are created.
16#[derive(PartialEq, Eq, Hash, Debug, Clone)]
17pub enum FileCreateInvalidation {
18  /// Invalidate the cache if this path is created.
19  Path(CachedPath),
20  /// Invalidate the cache if a file of the given name is created
21  /// above the given path in the file hierarchy.
22  FileName {
23    file_name: String,
24    above: CachedPath,
25  },
26  /// Invalidate the cache if a file matching the given glob is created.
27  Glob(String),
28}
29
30/// Tracks the files that are involved with a resolution, in order to invalidate caches.
31#[derive(Default, Debug)]
32pub struct Invalidations {
33  /// Files that should invalidate the cache when they are created.
34  pub invalidate_on_file_create:
35    RefCell<HashSet<FileCreateInvalidation, BuildHasherDefault<FxHasher>>>,
36  /// Files that should invalidate the cache when they are updated.
37  pub invalidate_on_file_change: RefCell<HashSet<CachedPath, BuildHasherDefault<IdentityHasher>>>,
38  /// Whether the resolution is non-deterministic, and should invalidate on process restart.
39  pub invalidate_on_startup: Cell<bool>,
40}
41
42impl Invalidations {
43  /// Invalidate the cache if this path is created.
44  pub fn invalidate_on_file_create(&self, path: CachedPath) {
45    self
46      .invalidate_on_file_create
47      .borrow_mut()
48      .insert(FileCreateInvalidation::Path(path));
49  }
50
51  /// Invalidate the cache if a file of the given name is created
52  /// above the given path in the file hierarchy.
53  pub fn invalidate_on_file_create_above<S: Into<String>>(&self, file_name: S, above: CachedPath) {
54    self
55      .invalidate_on_file_create
56      .borrow_mut()
57      .insert(FileCreateInvalidation::FileName {
58        file_name: file_name.into(),
59        above,
60      });
61  }
62
63  /// Invalidate the cache if a file matching the given glob is created.
64  pub fn invalidate_on_glob_create<S: Into<String>>(&self, glob: S) {
65    self
66      .invalidate_on_file_create
67      .borrow_mut()
68      .insert(FileCreateInvalidation::Glob(glob.into()));
69  }
70
71  /// Invalidate the cache if the given file changes.
72  pub fn invalidate_on_file_change(&self, invalidation: CachedPath) {
73    self
74      .invalidate_on_file_change
75      .borrow_mut()
76      .insert(invalidation);
77  }
78
79  /// Invalidate the cache whenever the process restarts.
80  pub fn invalidate_on_startup(&self) {
81    self.invalidate_on_startup.set(true)
82  }
83
84  /// Extend these invalidations with the given invalidations.
85  pub fn extend(&self, other: &Invalidations) {
86    for f in other.invalidate_on_file_create.borrow().iter() {
87      self
88        .invalidate_on_file_create
89        .borrow_mut()
90        .insert(f.clone());
91    }
92
93    for f in other.invalidate_on_file_change.borrow().iter() {
94      self
95        .invalidate_on_file_change
96        .borrow_mut()
97        .insert(f.clone());
98    }
99
100    if other.invalidate_on_startup.get() {
101      self.invalidate_on_startup();
102    }
103  }
104
105  pub(crate) fn read<V, F: FnOnce() -> Arc<Result<V, ResolverError>>>(
106    &self,
107    path: &CachedPath,
108    f: F,
109  ) -> Arc<Result<V, ResolverError>> {
110    let res = f();
111    match &*res {
112      Ok(_) => {
113        self.invalidate_on_file_change(path.clone());
114      }
115      Err(e) => {
116        if matches!(e, ResolverError::IOError(..)) {
117          self.invalidate_on_file_create(path.clone());
118        }
119      }
120    }
121    res
122  }
123}