eventp 1.0.0

Safe Rust abstraction over Linux epoll, offering a truly zero-cost event dispatch mechanism.
Documentation
use std::io;
use std::os::fd::RawFd;
use std::pin::Pin;

use crate::thin::ThinBoxSubscriber;
use crate::{EventpOps, EventpOpsAdd, Interest};

/// A deliberately narrowed view of `Pin<&mut Ep>` exposing only `add`,
/// `modify`, and `delete`.
///
/// `Eventp` is `!Unpin`, so once it is wrapped in `Pin<&mut _>` safe code can
/// no longer recover an `&mut Eventp` and use `mem::replace` (or any other
/// move-out operation) to destroy the loop while a handler is running. This
/// is the property that lets handlers safely receive a "mutable" view of the
/// reactor without invalidating the `&mut self` of the in-flight subscriber.
///
/// `Pinned` therefore intentionally does **not** behave like a full
/// `&mut Eventp`: it exposes exactly the three `epoll_ctl`-shaped operations
/// and nothing else. For the underlying mechanism, see
/// [technical](crate::_technical).
pub struct Pinned<'a, Ep>(pub Pin<&'a mut Ep>);

impl<'a, Ep> Pinned<'a, Ep> {
    /// Reborrows this `Pinned` with a shorter lifetime.
    ///
    /// This is the equivalent of [`Pin::as_mut`] for `Pinned`, allowing the
    /// caller to pass the reactor handle into a sub-call without giving up
    /// ownership of the outer `Pinned`.
    pub fn as_mut(&mut self) -> Pinned<'_, Ep> {
        Pinned(self.0.as_mut())
    }
}

impl<'a, Ep: EventpOps> EventpOpsAdd<Ep> for Pinned<'a, Ep> {
    #[doc = include_str!("../docs/eventp-ops.add.md")]
    fn add(&mut self, subscriber: ThinBoxSubscriber<Ep>) -> io::Result<()> {
        unsafe { self.0.as_mut().get_unchecked_mut().add(subscriber) }
    }
}

impl<'a, Ep> Pinned<'a, Ep>
where
    Ep: EventpOps,
{
    #[doc = include_str!("../docs/eventp-ops.modify.md")]
    pub fn modify(&mut self, fd: RawFd, interest: Interest) -> io::Result<()> {
        unsafe { self.0.as_mut().get_unchecked_mut().modify(fd, interest) }
    }

    #[doc = include_str!("../docs/eventp-ops.delete.md")]
    pub fn delete(&mut self, fd: RawFd) -> io::Result<()> {
        unsafe { self.0.as_mut().get_unchecked_mut().delete(fd) }
    }
}

/// This macro is primarily used in tests with [MockEventp](crate::MockEventp) to
/// create a `Pinned<'_, MockEventp>`.
/// For details on the underlying magic, see [technical](crate::_technical).
///
/// # Examples
///
/// ```rust
/// # use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd};
/// use eventp::{pinned, EventpOps, MockEventp, Pinned};
/// use mockall::predicate::*;
///
/// fn fn_to_test(fd: impl AsFd, mut eventp: Pinned<'_, impl EventpOps>) {
///     // do something..
///     eventp.delete(fd.as_fd().as_raw_fd()).unwrap();
/// }
///
/// let fd = unsafe { BorrowedFd::borrow_raw(1) };
/// let mut mock_eventp = MockEventp::new();
/// mock_eventp
///     .expect_delete()
///     .with(eq(fd.as_raw_fd()))
///     .times(1)
///     .returning(|_| Ok(()));
/// fn_to_test(fd, pinned!(mock_eventp))
/// ```
#[macro_export]
macro_rules! pinned {
    ($value:expr $(,)?) => {
        $crate::Pinned(::std::pin::pin!($value))
    };
}