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}