drone-fatfs 0.2.3

Bindings to ChaN's FatFs.
//! FatFs message types.

use core::{mem, ptr};
use drone_core::ffi::{c_void, CStr, CString};
use drone_core::io::SeekFrom;
use drone_fatfs_raw::{
  f_close, f_lseek, f_mkfs, f_mount, f_open, f_read, f_size, f_stat, f_sync,
  f_tell, f_write, FSIZE_t, BYTE, FF_MAX_SS, FM_ANY, UINT,
};
use file::File;
use file_info::FileInfo;
use fs::Fs;
use result::{Error, Fresult};

/// FatFs commands.
pub struct Cmd(pub(crate) CmdKind);

pub(crate) enum CmdKind {
  Mkfs(*const CStr),
  Mount(CString),
  Umount(Fs),
  Stat(*const CStr),
  Open(*const CStr, BYTE),
  Close(File),
  Read(*mut File, *mut [u8]),
  Write(*mut File, *const [u8]),
  Seek(*mut File, SeekFrom),
  Fsync(*mut File),
}

/// FatFs command results.
#[allow(unions_with_drop_fields)]
pub union CmdRes {
  pub(crate) mkfs: Result<(), Error>,
  pub(crate) mount: Result<Fs, Error>,
  pub(crate) umount: Result<(), Error>,
  pub(crate) stat: Result<FileInfo, Error>,
  pub(crate) open: Result<File, Error>,
  pub(crate) close: Result<(), Error>,
  pub(crate) read: Result<usize, Error>,
  pub(crate) write: Result<usize, Error>,
  pub(crate) seek: Result<u64, Error>,
  pub(crate) fsync: Result<(), Error>,
}

unsafe impl Send for Cmd {}
unsafe impl Send for CmdRes {}

impl Cmd {
  /// Run the command synchronously.
  pub fn run(self) -> CmdRes {
    match self {
      Cmd(CmdKind::Mkfs(path)) => CmdRes {
        mkfs: unsafe {
          let mut work: [u8; FF_MAX_SS as usize] = mem::uninitialized();
          f_mkfs(
            (*path).as_ptr(),
            FM_ANY as u8,
            0,
            &mut work as *mut _ as *mut c_void,
            FF_MAX_SS,
          ).map(|| ())
        },
      },
      Cmd(CmdKind::Mount(path)) => CmdRes {
        mount: unsafe {
          let mut fs = Fs::new_uninitialized(path);
          f_mount(fs.fs.as_mut(), fs.path.as_ptr(), 0).map(|| fs)
        },
      },
      Cmd(CmdKind::Umount(fs)) => CmdRes {
        umount: unsafe {
          let fr = f_mount(ptr::null_mut(), fs.path.as_ptr(), 0);
          drop(fs);
          fr.map(|| ())
        },
      },
      Cmd(CmdKind::Stat(path)) => CmdRes {
        stat: unsafe {
          let mut file_info = FileInfo::new_uninitialized();
          f_stat((*path).as_ptr(), file_info.as_raw_mut()).map(|| file_info)
        },
      },
      Cmd(CmdKind::Open(path, mode)) => CmdRes {
        open: unsafe {
          let mut file = File::new_uninitialized();
          f_open(file.as_raw_mut(), (*path).as_ptr(), mode).map(|| file)
        },
      },
      Cmd(CmdKind::Close(mut file)) => CmdRes {
        close: unsafe {
          let fr = f_close(file.as_raw_mut());
          drop(file);
          fr.map(|| ())
        },
      },
      Cmd(CmdKind::Read(file, buf)) => CmdRes {
        read: unsafe {
          let mut br = mem::uninitialized();
          f_read(
            (*file).as_raw_mut(),
            (*buf).as_mut_ptr() as *mut c_void,
            (*buf).len() as UINT,
            &mut br,
          ).map(|| br as usize)
        },
      },
      Cmd(CmdKind::Write(file, buf)) => CmdRes {
        write: unsafe {
          let mut br = mem::uninitialized();
          f_write(
            (*file).as_raw_mut(),
            (*buf).as_ptr() as *const c_void,
            (*buf).len() as UINT,
            &mut br,
          ).map(|| br as usize)
        },
      },
      Cmd(CmdKind::Seek(file, pos)) => CmdRes {
        seek: unsafe {
          let fp = (*file).as_raw_mut();
          let pos = match pos {
            SeekFrom::Start(pos) => pos as FSIZE_t,
            SeekFrom::End(pos) => f_size(fp).wrapping_add(pos as FSIZE_t),
            SeekFrom::Current(pos) => f_tell(fp).wrapping_add(pos as FSIZE_t),
          };
          f_lseek(fp, pos).map(|| u64::from(pos))
        },
      },
      Cmd(CmdKind::Fsync(file)) => CmdRes {
        fsync: unsafe { f_sync((*file).as_raw_mut()).map(|| ()) },
      },
    }
  }
}