#![no_std]
#![allow(clippy::result_unit_err)]
#![warn(missing_docs)]
#[macro_use]
extern crate cortex_m_semihosting;
use core::mem;
use cortex_m_semihosting::nr::open;
use cstr_core::CStr;
#[derive(Debug, Hash, PartialEq, Eq)]
pub struct File {
handle: isize,
}
#[repr(usize)]
#[allow(missing_docs)]
pub enum FileOpenMode {
Read = open::R,
ReadBinary = open::R_BINARY,
ReadWrite = open::RW,
ReadWriteAppend = open::RW_APPEND,
ReadWriteAppendBinary = open::RW_APPEND_BINARY,
ReadWriteTruncate = open::RW_TRUNC,
ReadWriteTruncateBinary = open::RW_TRUNC_BINARY,
WriteAppend = open::W_APPEND,
WriteAppendBinary = open::W_APPEND_BINARY,
WriteTruncate = open::W_TRUNC,
WriteTruncateBinary = open::W_TRUNC_BINARY,
}
pub enum SeekFrom {
Start(usize),
End(isize),
}
#[derive(Debug, PartialEq, Eq, Hash)]
pub enum SeekError {
PositionOutOfBounds,
Unknown,
}
impl File {
pub fn open(path: &CStr, mode: FileOpenMode) -> Result<File, ()> {
let handle = unsafe { syscall!(OPEN, path.as_ptr(), mode, path.to_bytes().len()) } as isize;
if handle == -1 {
return Err(());
}
Ok(File { handle })
}
pub fn write(&mut self, buf: &[u8]) -> Result<usize, ()> {
let not_written = unsafe { syscall!(WRITE, self.handle, buf.as_ptr(), buf.len()) };
if not_written > buf.len() {
return Err(());
}
Ok(buf.len() - not_written)
}
pub fn close(self) -> Result<(), File> {
match self.close_internal() {
Ok(()) => {
mem::forget(self);
Ok(())
}
Err(()) => Err(self),
}
}
fn close_internal(&self) -> Result<(), ()> {
let success = unsafe { syscall!(CLOSE, self.handle) };
if success != 0 {
return Err(());
}
Ok(())
}
#[allow(clippy::len_without_is_empty)]
pub fn len(&self) -> Result<usize, ()> {
let length = unsafe { syscall!(FLEN, self.handle) };
if (length as isize) < 0 {
return Err(());
}
Ok(length)
}
pub fn read(&mut self, buf: &mut [u8]) -> Result<usize, ()> {
let not_read = unsafe { syscall!(READ, self.handle, buf.as_mut_ptr(), buf.len()) };
if not_read > buf.len() {
return Err(());
}
Ok(buf.len() - not_read)
}
pub fn seek(&mut self, from: SeekFrom) -> Result<(), SeekError> {
let length = self.len().map_err(|()| SeekError::Unknown)?;
let pos = match from {
SeekFrom::Start(offset) => offset,
SeekFrom::End(offset) => length.wrapping_add(offset as usize),
};
if pos > length {
return Err(SeekError::PositionOutOfBounds);
}
let result = unsafe { self.seek_unchecked(pos) };
result.map_err(|()| SeekError::Unknown)
}
pub unsafe fn seek_unchecked(&mut self, pos: usize) -> Result<(), ()> {
let result = syscall!(SEEK, self.handle, pos) as isize;
if result < 0 {
return Err(());
}
Ok(())
}
pub fn rewind(&mut self) -> Result<(), ()> {
unsafe { self.seek_unchecked(0) }
}
}
impl Drop for File {
fn drop(&mut self) {
let _ = self.close_internal();
}
}