1#![feature(arbitrary_self_types)]
2#![feature(arbitrary_self_types_pointers)]
3#![feature(btree_cursors)] #![feature(io_error_more)]
5#![feature(min_specialization)]
6#![feature(normalize_lexically)]
9#![feature(trivial_bounds)]
10#![cfg_attr(windows, feature(junction_point))]
13#![cfg_attr(target_os = "wasi", feature(wasi_ext))]
15#![allow(clippy::needless_return)] #![allow(clippy::mutable_key_type)]
17
18mod content;
19mod disk;
20pub mod embed;
21mod error;
22mod fs_map;
23pub mod glob;
24mod globset;
25pub mod invalidation;
26mod invalidator_map;
27pub mod json;
28mod mutex_map;
29mod null_fs;
30mod path;
31mod path_map;
32mod read_glob;
33mod retry;
34pub mod rope;
35pub mod source_context;
36pub mod util;
37pub(crate) mod virtual_fs;
38mod watcher;
39#[cfg(windows)]
40pub mod windows;
41
42use std::{fmt::Debug, fs::FileType, path::PathBuf};
43
44use anyhow::Result;
45use auto_hash_map::AutoMap;
46use bincode::{Decode, Encode};
47use turbo_rcstr::RcStr;
48use turbo_tasks::{NonLocalValue, ResolvedVc, ValueToString, Vc, turbobail, turbofmt};
49
50pub(crate) use crate::{
51 content::FileComparison,
52 disk::{DiskFileSystemInner, format_absolute_fs_path},
53 error::AnyhowWrapper,
54};
55pub use crate::{
56 content::{
57 File, FileContent, FileJsonContent, FileLine, FileLinesContent, FileMeta, LinkContent,
58 LinkTarget, Permissions, PersistedFileContent, WriteLinkContent, WriteLinkTargetType,
59 },
60 disk::{DiskFileSystem, canonicalize_to_rcstr, validate_path_length},
61 fs_map::DiskFileSystemMap,
62 null_fs::NullFileSystem,
63 path::{
64 FileSystemPath, FileSystemPathOption, RealPathError, RealPathErrorType,
65 RealPathWithLinksResult, rebase,
66 },
67 read_glob::ReadGlobResult,
68 virtual_fs::VirtualFileSystem,
69 watcher::{DiskWatcherConfig, DiskWatcherPathMatcher, DiskWatcherRecursiveMode},
70};
71
72#[turbo_tasks::value_trait]
73pub trait FileSystem: ValueToString {
74 #[turbo_tasks::function]
76 fn root(self: ResolvedVc<Self>) -> Vc<FileSystemPath> {
77 FileSystemPath::new_normalized_unchecked(self, RcStr::default()).cell()
78 }
79 #[turbo_tasks::function]
80 fn read(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<FileContent>;
81 #[turbo_tasks::function]
83 fn read_link(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<LinkContent>;
84 #[turbo_tasks::function]
87 fn is_junction_point(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<bool>;
88 #[turbo_tasks::function]
89 fn raw_read_dir(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<RawDirectoryContent>;
90 #[turbo_tasks::function]
91 fn write(self: Vc<Self>, fs_path: FileSystemPath, content: Vc<FileContent>) -> Vc<()>;
92 #[turbo_tasks::function]
94 fn write_link(self: Vc<Self>, fs_path: FileSystemPath, target: Vc<WriteLinkContent>) -> Vc<()>;
95 #[turbo_tasks::function]
96 fn metadata(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<FileMeta>;
97}
98
99#[derive(Hash, Clone, Debug, PartialEq, Eq, NonLocalValue, Encode, Decode)]
100pub enum RawDirectoryEntry {
101 File,
102 Directory,
103 Symlink,
104 Other,
106}
107
108#[derive(Hash, Clone, Debug, PartialEq, Eq, NonLocalValue, Encode, Decode)]
109pub enum DirectoryEntry {
110 File(FileSystemPath),
111 Directory(FileSystemPath),
112 Symlink(FileSystemPath),
113 Other(FileSystemPath),
114 Error(RcStr),
115}
116
117impl DirectoryEntry {
118 pub async fn resolve_symlink(self) -> Result<Self> {
122 if let DirectoryEntry::Symlink(symlink) = &self {
123 let result = &*symlink.realpath_with_links().await?;
124 let real_path = match &result.path_result {
125 Ok(path) => path,
126 Err(error) => {
127 return Ok(DirectoryEntry::Error(RcStr::from(error.to_string())));
128 }
129 };
130 Ok(match *real_path.get_type().await? {
131 FileSystemEntryType::Directory => DirectoryEntry::Directory(real_path.clone()),
132 FileSystemEntryType::File => DirectoryEntry::File(real_path.clone()),
133 FileSystemEntryType::NotFound => DirectoryEntry::Error(
135 turbofmt!("Symlink {symlink} points at {real_path} which does not exist")
136 .await?,
137 ),
138 FileSystemEntryType::Symlink => turbobail!(
140 "Symlink {symlink} points at a symlink but realpath_with_links returned a path"
141 ),
142 _ => self,
143 })
144 } else {
145 Ok(self)
146 }
147 }
148
149 pub fn path(self) -> Option<FileSystemPath> {
150 match self {
151 DirectoryEntry::File(path)
152 | DirectoryEntry::Directory(path)
153 | DirectoryEntry::Symlink(path)
154 | DirectoryEntry::Other(path) => Some(path),
155 DirectoryEntry::Error(_) => None,
156 }
157 }
158}
159
160#[turbo_tasks::value]
161#[derive(Hash, Clone, Copy, Debug)]
162pub enum FileSystemEntryType {
163 NotFound,
164 File,
165 Directory,
166 Symlink,
167 Other,
169 Error,
170}
171
172impl From<FileType> for FileSystemEntryType {
173 fn from(file_type: FileType) -> Self {
174 match file_type {
175 t if t.is_dir() => FileSystemEntryType::Directory,
176 t if t.is_file() => FileSystemEntryType::File,
177 t if t.is_symlink() => FileSystemEntryType::Symlink,
178 _ => FileSystemEntryType::Other,
179 }
180 }
181}
182
183impl From<DirectoryEntry> for FileSystemEntryType {
184 fn from(entry: DirectoryEntry) -> Self {
185 FileSystemEntryType::from(&entry)
186 }
187}
188
189impl From<&DirectoryEntry> for FileSystemEntryType {
190 fn from(entry: &DirectoryEntry) -> Self {
191 match entry {
192 DirectoryEntry::File(_) => FileSystemEntryType::File,
193 DirectoryEntry::Directory(_) => FileSystemEntryType::Directory,
194 DirectoryEntry::Symlink(_) => FileSystemEntryType::Symlink,
195 DirectoryEntry::Other(_) => FileSystemEntryType::Other,
196 DirectoryEntry::Error(_) => FileSystemEntryType::Error,
197 }
198 }
199}
200
201impl From<RawDirectoryEntry> for FileSystemEntryType {
202 fn from(entry: RawDirectoryEntry) -> Self {
203 FileSystemEntryType::from(&entry)
204 }
205}
206
207impl From<&RawDirectoryEntry> for FileSystemEntryType {
208 fn from(entry: &RawDirectoryEntry) -> Self {
209 match entry {
210 RawDirectoryEntry::File => FileSystemEntryType::File,
211 RawDirectoryEntry::Directory => FileSystemEntryType::Directory,
212 RawDirectoryEntry::Symlink => FileSystemEntryType::Symlink,
213 RawDirectoryEntry::Other => FileSystemEntryType::Other,
214 }
215 }
216}
217
218#[turbo_tasks::value]
219#[derive(Debug)]
220pub enum RawDirectoryContent {
221 Entries(AutoMap<RcStr, RawDirectoryEntry>),
224 NotFound,
225}
226
227impl RawDirectoryContent {
228 pub fn new(entries: AutoMap<RcStr, RawDirectoryEntry>) -> Vc<Self> {
229 Self::cell(RawDirectoryContent::Entries(entries))
230 }
231
232 pub fn not_found() -> Vc<Self> {
233 Self::cell(RawDirectoryContent::NotFound)
234 }
235}
236
237#[turbo_tasks::value]
238#[derive(Debug)]
239pub enum DirectoryContent {
240 Entries(AutoMap<RcStr, DirectoryEntry>),
241 NotFound,
242}
243
244impl DirectoryContent {
245 pub fn new(entries: AutoMap<RcStr, DirectoryEntry>) -> Vc<Self> {
246 Self::cell(DirectoryContent::Entries(entries))
247 }
248
249 pub fn not_found() -> Vc<Self> {
250 Self::cell(DirectoryContent::NotFound)
251 }
252}
253
254pub async fn to_sys_path(path: FileSystemPath) -> Result<Option<PathBuf>> {
255 if let Some(fs) = ResolvedVc::try_downcast_type::<DiskFileSystem>(path.fs) {
256 let sys_path = fs.await?.to_sys_path(&path);
257 return Ok(Some(sys_path));
258 }
259
260 Ok(None)
261}