use alloc::boxed::Box;
use alloc::vec;
use alloc::vec::Vec;
use core::ffi::c_void;
use core::mem::MaybeUninit;
use littlefs_rust_core::{LfsFile, LfsFileConfig};
use crate::error::{from_lfs_result, from_lfs_size, Error};
use crate::filesystem::Filesystem;
use crate::metadata::{OpenFlags, SeekFrom};
use crate::storage::Storage;
pub(crate) struct FileAllocation {
pub(crate) file: MaybeUninit<LfsFile>,
_cache: Vec<u8>,
pub(crate) file_config: LfsFileConfig,
}
impl FileAllocation {
pub(crate) fn new(cache_size: u32) -> Self {
let mut cache = vec![0u8; cache_size as usize];
let file_config = LfsFileConfig {
buffer: cache.as_mut_ptr() as *mut c_void,
attrs: core::ptr::null_mut(),
attr_count: 0,
};
Self {
file: MaybeUninit::zeroed(),
_cache: cache,
file_config,
}
}
}
pub struct File<'a, S: Storage> {
fs: &'a Filesystem<S>,
alloc: Box<FileAllocation>,
closed: bool,
}
impl<'a, S: Storage> File<'a, S> {
pub(crate) fn open(fs: &'a Filesystem<S>, path: &str, flags: OpenFlags) -> Result<Self, Error> {
let mut alloc = Box::new(FileAllocation::new(fs.cache_size()));
let path_bytes = null_terminate(path);
{
let mut inner = fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_opencfg(
inner.lfs.as_mut_ptr(),
alloc.file.as_mut_ptr(),
path_bytes.as_ptr(),
flags.bits() as i32,
&alloc.file_config as *const LfsFileConfig,
);
from_lfs_result(rc)?;
}
Ok(File {
fs,
alloc,
closed: false,
})
}
pub fn read(&self, buf: &mut [u8]) -> Result<u32, Error> {
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_read(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
buf.as_mut_ptr() as *mut c_void,
buf.len() as u32,
);
drop(inner);
from_lfs_size(rc)
}
pub fn write(&self, data: &[u8]) -> Result<u32, Error> {
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_write(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
data.as_ptr() as *const c_void,
data.len() as u32,
);
drop(inner);
from_lfs_size(rc)
}
pub fn seek(&self, pos: SeekFrom) -> Result<u32, Error> {
let (off, whence) = match pos {
SeekFrom::Start(n) => (
n as i32,
littlefs_rust_core::lfs_type::lfs_whence_flags::LFS_SEEK_SET,
),
SeekFrom::Current(n) => (
n,
littlefs_rust_core::lfs_type::lfs_whence_flags::LFS_SEEK_CUR,
),
SeekFrom::End(n) => (
n,
littlefs_rust_core::lfs_type::lfs_whence_flags::LFS_SEEK_END,
),
};
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_seek(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
off,
whence,
);
drop(inner);
from_lfs_size(rc)
}
pub fn tell(&self) -> u32 {
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_tell(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
);
drop(inner);
rc as u32
}
pub fn size(&self) -> u32 {
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_size(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
);
drop(inner);
rc as u32
}
pub fn sync(&self) -> Result<(), Error> {
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_sync(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
);
drop(inner);
from_lfs_result(rc)
}
pub fn truncate(&self, size: u32) -> Result<(), Error> {
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_truncate(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
size,
);
drop(inner);
from_lfs_result(rc)
}
pub fn close(mut self) -> Result<(), Error> {
self.closed = true;
let mut inner = self.fs.inner.borrow_mut();
let rc = littlefs_rust_core::lfs_file_close(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
);
from_lfs_result(rc)
}
}
impl<S: Storage> Drop for File<'_, S> {
fn drop(&mut self) {
if !self.closed {
if let Ok(mut inner) = self.fs.inner.try_borrow_mut() {
let _ = littlefs_rust_core::lfs_file_close(
inner.lfs.as_mut_ptr(),
self.alloc.file.as_ptr() as *mut LfsFile,
);
}
}
}
}
fn null_terminate(s: &str) -> Vec<u8> {
let mut v: Vec<u8> = s.bytes().collect();
v.push(0);
v
}