1use std::cell::RefCell;
4use std::collections::HashMap;
5use std::path::Path;
6use std::path::PathBuf;
7
8use sys_traits::BaseFsCanonicalize;
9use sys_traits::BaseFsOpen;
10use sys_traits::BaseFsRead;
11use sys_traits::BaseFsReadDir;
12use sys_traits::FileType;
13use sys_traits::FsCanonicalize;
14use sys_traits::FsMetadata;
15use sys_traits::FsMetadataValue;
16use sys_traits::FsOpen;
17use sys_traits::FsRead;
18use sys_traits::FsReadDir;
19
20pub trait NodeResolutionCache:
21 std::fmt::Debug + deno_maybe_sync::MaybeSend + deno_maybe_sync::MaybeSync
22{
23 fn get_canonicalized(
24 &self,
25 path: &Path,
26 ) -> Option<Result<PathBuf, std::io::Error>>;
27 fn set_canonicalized(&self, from: PathBuf, to: &std::io::Result<PathBuf>);
28 fn get_file_type(&self, path: &Path) -> Option<Option<FileType>>;
29 fn set_file_type(&self, path: PathBuf, value: Option<FileType>);
30}
31
32thread_local! {
33 static CANONICALIZED_CACHE: RefCell<HashMap<PathBuf, Option<PathBuf>>> = RefCell::new(HashMap::new());
34 static FILE_TYPE_CACHE: RefCell<HashMap<PathBuf, Option<FileType>>> = RefCell::new(HashMap::new());
35}
36
37#[derive(Debug)]
40pub struct NodeResolutionThreadLocalCache;
41
42impl NodeResolutionThreadLocalCache {
43 pub fn clear() {
44 CANONICALIZED_CACHE.with_borrow_mut(|cache| cache.clear());
45 FILE_TYPE_CACHE.with_borrow_mut(|cache| cache.clear());
46 }
47}
48
49impl NodeResolutionCache for NodeResolutionThreadLocalCache {
50 fn get_canonicalized(
51 &self,
52 path: &Path,
53 ) -> Option<Result<PathBuf, std::io::Error>> {
54 CANONICALIZED_CACHE.with_borrow(|cache| {
55 let item = cache.get(path)?;
56 Some(match item {
57 Some(value) => Ok(value.clone()),
58 None => Err(std::io::Error::new(
59 std::io::ErrorKind::NotFound,
60 "Not found.",
61 )),
62 })
63 })
64 }
65
66 fn set_canonicalized(&self, from: PathBuf, to: &std::io::Result<PathBuf>) {
67 CANONICALIZED_CACHE.with_borrow_mut(|cache| match to {
68 Ok(to) => {
69 cache.insert(from, Some(to.clone()));
70 }
71 Err(err) => {
72 if err.kind() == std::io::ErrorKind::NotFound {
73 cache.insert(from, None);
74 }
75 }
76 });
77 }
78
79 fn get_file_type(&self, path: &Path) -> Option<Option<FileType>> {
80 FILE_TYPE_CACHE.with_borrow(|cache| cache.get(path).cloned())
81 }
82
83 fn set_file_type(&self, path: PathBuf, value: Option<FileType>) {
84 FILE_TYPE_CACHE.with_borrow_mut(|cache| {
85 cache.insert(path, value);
86 })
87 }
88}
89
90#[allow(clippy::disallowed_types)]
91pub type NodeResolutionCacheRc =
92 deno_maybe_sync::MaybeArc<dyn NodeResolutionCache>;
93
94#[derive(Debug, Default)]
95pub struct NodeResolutionSys<TSys> {
96 sys: TSys,
97 cache: Option<NodeResolutionCacheRc>,
98}
99
100impl<TSys: Clone> Clone for NodeResolutionSys<TSys> {
101 fn clone(&self) -> Self {
102 Self {
103 sys: self.sys.clone(),
104 cache: self.cache.clone(),
105 }
106 }
107}
108
109impl<TSys: FsMetadata> NodeResolutionSys<TSys> {
110 pub fn new(sys: TSys, store: Option<NodeResolutionCacheRc>) -> Self {
111 Self { sys, cache: store }
112 }
113
114 pub fn is_file(&self, path: &Path) -> bool {
115 match self.get_file_type(path) {
116 Ok(file_type) => file_type.is_file(),
117 Err(_) => false,
118 }
119 }
120
121 pub fn is_dir(&self, path: &Path) -> bool {
122 match self.get_file_type(path) {
123 Ok(file_type) => file_type.is_dir(),
124 Err(_) => false,
125 }
126 }
127
128 pub fn exists_(&self, path: &Path) -> bool {
129 self.get_file_type(path).is_ok()
130 }
131
132 pub fn get_file_type(&self, path: &Path) -> std::io::Result<FileType> {
133 {
134 if let Some(maybe_value) =
135 self.cache.as_ref().and_then(|c| c.get_file_type(path))
136 {
137 return match maybe_value {
138 Some(value) => Ok(value),
139 None => Err(std::io::Error::new(
140 std::io::ErrorKind::NotFound,
141 "Not found.",
142 )),
143 };
144 }
145 }
146 match self.sys.fs_metadata(path) {
147 Ok(metadata) => {
148 if let Some(cache) = &self.cache {
149 cache.set_file_type(path.to_path_buf(), Some(metadata.file_type()));
150 }
151 Ok(metadata.file_type())
152 }
153 Err(err) => {
154 if let Some(cache) = &self.cache {
155 cache.set_file_type(path.to_path_buf(), None);
156 }
157 Err(err)
158 }
159 }
160 }
161}
162
163impl<TSys: FsCanonicalize> BaseFsCanonicalize for NodeResolutionSys<TSys> {
164 fn base_fs_canonicalize(&self, from: &Path) -> std::io::Result<PathBuf> {
165 if let Some(cache) = &self.cache
166 && let Some(result) = cache.get_canonicalized(from)
167 {
168 return result;
169 }
170 let result = self.sys.base_fs_canonicalize(from);
171 if let Some(cache) = &self.cache {
172 cache.set_canonicalized(from.to_path_buf(), &result);
173 }
174 result
175 }
176}
177
178impl<TSys: FsReadDir> BaseFsReadDir for NodeResolutionSys<TSys> {
179 type ReadDirEntry = TSys::ReadDirEntry;
180
181 #[inline(always)]
182 fn base_fs_read_dir(
183 &self,
184 path: &Path,
185 ) -> std::io::Result<
186 Box<dyn Iterator<Item = std::io::Result<Self::ReadDirEntry>>>,
187 > {
188 self.sys.base_fs_read_dir(path)
189 }
190}
191
192impl<TSys: FsRead> BaseFsRead for NodeResolutionSys<TSys> {
193 #[inline(always)]
194 fn base_fs_read(
195 &self,
196 path: &Path,
197 ) -> std::io::Result<std::borrow::Cow<'static, [u8]>> {
198 self.sys.base_fs_read(path)
199 }
200}
201
202impl<TSys: FsOpen> BaseFsOpen for NodeResolutionSys<TSys> {
203 type File = TSys::File;
204
205 fn base_fs_open(
206 &self,
207 path: &Path,
208 flags: &sys_traits::OpenOptions,
209 ) -> std::io::Result<Self::File> {
210 self.sys.base_fs_open(path, flags)
211 }
212}