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