dfir_rs/scheduled/
port.rs

1//! Organizational module for [`SendCtx`]/[`RecvCtx`] structs and [`SendPort`]/[`RecvPort`] structs.
2use std::cell::RefMut;
3use std::marker::PhantomData;
4
5use ref_cast::RefCast;
6use sealed::sealed;
7
8use super::HandoffId;
9use crate::scheduled::graph::Dfir;
10use crate::scheduled::handoff::{CanReceive, Handoff, TeeingHandoff, TryCanReceive};
11
12/// An empty trait used to denote [`Polarity`]: either **send** or **receive**.
13///
14/// [`SendPort`] and [`RecvPort`] have identical representations (via [`Port`]) but are not
15/// interchangable, so [`SEND`] and [`RECV`] which implement this trait are used to differentiate
16/// between the two polarities.
17#[sealed]
18pub trait Polarity: 'static {}
19
20/// An uninstantiable type used to tag port [`Polarity`] as **send**.
21///
22/// See also: [`RECV`].
23#[expect(clippy::upper_case_acronyms, reason = "marker type")]
24pub enum SEND {}
25/// An uninstantiable type used to tag port [`Polarity`] as **receive**.
26///
27/// See also: [`SEND`].
28#[expect(clippy::upper_case_acronyms, reason = "marker type")]
29pub enum RECV {}
30#[sealed]
31impl Polarity for SEND {}
32#[sealed]
33impl Polarity for RECV {}
34
35/// Lightweight ID struct representing an input or output port for a [`Handoff`] added to a
36/// [`Dfir`] instance..
37#[must_use]
38pub struct Port<S: Polarity, H>
39where
40    H: Handoff,
41{
42    pub(crate) handoff_id: HandoffId,
43    #[expect(clippy::type_complexity, reason = "phantom data")]
44    pub(crate) _marker: PhantomData<(*const S, fn() -> H)>,
45}
46/// Send-specific variant of [`Port`]. An output port.
47pub type SendPort<H> = Port<SEND, H>;
48/// Recv-specific variant of [`Port`]. An input port.
49pub type RecvPort<H> = Port<RECV, H>;
50
51/// Methods for [`TeeingHandoff`] teeing and dropping.
52impl<T: Clone> RecvPort<TeeingHandoff<T>> {
53    /// Tees this [`TeeingHandoff`], given the [`Dfir`] instance it belongs to.
54    pub fn tee(&self, hf: &mut Dfir) -> RecvPort<TeeingHandoff<T>> {
55        hf.teeing_handoff_tee(self)
56    }
57
58    /// Marks this output of a [`TeeingHandoff`] as dropped so that no more data will be sent to
59    /// it, given the [`Dfir`] instance it belongs to.
60    ///
61    /// It is recommended to not not use this method and instead simply avoid teeing a
62    /// [`TeeingHandoff`] when it is not needed.
63    pub fn drop(self, hf: &mut Dfir) {
64        hf.teeing_handoff_drop(self)
65    }
66}
67
68/// Wrapper around a handoff to differentiate between output and input.
69#[derive(RefCast)]
70#[repr(transparent)]
71pub struct PortCtx<S: Polarity, H> {
72    pub(crate) handoff: H,
73    pub(crate) _marker: PhantomData<*const S>,
74}
75/// Send-specific [`PortCtx`]. Output to send into a handoff.
76pub type SendCtx<H> = PortCtx<SEND, H>;
77/// Recv-specific [`PortCtx`]. Input to receive from a handoff.
78pub type RecvCtx<H> = PortCtx<RECV, H>;
79
80/// Context provided to a subgraph for reading from a handoff. Corresponds to a [`SendPort`].
81impl<H: Handoff> SendCtx<H> {
82    /// Alias for [`Handoff::give`] on the inner `H`.
83    pub fn give<T>(&self, item: T) -> T
84    where
85        H: CanReceive<T>,
86    {
87        <H as CanReceive<T>>::give(&self.handoff, item)
88    }
89
90    /// Alias for [`Handoff::try_give`] on the inner `H`.
91    pub fn try_give<T>(&self, item: T) -> Result<T, T>
92    where
93        H: TryCanReceive<T>,
94    {
95        <H as TryCanReceive<T>>::try_give(&self.handoff, item)
96    }
97}
98
99/// Context provided to a subgraph for reading from a handoff. Corresponds to a [`RecvPort`].
100impl<H: Handoff> RecvCtx<H> {
101    /// See [`Handoff::take_inner`].
102    pub fn take_inner(&self) -> H::Inner {
103        self.handoff.take_inner()
104    }
105
106    /// See [`Handoff::borrow_mut_swap`].
107    pub fn borrow_mut_swap(&self) -> RefMut<H::Inner> {
108        self.handoff.borrow_mut_swap()
109    }
110}