sark 0.12.0

Simple Asynchronous Rust webKit - Server
Documentation
use std::path::PathBuf;
use std::pin::Pin;
use std::task::Poll;

use dope::io::file::{O_CLOEXEC, O_RDONLY, OpenPath};
use dope::manifold::file::{Files, metadata::Metadata, source::Source};
use dope_fiber::abi::Fiber;
use dope_fiber::file::{open::Open, read_exact::ReadExact, stat::Stat};
use dope_fiber::raw::task::Context;
use o3::buffer::Shared;
use o3::mem::{ByteBudgetHandle, ByteLease};
use sark_core::http::Response;

#[derive(Clone, Copy)]
pub(super) enum LoadError {
    NotFound,
    Overloaded,
}

impl LoadError {
    pub(super) fn response(self) -> Response {
        match self {
            Self::NotFound => Response::not_found(),
            Self::Overloaded => Response::new(sark_core::http::StatusCode::SERVICE_UNAVAILABLE),
        }
    }
}

pub(super) struct LoadedFile {
    pub(super) body: Shared,
    pub(super) metadata: Metadata,
}

pub(super) struct ReadFile<'f, 'b, 'd, const ID: u8, const N: usize> {
    files: &'f Files<'d, ID, N>,
    path: Option<PathBuf>,
    budget: ByteBudgetHandle<'b>,
    max_file_bytes: usize,
    lease: Option<ByteLease<'b>>,
    metadata: Option<Metadata>,
    open: Option<Open<'f, 'd, ID, N>>,
    stat: Option<Stat<'f, 'd, ID, N, Source<'d>>>,
    read: Option<ReadExact<'f, 'd, ID, N>>,
    done: bool,
}

impl<'f, 'b, 'd, const ID: u8, const N: usize> ReadFile<'f, 'b, 'd, ID, N> {
    pub(super) fn new(
        files: &'f Files<'d, ID, N>,
        path: PathBuf,
        budget: ByteBudgetHandle<'b>,
        max_file_bytes: usize,
    ) -> Self {
        Self {
            files,
            path: Some(path),
            budget,
            max_file_bytes,
            lease: None,
            metadata: None,
            open: None,
            stat: None,
            read: None,
            done: false,
        }
    }

    fn release(&mut self) {
        self.lease = None;
    }

    fn fail(&mut self, error: LoadError) -> Poll<Result<LoadedFile, LoadError>> {
        self.release();
        self.done = true;
        Poll::Ready(Err(error))
    }
}

impl<const ID: u8, const N: usize> Drop for ReadFile<'_, '_, '_, ID, N> {
    fn drop(&mut self) {
        self.release();
    }
}

impl<'f, 'b, 'd, const ID: u8, const N: usize> Fiber<'d> for ReadFile<'f, 'b, 'd, ID, N> {
    type Output = Result<LoadedFile, LoadError>;

    fn poll(self: Pin<&mut Self>, mut cx: Pin<&mut Context<'_, 'd>>) -> Poll<Self::Output> {
        let this = self.get_mut();
        if this.done {
            panic!("file fiber polled after completion");
        }

        if this.open.is_none() && this.stat.is_none() && this.read.is_none() {
            let path = this.path.take().expect("file path missing");
            let Some(path) = path.to_str() else {
                return this.fail(LoadError::NotFound);
            };
            let Ok(path) = OpenPath::new(path) else {
                return this.fail(LoadError::NotFound);
            };
            this.open = Some(Open::direct(this.files, path, O_RDONLY | O_CLOEXEC));
        }

        if let Some(open) = this.open.as_mut() {
            let Poll::Ready(result) = Fiber::poll(Pin::new(open), cx.as_mut()) else {
                return Poll::Pending;
            };
            this.open = None;
            let Ok(source) = result else {
                return this.fail(LoadError::NotFound);
            };
            this.stat = Some(Stat::source(this.files, source));
        }

        if let Some(stat) = this.stat.as_mut() {
            let Poll::Ready((source, result)) = Fiber::poll(Pin::new(stat), cx.as_mut()) else {
                return Poll::Pending;
            };
            this.stat = None;
            let Ok(metadata) = result else {
                return this.fail(LoadError::NotFound);
            };
            if !metadata.is_file() {
                return this.fail(LoadError::NotFound);
            }
            let Ok(expected) = usize::try_from(metadata.len()) else {
                return this.fail(LoadError::NotFound);
            };
            if expected > this.max_file_bytes {
                return this.fail(LoadError::NotFound);
            }
            let Ok(read_len) = u32::try_from(expected) else {
                return this.fail(LoadError::NotFound);
            };
            let Some(lease) = this.budget.try_acquire(expected) else {
                return this.fail(LoadError::Overloaded);
            };
            this.lease = Some(lease);
            this.metadata = Some(metadata);
            this.read = Some(ReadExact::new(this.files, source, read_len, 0));
        }

        let read = this.read.as_mut().expect("read child missing");
        let Poll::Ready((source, buffer, result)) = Fiber::poll(Pin::new(read), cx.as_mut()) else {
            return Poll::Pending;
        };
        this.read = None;
        drop(source);
        let Ok(()) = result else {
            return this.fail(LoadError::NotFound);
        };
        let body = Shared::from(buffer);
        let metadata = this.metadata.take().expect("file metadata missing");
        this.release();
        this.done = true;
        Poll::Ready(Ok(LoadedFile { body, metadata }))
    }
}