irid-std 0.1.2

A replacement for std when running without a filesystem on the irid kernel
Documentation
use core::mem::ManuallyDrop;

use alloc::sync::Arc;
use irid_syscall::io::{FileDescriptor, OpenFlags, close, open};
use thiserror::Error;

use crate::{fs::{Metadata, Permissions, metadata}, io::{self, Read, Seek, SeekFrom, WithPath, Write}, os::fd::{AsRawFd, 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 Read for &File {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
        Ok(irid_syscall::io::read(self.handle.desc, buf)?)
    }
}

impl Read for File {
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, io::Error> {
        Ok(irid_syscall::io::read(self.handle.desc, buf)?)
    }
}

impl Write for File {
    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
        Ok(irid_syscall::io::write(self.handle.desc, buf)?)
    }

    fn flush(&mut self) -> Result<(), io::Error> {
        Ok(())
    }
}

impl Write for &File {
    fn write(&mut self, buf: &[u8]) -> Result<usize, io::Error> {
        Ok(irid_syscall::io::write(self.handle.desc, buf)?)
    }

    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) => Ok(irid_syscall::io::seek_from_start(self.handle.desc, offset)?),
            SeekFrom::End(offset) => Ok(irid_syscall::io::seek_from_end(self.handle.desc, offset)?),
            SeekFrom::Current(offset) => Ok(irid_syscall::io::seek_relative(self.handle.desc, offset)?),
        }
    }
}

impl Seek for &File {
    fn seek(&mut self, pos: SeekFrom) -> Result<u64, io::Error> {
        match pos {
            SeekFrom::Start(offset) => Ok(irid_syscall::io::seek_from_start(self.handle.desc, offset)?),
            SeekFrom::End(offset) => Ok(irid_syscall::io::seek_from_end(self.handle.desc, offset)?),
            SeekFrom::Current(offset) => Ok(irid_syscall::io::seek_relative(self.handle.desc, offset)?),
        }
    }
}

#[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(io::Error::from).with_path(path.as_ref().display())?;

        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")
    }
}