Skip to main content

graphrefly_operators/
binding.rs

1//! [`OperatorBinding`] — closure-registration helper trait for the
2//! operators crate. Sub-trait of [`BindingBoundary`] (D015).
3//!
4//! Operator factories ([`super::transform::map`], etc.) accept user
5//! closures of shape `Fn(T) -> R` / `Fn(T) -> bool` / `Fn(R, T) -> R`.
6//! At the FFI plane (per the handle-protocol cleaving plane invariant),
7//! Core only ever sees opaque [`HandleId`] integers. Bindings therefore
8//! need to wrap user closures into the `Fn(HandleId) -> HandleId` shape
9//! (and friends) — that wrapping is binding-side because it requires
10//! deref+intern operations against the binding's value registry, and
11//! returning a [`FnId`] that Core stores in the [`OperatorOp`] variant.
12//!
13//! Bindings impl both [`BindingBoundary`] (Core-callable FFI for
14//! `project_each` / `predicate_each` / `fold_each` / `pairwise_pack`) AND
15//! [`OperatorBinding`] (the closure-registration calls below).
16//!
17//! See [`OperatorOp`] for the per-operator FFI dispatch and `D016` in
18//! `docs/rust-port-decisions.md` for the wrapping discipline.
19//!
20//! [`BindingBoundary`]: graphrefly_core::BindingBoundary
21//! [`HandleId`]: graphrefly_core::HandleId
22//! [`FnId`]: graphrefly_core::FnId
23//! [`OperatorOp`]: graphrefly_core::OperatorOp
24
25use graphrefly_core::{BindingBoundary, FnId, HandleId};
26
27/// Closure-registration interface used by transform-operator factories.
28///
29/// Each method takes ownership of a user closure (boxed for type erasure)
30/// and returns the [`FnId`] under which the binding registered it. The
31/// operator factory then passes that `FnId` into [`graphrefly_core::OperatorOp`]
32/// so the wave engine's per-fire FFI calls can reach back through the
33/// registry.
34///
35/// # Closure shape
36///
37/// All closures take and return [`HandleId`] (or `bool` for predicates) —
38/// not `T` / `R`. Wrapping `Fn(T) -> R` into `Fn(HandleId) -> HandleId` is
39/// a binding-side concern: deref incoming handles to `T`, run user code,
40/// intern the output to a fresh `HandleId`. See `D016` for rationale.
41///
42/// Implementations must be `Send + Sync` for the closure storage so the
43/// binding can be shared across threads (matching `BindingBoundary`'s
44/// `Send + Sync` super-bounds).
45pub trait OperatorBinding: BindingBoundary {
46    /// Register a single-arg projector: `Fn(T) -> R` wrapped into
47    /// `Fn(HandleId) -> HandleId`. Used by [`super::transform::map`].
48    /// The returned [`FnId`] is passed to
49    /// [`graphrefly_core::OperatorOp::Map`].
50    fn register_projector(&self, f: Box<dyn Fn(HandleId) -> HandleId + Send + Sync>) -> FnId;
51
52    /// Register a single-arg predicate: `Fn(T) -> bool`. Used by
53    /// [`super::transform::filter`]. The returned [`FnId`] is passed to
54    /// [`graphrefly_core::OperatorOp::Filter`].
55    fn register_predicate(&self, f: Box<dyn Fn(HandleId) -> bool + Send + Sync>) -> FnId;
56
57    /// Register a left-fold reducer: `Fn(R, T) -> R` wrapped into
58    /// `Fn(HandleId, HandleId) -> HandleId`. Used by
59    /// [`super::transform::scan`] and [`super::transform::reduce`]. The
60    /// returned [`FnId`] is passed to [`graphrefly_core::OperatorOp::Scan`]
61    /// or [`graphrefly_core::OperatorOp::Reduce`].
62    fn register_folder(&self, f: Box<dyn Fn(HandleId, HandleId) -> HandleId + Send + Sync>)
63        -> FnId;
64
65    /// Register a custom-equals oracle: `Fn(T, T) -> bool` reused via
66    /// [`BindingBoundary::custom_equals`]. Used by
67    /// [`super::transform::distinct_until_changed`]. The returned
68    /// [`FnId`] is passed to
69    /// [`graphrefly_core::OperatorOp::DistinctUntilChanged`].
70    fn register_equals(&self, f: Box<dyn Fn(HandleId, HandleId) -> bool + Send + Sync>) -> FnId;
71
72    /// Register a pairwise packer: `Fn(prev: T, current: T) -> (T, T)`
73    /// wrapped into `Fn(HandleId, HandleId) -> HandleId` (where the
74    /// returned handle resolves to the binding's tuple representation).
75    /// Used by [`super::transform::pairwise`]. The returned [`FnId`] is
76    /// passed to [`graphrefly_core::OperatorOp::Pairwise`].
77    fn register_pairwise_packer(
78        &self,
79        f: Box<dyn Fn(HandleId, HandleId) -> HandleId + Send + Sync>,
80    ) -> FnId;
81
82    /// Register a tuple packer: `Fn(&[T]) -> Tuple` wrapped into
83    /// `Fn(&[HandleId]) -> HandleId` (where the returned handle resolves
84    /// to the binding's N-ary tuple representation).
85    /// Used by [`super::combine::combine`] and
86    /// [`super::combine::with_latest_from`]. The returned [`FnId`] is
87    /// passed to [`graphrefly_core::OperatorOp::Combine`] or
88    /// [`graphrefly_core::OperatorOp::WithLatestFrom`].
89    fn register_packer(&self, f: super::combine::PackerFn) -> FnId;
90}