Skip to main content

virtual_filesystem/
file.rs

1use std::fs;
2use std::io::{Read, Seek, Write};
3use std::path::PathBuf;
4
5/// The type of a file.
6#[derive(Debug, Copy, Clone, Eq, PartialEq)]
7pub enum FileType {
8    /// A directory.
9    Directory,
10    /// A file.
11    File,
12    /// The file type is unknown or unsupported.
13    Unknown,
14}
15
16impl From<fs::FileType> for FileType {
17    fn from(value: fs::FileType) -> Self {
18        if value.is_dir() {
19            Self::Directory
20        } else if value.is_file() {
21            Self::File
22        } else {
23            Self::Unknown
24        }
25    }
26}
27
28/// A directory entry.
29#[derive(Debug, Clone)]
30pub struct DirEntry {
31    /// The path to the file.
32    pub path: PathBuf,
33    /// Metadata about the file.
34    pub metadata: Metadata,
35}
36
37impl DirEntry {
38    /// Returns true if the entry is a directory.
39    pub fn is_directory(&self) -> bool {
40        self.metadata.is_directory()
41    }
42
43    /// Returns true if the entry is a file.
44    pub fn is_file(&self) -> bool {
45        self.metadata.is_directory()
46    }
47
48    /// Returns the length of the file, in bytes.
49    #[allow(clippy::len_without_is_empty)]
50    pub fn len(&self) -> u64 {
51        self.metadata.len()
52    }
53}
54
55/// Metadata about a file.
56#[derive(Debug, Clone, Eq, PartialEq)]
57pub struct Metadata {
58    /// True if the entry is a directory.
59    pub file_type: FileType,
60    /// The length of the file.
61    pub len: u64,
62}
63
64impl Metadata {
65    /// Returns metadata for a directory
66    pub fn directory() -> Self {
67        Self {
68            file_type: FileType::Directory,
69            len: 0,
70        }
71    }
72
73    /// Returns metadata for a file.
74    pub fn file(len: u64) -> Self {
75        Self {
76            file_type: FileType::File,
77            len,
78        }
79    }
80
81    /// Returns true if the entry is a directory.
82    pub fn is_directory(&self) -> bool {
83        self.file_type == FileType::Directory
84    }
85
86    /// Returns true if the entry is a file.
87    pub fn is_file(&self) -> bool {
88        self.file_type == FileType::File
89    }
90
91    /// Returns the length of the file, in bytes.
92    #[allow(clippy::len_without_is_empty)]
93    pub fn len(&self) -> u64 {
94        self.len
95    }
96}
97
98impl From<fs::Metadata> for Metadata {
99    fn from(value: fs::Metadata) -> Self {
100        let file_type = value.file_type().into();
101        Self {
102            len: if file_type == FileType::Directory { 0 } else { value.len() },
103            file_type,
104        }
105    }
106}
107
108/// Options for opening a file. The default mode is read-only.
109#[derive(Debug)]
110pub struct OpenOptions {
111    /// True if the file should be able to be appended to.
112    pub append: bool,
113    /// True if the file should be created if not present.
114    pub create: bool,
115    /// True if the file should be able to be read.
116    pub read: bool,
117    /// True if the file should be truncated.
118    pub truncate: bool,
119    /// True if the file should be written to.
120    pub write: bool,
121}
122
123impl From<&OpenOptions> for fs::OpenOptions {
124    fn from(value: &OpenOptions) -> Self {
125        Self::new()
126            .create(value.create)
127            .append(value.append)
128            .truncate(value.truncate)
129            .read(value.read)
130            .clone()
131    }
132}
133
134impl OpenOptions {
135    /// # Arguments
136    /// `append`: If true, the file should be opened with the cursor set to the end of the file,
137    /// rather than overwriting the file contents. Note that setting this to true will implicitly
138    /// enable writing.  
139    pub fn append(mut self, append: bool) -> Self {
140        if append {
141            self.write = true;
142        }
143        self.append = append;
144        self.truncate = !append;
145        self
146    }
147
148    /// # Arguments
149    /// `append`: If true, the file should be created if it does not exist. Note that setting this
150    /// to true will implicitly enable writing.  
151    pub fn create(mut self, create: bool) -> Self {
152        if create {
153            self.write = true;
154        }
155        self.create = true;
156        self
157    }
158
159    /// # Arguments
160    /// `read`: If true, the file should be able to be read in entirety.  
161    pub fn read(mut self, read: bool) -> Self {
162        self.read = read;
163        self
164    }
165
166    /// # Arguments
167    /// `truncate`: If true, the file should be opened with the cursor set to the beginning of the
168    /// file, overwriting all contents. Note that setting this to true will implicitly enable
169    /// writing.  
170    pub fn truncate(mut self, truncate: bool) -> Self {
171        if truncate {
172            self.write = true;
173        }
174        self.append = !truncate;
175        self.truncate = truncate;
176        self
177    }
178
179    /// # Arguments
180    /// `write`: If true, the file should be able to be written. By default, this will truncate
181    /// the contents of the file, unless `append` is set.
182    pub fn write(mut self, write: bool) -> Self {
183        self.write = write;
184        self
185    }
186}
187
188impl Default for OpenOptions {
189    fn default() -> Self {
190        Self {
191            append: false,
192            create: false,
193            read: true,
194            truncate: false,
195            write: false,
196        }
197    }
198}
199
200/// A file that can be read.
201pub trait File: Read + Write + Seek {
202    /// Returns the directory entry for the file.
203    fn metadata(&self) -> crate::Result<Metadata>;
204
205    /// Reads a file into a vector.
206    fn read_into_vec(&mut self) -> crate::Result<Vec<u8>> {
207        let mut vec = Vec::with_capacity(self.metadata()?.len() as usize);
208        self.read_to_end(&mut vec)?;
209        Ok(vec)
210    }
211
212    /// Reads a file into a string.
213    fn read_into_string(&mut self) -> crate::Result<String> {
214        let mut str = String::with_capacity(self.metadata()?.len() as usize);
215        self.read_to_string(&mut str)?;
216        Ok(str)
217    }
218}