use core::marker::PhantomData;
use core::pin::Pin;
use core::task::Poll;
use dope::DriverRef;
use o3::buffer::Shared;
use pin_project::pin_project;
use crate::{Context, Fiber};
pub trait SplitTask<'d> {
type Input;
type State: ?Sized;
type Context: ?Sized;
type Output: 'static;
type Error: 'static;
fn build<'req>(
view: SplitView<'req>,
input: Self::Input,
state: &'req Self::State,
context: &'req Self::Context,
) -> Result<impl Fiber<'d, Output = Self::Output> + 'req, Self::Error>
where
'd: 'req,
Self::State: 'req,
Self::Context: 'req;
}
#[derive(Clone, Copy)]
pub struct FiberScope<'d> {
driver: PhantomData<fn(&'d ()) -> &'d ()>,
}
impl<'d> FiberScope<'d> {
pub fn from_driver(_driver: DriverRef<'d>) -> Self {
Self {
driver: PhantomData,
}
}
}
#[pin_project]
pub struct OwnerFiber<F, O> {
#[pin]
fiber: F,
owner: O,
}
impl<F, O> OwnerFiber<F, O> {
pub fn from_parts(fiber: F, owner: O) -> Self {
Self { fiber, owner }
}
}
impl<'d, F, O> Fiber<'d> for OwnerFiber<F, O>
where
F: Fiber<'d>,
F::Output: 'static,
{
type Output = F::Output;
fn poll(self: Pin<&mut Self>, context: Pin<&mut Context<'_, 'd>>) -> Poll<Self::Output> {
Fiber::poll(self.project().fiber, context)
}
}
pub struct SplitBytes {
request: Shared,
body: Option<Shared>,
split: usize,
}
impl SplitBytes {
pub fn new(request: Shared, body: Option<Shared>, split: usize) -> Self {
debug_assert!(split <= request.len());
Self {
request,
body,
split,
}
}
pub fn view(&self) -> SplitView<'_> {
let head = &self.request.as_slice()[..self.split];
let body = match &self.body {
Some(body) => body.as_slice(),
None => &self.request.as_slice()[self.split..],
};
SplitView { head, body }
}
unsafe fn domain_view<'d>(&self) -> SplitView<'d> {
let SplitView { head, body } = self.view();
unsafe {
SplitView {
head: &*(head as *const [u8]),
body: &*(body as *const [u8]),
}
}
}
}
#[derive(Clone, Copy)]
pub struct SplitView<'a> {
head: &'a [u8],
body: &'a [u8],
}
impl<'a> SplitView<'a> {
pub fn head(self) -> &'a [u8] {
self.head
}
pub fn body(self) -> &'a [u8] {
self.body
}
pub fn into_parts(self) -> (&'a [u8], &'a [u8]) {
(self.head, self.body)
}
}
impl<F> OwnerFiber<F, SplitBytes> {
pub unsafe fn try_from_split<'d, E>(
owner: SplitBytes,
_scope: FiberScope<'d>,
build: impl FnOnce(SplitView<'d>) -> Result<F, E>,
) -> Result<Self, E>
where
F: Fiber<'d>,
F::Output: 'static,
E: 'static,
{
let view = unsafe { owner.domain_view() };
let fiber = build(view)?;
Ok(Self { fiber, owner })
}
}
pub fn try_from_split_task<'req, 'd, T>(
owner: SplitBytes,
input: T::Input,
state: &'req T::State,
context: &'req T::Context,
) -> Result<OwnerFiber<impl Fiber<'d, Output = T::Output> + 'req, SplitBytes>, T::Error>
where
T: SplitTask<'d>,
'd: 'req,
T::State: 'req,
T::Context: 'req,
{
let view = unsafe { owner.domain_view() };
let fiber = T::build(view, input, state, context)?;
Ok(OwnerFiber { fiber, owner })
}