#![cfg_attr(not(test), no_std)]
#![feature(error_in_core)]
#![feature(const_trait_impl)]
#[macro_use]
extern crate sys;
extern crate alloc;
use core::ffi::c_char;
use core::ffi::c_int;
use core::ffi::c_uint;
use core::ffi::c_void;
use alloc::string::String;
use alloc::vec::Vec;
use error::Error;
use file::AnyFile;
use options::FileOptionsExt;
use options::OpenOptions;
use seek::Whence;
pub use sys::ffi::FileStat;
pub use sys::ffi::FileOptions;
use sys::ffi::CString;
use sys::ffi::CStr;
use file::File;
use error::ApiError;
pub mod api;
pub mod file;
pub mod seek;
pub mod options;
pub mod error;
pub type Path = str;
pub fn read<P: AsRef<Path>>(path: P, data_dir: bool) -> Result<Vec<u8>, ApiError> {
let fs = Fs::Cached();
let opts = FileOptions::new().read(true).read_data(data_dir);
let mut file = fs.open_with(api::Default, &path, opts)?;
let size = fs.metadata(path).map(|m| m.size).ok().unwrap_or(0);
let mut buf = alloc::vec![0; size as usize];
fs.read(&mut file, &mut buf, size)?;
Ok(buf)
}
pub fn read_to_string<P: AsRef<Path>>(path: P, data_dir: bool) -> Result<String, ApiError> {
let buf = read(path, data_dir)?;
alloc::string::String::from_utf8(buf).map_err(Into::into)
}
pub fn write<P: AsRef<Path>, C: AsRef<[u8]>>(path: P, contents: C) -> Result<(), ApiError> {
let mut file = File::options().write(true).append(false).open(&path)?;
file.write(contents.as_ref())?;
Ok(())
}
#[inline(always)]
pub fn remove<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::Default().remove(path) }
#[inline(always)]
pub fn metadata<P: AsRef<Path>>(path: P) -> Result<FileStat, ApiError> { Fs::Default().metadata(path) }
#[inline(always)]
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<(), ApiError> {
Fs::Default().rename(from, to)
}
#[inline(always)]
pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::Default().create_dir(path) }
#[inline(always)]
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::Default().remove_dir_all(path) }
#[derive(Debug, Clone, Copy)]
pub struct Fs<Api = api::Default>(Api);
impl Fs<api::Default> {
#[allow(non_snake_case)]
pub fn Default() -> Self { Self(Default::default()) }
}
impl Fs<api::Cache> {
#[allow(non_snake_case)]
pub fn Cached() -> Self { Self(Default::default()) }
}
impl<Api: Default + api::Api> Default for Fs<Api> {
fn default() -> Self { Self(Default::default()) }
}
impl<Api: Default + api::Api> Fs<Api> {
pub fn new() -> Self { Self(Default::default()) }
}
impl<Api: api::Api> Fs<Api> {
pub fn new_with(api: Api) -> Self { Self(api) }
}
mod ops {
use super::*;
pub fn open<Api: api::Api, P: AsRef<Path>, Opts: OpenOptions>(api: Api,
path: P,
options: Opts)
-> Result<File<Api>, ApiError> {
let path = CString::new(path.as_ref())?;
let f = api.open();
let ptr = unsafe { f(path.as_ptr() as _, options.into()) };
Ok(File(ptr as _, api))
}
pub fn open_with<UApi: api::Api, FApi: api::Api, P: AsRef<Path>, Opts: OpenOptions>(
using: UApi,
api: FApi,
path: P,
options: Opts)
-> Result<File<FApi>, ApiError> {
let path = CString::new(path.as_ref())?;
let f = using.open();
let ptr = unsafe { f(path.as_ptr() as _, options.into()) };
Ok(File(ptr as _, api))
}
pub fn close<Api: api::Api>(mut file: File<Api>) -> Result<(), Error> {
let f = file.1.close();
let result = unsafe { f(file.0 as _) };
file.0 = core::ptr::null_mut();
Error::ok_from_code(result)?;
Ok(())
}
pub fn close_with<Api: api::Api, FApi: api::Api>(api: Api, mut file: File<FApi>) -> Result<(), Error> {
let f = api.close();
let result = unsafe { f(file.0) };
file.0 = core::ptr::null_mut();
Error::ok_from_code(result)?;
Ok(())
}
pub fn seek<Api: api::Api>(file: &mut File<Api>, pos: c_int, whence: Whence) -> Result<(), Error> {
let f = file.1.seek();
let result = unsafe { f(file.0, pos, whence as _) };
Error::ok_from_code(result)?;
Ok(())
}
pub fn seek_with<Api: api::Api>(api: Api,
file: &mut impl AnyFile,
pos: c_int,
whence: Whence)
-> Result<(), Error> {
let f = api.seek();
let result = unsafe { f(file.as_raw(), pos, whence as _) };
Error::ok_from_code(result)?;
Ok(())
}
pub fn tell<Api: api::Api>(file: &mut File<Api>) -> Result<c_uint, Error> {
let f = file.1.tell();
let result = unsafe { f(file.0) };
Error::ok_from_code(result)
}
pub fn tell_with<Api: api::Api>(api: Api, file: &mut impl AnyFile) -> Result<c_uint, Error> {
let f = api.tell();
let result = unsafe { f(file.as_raw()) };
Error::ok_from_code(result)
}
pub fn read<Api: api::Api>(file: &mut File<Api>, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, Error> {
let f = file.1.read();
let result = unsafe { f(file.0, to.as_mut_ptr() as *mut _, len) };
Error::ok_from_code(result)
}
pub fn write<Api: api::Api>(file: &mut File<Api>, from: &[u8]) -> Result<c_uint, Error> {
let f = file.1.write();
let result = unsafe { f(file.0, from.as_ptr() as *mut _, from.len() as _) };
Error::ok_from_code(result)
}
pub fn flush<Api: api::Api>(file: &mut File<Api>) -> Result<c_uint, Error> {
let f = file.1.flush();
let result = unsafe { f(file.0) };
Error::ok_from_code(result)
}
}
impl<Api: api::Api> Fs<Api> {
#[doc(alias = "sys::ffi::playdate_file::open")]
#[inline(always)]
pub fn open<P: AsRef<Path>, Opts: OpenOptions>(&self, path: P, options: Opts) -> Result<File<Api>, ApiError>
where Api: Copy {
ops::open(self.0, path, options)
}
#[doc(alias = "sys::ffi::playdate_file::open")]
#[inline(always)]
pub fn open_with<T: api::Api, P: AsRef<Path>, Opts: OpenOptions>(&self,
api: T,
path: P,
options: Opts)
-> Result<File<T>, ApiError> {
ops::open_with(&self.0, api, path, options)
}
#[doc(alias = "sys::ffi::playdate_file::close")]
#[inline(always)]
pub fn close<T: api::Api>(&self, file: File<T>) -> Result<(), Error> { ops::close_with(&self.0, file) }
#[doc(alias = "sys::ffi::playdate_file::tell")]
#[inline(always)]
pub fn tell(&self, file: &mut impl AnyFile) -> Result<c_uint, Error> { crate::ops::tell_with(&self.0, file) }
#[doc(alias = "sys::ffi::playdate_file::seek")]
#[inline(always)]
pub fn seek_raw(&self, file: &mut impl AnyFile, pos: c_int, whence: Whence) -> Result<(), Error> {
crate::ops::seek_with(&self.0, file, pos, whence)
}
#[doc(alias = "sys::ffi::playdate_file::read")]
pub fn read(&self, file: &mut impl AnyFile, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, Error> {
let f = self.0.read();
let result = unsafe { f(file.as_raw(), to.as_mut_ptr() as *mut _, len) };
Error::ok_from_code(result)
}
#[doc(alias = "sys::ffi::playdate_file::write")]
pub fn write(&self, file: &mut impl AnyFile, from: &[u8]) -> Result<c_uint, Error> {
let f = self.0.write();
let result = unsafe { f(file.as_raw(), from.as_ptr() as *mut _, from.len() as _) };
Error::ok_from_code(result)
}
#[doc(alias = "sys::ffi::playdate_file::flush")]
pub fn flush(&self, file: &mut impl AnyFile) -> Result<c_uint, Error> {
let f = self.0.flush();
let result = unsafe { f(file.as_raw()) };
Error::ok_from_code(result)
}
#[doc(alias = "sys::ffi::playdate_file::stat")]
pub fn metadata<P: AsRef<Path>>(&self, path: P) -> Result<FileStat, ApiError> {
let mut stat = FileStat { isdir: 0,
size: 0,
m_year: 0,
m_month: 0,
m_day: 0,
m_hour: 0,
m_minute: 0,
m_second: 0 };
self.metadata_to(path, &mut stat).map(|_| stat)
}
#[doc(alias = "sys::ffi::playdate_file::stat")]
pub fn metadata_to<P: AsRef<Path>>(&self, path: P, metadata: &mut FileStat) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self.0.stat();
let result = unsafe { f(path.as_ptr() as _, metadata as *mut _) };
Error::ok_from_code(result)?;
Ok(())
}
#[doc(alias = "sys::ffi::playdate_file::mkdir")]
pub fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self.0.mkdir();
let result = unsafe { f(path.as_ptr() as _) };
Error::ok_from_code(result)?;
Ok(())
}
#[doc(alias = "sys::ffi::playdate_file::unlink")]
pub fn remove<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self.0.unlink();
let result = unsafe { f(path.as_ptr() as _, 0) };
Error::ok_from_code(result)?;
Ok(())
}
#[doc(alias = "sys::ffi::playdate_file::unlink")]
pub fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self.0.unlink();
let result = unsafe { f(path.as_ptr() as _, 1) };
Error::ok_from_code(result)?;
Ok(())
}
#[doc(alias = "sys::ffi::playdate_file::rename")]
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> Result<(), ApiError> {
let from = CString::new(from.as_ref())?;
let to = CString::new(to.as_ref())?;
let f = self.0.rename();
let result = unsafe { f(from.as_ptr() as _, to.as_ptr() as _) };
Error::ok_from_code(result)?;
Ok(())
}
#[doc(alias = "sys::ffi::playdate_file::listfiles")]
pub fn read_dir<P, Fn>(&self, path: P, mut callback: Fn, include_hidden: bool) -> Result<(), ApiError>
where P: AsRef<Path>,
Fn: FnMut(String) {
unsafe extern "C" fn proxy<Fn: FnMut(String)>(filename: *const c_char, userdata: *mut c_void) {
let filename = CStr::from_ptr(filename as _).to_string_lossy().into_owned();
if let Some(callback) = (userdata as *mut _ as *mut Fn).as_mut() {
callback(filename);
} else {
panic!("Fs.read_dir: missed callback");
}
}
let path = CString::new(path.as_ref())?;
let callback_ref = (&mut callback) as *mut Fn as *mut _;
let f = self.0.listfiles();
let result = unsafe {
f(
path.as_ptr() as _,
Some(proxy::<Fn>),
callback_ref,
include_hidden as _,
)
};
Error::ok_from_code(result)?;
Ok(())
}
}
pub mod prelude {
pub use sys::ffi::FileStat;
pub use sys::ffi::FileOptions;
pub use crate::error::ApiError as FsApiError;
pub use crate::error::Error as FsError;
pub use crate::Path;
pub use crate::Fs;
pub use crate::file::*;
pub use crate::options::*;
pub use crate::seek::SeekFrom;
}