#![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 options::FileOptionsExt;
use options::OpenOptions;
use seek::SeekFrom;
use seek::Whence;
use sys::error::OkOrNullFnErr;
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 cache;
pub mod file;
pub mod seek;
pub mod options;
pub mod error;
pub type Path = str;
fn read<P: AsRef<Path>>(path: P, data_dir: bool) -> Result<Vec<u8>, ApiError> {
let fs = Fs::new()?;
let mut file = File::open(&path, data_dir)?;
let size = fs.metadata(path).map(|m| m.size).ok().unwrap_or(0);
let mut buf = Vec::<u8>::with_capacity(size as usize);
buf.resize(size as usize, 0);
fs.read(&mut file, &mut buf, size)?;
Ok(buf)
}
pub fn read_to_string<P: AsRef<Path>>(path: P, data_partition: bool) -> Result<String, ApiError> {
let buf = read(path, data_partition)?;
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 fs = Fs::new()?;
let mut file = File::options().write(true).append(false).open(&path)?;
fs.write(&mut file, contents.as_ref())?;
Ok(())
}
#[inline(always)]
pub fn remove<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::new()?.remove(path) }
#[inline(always)]
pub fn rename<P: AsRef<Path>, Q: AsRef<Path>>(from: P, to: Q) -> Result<(), ApiError> {
Fs::new()?.rename(from, to)
}
#[inline(always)]
pub fn create_dir<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::new()?.create_dir(path) }
#[inline(always)]
pub fn remove_dir_all<P: AsRef<Path>>(path: P) -> Result<(), ApiError> { Fs::new()?.remove_dir_all(path) }
pub trait FileSystemRaw {
#![doc(hidden)]
type AccessError: core::error::Error;
fn _open(&self) -> Result<cache::FnOpen, Self::AccessError>;
fn _close(&self) -> Result<cache::FnClose, Self::AccessError>;
fn _flush(&self) -> Result<cache::FnFlush, Self::AccessError>;
fn _geterr(&self) -> Result<cache::FnGeterr, Self::AccessError>;
fn _listfiles(&self) -> Result<cache::FnListfiles, Self::AccessError>;
fn _mkdir(&self) -> Result<cache::FnMkdir, Self::AccessError>;
fn _read(&self) -> Result<cache::FnRead, Self::AccessError>;
fn _rename(&self) -> Result<cache::FnRename, Self::AccessError>;
fn _seek(&self) -> Result<cache::FnSeek, Self::AccessError>;
fn _stat(&self) -> Result<cache::FnStat, Self::AccessError>;
fn _tell(&self) -> Result<cache::FnTell, Self::AccessError>;
fn _unlink(&self) -> Result<cache::FnUnlink, Self::AccessError>;
fn _write(&self) -> Result<cache::FnWrite, Self::AccessError>;
}
pub trait FileSystem: FileSystemRaw
where <Self as FileSystem>::Error: From<Self::AccessError> {
type Error: core::error::Error + From<Self::AccessError>;
fn open<P: AsRef<Path>, Opts: OpenOptions>(&self, path: P, options: Opts) -> Result<File, Self::Error>;
fn close(&self, file: File) -> Result<(), Self::Error>;
fn tell(&self, file: &mut File) -> Result<c_uint, Self::Error>;
fn seek(&self, file: &mut File, pos: SeekFrom) -> Result<c_uint, Self::Error> {
let (whence, pos) = pos.into_parts();
self.seek_raw(file, pos, whence)
}
fn seek_raw(&self, file: &mut File, pos: c_int, whence: Whence) -> Result<c_uint, Self::Error>;
fn read(&self, file: &mut File, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, Self::Error>;
fn write(&self, file: &mut File, from: &[u8]) -> Result<c_uint, Self::Error>;
fn flush(&self, file: &mut File) -> Result<c_uint, Self::Error>;
fn metadata<P: AsRef<Path>>(&self, path: P) -> Result<FileStat, Self::Error>;
fn metadata_to<P: AsRef<Path>>(&self, path: P, metadata: &mut FileStat) -> Result<(), Self::Error>;
fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::Error>;
fn remove<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::Error>;
fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), Self::Error>;
fn rename<P: AsRef<Path>, Q: AsRef<Path>>(&self, from: P, to: Q) -> Result<(), Self::Error>;
fn read_dir<P, Fn>(&self, path: P, callback: Fn, include_hidden: bool) -> Result<(), Self::Error>
where P: AsRef<Path>,
Fn: FnMut(String);
}
impl<T, E> FileSystem for T
where T: FileSystemRaw<AccessError = E>,
ApiError: From<E>
{
type Error = ApiError;
fn open<P: AsRef<Path>, Opts: OpenOptions>(&self, path: P, options: Opts) -> Result<File, ApiError> {
let path = CString::new(path.as_ref())?;
let f = self._open()?;
let ptr = unsafe { f(path.as_ptr() as _, options.into()) };
Ok(File(ptr as _))
}
fn close(&self, mut file: File) -> Result<(), ApiError> {
let f = self._close()?;
let result = unsafe { f(file.0 as _) };
file.0 = core::ptr::null_mut();
Error::ok_from_code(result)?;
Ok(())
}
fn tell(&self, file: &mut File) -> Result<c_uint, ApiError> {
let f = self._tell()?;
let result = unsafe { f(file.0) };
Error::ok_from_code(result)
}
fn seek_raw(&self, file: &mut File, pos: c_int, whence: Whence) -> Result<c_uint, ApiError> {
let f = self._seek()?;
let result = unsafe { f(file.0, pos, whence as _) };
Error::ok_from_code(result)
}
fn read(&self, file: &mut File, to: &mut Vec<u8>, len: c_uint) -> Result<c_uint, ApiError> {
let f = self._read()?;
let result = unsafe { f(file.0, to.as_mut_ptr() as *mut _, len) };
Error::ok_from_code(result).into()
}
fn write(&self, file: &mut File, from: &[u8]) -> Result<c_uint, ApiError> {
let f = self._write()?;
let result = unsafe { f(file.0, from.as_ptr() as *mut _, from.len() as _) };
Error::ok_from_code(result)
}
fn flush(&self, file: &mut File) -> Result<c_uint, ApiError> {
let f = self._flush()?;
let result = unsafe { f(file.0) };
Error::ok_from_code(result)
}
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)
}
fn metadata_to<P: AsRef<Path>>(&self, path: P, metadata: &mut FileStat) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self._stat()?;
let result = unsafe { f(path.as_ptr() as _, metadata as *mut _) };
Error::ok_from_code(result).map(|_| ())
}
fn create_dir<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self._mkdir()?;
let result = unsafe { f(path.as_ptr() as _) };
Error::ok_from_code(result).map(|_| ())
}
fn remove<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self._unlink()?;
let result = unsafe { f(path.as_ptr() as _, 0) };
Error::ok_from_code(result).map(|_| ())
}
fn remove_dir_all<P: AsRef<Path>>(&self, path: P) -> Result<(), ApiError> {
let path = CString::new(path.as_ref())?;
let f = self._unlink()?;
let result = unsafe { f(path.as_ptr() as _, 1) };
Error::ok_from_code(result).map(|_| ())
}
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._rename()?;
let result = unsafe { f(from.as_ptr() as _, to.as_ptr() as _) };
Error::ok_from_code(result).map(|_| ())
}
fn read_dir<P, Fn>(&self, path: P, mut callback: Fn, include_hidden: bool) -> Result<(), ApiError>
where P: AsRef<Path>,
Fn: FnMut(String) {
let path = CString::new(path.as_ref())?;
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 callback_ref = (&mut callback) as *mut Fn as *mut _;
let f = self._listfiles()?;
let result = unsafe {
f(
path.as_ptr() as _,
Some(proxy::<Fn>),
callback_ref,
include_hidden as _,
)
};
Error::ok_from_code(result).map(|_| ())
}
}
pub struct Fs(&'static sys::ffi::playdate_file);
impl Fs {
pub fn new() -> Result<Fs, sys::error::NullPtrError> { Ok(Self(sys::api_ok!(file)?)) }
}
impl FileSystemRaw for Fs {
#![doc(hidden)]
type AccessError = sys::error::NullPtrError;
#[inline(always)]
fn _close(&self) -> Result<cache::FnClose, Self::AccessError> { self.0.close.ok_or_null() }
#[inline(always)]
fn _flush(&self) -> Result<cache::FnFlush, Self::AccessError> { self.0.flush.ok_or_null() }
#[inline(always)]
fn _geterr(&self) -> Result<cache::FnGeterr, Self::AccessError> { self.0.geterr.ok_or_null() }
#[inline(always)]
fn _listfiles(&self) -> Result<cache::FnListfiles, Self::AccessError> { self.0.listfiles.ok_or_null() }
#[inline(always)]
fn _mkdir(&self) -> Result<cache::FnMkdir, Self::AccessError> { self.0.mkdir.ok_or_null() }
#[inline(always)]
fn _open(&self) -> Result<cache::FnOpen, Self::AccessError> { self.0.open.ok_or_null() }
#[inline(always)]
fn _read(&self) -> Result<cache::FnRead, Self::AccessError> { self.0.read.ok_or_null() }
#[inline(always)]
fn _rename(&self) -> Result<cache::FnRename, Self::AccessError> { self.0.rename.ok_or_null() }
#[inline(always)]
fn _seek(&self) -> Result<cache::FnSeek, Self::AccessError> { self.0.seek.ok_or_null() }
#[inline(always)]
fn _stat(&self) -> Result<cache::FnStat, Self::AccessError> { self.0.stat.ok_or_null() }
#[inline(always)]
fn _tell(&self) -> Result<cache::FnTell, Self::AccessError> { self.0.tell.ok_or_null() }
#[inline(always)]
fn _unlink(&self) -> Result<cache::FnUnlink, Self::AccessError> { self.0.unlink.ok_or_null() }
#[inline(always)]
fn _write(&self) -> Result<cache::FnWrite, Self::AccessError> { self.0.write.ok_or_null() }
}
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;
}