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
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
//! `MobileObserver` — mobile read-path control-plane hook (audit F-5.4, #1392).
//!
//! # Governance parity on mobile
//!
//! The core read gate (`Database::open_with_observer` +
//! [`Database::gated_search`](velesdb_core::Database::gated_search) /
//! [`Database::authorize_read`](velesdb_core::Database::authorize_read)) is
//! already wired on `server` and `python`, and notify-only on `tauri`, but was
//! historically **absent** on mobile: [`crate::VelesDatabase`] opened via
//! `Database::open` and every read went through a *detached*
//! [`VectorCollection`](velesdb_core::VectorCollection) leaf that has no
//! observer reference. This module restores parity.
//!
//! Unlike the WASM sibling — whose single-threaded `Rc<RefCell<…>>` store
//! cannot satisfy core's `Send + Sync` observer bound and therefore mirrors the
//! contract with a wasm-local trait — mobile's [`crate::VelesDatabase`] is a
//! `Send + Sync` UniFFI object. It can (and does) wire the **real** core
//! [`DatabaseObserver`](velesdb_core::DatabaseObserver) seam directly, so a
//! denying observer actually fails the read closed and an
//! `AllowWithScope` observer narrows results — this is a genuine gate, not
//! notify-only.
//!
//! # Foreign (Kotlin / Swift) observers
//!
//! [`MobileObserver`] is a UniFFI **foreign-implementable** trait
//! (`with_foreign`): a Kotlin or Swift class can implement it and register it
//! through
//! [`VelesDatabase::open_with_observer`](crate::VelesDatabase::open_with_observer).
//! [`ForeignObserver`] adapts such an instance to core's `DatabaseObserver` so
//! every governed read consults it before touching the store.
//!
//! # Contract (inherited from core)
//!
//! - [`MobileObserver::on_query_request`] defaults to [`MobileAccessDecision::Allow`],
//! so an observer overriding nothing behaves exactly as no observer at all.
//! - Implementations MUST NOT panic.
//! - Denial flows through [`MobileAccessDecision::Deny`], **not** an error
//! channel: `Deny` carries the message surfaced to the caller (the read
//! returns that error and zero results), `Allow` executes unmodified.
//! - With no observer registered the gate is a single `Option` check (the
//! zero-overhead contract of the core gate).
//!
//! # Follow-up
//!
//! Scope narrowing (`AccessDecision::AllowWithScope`) is honoured end-to-end for
//! observers wired at the Rust level, but is **not** yet expressible from the
//! foreign `MobileAccessDecision` enum (which carries only `Allow` / `Deny`),
//! mirroring the WASM decision surface. Surfacing a foreign scope filter is an
//! additive follow-up; adding a variant to `MobileAccessDecision` is
//! non-breaking.
use Arc;
use ;
/// The read operation being gated (UniFFI mirror of core's `QueryOperationKind`).
/// Read-time context handed to a foreign [`MobileObserver`].
///
/// Owned (not borrowed) because it crosses the UniFFI boundary. `principal` and
/// `tenant_hint` are opaque, caller-supplied identity/tenant hints forwarded
/// untouched — the gate never interprets them (they are only meaningful when a
/// trusted embedder forwards a verified identity: the local-SDK trust boundary).
/// The control-plane decision returned by a foreign [`MobileObserver`].
///
/// Kept intentionally small for the FFI boundary: `Allow` executes the read
/// unmodified, `Deny { reason }` aborts with `reason` and zero results. Scope
/// narrowing (`AllowWithScope` in core) is deliberately **not** replicated at
/// the foreign boundary yet — see the module-level follow-up note; adding a
/// variant later is additive.
/// Foreign-implementable read-path observer (UniFFI callback interface).
///
/// A Kotlin/Swift class implements this trait and registers it via
/// [`VelesDatabase::open_with_observer`](crate::VelesDatabase::open_with_observer).
/// Every governed read routed through the database (dense / text / hybrid /
/// sparse / multi-query search, `VelesQL` `SELECT` / `MATCH`) consults it before
/// touching the store.
/// Adapts a foreign [`MobileObserver`] to core's [`DatabaseObserver`] so it can
/// be injected through
/// [`Database::open_with_observer`](velesdb_core::Database::open_with_observer).
///
/// Only the read-path hook (`on_query_request`) is bridged; the lifecycle hooks
/// keep their no-op defaults (mobile has no event stream to forward them to).
pub