1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! [`OperatorBinding`] — closure-registration helper trait for the
//! operators crate. Sub-trait of [`BindingBoundary`] (D015).
//!
//! Operator factories ([`super::transform::map`], etc.) accept user
//! closures of shape `Fn(T) -> R` / `Fn(T) -> bool` / `Fn(R, T) -> R`.
//! At the FFI plane (per the handle-protocol cleaving plane invariant),
//! Core only ever sees opaque [`HandleId`] integers. Bindings therefore
//! need to wrap user closures into the `Fn(HandleId) -> HandleId` shape
//! (and friends) — that wrapping is binding-side because it requires
//! deref+intern operations against the binding's value registry, and
//! returning a [`FnId`] that Core stores in the [`OperatorOp`] variant.
//!
//! Bindings impl both [`BindingBoundary`] (Core-callable FFI for
//! `project_each` / `predicate_each` / `fold_each` / `pairwise_pack`) AND
//! [`OperatorBinding`] (the closure-registration calls below).
//!
//! See [`OperatorOp`] for the per-operator FFI dispatch and `D016` in
//! `docs/rust-port-decisions.md` for the wrapping discipline.
//!
//! [`BindingBoundary`]: graphrefly_core::BindingBoundary
//! [`HandleId`]: graphrefly_core::HandleId
//! [`FnId`]: graphrefly_core::FnId
//! [`OperatorOp`]: graphrefly_core::OperatorOp
use ;
/// Closure-registration interface used by transform-operator factories.
///
/// Each method takes ownership of a user closure (boxed for type erasure)
/// and returns the [`FnId`] under which the binding registered it. The
/// operator factory then passes that `FnId` into [`graphrefly_core::OperatorOp`]
/// so the wave engine's per-fire FFI calls can reach back through the
/// registry.
///
/// # Closure shape
///
/// All closures take and return [`HandleId`] (or `bool` for predicates) —
/// not `T` / `R`. Wrapping `Fn(T) -> R` into `Fn(HandleId) -> HandleId` is
/// a binding-side concern: deref incoming handles to `T`, run user code,
/// intern the output to a fresh `HandleId`. See `D016` for rationale.
///
/// Implementations must be `Send + Sync` for the closure storage so the
/// binding can be shared across threads (matching `BindingBoundary`'s
/// `Send + Sync` super-bounds).