Skip to main content

maelstrom_util/cache/fs/
std.rs

1use super::Metadata;
2use fs2::FileExt as _;
3use std::{
4    ffi::OsString,
5    fmt::Debug,
6    fs::{self, File, OpenOptions},
7    io::{self, ErrorKind, Read as _, Write as _},
8    os::unix::fs as unix_fs,
9    path::{Path, PathBuf},
10    thread,
11};
12use tempfile::{self, NamedTempFile};
13
14/// The standard implementation of CacheFs that uses [std] and [rand].
15#[derive(Clone)]
16pub struct Fs;
17
18impl super::Fs for Fs {
19    type Error = io::Error;
20
21    fn rand_u64(&self) -> u64 {
22        rand::random()
23    }
24
25    fn metadata(&self, path: &Path) -> io::Result<Option<Metadata>> {
26        match fs::symlink_metadata(path) {
27            Ok(metadata) => Ok(Some(metadata.into())),
28            Err(err) if err.kind() == ErrorKind::NotFound => Ok(None),
29            Err(err) => Err(err),
30        }
31    }
32
33    fn read_file(&self, path: &Path, contents: &mut [u8]) -> io::Result<usize> {
34        File::open(path)?.read(contents)
35    }
36
37    fn read_dir(
38        &self,
39        path: &Path,
40    ) -> io::Result<impl Iterator<Item = io::Result<(OsString, Metadata)>>> {
41        fs::read_dir(path).map(|dirents| {
42            dirents.map(|dirent| -> io::Result<_> {
43                let dirent = dirent?;
44                Ok((dirent.file_name(), dirent.metadata()?.into()))
45            })
46        })
47    }
48
49    fn create_file(&self, path: &Path, contents: &[u8]) -> io::Result<()> {
50        File::create_new(path)?.write_all(contents)
51    }
52
53    type FileLock = FileLock;
54
55    fn possibly_create_file_and_try_exclusive_lock(
56        &self,
57        path: &Path,
58    ) -> io::Result<Option<FileLock>> {
59        let file = OpenOptions::new()
60            .read(true)
61            .write(true)
62            .create(true)
63            .truncate(false)
64            .open(path)?;
65        match file.try_lock_exclusive() {
66            Ok(()) => Ok(Some(FileLock { _file: file })),
67            Err(err) => {
68                if err.kind() == fs2::lock_contended_error().kind() {
69                    Ok(None)
70                } else {
71                    Err(err)
72                }
73            }
74        }
75    }
76
77    fn symlink(&self, target: &Path, link: &Path) -> io::Result<()> {
78        unix_fs::symlink(target, link)
79    }
80
81    fn mkdir(&self, path: &Path) -> io::Result<()> {
82        fs::create_dir(path)
83    }
84
85    fn mkdir_recursively(&self, path: &Path) -> io::Result<()> {
86        fs::create_dir_all(path)
87    }
88
89    fn remove(&self, path: &Path) -> io::Result<()> {
90        fs::remove_file(path)
91    }
92
93    fn rmdir_recursively_on_thread(&self, path: PathBuf) -> io::Result<()> {
94        if !fs::metadata(&path)?.is_dir() {
95            // We want ErrorKind::NotADirectory, but it's currently unstable.
96            Err(io::Error::from(ErrorKind::InvalidInput))
97        } else {
98            thread::spawn(move || {
99                let _ = fs::remove_dir_all(path);
100            });
101            Ok(())
102        }
103    }
104
105    fn rename(&self, source: &Path, destination: &Path) -> io::Result<()> {
106        fs::rename(source, destination)
107    }
108
109    type TempFile = TempFile;
110
111    fn temp_file(&self, parent: &Path) -> io::Result<Self::TempFile> {
112        Ok(TempFile(NamedTempFile::new_in(parent)?.into_temp_path()))
113    }
114
115    fn persist_temp_file(&self, temp_file: Self::TempFile, target: &Path) -> io::Result<()> {
116        Ok(temp_file.0.persist(target)?)
117    }
118
119    type TempDir = TempDir;
120
121    fn temp_dir(&self, parent: &Path) -> io::Result<Self::TempDir> {
122        Ok(TempDir(tempfile::TempDir::new_in(parent)?))
123    }
124
125    fn persist_temp_dir(&self, temp_dir: Self::TempDir, target: &Path) -> io::Result<()> {
126        fs::rename(temp_dir.0.into_path(), target)
127    }
128}
129
130pub struct FileLock {
131    _file: File,
132}
133
134#[derive(Debug)]
135pub struct TempFile(tempfile::TempPath);
136
137impl super::TempFile for TempFile {
138    fn path(&self) -> &Path {
139        &self.0
140    }
141}
142
143#[derive(Debug)]
144pub struct TempDir(tempfile::TempDir);
145
146impl super::TempDir for TempDir {
147    fn path(&self) -> &Path {
148        self.0.path()
149    }
150}