fs.rs

  1use anyhow::{anyhow, Result};
  2use fsevent::EventStream;
  3use futures::{Stream, StreamExt};
  4use smol::io::{AsyncReadExt, AsyncWriteExt};
  5use std::{
  6    io,
  7    os::unix::fs::MetadataExt,
  8    path::{Component, Path, PathBuf},
  9    pin::Pin,
 10    time::{Duration, SystemTime},
 11};
 12use text::Rope;
 13
 14#[async_trait::async_trait]
 15pub trait Fs: Send + Sync {
 16    async fn create_dir(&self, path: &Path) -> Result<()>;
 17    async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()>;
 18    async fn copy(
 19        &self,
 20        source: &Path,
 21        target: &Path,
 22        options: CopyOptions,
 23    ) -> Result<Vec<PathBuf>>;
 24    async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()>;
 25    async fn remove_dir(&self, path: &Path, options: RemoveOptions) -> Result<()>;
 26    async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()>;
 27    async fn open_sync(&self, path: &Path) -> Result<Box<dyn io::Read>>;
 28    async fn load(&self, path: &Path) -> Result<String>;
 29    async fn save(&self, path: &Path, text: &Rope) -> Result<()>;
 30    async fn canonicalize(&self, path: &Path) -> Result<PathBuf>;
 31    async fn is_file(&self, path: &Path) -> bool;
 32    async fn metadata(&self, path: &Path) -> Result<Option<Metadata>>;
 33    async fn read_dir(
 34        &self,
 35        path: &Path,
 36    ) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>>;
 37    async fn watch(
 38        &self,
 39        path: &Path,
 40        latency: Duration,
 41    ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>>;
 42    fn is_fake(&self) -> bool;
 43    #[cfg(any(test, feature = "test-support"))]
 44    fn as_fake(&self) -> &FakeFs;
 45}
 46
 47#[derive(Copy, Clone, Default)]
 48pub struct CreateOptions {
 49    pub overwrite: bool,
 50    pub ignore_if_exists: bool,
 51}
 52
 53#[derive(Copy, Clone, Default)]
 54pub struct CopyOptions {
 55    pub overwrite: bool,
 56    pub ignore_if_exists: bool,
 57}
 58
 59#[derive(Copy, Clone, Default)]
 60pub struct RenameOptions {
 61    pub overwrite: bool,
 62    pub ignore_if_exists: bool,
 63}
 64
 65#[derive(Copy, Clone, Default)]
 66pub struct RemoveOptions {
 67    pub recursive: bool,
 68    pub ignore_if_not_exists: bool,
 69}
 70
 71#[derive(Clone, Debug)]
 72pub struct Metadata {
 73    pub inode: u64,
 74    pub mtime: SystemTime,
 75    pub is_symlink: bool,
 76    pub is_dir: bool,
 77}
 78
 79pub struct RealFs;
 80
 81#[async_trait::async_trait]
 82impl Fs for RealFs {
 83    async fn create_dir(&self, path: &Path) -> Result<()> {
 84        Ok(smol::fs::create_dir_all(path).await?)
 85    }
 86
 87    async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()> {
 88        let mut open_options = smol::fs::OpenOptions::new();
 89        open_options.write(true).create(true);
 90        if options.overwrite {
 91            open_options.truncate(true);
 92        } else if !options.ignore_if_exists {
 93            open_options.create_new(true);
 94        }
 95        open_options.open(path).await?;
 96        Ok(())
 97    }
 98
 99    async fn copy(
100        &self,
101        source: &Path,
102        target: &Path,
103        options: CopyOptions,
104    ) -> Result<Vec<PathBuf>> {
105        if !options.overwrite && smol::fs::metadata(target).await.is_ok() {
106            if options.ignore_if_exists {
107                return Ok(Default::default());
108            } else {
109                return Err(anyhow!("{target:?} already exists"));
110            }
111        }
112
113        let mut paths = vec![target.to_path_buf()];
114        let metadata = smol::fs::metadata(source).await?;
115        let _ = smol::fs::remove_dir_all(target).await;
116        if metadata.is_dir() {
117            self.create_dir(target).await?;
118            let mut children = smol::fs::read_dir(source).await?;
119            while let Some(child) = children.next().await {
120                if let Ok(child) = child {
121                    let child_source_path = child.path();
122                    let child_target_path = target.join(child.file_name());
123                    paths.extend(
124                        self.copy(&child_source_path, &child_target_path, options)
125                            .await?,
126                    );
127                }
128            }
129        } else {
130            smol::fs::copy(source, target).await?;
131        }
132
133        Ok(paths)
134    }
135
136    async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()> {
137        if !options.overwrite && smol::fs::metadata(target).await.is_ok() {
138            if options.ignore_if_exists {
139                return Ok(());
140            } else {
141                return Err(anyhow!("{target:?} already exists"));
142            }
143        }
144
145        smol::fs::rename(source, target).await?;
146        Ok(())
147    }
148
149    async fn remove_dir(&self, path: &Path, options: RemoveOptions) -> Result<()> {
150        let result = if options.recursive {
151            smol::fs::remove_dir_all(path).await
152        } else {
153            smol::fs::remove_dir(path).await
154        };
155        match result {
156            Ok(()) => Ok(()),
157            Err(err) if err.kind() == io::ErrorKind::NotFound && options.ignore_if_not_exists => {
158                Ok(())
159            }
160            Err(err) => Err(err)?,
161        }
162    }
163
164    async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()> {
165        match smol::fs::remove_file(path).await {
166            Ok(()) => Ok(()),
167            Err(err) if err.kind() == io::ErrorKind::NotFound && options.ignore_if_not_exists => {
168                Ok(())
169            }
170            Err(err) => Err(err)?,
171        }
172    }
173
174    async fn open_sync(&self, path: &Path) -> Result<Box<dyn io::Read>> {
175        Ok(Box::new(std::fs::File::open(path)?))
176    }
177
178    async fn load(&self, path: &Path) -> Result<String> {
179        let mut file = smol::fs::File::open(path).await?;
180        let mut text = String::new();
181        file.read_to_string(&mut text).await?;
182        Ok(text)
183    }
184
185    async fn save(&self, path: &Path, text: &Rope) -> Result<()> {
186        let buffer_size = text.summary().bytes.min(10 * 1024);
187        let file = smol::fs::File::create(path).await?;
188        let mut writer = smol::io::BufWriter::with_capacity(buffer_size, file);
189        for chunk in text.chunks() {
190            writer.write_all(chunk.as_bytes()).await?;
191        }
192        writer.flush().await?;
193        Ok(())
194    }
195
196    async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
197        Ok(smol::fs::canonicalize(path).await?)
198    }
199
200    async fn is_file(&self, path: &Path) -> bool {
201        smol::fs::metadata(path)
202            .await
203            .map_or(false, |metadata| metadata.is_file())
204    }
205
206    async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
207        let symlink_metadata = match smol::fs::symlink_metadata(path).await {
208            Ok(metadata) => metadata,
209            Err(err) => {
210                return match (err.kind(), err.raw_os_error()) {
211                    (io::ErrorKind::NotFound, _) => Ok(None),
212                    (io::ErrorKind::Other, Some(libc::ENOTDIR)) => Ok(None),
213                    _ => Err(anyhow::Error::new(err)),
214                }
215            }
216        };
217
218        let is_symlink = symlink_metadata.file_type().is_symlink();
219        let metadata = if is_symlink {
220            smol::fs::metadata(path).await?
221        } else {
222            symlink_metadata
223        };
224        Ok(Some(Metadata {
225            inode: metadata.ino(),
226            mtime: metadata.modified().unwrap(),
227            is_symlink,
228            is_dir: metadata.file_type().is_dir(),
229        }))
230    }
231
232    async fn read_dir(
233        &self,
234        path: &Path,
235    ) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> {
236        let result = smol::fs::read_dir(path).await?.map(|entry| match entry {
237            Ok(entry) => Ok(entry.path()),
238            Err(error) => Err(anyhow!("failed to read dir entry {:?}", error)),
239        });
240        Ok(Box::pin(result))
241    }
242
243    async fn watch(
244        &self,
245        path: &Path,
246        latency: Duration,
247    ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>> {
248        let (tx, rx) = smol::channel::unbounded();
249        let (stream, handle) = EventStream::new(&[path], latency);
250        std::mem::forget(handle);
251        std::thread::spawn(move || {
252            stream.run(move |events| smol::block_on(tx.send(events)).is_ok());
253        });
254        Box::pin(rx)
255    }
256
257    fn is_fake(&self) -> bool {
258        false
259    }
260    #[cfg(any(test, feature = "test-support"))]
261    fn as_fake(&self) -> &FakeFs {
262        panic!("called `RealFs::as_fake`")
263    }
264}
265
266#[cfg(any(test, feature = "test-support"))]
267#[derive(Clone, Debug)]
268struct FakeFsEntry {
269    metadata: Metadata,
270    content: Option<String>,
271}
272
273#[cfg(any(test, feature = "test-support"))]
274struct FakeFsState {
275    entries: std::collections::BTreeMap<PathBuf, FakeFsEntry>,
276    next_inode: u64,
277    event_txs: Vec<smol::channel::Sender<Vec<fsevent::Event>>>,
278}
279
280#[cfg(any(test, feature = "test-support"))]
281impl FakeFsState {
282    fn validate_path(&self, path: &Path) -> Result<()> {
283        if path.is_absolute()
284            && path
285                .parent()
286                .and_then(|path| self.entries.get(path))
287                .map_or(false, |e| e.metadata.is_dir)
288        {
289            Ok(())
290        } else {
291            Err(anyhow!("invalid path {:?}", path))
292        }
293    }
294
295    async fn emit_event<I, T>(&mut self, paths: I)
296    where
297        I: IntoIterator<Item = T>,
298        T: Into<PathBuf>,
299    {
300        let events = paths
301            .into_iter()
302            .map(|path| fsevent::Event {
303                event_id: 0,
304                flags: fsevent::StreamFlags::empty(),
305                path: path.into(),
306            })
307            .collect::<Vec<_>>();
308
309        self.event_txs.retain(|tx| {
310            let _ = tx.try_send(events.clone());
311            !tx.is_closed()
312        });
313    }
314}
315
316#[cfg(any(test, feature = "test-support"))]
317pub struct FakeFs {
318    // Use an unfair lock to ensure tests are deterministic.
319    state: futures::lock::Mutex<FakeFsState>,
320    executor: std::sync::Weak<gpui::executor::Background>,
321}
322
323#[cfg(any(test, feature = "test-support"))]
324impl FakeFs {
325    pub fn new(executor: std::sync::Arc<gpui::executor::Background>) -> std::sync::Arc<Self> {
326        let mut entries = std::collections::BTreeMap::new();
327        entries.insert(
328            Path::new("/").to_path_buf(),
329            FakeFsEntry {
330                metadata: Metadata {
331                    inode: 0,
332                    mtime: SystemTime::now(),
333                    is_dir: true,
334                    is_symlink: false,
335                },
336                content: None,
337            },
338        );
339        std::sync::Arc::new(Self {
340            executor: std::sync::Arc::downgrade(&executor),
341            state: futures::lock::Mutex::new(FakeFsState {
342                entries,
343                next_inode: 1,
344                event_txs: Default::default(),
345            }),
346        })
347    }
348
349    pub async fn insert_dir(&self, path: impl AsRef<Path>) {
350        let mut state = self.state.lock().await;
351        let path = path.as_ref();
352        state.validate_path(path).unwrap();
353
354        let inode = state.next_inode;
355        state.next_inode += 1;
356        state.entries.insert(
357            path.to_path_buf(),
358            FakeFsEntry {
359                metadata: Metadata {
360                    inode,
361                    mtime: SystemTime::now(),
362                    is_dir: true,
363                    is_symlink: false,
364                },
365                content: None,
366            },
367        );
368        state.emit_event(&[path]).await;
369    }
370
371    pub async fn insert_file(&self, path: impl AsRef<Path>, content: String) {
372        let mut state = self.state.lock().await;
373        let path = path.as_ref();
374        state.validate_path(path).unwrap();
375
376        let inode = state.next_inode;
377        state.next_inode += 1;
378        state.entries.insert(
379            path.to_path_buf(),
380            FakeFsEntry {
381                metadata: Metadata {
382                    inode,
383                    mtime: SystemTime::now(),
384                    is_dir: false,
385                    is_symlink: false,
386                },
387                content: Some(content),
388            },
389        );
390        state.emit_event(&[path]).await;
391    }
392
393    #[must_use]
394    pub fn insert_tree<'a>(
395        &'a self,
396        path: impl 'a + AsRef<Path> + Send,
397        tree: serde_json::Value,
398    ) -> futures::future::BoxFuture<'a, ()> {
399        use futures::FutureExt as _;
400        use serde_json::Value::*;
401
402        async move {
403            let path = path.as_ref();
404
405            match tree {
406                Object(map) => {
407                    self.insert_dir(path).await;
408                    for (name, contents) in map {
409                        let mut path = PathBuf::from(path);
410                        path.push(name);
411                        self.insert_tree(&path, contents).await;
412                    }
413                }
414                Null => {
415                    self.insert_dir(&path).await;
416                }
417                String(contents) => {
418                    self.insert_file(&path, contents).await;
419                }
420                _ => {
421                    panic!("JSON object must contain only objects, strings, or null");
422                }
423            }
424        }
425        .boxed()
426    }
427
428    pub async fn files(&self) -> Vec<PathBuf> {
429        self.state
430            .lock()
431            .await
432            .entries
433            .iter()
434            .filter_map(|(path, entry)| entry.content.as_ref().map(|_| path.clone()))
435            .collect()
436    }
437
438    async fn simulate_random_delay(&self) {
439        self.executor
440            .upgrade()
441            .expect("executor has been dropped")
442            .simulate_random_delay()
443            .await;
444    }
445}
446
447#[cfg(any(test, feature = "test-support"))]
448#[async_trait::async_trait]
449impl Fs for FakeFs {
450    async fn create_dir(&self, path: &Path) -> Result<()> {
451        self.simulate_random_delay().await;
452        let state = &mut *self.state.lock().await;
453        let path = normalize_path(path);
454        let mut ancestor_path = PathBuf::new();
455        let mut created_dir_paths = Vec::new();
456        for component in path.components() {
457            ancestor_path.push(component);
458            let entry = state
459                .entries
460                .entry(ancestor_path.clone())
461                .or_insert_with(|| {
462                    let inode = state.next_inode;
463                    state.next_inode += 1;
464                    created_dir_paths.push(ancestor_path.clone());
465                    FakeFsEntry {
466                        metadata: Metadata {
467                            inode,
468                            mtime: SystemTime::now(),
469                            is_dir: true,
470                            is_symlink: false,
471                        },
472                        content: None,
473                    }
474                });
475            if !entry.metadata.is_dir {
476                return Err(anyhow!(
477                    "cannot create directory because {:?} is a file",
478                    ancestor_path
479                ));
480            }
481        }
482        state.emit_event(&created_dir_paths).await;
483
484        Ok(())
485    }
486
487    async fn create_file(&self, path: &Path, options: CreateOptions) -> Result<()> {
488        self.simulate_random_delay().await;
489        let mut state = self.state.lock().await;
490        let path = normalize_path(path);
491        state.validate_path(&path)?;
492        if let Some(entry) = state.entries.get_mut(&path) {
493            if entry.metadata.is_dir || entry.metadata.is_symlink {
494                return Err(anyhow!(
495                    "cannot create file because {:?} is a dir or a symlink",
496                    path
497                ));
498            }
499
500            if options.overwrite {
501                entry.metadata.mtime = SystemTime::now();
502                entry.content = Some(Default::default());
503            } else if !options.ignore_if_exists {
504                return Err(anyhow!(
505                    "cannot create file because {:?} already exists",
506                    &path
507                ));
508            }
509        } else {
510            let inode = state.next_inode;
511            state.next_inode += 1;
512            let entry = FakeFsEntry {
513                metadata: Metadata {
514                    inode,
515                    mtime: SystemTime::now(),
516                    is_dir: false,
517                    is_symlink: false,
518                },
519                content: Some(Default::default()),
520            };
521            state.entries.insert(path.to_path_buf(), entry);
522        }
523        state.emit_event(&[path]).await;
524
525        Ok(())
526    }
527
528    async fn rename(&self, source: &Path, target: &Path, options: RenameOptions) -> Result<()> {
529        let source = normalize_path(source);
530        let target = normalize_path(target);
531
532        let mut state = self.state.lock().await;
533        state.validate_path(&source)?;
534        state.validate_path(&target)?;
535
536        if !options.overwrite && state.entries.contains_key(&target) {
537            if options.ignore_if_exists {
538                return Ok(());
539            } else {
540                return Err(anyhow!("{target:?} already exists"));
541            }
542        }
543
544        let mut removed = Vec::new();
545        state.entries.retain(|path, entry| {
546            if let Ok(relative_path) = path.strip_prefix(&source) {
547                removed.push((relative_path.to_path_buf(), entry.clone()));
548                false
549            } else {
550                true
551            }
552        });
553
554        for (relative_path, entry) in removed {
555            let new_path = normalize_path(&target.join(relative_path));
556            state.entries.insert(new_path, entry);
557        }
558
559        state.emit_event(&[source, target]).await;
560        Ok(())
561    }
562
563    async fn copy(
564        &self,
565        source: &Path,
566        target: &Path,
567        options: CopyOptions,
568    ) -> Result<Vec<PathBuf>> {
569        let source = normalize_path(source);
570        let target = normalize_path(target);
571
572        let mut state = self.state.lock().await;
573        state.validate_path(&source)?;
574        state.validate_path(&target)?;
575
576        if !options.overwrite && state.entries.contains_key(&target) {
577            if options.ignore_if_exists {
578                return Ok(Default::default());
579            } else {
580                return Err(anyhow!("{target:?} already exists"));
581            }
582        }
583
584        let mut new_entries = Vec::new();
585        for (path, entry) in &state.entries {
586            if let Ok(relative_path) = path.strip_prefix(&source) {
587                new_entries.push((relative_path.to_path_buf(), entry.clone()));
588            }
589        }
590
591        let mut paths = Vec::new();
592        for (relative_path, entry) in new_entries {
593            let new_path = normalize_path(&target.join(relative_path));
594            paths.push(new_path.clone());
595            state.entries.insert(new_path, entry);
596        }
597
598        state.emit_event(&paths).await;
599        Ok(paths)
600    }
601
602    async fn remove_dir(&self, dir_path: &Path, options: RemoveOptions) -> Result<()> {
603        let dir_path = normalize_path(dir_path);
604        let mut state = self.state.lock().await;
605        state.validate_path(&dir_path)?;
606        if let Some(entry) = state.entries.get(&dir_path) {
607            if !entry.metadata.is_dir {
608                return Err(anyhow!(
609                    "cannot remove {dir_path:?} because it is not a dir"
610                ));
611            }
612
613            if !options.recursive {
614                let descendants = state
615                    .entries
616                    .keys()
617                    .filter(|path| path.starts_with(path))
618                    .count();
619                if descendants > 1 {
620                    return Err(anyhow!("{dir_path:?} is not empty"));
621                }
622            }
623
624            state.entries.retain(|path, _| !path.starts_with(&dir_path));
625            state.emit_event(&[dir_path]).await;
626        } else if !options.ignore_if_not_exists {
627            return Err(anyhow!("{dir_path:?} does not exist"));
628        }
629
630        Ok(())
631    }
632
633    async fn remove_file(&self, path: &Path, options: RemoveOptions) -> Result<()> {
634        let path = normalize_path(path);
635        let mut state = self.state.lock().await;
636        state.validate_path(&path)?;
637        if let Some(entry) = state.entries.get(&path) {
638            if entry.metadata.is_dir {
639                return Err(anyhow!("cannot remove {path:?} because it is not a file"));
640            }
641
642            state.entries.remove(&path);
643            state.emit_event(&[path]).await;
644        } else if !options.ignore_if_not_exists {
645            return Err(anyhow!("{path:?} does not exist"));
646        }
647        Ok(())
648    }
649
650    async fn open_sync(&self, path: &Path) -> Result<Box<dyn io::Read>> {
651        let text = self.load(path).await?;
652        Ok(Box::new(io::Cursor::new(text)))
653    }
654
655    async fn load(&self, path: &Path) -> Result<String> {
656        let path = normalize_path(path);
657        self.simulate_random_delay().await;
658        let state = self.state.lock().await;
659        let text = state
660            .entries
661            .get(&path)
662            .and_then(|e| e.content.as_ref())
663            .ok_or_else(|| anyhow!("file {:?} does not exist", path))?;
664        Ok(text.clone())
665    }
666
667    async fn save(&self, path: &Path, text: &Rope) -> Result<()> {
668        self.simulate_random_delay().await;
669        let mut state = self.state.lock().await;
670        let path = normalize_path(path);
671        state.validate_path(&path)?;
672        if let Some(entry) = state.entries.get_mut(&path) {
673            if entry.metadata.is_dir {
674                Err(anyhow!("cannot overwrite a directory with a file"))
675            } else {
676                entry.content = Some(text.chunks().collect());
677                entry.metadata.mtime = SystemTime::now();
678                state.emit_event(&[path]).await;
679                Ok(())
680            }
681        } else {
682            let inode = state.next_inode;
683            state.next_inode += 1;
684            let entry = FakeFsEntry {
685                metadata: Metadata {
686                    inode,
687                    mtime: SystemTime::now(),
688                    is_dir: false,
689                    is_symlink: false,
690                },
691                content: Some(text.chunks().collect()),
692            };
693            state.entries.insert(path.to_path_buf(), entry);
694            state.emit_event(&[path]).await;
695            Ok(())
696        }
697    }
698
699    async fn canonicalize(&self, path: &Path) -> Result<PathBuf> {
700        self.simulate_random_delay().await;
701        Ok(normalize_path(path))
702    }
703
704    async fn is_file(&self, path: &Path) -> bool {
705        let path = normalize_path(path);
706        self.simulate_random_delay().await;
707        let state = self.state.lock().await;
708        state
709            .entries
710            .get(&path)
711            .map_or(false, |entry| !entry.metadata.is_dir)
712    }
713
714    async fn metadata(&self, path: &Path) -> Result<Option<Metadata>> {
715        self.simulate_random_delay().await;
716        let state = self.state.lock().await;
717        let path = normalize_path(path);
718        Ok(state.entries.get(&path).map(|entry| entry.metadata.clone()))
719    }
720
721    async fn read_dir(
722        &self,
723        abs_path: &Path,
724    ) -> Result<Pin<Box<dyn Send + Stream<Item = Result<PathBuf>>>>> {
725        use futures::{future, stream};
726        self.simulate_random_delay().await;
727        let state = self.state.lock().await;
728        let abs_path = normalize_path(abs_path);
729        Ok(Box::pin(stream::iter(state.entries.clone()).filter_map(
730            move |(child_path, _)| {
731                future::ready(if child_path.parent() == Some(&abs_path) {
732                    Some(Ok(child_path))
733                } else {
734                    None
735                })
736            },
737        )))
738    }
739
740    async fn watch(
741        &self,
742        path: &Path,
743        _: Duration,
744    ) -> Pin<Box<dyn Send + Stream<Item = Vec<fsevent::Event>>>> {
745        let mut state = self.state.lock().await;
746        self.simulate_random_delay().await;
747        let (tx, rx) = smol::channel::unbounded();
748        state.event_txs.push(tx);
749        let path = path.to_path_buf();
750        let executor = self.executor.clone();
751        Box::pin(futures::StreamExt::filter(rx, move |events| {
752            let result = events.iter().any(|event| event.path.starts_with(&path));
753            let executor = executor.clone();
754            async move {
755                if let Some(executor) = executor.clone().upgrade() {
756                    executor.simulate_random_delay().await;
757                }
758                result
759            }
760        }))
761    }
762
763    fn is_fake(&self) -> bool {
764        true
765    }
766
767    #[cfg(any(test, feature = "test-support"))]
768    fn as_fake(&self) -> &FakeFs {
769        self
770    }
771}
772
773pub fn normalize_path(path: &Path) -> PathBuf {
774    let mut components = path.components().peekable();
775    let mut ret = if let Some(c @ Component::Prefix(..)) = components.peek().cloned() {
776        components.next();
777        PathBuf::from(c.as_os_str())
778    } else {
779        PathBuf::new()
780    };
781
782    for component in components {
783        match component {
784            Component::Prefix(..) => unreachable!(),
785            Component::RootDir => {
786                ret.push(component.as_os_str());
787            }
788            Component::CurDir => {}
789            Component::ParentDir => {
790                ret.pop();
791            }
792            Component::Normal(c) => {
793                ret.push(c);
794            }
795        }
796    }
797    ret
798}