1use std::io::{Read, Seek, Write};
4
5use crate::{
6 error::{Error, Operation, Result},
7 path::{Path, PathBuf},
8};
9
10#[cfg(unix)]
11const EXECUTABLE_MODE: u32 = 0o755;
12
13#[derive(Debug)]
15pub struct File {
16 inner: std::fs::File,
17 path: PathBuf,
18}
19
20impl File {
21 pub(crate) fn from_open_file(inner: std::fs::File, path: PathBuf) -> Self {
22 Self { inner, path }
23 }
24
25 #[must_use]
27 pub fn path(&self) -> &Path {
28 &self.path
29 }
30
31 pub fn sync(&self) -> Result<()> {
37 self.inner
38 .sync_all()
39 .map_err(|source| Error::new(Operation::Sync, &self.path, source))
40 }
41
42 #[cfg(unix)]
43 pub(crate) fn make_executable(&self) -> Result<()> {
44 use std::os::unix::fs::PermissionsExt as _;
45
46 self.inner
47 .set_permissions(std::fs::Permissions::from_mode(EXECUTABLE_MODE))
48 .map_err(|source| Error::new(Operation::SecureFile, &self.path, source))
49 }
50
51 #[cfg(all(not(unix), not(target_family = "wasm")))]
52 pub(crate) fn make_executable(&self) -> Result<()> {
53 Ok(())
54 }
55}
56
57impl Read for File {
58 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
59 self.inner.read(buf)
60 }
61}
62
63impl Write for File {
64 fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
65 self.inner.write(buf)
66 }
67
68 fn flush(&mut self) -> std::io::Result<()> {
69 self.inner.flush()
70 }
71}
72
73impl Seek for File {
74 fn seek(&mut self, pos: std::io::SeekFrom) -> std::io::Result<u64> {
75 self.inner.seek(pos)
76 }
77}
78
79pub fn open(path: &Path) -> Result<File> {
85 std::fs::File::open(path)
86 .map(|inner| File::from_open_file(inner, path.to_path_buf()))
87 .map_err(|source| Error::new(Operation::OpenFile, path, source))
88}
89
90pub fn create(path: &Path) -> Result<File> {
96 std::fs::File::create(path)
97 .map(|inner| File::from_open_file(inner, path.to_path_buf()))
98 .map_err(|source| Error::new(Operation::CreateFile, path, source))
99}
100
101pub fn create_new(path: &Path) -> Result<File> {
107 std::fs::OpenOptions::new()
108 .write(true)
109 .create_new(true)
110 .open(path)
111 .map(|inner| File::from_open_file(inner, path.to_path_buf()))
112 .map_err(|source| Error::new(Operation::CreateFile, path, source))
113}
114
115pub fn read_bytes(path: &Path) -> Result<Vec<u8>> {
121 std::fs::read(path).map_err(|source| Error::new(Operation::ReadFile, path, source))
122}
123
124pub fn read_text(path: &Path) -> Result<String> {
130 std::fs::read_to_string(path).map_err(|source| Error::new(Operation::ReadFile, path, source))
131}
132
133pub fn read_text_if_exists(path: &Path) -> Result<Option<String>> {
139 match read_text(path) {
140 Ok(contents) => Ok(Some(contents)),
141 Err(error) if error.is_not_found() => Ok(None),
142 Err(error) => Err(error),
143 }
144}
145
146pub fn write_bytes(path: &Path, contents: impl AsRef<[u8]>) -> Result<()> {
154 std::fs::write(path, contents).map_err(|source| Error::new(Operation::WriteFile, path, source))
155}
156
157pub fn write_text(path: &Path, contents: &str) -> Result<()> {
163 write_bytes(path, contents)
164}