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}