1use super::{call_host, Errno, Operation, PointerArgs};
2
3use cstr_core::CString;
4
5use core::fmt;
6
7#[derive(Debug)]
8pub enum Error {
9 InvalidPath,
10 EndOfFile,
11 WriteError(usize),
12 Errno(Errno),
13}
14
15impl From<Errno> for Error {
16 fn from(errno: Errno) -> Self {
17 Error::Errno(errno)
18 }
19}
20
21pub struct Readable;
22pub struct Writeable;
23pub struct ReadWriteable;
24
25pub enum AccessType {
26 Binary,
27 Text,
28}
29
30pub struct File<MODE> {
31 fd: usize,
32 _pd: core::marker::PhantomData<MODE>,
33}
34
35#[repr(usize)]
36pub enum Mode {
37 Read = 0,
38 Write = 1,
39 Append = 2,
40}
41
42const BINARY_BIT_OFFSET: usize = 0;
43const READ_WRITE_BIT_OFFSET: usize = 1;
44const MODE_BIT_OFFSET: usize = 2;
45
46#[repr(C)]
47pub(crate) struct OpenArgs {
48 file_path: *const u8,
49 mode: usize,
50 length: usize,
51}
52
53impl<'a> PointerArgs for OpenArgs {}
54
55#[repr(C)]
56pub(crate) struct ReadArgs<'a> {
57 fd: usize,
58 buffer: &'a mut u8,
59 length: usize,
60}
61
62impl<'a> PointerArgs for ReadArgs<'a> {}
63
64#[repr(C)]
65pub(crate) struct WriteArgs<'a> {
66 fd: usize,
67 buffer: &'a u8,
68 length: usize,
69}
70
71impl<'a> PointerArgs for WriteArgs<'a> {}
72
73#[repr(C)]
74pub(crate) struct CloseArgs {
75 fd: usize,
76}
77
78impl PointerArgs for CloseArgs {}
79
80#[repr(C)]
81pub(crate) struct SeekArgs {
82 fd: usize,
83 offset: usize,
84}
85
86impl PointerArgs for SeekArgs {}
87
88#[repr(C)]
89pub(crate) struct FlenArgs {
90 fd: usize,
91}
92
93impl PointerArgs for FlenArgs {}
94
95#[repr(C)]
96pub(crate) struct RemoveArgs {
97 file_path: *const u8,
98 length: usize,
99}
100
101impl PointerArgs for RemoveArgs {}
102
103#[repr(C)]
104pub(crate) struct RenameArgs {
105 file_path: *const u8,
106 length: usize,
107 new_file_path: *const u8,
108 new_length: usize,
109}
110
111impl PointerArgs for RenameArgs {}
112
113fn open_with_mode(path: &str, mode: usize) -> Result<File<ReadWriteable>, Error> {
114 let cpath = match CString::new(path) {
115 Ok(path) => path,
116 Err(_) => return Err(Error::InvalidPath),
117 };
118
119 let mut op = Operation::Open(OpenArgs {
120 file_path: cpath.as_c_str() as *const _ as *const _,
121 mode,
122 length: path.len(),
123 });
124
125 let result = call_host(&mut op)?;
126
127 Ok(File {
128 fd: result,
129 _pd: core::marker::PhantomData,
130 })
131}
132
133pub fn open(path: &str, access_type: AccessType) -> Result<File<Readable>, Error> {
134 let binary = matches!(access_type, AccessType::Binary);
135 let mode =
136 ((Mode::Read as usize) << MODE_BIT_OFFSET) | ((binary as usize) << BINARY_BIT_OFFSET);
137
138 open_with_mode(path, mode).map(|file| file.as_readonly())
139}
140
141pub fn open_read_write(path: &str, access_type: AccessType) -> Result<File<ReadWriteable>, Error> {
142 let binary = matches!(access_type, AccessType::Binary);
143 let mode = ((Mode::Write as usize) << MODE_BIT_OFFSET)
144 | ((binary as usize) << BINARY_BIT_OFFSET)
145 | (1 << READ_WRITE_BIT_OFFSET);
146
147 open_with_mode(path, mode)
148}
149
150pub fn create(path: &str, access_type: AccessType) -> Result<File<Writeable>, Error> {
151 let binary = matches!(access_type, AccessType::Binary);
152 let mode =
153 ((Mode::Write as usize) << MODE_BIT_OFFSET) | ((binary as usize) << BINARY_BIT_OFFSET);
154
155 open_with_mode(path, mode).map(|file| file.as_writeonly())
156}
157
158pub fn append(path: &str, access_type: AccessType) -> Result<File<Writeable>, Error> {
159 let binary = matches!(access_type, AccessType::Binary);
160 let mode =
161 ((Mode::Append as usize) << MODE_BIT_OFFSET) | ((binary as usize) << BINARY_BIT_OFFSET);
162
163 open_with_mode(path, mode).map(|file| file.as_writeonly())
164}
165
166pub fn remove(path: &str) -> Result<(), Error> {
167 let cpath = match CString::new(path) {
168 Ok(path) => path,
169 Err(_) => return Err(Error::InvalidPath),
170 };
171
172 let mut op = Operation::Remove(RemoveArgs {
173 file_path: cpath.as_c_str() as *const _ as *const _,
174 length: path.len(),
175 });
176
177 call_host(&mut op)?;
178 Ok(())
179}
180
181pub fn rename(path: &str, new_path: &str) -> Result<(), Error> {
182 let cpath = match CString::new(path) {
183 Ok(path) => path,
184 Err(_) => return Err(Error::InvalidPath),
185 };
186
187 let new_cpath = match CString::new(new_path) {
188 Ok(path) => path,
189 Err(_) => return Err(Error::InvalidPath),
190 };
191
192 let mut op = Operation::Rename(RenameArgs {
193 file_path: cpath.as_c_str() as *const _ as *const _,
194 length: path.len(),
195 new_file_path: new_cpath.as_c_str() as *const _ as *const _,
196 new_length: new_path.len(),
197 });
198
199 call_host(&mut op)?;
200 Ok(())
201}
202
203impl<MODE> File<MODE> {
204 fn write_internal(&mut self, buffer: &[u8]) -> Result<(), Error> {
205 let length = buffer.len();
206 let mut op = Operation::Write(WriteArgs {
207 fd: self.fd,
208 buffer: &buffer[0],
209 length,
210 });
211
212 let result = call_host(&mut op)?;
213
214 if result != 0 {
215 Err(Error::WriteError(result))
216 } else {
217 Ok(())
218 }
219 }
220
221 fn read_internal(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
222 let length = buffer.len();
223 let mut op = Operation::Read(ReadArgs {
224 fd: self.fd,
225 buffer: &mut buffer[0],
226 length,
227 });
228
229 let result = call_host(&mut op)?;
230
231 if result == length {
232 Err(Error::EndOfFile)
233 } else {
234 Ok(length - result)
235 }
236 }
237
238 pub fn seek(&mut self, byte_offset: usize) -> Result<(), Error> {
239 let mut op = Operation::Seek(SeekArgs {
240 fd: self.fd,
241 offset: byte_offset,
242 });
243
244 call_host(&mut op)?;
245 Ok(())
246 }
247
248 pub fn length(&self) -> Result<usize, Error> {
249 let mut op = Operation::Flen(FlenArgs { fd: self.fd });
250
251 let result = call_host(&mut op)?;
252 Ok(result as usize)
253 }
254}
255
256impl File<Readable> {
257 pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
258 self.read_internal(buffer)
259 }
260}
261
262impl File<Writeable> {
263 pub fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
264 self.write_internal(buffer)
265 }
266}
267
268impl File<ReadWriteable> {
269 pub fn write(&mut self, buffer: &[u8]) -> Result<(), Error> {
270 self.write_internal(buffer)
271 }
272
273 pub fn read(&mut self, buffer: &mut [u8]) -> Result<usize, Error> {
274 self.read_internal(buffer)
275 }
276
277 pub fn as_readonly(self) -> File<Readable> {
278 let file = core::mem::ManuallyDrop::new(self);
279 File {
280 fd: file.fd,
281 _pd: core::marker::PhantomData,
282 }
283 }
284
285 pub fn as_writeonly(self) -> File<Writeable> {
286 let file = core::mem::ManuallyDrop::new(self);
287 File {
288 fd: file.fd,
289 _pd: core::marker::PhantomData,
290 }
291 }
292}
293
294impl<MODE> Drop for File<MODE> {
295 fn drop(&mut self) {
296 let mut op = Operation::Close(CloseArgs { fd: self.fd });
297 call_host(&mut op).ok();
298 }
299}
300
301impl fmt::Write for File<Writeable> {
302 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
303 self.write(s.as_bytes()).expect("No error writing files?");
304 Ok(())
305 }
306}
307
308impl fmt::Write for File<ReadWriteable> {
309 fn write_str(&mut self, s: &str) -> Result<(), fmt::Error> {
310 self.write(s.as_bytes()).expect("No error writing files?");
311 Ok(())
312 }
313}