1#![cfg_attr(not(test), no_std)]
2#![feature(const_trait_impl)]
3
4#[macro_use]
5extern crate sys;
6extern crate alloc;
7
8use core::ffi::c_char;
9use core::ffi::c_int;
10use core::ffi::c_uint;
11use core::ffi::c_void;
12use alloc::string::String;
13use alloc::vec::Vec;
14
15use error::Error;
16use file::AnyFile;
17use options::FileOptionsExt;
18use options::OpenOptions;
19use seek::Whence;
20pub use sys::ffi::FileStat;
21pub use sys::ffi::FileOptions;
22use sys::ffi::CString;
23use sys::ffi::CStr;
24
25use file::File;
26use error::ApiError;
27
28
29pub mod api;
30pub mod file;
31pub mod seek;
32pub mod options;
33pub mod error;
34
35
36pub type Path = str;
37
38
39pub fn read<P: AsRef<Path>>(path: P, data_dir: bool) -> Result<Vec<u8>, ApiError> {
42 let fs = Fs::Cached();
43 let opts = FileOptions::new().read(true).read_data(data_dir);
44 let mut file = fs.open_with(api::Default, &path, opts)?;
45
46 let size = fs.metadata(path).map(|m| m.size).ok().unwrap_or(0);
48
49 let mut buf = alloc::vec![0; size as usize];
51
52 fs.read(&mut file, &mut buf, size)?;
53 Ok(buf)
54}
55
56
57pub fn read_to_string<P: AsRef<Path>>(path: P, data_dir: bool) -> Result<String, ApiError> {
60 let buf = read(path, data_dir)?;
61 alloc::string::String::from_utf8(buf).map_err(Into::into)
62}
63
64
65pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<(), ApiError> {
74 let mut file = File::options().write(true).append(false).open(&path)?;
75 file.write(contents.as_ref())?;
76 Ok(())
77}
78
79
80#[inline(always)]
81pub fn remove<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::Default().remove(path) }
87
88
89#[inline(always)]
93pub fn metadata<P: AsRef<Path>>(path: P) -> Result<FileStat, ApiError> { Fs::Default().metadata(path) }
94
95
96#[inline(always)]
102pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<(), ApiError> {
103 Fs::Default().rename(from, to)
104}
105
106#[inline(always)]
110pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::Default().create_dir(path) }
111
112#[inline(always)]
117pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::Default().remove_dir_all(path) }
125
126#[derive(Debug, Clone, Copy)]
134pub struct Fs<Api = api::Default>(Api);
135
136impl Fs<api::Default> {
137 #[allow(non_snake_case)]
141 pub fn Default() -> Self { Self(Default::default()) }
142}
143
144impl Fs<api::Cache> {
145 #[allow(non_snake_case)]
149 pub fn Cached() -> Self { Self(Default::default()) }
150}
151
152impl<Api: Default + api::Api> Default for Fs<Api> {
153 fn default() -> Self { Self(Default::default()) }
154}
155
156impl<Api: Default + api::Api> Fs<Api> {
157 pub fn new() -> Self { Self(Default::default()) }
158}
159
160impl<Api: api::Api> Fs<Api> {
161 pub fn new_with(api: Api) -> Self { Self(api) }
162}
163
164
165mod ops {
166 use super::*;
167
168 pub fn open<Api: api::Api, P: AsRef<Path>, Opts: OpenOptions>(api: Api,
169 path: P,
170 options: Opts)
171 -> Result<File<Api>, ApiError> {
172 let path = CString::new(path.as_ref())?;
173 let f = api.open();
174 let ptr = unsafe { f(path.as_ptr() as _, options.into()) };
175 Ok(File(ptr as _, api))
176 }
177
178 pub fn open_with<UApi: api::Api, FApi: api::Api, P: AsRef<Path>, Opts: OpenOptions>(
179 using: UApi,
180 api: FApi,
181 path: P,
182 options: Opts)
183 -> Result<File<FApi>, ApiError> {
184 let path = CString::new(path.as_ref())?;
185 let f = using.open();
186 let ptr = unsafe { f(path.as_ptr() as _, options.into()) };
187 Ok(File(ptr as _, api))
188 }
189
190 pub fn close<Api: api::Api>(mut file: File<Api>) -> Result<(), Error> {
191 let f = file.1.close();
192 let result = unsafe { f(file.0 as _) };
193 file.0 = core::ptr::null_mut();
194 Error::ok_from_code(result)?;
195 Ok(())
196 }
197
198 pub fn close_with<Api: api::Api, FApi: api::Api>(api: Api, mut file: File<FApi>) -> Result<(), Error> {
199 let f = api.close();
200 let result = unsafe { f(file.0) };
201 file.0 = core::ptr::null_mut();
202 Error::ok_from_code(result)?;
203 Ok(())
204 }
205
206 pub fn seek<Api: api::Api>(file: &mut File<Api>, pos: c_int, whence: Whence) -> Result<(), Error> {
207 let f = file.1.seek();
208 let result = unsafe { f(file.0, pos, whence as _) };
209 Error::ok_from_code(result)?;
210 Ok(())
211 }
212
213 pub fn seek_with<Api: api::Api>(api: Api,
214 file: &mut impl AnyFile,
215 pos: c_int,
216 whence: Whence)
217 -> Result<(), Error> {
218 let f = api.seek();
219 let result = unsafe { f(file.as_raw(), pos, whence as _) };
220 Error::ok_from_code(result)?;
221 Ok(())
222 }
223
224 pub fn tell<Api: api::Api>(file: &mut File<Api>) -> Result<c_uint, Error> {
225 let f = file.1.tell();
226 let result = unsafe { f(file.0) };
227 Error::ok_from_code(result)
228 }
229
230 pub fn tell_with<Api: api::Api>(api: Api, file: &mut impl AnyFile) -> Result<c_uint, Error> {
231 let f = api.tell();
232 let result = unsafe { f(file.as_raw()) };
233 Error::ok_from_code(result)
234 }
235
236
237 pub fn read<Api: api::Api>(file: &mut File<Api>, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, Error> {
238 let f = file.1.read();
239 let result = unsafe { f(file.0, to.as_mut_ptr() as *mut _, len) };
240 Error::ok_from_code(result)
241 }
242
243 pub fn write<Api: api::Api>(file: &mut File<Api>, from: &[u8]) -> Result<c_uint, Error> {
244 let f = file.1.write();
245 let result = unsafe { f(file.0, from.as_ptr() as *mut _, from.len() as _) };
246 Error::ok_from_code(result)
247 }
248
249 pub fn flush<Api: api::Api>(file: &mut File<Api>) -> Result<c_uint, Error> {
250 let f = file.1.flush();
251 let result = unsafe { f(file.0) };
252 Error::ok_from_code(result)
253 }
254}
255
256
257impl<Api: api::Api> Fs<Api> {
258 #[doc(alias = "sys::ffi::playdate_file::open")]
264 #[inline(always)]
265 pub fn open<P: AsRef<Path>, Opts: OpenOptions>(&self, path: P, options: Opts) -> Result<File<Api>, ApiError>
266 where Api: Copy {
267 ops::open(self.0, path, options)
268 }
269
270 #[doc(alias = "sys::ffi::playdate_file::open")]
276 #[inline(always)]
277 pub fn open_with<T: api::Api, P: AsRef<Path>, Opts: OpenOptions>(&self,
278 api: T,
279 path: P,
280 options: Opts)
281 -> Result<File<T>, ApiError> {
282 ops::open_with(&self.0, api, path, options)
283 }
284
285 #[doc(alias = "sys::ffi::playdate_file::close")]
289 #[inline(always)]
290 pub fn close<T: api::Api>(&self, file: File<T>) -> Result<(), Error> { ops::close_with(&self.0, file) }
291
292
293 #[doc(alias = "sys::ffi::playdate_file::tell")]
297 #[inline(always)]
298 pub fn tell(&self, file: &mut impl AnyFile) -> Result<c_uint, Error> { crate::ops::tell_with(&self.0, file) }
299
300 #[doc(alias = "sys::ffi::playdate_file::seek")]
307 #[inline(always)]
308 pub fn seek_raw(&self, file: &mut impl AnyFile, pos: c_int, whence: Whence) -> Result<(), Error> {
309 crate::ops::seek_with(&self.0, file, pos, whence)
310 }
311
312
313 #[doc(alias = "sys::ffi::playdate_file::read")]
326 pub fn read(&self, file: &mut impl AnyFile, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, Error> {
327 let f = self.0.read();
328 let result = unsafe { f(file.as_raw(), to.as_mut_ptr() as *mut _, len) };
329 Error::ok_from_code(result)
330 }
331
332
333 #[doc(alias = "sys::ffi::playdate_file::write")]
339 pub fn write(&self, file: &mut impl AnyFile, from: &[u8]) -> Result<c_uint, Error> {
340 let f = self.0.write();
341 let result = unsafe { f(file.as_raw(), from.as_ptr() as *mut _, from.len() as _) };
342 Error::ok_from_code(result)
343 }
344
345 #[doc(alias = "sys::ffi::playdate_file::flush")]
351 pub fn flush(&self, file: &mut impl AnyFile) -> Result<c_uint, Error> {
352 let f = self.0.flush();
353 let result = unsafe { f(file.as_raw()) };
354 Error::ok_from_code(result)
355 }
356
357
358 #[doc(alias = "sys::ffi::playdate_file::stat")]
362 pub fn metadata<P: AsRef<Path>>(&self, path: P) -> Result<FileStat, ApiError> {
363 let mut stat = FileStat { isdir: 0,
364 size: 0,
365 m_year: 0,
366 m_month: 0,
367 m_day: 0,
368 m_hour: 0,
369 m_minute: 0,
370 m_second: 0 };
371 self.metadata_to(path, &mut stat).map(|_| stat)
372 }
373
374 #[doc(alias = "sys::ffi::playdate_file::stat")]
378 pub fn metadata_to<P: AsRef<Path>>(&self, path: P, metadata: &mut FileStat) -> Result<(), ApiError> {
379 let path = CString::new(path.as_ref())?;
380 let f = self.0.stat();
381 let result = unsafe { f(path.as_ptr() as _, metadata as *mut _) };
382 Error::ok_from_code(result)?;
383 Ok(())
384 }
385
386
387 #[doc(alias = "sys::ffi::playdate_file::mkdir")]
395 pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
396 let path = CString::new(path.as_ref())?;
397 let f = self.0.mkdir();
398 let result = unsafe { f(path.as_ptr() as _) };
399 Error::ok_from_code(result)?;
400 Ok(())
401 }
402
403 #[doc(alias = "sys::ffi::playdate_file::unlink")]
410 pub fn remove<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
411 let path = CString::new(path.as_ref())?;
412 let f = self.0.unlink();
413 let result = unsafe { f(path.as_ptr() as _, 0) };
414 Error::ok_from_code(result)?;
415 Ok(())
416 }
417
418 #[doc(alias = "sys::ffi::playdate_file::unlink")]
425 pub fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
426 let path = CString::new(path.as_ref())?;
427 let f = self.0.unlink();
428 let result = unsafe { f(path.as_ptr() as _, 1) };
429 Error::ok_from_code(result)?;
430 Ok(())
431 }
432
433 #[doc(alias = "sys::ffi::playdate_file::rename")]
441 pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> Result<(), ApiError> {
442 let from = CString::new(from.as_ref())?;
443 let to = CString::new(to.as_ref())?;
444 let f = self.0.rename();
445 let result = unsafe { f(from.as_ptr() as _, to.as_ptr() as _) };
446 Error::ok_from_code(result)?;
447 Ok(())
448 }
449
450
451 #[doc(alias = "sys::ffi::playdate_file::listfiles")]
466 pub fn read_dir<P, Fn>(&self, path: P, mut callback: Fn, include_hidden: bool) -> Result<(), ApiError>
467 where P: AsRef<Path>,
468 Fn: FnMut(String) {
469 unsafe extern "C" fn proxy<Fn: FnMut(String)>(filename: *const c_char, userdata: *mut c_void) {
470 let filename = CStr::from_ptr(filename as _).to_string_lossy().into_owned();
472
473 if let Some(callback) = (userdata as *mut _ as *mut Fn).as_mut() {
474 callback(filename);
475 } else {
476 panic!("Fs.read_dir: missed callback");
477 }
478 }
479
480 let path = CString::new(path.as_ref())?;
481
482 let callback_ref = (&mut callback) as *mut Fn as *mut _;
484
485 let f = self.0.listfiles();
486 let result = unsafe {
487 f(
488 path.as_ptr() as _,
489 Some(proxy::<Fn>),
490 callback_ref,
491 include_hidden as _,
492 )
493 };
494 Error::ok_from_code(result)?;
495 Ok(())
496 }
497}
498
499
500pub mod prelude {
501 pub use sys::ffi::FileStat;
502 pub use sys::ffi::FileOptions;
503 pub use crate::error::ApiError as FsApiError;
504 pub use crate::error::Error as FsError;
505 pub use crate::Path;
506 pub use crate::Fs;
507 pub use crate::file::*;
508 pub use crate::options::*;
509 pub use crate::seek::SeekFrom;
510}