use super::*;
use serde::Deserialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct FileType {
is_dir: bool,
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.is_dir
}
pub fn is_file(&self) -> bool {
!self.is_dir
}
}
#[derive(Debug, Clone)]
pub struct Metadata {
size: u64,
is_dir: bool,
mtime: u64,
}
impl Metadata {
pub fn len(&self) -> u64 {
self.size
}
pub fn is_empty(&self) -> bool {
self.size == 0
}
pub fn is_dir(&self) -> bool {
self.is_dir
}
pub fn is_file(&self) -> bool {
!self.is_dir
}
pub fn file_type(&self) -> FileType {
FileType {
is_dir: self.is_dir,
}
}
pub fn modified(&self) -> Result<std::time::SystemTime, SysError> {
if self.mtime == 0 {
return Err(SysError::ApiError("modification time unavailable".into()));
}
Ok(std::time::UNIX_EPOCH + std::time::Duration::from_secs(self.mtime))
}
}
#[derive(Debug, Clone)]
pub struct DirEntry {
path: String,
name_offset: usize,
}
impl DirEntry {
pub fn file_name(&self) -> &str {
&self.path[self.name_offset..]
}
pub fn path(&self) -> &str {
&self.path
}
}
#[derive(Debug)]
pub struct ReadDir {
entries: std::vec::IntoIter<DirEntry>,
}
impl Iterator for ReadDir {
type Item = DirEntry;
fn next(&mut self) -> Option<Self::Item> {
self.entries.next()
}
fn size_hint(&self) -> (usize, Option<usize>) {
self.entries.size_hint()
}
}
pub fn exists(path: impl AsRef<[u8]>) -> Result<bool, SysError> {
let result = unsafe { astrid_fs_exists(path.as_ref().to_vec())? };
Ok(!result.is_empty() && result[0] != 0)
}
pub fn read(path: impl AsRef<[u8]>) -> Result<Vec<u8>, SysError> {
let result = unsafe { astrid_read_file(path.as_ref().to_vec())? };
Ok(result)
}
pub fn read_to_string(path: impl AsRef<[u8]>) -> Result<String, SysError> {
let bytes = read(path)?;
String::from_utf8(bytes).map_err(|e| SysError::ApiError(e.to_string()))
}
pub fn write(path: impl AsRef<[u8]>, contents: impl AsRef<[u8]>) -> Result<(), SysError> {
unsafe { astrid_write_file(path.as_ref().to_vec(), contents.as_ref().to_vec())? };
Ok(())
}
pub fn create_dir(path: impl AsRef<[u8]>) -> Result<(), SysError> {
unsafe { astrid_fs_mkdir(path.as_ref().to_vec())? };
Ok(())
}
pub fn read_dir(path: impl AsRef<[u8]>) -> Result<ReadDir, SysError> {
let result = unsafe { astrid_fs_readdir(path.as_ref().to_vec())? };
let path_str = String::from_utf8_lossy(path.as_ref());
let parent = if path_str.ends_with('/') || path_str.is_empty() {
path_str.into_owned()
} else {
format!("{path_str}/")
};
let names: Vec<String> = serde_json::from_slice(&result)?;
let entries = names
.into_iter()
.map(|name| {
let full_path = format!("{parent}{name}");
let name_offset = parent.len();
DirEntry {
path: full_path,
name_offset,
}
})
.collect::<Vec<_>>();
Ok(ReadDir {
entries: entries.into_iter(),
})
}
pub fn metadata(path: impl AsRef<[u8]>) -> Result<Metadata, SysError> {
let result = unsafe { astrid_fs_stat(path.as_ref().to_vec())? };
#[derive(Deserialize)]
struct RawMetadata {
size: u64,
#[serde(rename = "isDir")]
is_dir: bool,
mtime: u64,
}
let raw: RawMetadata = serde_json::from_slice(&result)?;
Ok(Metadata {
size: raw.size,
is_dir: raw.is_dir,
mtime: raw.mtime,
})
}
pub fn remove_file(path: impl AsRef<[u8]>) -> Result<(), SysError> {
unsafe { astrid_fs_unlink(path.as_ref().to_vec())? };
Ok(())
}