Skip to main content

ax_fs/api/
file.rs

1// Copyright 2025 The Axvisor Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use core::fmt;
16
17use ax_io::{Result, SeekFrom, prelude::*};
18
19use crate::fops;
20
21/// A structure representing a type of file with accessors for each file type.
22/// It is returned by [`Metadata::file_type`] method.
23pub type FileType = fops::FileType;
24
25/// Representation of the various permissions on a file.
26pub type Permissions = fops::FilePerm;
27
28/// An object providing access to an open file on the filesystem.
29pub struct File {
30    inner: fops::File,
31}
32
33/// Metadata information about a file.
34pub struct Metadata(fops::FileAttr);
35
36/// Options and flags which can be used to configure how a file is opened.
37#[derive(Clone, Debug)]
38pub struct OpenOptions(fops::OpenOptions);
39
40impl Default for OpenOptions {
41    fn default() -> Self {
42        Self::new()
43    }
44}
45
46impl OpenOptions {
47    /// Creates a blank new set of options ready for configuration.
48    pub const fn new() -> Self {
49        OpenOptions(fops::OpenOptions::new())
50    }
51
52    /// Sets the option for read access.
53    pub fn read(&mut self, read: bool) -> &mut Self {
54        self.0.read(read);
55        self
56    }
57
58    /// Sets the option for write access.
59    pub fn write(&mut self, write: bool) -> &mut Self {
60        self.0.write(write);
61        self
62    }
63
64    /// Sets the option for the append mode.
65    pub fn append(&mut self, append: bool) -> &mut Self {
66        self.0.append(append);
67        self
68    }
69
70    /// Sets the option for truncating a previous file.
71    pub fn truncate(&mut self, truncate: bool) -> &mut Self {
72        self.0.truncate(truncate);
73        self
74    }
75
76    /// Sets the option to create a new file, or open it if it already exists.
77    pub fn create(&mut self, create: bool) -> &mut Self {
78        self.0.create(create);
79        self
80    }
81
82    /// Sets the option to create a new file, failing if it already exists.
83    pub fn create_new(&mut self, create_new: bool) -> &mut Self {
84        self.0.create_new(create_new);
85        self
86    }
87
88    /// Opens a file at `path` with the options specified by `self`.
89    pub fn open(&self, path: &str) -> Result<File> {
90        fops::File::open(path, &self.0).map(|inner| File { inner })
91    }
92}
93
94impl Metadata {
95    /// Returns the file type for this metadata.
96    pub const fn file_type(&self) -> FileType {
97        self.0.file_type()
98    }
99
100    /// Returns `true` if this metadata is for a directory. The
101    /// result is mutually exclusive to the result of
102    /// [`Metadata::is_file`].
103    pub const fn is_dir(&self) -> bool {
104        self.0.is_dir()
105    }
106
107    /// Returns `true` if this metadata is for a regular file. The
108    /// result is mutually exclusive to the result of
109    /// [`Metadata::is_dir`].
110    pub const fn is_file(&self) -> bool {
111        self.0.is_file()
112    }
113
114    /// Returns the size of the file, in bytes, this metadata is for.
115    #[allow(clippy::len_without_is_empty)]
116    pub const fn len(&self) -> u64 {
117        self.0.size()
118    }
119
120    /// Returns the permissions of the file this metadata is for.
121    pub const fn permissions(&self) -> Permissions {
122        self.0.perm()
123    }
124
125    /// Returns the total size of this file in bytes.
126    pub const fn size(&self) -> u64 {
127        self.0.size()
128    }
129
130    /// Returns the number of blocks allocated to the file, in 512-byte units.
131    pub const fn blocks(&self) -> u64 {
132        self.0.blocks()
133    }
134}
135
136impl fmt::Debug for Metadata {
137    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
138        f.debug_struct("Metadata")
139            .field("file_type", &self.file_type())
140            .field("is_dir", &self.is_dir())
141            .field("is_file", &self.is_file())
142            .field("permissions", &self.permissions())
143            .finish_non_exhaustive()
144    }
145}
146
147impl File {
148    /// Attempts to open a file in read-only mode.
149    pub fn open(path: &str) -> Result<Self> {
150        OpenOptions::new().read(true).open(path)
151    }
152
153    /// Opens a file in write-only mode.
154    pub fn create(path: &str) -> Result<Self> {
155        OpenOptions::new()
156            .write(true)
157            .create(true)
158            .truncate(true)
159            .open(path)
160    }
161
162    /// Creates a new file in read-write mode; error if the file exists.
163    pub fn create_new(path: &str) -> Result<Self> {
164        OpenOptions::new()
165            .read(true)
166            .write(true)
167            .create_new(true)
168            .open(path)
169    }
170
171    /// Returns a new OpenOptions object.
172    pub fn options() -> OpenOptions {
173        OpenOptions::new()
174    }
175
176    /// Truncates or extends the underlying file, updating the size of
177    /// this file to become `size`.
178    pub fn set_len(&self, size: u64) -> Result<()> {
179        self.inner.truncate(size)
180    }
181
182    /// Queries metadata about the underlying file.
183    pub fn metadata(&self) -> Result<Metadata> {
184        self.inner.get_attr().map(Metadata)
185    }
186}
187
188impl Read for File {
189    fn read(&mut self, buf: &mut [u8]) -> Result<usize> {
190        self.inner.read(buf)
191    }
192}
193
194impl Write for File {
195    fn write(&mut self, buf: &[u8]) -> Result<usize> {
196        self.inner.write(buf)
197    }
198
199    fn flush(&mut self) -> Result<()> {
200        self.inner.flush()
201    }
202}
203
204impl Seek for File {
205    fn seek(&mut self, pos: SeekFrom) -> Result<u64> {
206        self.inner.seek(pos)
207    }
208}