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