use core::mem::ManuallyDrop;
use alloc::{format, sync::Arc};
use irid_syscall::io::{FileDescriptor, OpenFlags, close, open};
use thiserror::Error;
use crate::{fs::{Metadata, Permissions, metadata}, io::{self, Read, Seek, SeekFrom, Write, convert_error}, os::fd::{AsRawFd, FromRawFd, RawFd}, path::{Path, PathBuf}};
#[derive(Debug)]
pub struct File {
handle: Arc<FileDescriptorHandle>,
path: PathBuf
}
impl File {
pub fn open(path: impl AsRef<Path>) -> Result<File, io::Error> {
OpenOptions::new().read(true).open(path)
}
pub fn create(path: impl AsRef<Path>) -> Result<File, io::Error> {
OpenOptions::new().write(true).create(true).open(path)
}
pub fn create_new(path: impl AsRef<Path>) -> Result<File, io::Error> {
OpenOptions::new().write(true).create(true).create_new(true).open(path)
}
pub fn options() -> OpenOptions {
OpenOptions::new()
}
pub fn sync_all(&self) -> Result<(), io::Error> {
todo!("sync_all")
}
pub fn sync_data(&self) -> Result<(), io::Error> {
todo!("sync_data")
}
pub fn lock(&self) -> Result<(), io::Error> {
todo!("lock")
}
pub fn lock_shared(&self) -> Result<(), io::Error> {
todo!("lock_shared")
}
pub fn try_lock(&self) -> Result<(), TryLockError> {
todo!("lock")
}
pub fn try_lock_shared(&self) -> Result<(), TryLockError> {
todo!("lock_shared")
}
pub fn unlock(&self) -> Result<(), io::Error> {
todo!("unlock")
}
pub fn set_len(&self, size: u64) -> Result<(), io::Error> {
todo!("truncate")
}
pub fn metadata(&self) -> Result<Metadata, io::Error> {
metadata(&self.path)
}
pub fn try_clone(&self) -> Result<File, io::Error> {
Ok(File {
handle: self.handle.clone(),
path: self.path.clone(),
})
}
pub fn set_permissions(&self, perm: Permissions) -> Result<(), io::Error> {
todo!("set_permissions")
}
pub(crate) fn into_raw(self) -> FileDescriptor {
let file = ManuallyDrop::new(self);
file.handle.desc
}
}
impl AsRawFd for File {
fn as_raw_fd(&self) -> RawFd {
self.handle.desc
}
}
impl FromRawFd for File {
unsafe fn from_raw_fd(fd: RawFd) -> Self {
Self {
handle: Arc::new(FileDescriptorHandle { desc: fd }),
path: Path::new(&format!("/proc/self/fd/{fd}")).to_path_buf(),
}
}
}
impl Read for &File {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
irid_syscall::io::read(self.handle.desc, buf).map_err(convert_error)
}
}
impl Read for File {
fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
irid_syscall::io::read(self.handle.desc, buf).map_err(convert_error)
}
}
impl Write for File {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
irid_syscall::io::write(self.handle.desc, buf).map_err(convert_error)
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}
impl Write for &File {
fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
irid_syscall::io::write(self.handle.desc, buf).map_err(convert_error)
}
fn flush(&mut self) -> Result<(), io::Error> {
Ok(())
}
}
impl Seek for File {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
match pos {
SeekFrom::Start(offset) => irid_syscall::io::seek_from_start(self.handle.desc, offset).map_err(convert_error),
SeekFrom::End(offset) => irid_syscall::io::seek_from_end(self.handle.desc, offset).map_err(convert_error),
SeekFrom::Current(offset) => irid_syscall::io::seek_relative(self.handle.desc, offset).map_err(convert_error),
}
}
}
impl Seek for &File {
fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
match pos {
SeekFrom::Start(offset) => irid_syscall::io::seek_from_start(self.handle.desc, offset).map_err(convert_error),
SeekFrom::End(offset) => irid_syscall::io::seek_from_end(self.handle.desc, offset).map_err(convert_error),
SeekFrom::Current(offset) => irid_syscall::io::seek_relative(self.handle.desc, offset).map_err(convert_error),
}
}
}
#[derive(Clone, Debug)]
pub struct OpenOptions {
flags: OpenFlags,
truncate: bool
}
impl OpenOptions {
pub fn new() -> Self {
Self {
flags: OpenFlags::REQUIRE_NON_DIRECTORY,
truncate: false
}
}
pub fn read(&mut self, read: bool) -> &mut Self {
if read {
self.flags |= OpenFlags::READ;
} else {
self.flags -= OpenFlags::READ;
}
self
}
pub fn write(&mut self, write: bool) -> &mut Self {
if write {
self.flags |= OpenFlags::WRITE;
} else {
self.flags -= OpenFlags::WRITE;
}
self
}
pub fn append(&mut self, append: bool) -> &mut Self {
if append {
self.flags |= OpenFlags::APPEND | OpenFlags::WRITE;
} else {
self.flags -= OpenFlags::APPEND;
}
self
}
pub fn truncate(&mut self, truncate: bool) -> &mut Self {
if truncate {
self.truncate = true;
} else {
self.truncate = false;
}
self
}
pub fn create(&mut self, create: bool) -> &mut Self {
if create {
self.flags |= OpenFlags::CREATE;
} else {
self.flags -= OpenFlags::CREATE;
}
self
}
pub fn create_new(&mut self, create_new: bool) -> &mut Self {
if create_new {
self.flags |= OpenFlags::REQUIRE_CREATE;
} else {
self.flags -= OpenFlags::REQUIRE_CREATE;
}
self
}
pub fn open(&self, path: impl AsRef<Path>) -> Result<File, io::Error> {
let desc = open(path.as_ref().as_str(), self.flags).map_err(convert_error)?;
if self.truncate {
todo!("truncate")
}
Ok(File {
handle: Arc::new(FileDescriptorHandle { desc }),
path: path.as_ref().to_path_buf()
})
}
}
#[derive(Debug, Error)]
pub enum TryLockError {
#[error("{0}")]
Error(#[from] io::Error),
#[error("operation would block")]
WouldBlock
}
#[derive(Debug)]
struct FileDescriptorHandle {
desc: FileDescriptor
}
impl Drop for FileDescriptorHandle {
fn drop(&mut self) {
close(self.desc).expect("failed to close file")
}
}