maelstrom_util/cache/fs/
std.rs1use 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#[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 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}