use core::io::ErrorKind;
use alloc::{borrow::ToOwned, string::String, vec::Vec};
use irid_syscall::{SystemError, ffi::io::{FileInfo, FilePermissions}, io::{FileDescriptor, OpenFlags, close, file_info, open, read}};
use crate::{io::{Error, Read, convert_error}, path::{Path, PathBuf}};
use irid_syscall::ffi::io::FileType as NativeFileType;
mod dir;
mod file;
pub use dir::DirEntry;
pub use file::{File, OpenOptions, TryLockError};
pub fn canonicalize(path: impl AsRef<Path>) -> Result<PathBuf, Error> {
todo!()
}
pub fn copy(source: impl AsRef<Path>, destination: impl AsRef<Path>) -> Result<u64, SystemError> {
todo!()
}
pub fn create_dir(path: impl AsRef<Path>) -> Result<(), Error> {
irid_syscall::io::create_directory(path.as_ref().as_str()).map_err(convert_error)?;
Ok(())
}
pub fn create_dir_all(path: impl AsRef<Path>) -> Result<(), Error> {
let path = path.as_ref();
for path in path.ancestors().collect::<Vec<_>>().into_iter().rev().chain(Some(path.as_ref())) {
match create_dir(path) {
Ok(_) => (),
Err(err) => if err.kind() == ErrorKind::AlreadyExists {
()
} else {
Err(err)?
},
}
}
Ok(())
}
pub fn exists(path: impl AsRef<Path>) -> Result<bool, Error> {
match irid_syscall::io::open(path.as_ref().as_str(), OpenFlags::empty()).map_err(convert_error) {
Ok(file) => {
irid_syscall::io::close(file).map_err(convert_error)?;
Ok(true)
}
Err(err) => if err.kind() == ErrorKind::AlreadyExists {
Ok(false)
} else {
Err(err)?
},
}
}
pub fn hard_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<(), Error> {
todo!()
}
pub fn soft_link(original: impl AsRef<Path>, link: impl AsRef<Path>) -> Result<(), Error> {
let path = canonicalize(original)?;
let file = irid_syscall::io::open(link.as_ref().as_str(), OpenFlags::WRITE | OpenFlags::SYMBOLIC_LINK | OpenFlags::CREATE)
.map_err(convert_error)?;
let result = irid_syscall::io::write(file, path.as_str().as_bytes())
.map_err(convert_error);
irid_syscall::io::close(file).map_err(convert_error)?;
result.map(|_| ())
}
pub fn metadata(path: impl AsRef<Path>) -> Result<Metadata, Error> {
Ok(Metadata { info: file_info(path.as_ref().as_str(), true).map_err(convert_error)? })
}
pub fn symlink_metadata(path: impl AsRef<Path>) -> Result<Metadata, Error> {
Ok(Metadata { info: file_info(path.as_ref().as_str(), false).map_err(convert_error)? })
}
pub struct Metadata {
info: FileInfo,
}
impl Metadata {
pub fn file_type(&self) -> FileType {
FileType { inner: self.info.file_type }
}
pub fn is_dir(&self) -> bool {
self.file_type().is_dir()
}
pub fn is_file(&self) -> bool {
self.file_type().is_file()
}
pub fn is_symlink(&self) -> bool {
self.file_type().is_symlink()
}
pub fn length(&self) -> u64 {
todo!()
}
pub fn permissions(&self) -> Permissions {
Permissions { permissions: self.info.access.permissions.clone() }
}
}
pub fn rename(from: impl AsRef<Path>, to: impl AsRef<Path>) -> Result<(), Error> {
irid_syscall::io::move_file(from.as_ref().as_str(), to.as_ref().as_str()).map_err(convert_error)
}
pub fn read_link(path: impl AsRef<Path>) -> Result<PathBuf, Error> {
todo!()
}
pub fn read_dir(path: impl AsRef<Path>) -> Result<impl Iterator<Item = Result<DirEntry, Error>>, Error> {
let mut file = AutoFile::open(&path, OpenFlags::REQUIRE_DIRECTORY | OpenFlags::READ)?;
let mut buffer = Vec::new();
file.read_to_end(&mut buffer)?;
let text = unsafe { String::from_utf8_unchecked(buffer) };
Ok(text.split("\n").filter(|s| !s.is_empty()).map(|name| {
let mut path = path.as_ref().to_owned();
path.push(Path::new(name));
Ok(DirEntry {
path
})
}).collect::<Vec<_>>().into_iter())
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct FileType {
inner: NativeFileType
}
impl FileType {
pub fn is_dir(&self) -> bool {
self.inner == NativeFileType::Directory
}
pub fn is_file(&self) -> bool {
!self.is_dir() && !self.is_symlink()
}
pub fn is_symlink(&self) -> bool {
self.inner == NativeFileType::SymbolicLink
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Permissions {
permissions: FilePermissions
}
impl Permissions {
pub fn readonly(&self) -> bool {
!(self.permissions.contains(FilePermissions::WRITE_USER)
| self.permissions.contains(FilePermissions::WRITE_GROUP)
| self.permissions.contains(FilePermissions::WRITE_OTHER))
}
pub fn set_readonly(&mut self, readonly: bool) {
if readonly {
self.permissions -= FilePermissions::WRITE_USER | FilePermissions::WRITE_OTHER | FilePermissions::WRITE_GROUP;
} else {
self.permissions |= FilePermissions::WRITE_USER | FilePermissions::WRITE_OTHER | FilePermissions::WRITE_GROUP;
}
}
}
struct AutoFile {
desc: FileDescriptor
}
impl AutoFile {
pub fn open(path: impl AsRef<Path>, flags: OpenFlags) -> Result<Self, Error> {
Ok(Self {
desc: open(path.as_ref().as_str(), flags).map_err(convert_error)?
})
}
}
impl Read for AutoFile {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
read(self.desc, buf).map_err(convert_error)
}
}
impl Drop for AutoFile {
fn drop(&mut self) {
close(self.desc).expect("failed to close file");
}
}