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#![allow(clippy::needless_return)] #![allow(clippy::mutable_key_type)]
15
16mod content;
17mod disk;
18pub mod embed;
19mod error;
20pub mod glob;
21mod globset;
22pub mod invalidation;
23mod invalidator_map;
24pub mod json;
25mod mutex_map;
26mod null_fs;
27mod path;
28mod path_map;
29mod read_glob;
30mod retry;
31pub mod rope;
32pub mod source_context;
33pub mod util;
34pub(crate) mod virtual_fs;
35mod watcher;
36mod windows;
37
38use std::{fmt::Debug, fs::FileType, path::PathBuf};
39
40use anyhow::Result;
41use auto_hash_map::AutoMap;
42use bincode::{Decode, Encode};
43use turbo_rcstr::RcStr;
44use turbo_tasks::{
45 NonLocalValue, ResolvedVc, ValueToString, Vc, trace::TraceRawVcs, turbobail, turbofmt,
46};
47
48pub(crate) use crate::{
49 content::FileComparison,
50 disk::{DiskFileSystemInner, format_absolute_fs_path},
51 error::AnyhowWrapper,
52};
53pub use crate::{
54 content::{
55 File, FileContent, FileJsonContent, FileLine, FileLinesContent, FileMeta, LinkContent,
56 LinkType, Permissions, PersistedFileContent,
57 },
58 disk::{DiskFileSystem, canonicalize_to_rcstr, validate_path_length},
59 null_fs::NullFileSystem,
60 path::{FileSystemPath, FileSystemPathOption, RealPathResult, RealPathResultError, rebase},
61 read_glob::ReadGlobResult,
62 virtual_fs::VirtualFileSystem,
63 watcher::{DiskWatcherConfig, DiskWatcherRecursiveMode},
64 windows::to_verbatim_with_case_folded_disk,
65};
66
67#[turbo_tasks::value_trait]
68pub trait FileSystem: ValueToString {
69 #[turbo_tasks::function]
71 fn root(self: ResolvedVc<Self>) -> Vc<FileSystemPath> {
72 FileSystemPath::new_normalized_unchecked(self, RcStr::default()).cell()
73 }
74 #[turbo_tasks::function]
75 fn read(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<FileContent>;
76 #[turbo_tasks::function]
85 fn read_link(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<LinkContent>;
86 #[turbo_tasks::function]
87 fn raw_read_dir(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<RawDirectoryContent>;
88 #[turbo_tasks::function]
89 fn write(self: Vc<Self>, fs_path: FileSystemPath, content: Vc<FileContent>) -> Vc<()>;
90 #[turbo_tasks::function]
92 fn write_link(self: Vc<Self>, fs_path: FileSystemPath, target: Vc<LinkContent>) -> Vc<()>;
93 #[turbo_tasks::function]
94 fn metadata(self: Vc<Self>, fs_path: FileSystemPath) -> Vc<FileMeta>;
95}
96
97#[derive(Hash, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
98pub enum RawDirectoryEntry {
99 File,
100 Directory,
101 Symlink,
102 Other,
104}
105
106#[derive(Hash, Clone, Debug, PartialEq, Eq, TraceRawVcs, NonLocalValue, Encode, Decode)]
107pub enum DirectoryEntry {
108 File(FileSystemPath),
109 Directory(FileSystemPath),
110 Symlink(FileSystemPath),
111 Other(FileSystemPath),
112 Error(RcStr),
113}
114
115impl DirectoryEntry {
116 pub async fn resolve_symlink(self) -> Result<Self> {
120 if let DirectoryEntry::Symlink(symlink) = &self {
121 let result = &*symlink.realpath_with_links().await?;
122 let real_path = match &result.path_result {
123 Ok(path) => path,
124 Err(error) => {
125 return Ok(DirectoryEntry::Error(
126 error.as_error_message(symlink, result).await?,
127 ));
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}