Skip to content

Commit 65b0882

Browse files
committed
refactor(ffi): back the search iterators with SDK streams
The SDK's message search now exposes streams instead of iterators. Keep the `RoomSearchIterator`/`GlobalSearchIterator` objects and their batch- oriented `next_events()` surface unchanged but back them with a boxed SDK search stream they pull `num_results_per_batch` items from.
1 parent ca6381a commit 65b0882

1 file changed

Lines changed: 54 additions & 20 deletions

File tree

bindings/matrix-sdk-ffi/src/search.rs

Lines changed: 54 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212
// See the License for that specific language governing permissions and
1313
// limitations under the License.
1414

15+
use std::pin::Pin;
16+
17+
use futures_util::{Stream, StreamExt as _};
1518
use matrix_sdk::{
16-
deserialized_responses::TimelineEvent,
17-
message_search::{
18-
GlobalSearchIterator as SdkGlobalSearchIterator,
19-
RoomSearchIterator as SdkRoomSearchIterator, SearchError as SdkSearchError,
20-
},
19+
deserialized_responses::TimelineEvent, message_search::SearchError as SdkSearchError,
2120
};
2221
use matrix_sdk_ui::timeline::TimelineDetails;
22+
use ruma::OwnedRoomId;
2323
use tokio::sync::Mutex;
2424

2525
use crate::{
@@ -47,6 +47,13 @@ impl From<SdkSearchError> for SearchError {
4747
}
4848
}
4949

50+
/// A boxed stream of the [`TimelineEvent`]s matching a single-room search.
51+
type RoomEventStream = Pin<Box<dyn Stream<Item = Result<TimelineEvent, SdkSearchError>> + Send>>;
52+
53+
/// A boxed stream of the `(room_id, event)`s matching a global search.
54+
type GlobalEventStream =
55+
Pin<Box<dyn Stream<Item = Result<(OwnedRoomId, TimelineEvent), SdkSearchError>> + Send>>;
56+
5057
#[matrix_sdk_ffi_macros::export]
5158
impl Room {
5259
/// Search for messages in this room matching the given query, returning an
@@ -55,28 +62,40 @@ impl Room {
5562
pub fn search_messages(&self, query: String, num_results_per_batch: u32) -> RoomSearchIterator {
5663
RoomSearchIterator {
5764
sdk_room: (*self.inner).clone(),
58-
inner: Mutex::new(self.inner.search_messages(query, num_results_per_batch as usize)),
65+
stream: Mutex::new(Box::pin(self.inner.search_messages_events(query))),
66+
num_results_per_batch: num_results_per_batch as usize,
5967
}
6068
}
6169
}
6270

6371
#[derive(uniffi::Object)]
6472
pub struct RoomSearchIterator {
6573
sdk_room: matrix_sdk::room::Room,
66-
inner: Mutex<SdkRoomSearchIterator>,
74+
stream: Mutex<RoomEventStream>,
75+
num_results_per_batch: usize,
6776
}
6877

6978
#[matrix_sdk_ffi_macros::export]
7079
impl RoomSearchIterator {
7180
/// Return a list of events for the next batch of search results, or `None`
7281
/// if there are no more results.
7382
pub async fn next_events(&self) -> Result<Option<Vec<RoomSearchResult>>, SearchError> {
74-
let Some(events) = self.inner.lock().await.next_events().await? else {
83+
let mut stream = self.stream.lock().await;
84+
85+
let mut events = Vec::with_capacity(self.num_results_per_batch);
86+
while events.len() < self.num_results_per_batch {
87+
let Some(event) = stream.next().await else {
88+
break;
89+
};
90+
events.push(event?);
91+
}
92+
93+
if events.is_empty() {
7594
return Ok(None);
76-
};
95+
}
96+
drop(stream);
7797

7898
let mut results = Vec::with_capacity(events.len());
79-
8099
for event in events {
81100
if let Some(result) = RoomSearchResult::from(&self.sdk_room, event).await {
82101
results.push(result);
@@ -142,15 +161,19 @@ impl Client {
142161
num_results_per_batch: u32,
143162
) -> Result<GlobalSearchIterator, ClientError> {
144163
let sdk_client = (*self.inner).clone();
145-
let mut search = sdk_client.search_messages(query, num_results_per_batch as usize);
164+
let mut builder = sdk_client.search_messages(query);
146165

147166
match filter {
148167
SearchRoomFilter::Rooms => {}
149-
SearchRoomFilter::Dms => search = search.only_dm_rooms().await?,
150-
SearchRoomFilter::NonDms => search = search.no_dms().await?,
168+
SearchRoomFilter::Dms => builder = builder.only_dm_rooms().await?,
169+
SearchRoomFilter::NonDms => builder = builder.no_dms().await?,
151170
}
152171

153-
Ok(GlobalSearchIterator { sdk_client, inner: Mutex::new(search.build()) })
172+
Ok(GlobalSearchIterator {
173+
sdk_client,
174+
stream: Mutex::new(Box::pin(builder.build_events())),
175+
num_results_per_batch: num_results_per_batch as usize,
176+
})
154177
}
155178
}
156179

@@ -163,21 +186,32 @@ pub struct GlobalSearchResult {
163186
#[derive(uniffi::Object)]
164187
pub struct GlobalSearchIterator {
165188
sdk_client: matrix_sdk::Client,
166-
inner: Mutex<SdkGlobalSearchIterator>,
189+
stream: Mutex<GlobalEventStream>,
190+
num_results_per_batch: usize,
167191
}
168192

169193
#[matrix_sdk_ffi_macros::export]
170194
impl GlobalSearchIterator {
171195
/// Return a list of events for the next batch of search results, or `None`
172196
/// if there are no more results.
173197
pub async fn next_events(&self) -> Result<Option<Vec<GlobalSearchResult>>, SearchError> {
174-
let Some(events) = self.inner.lock().await.next_events().await? else {
175-
return Ok(None);
176-
};
198+
let mut stream = self.stream.lock().await;
177199

178-
let mut results = Vec::with_capacity(events.len());
200+
let mut batch = Vec::with_capacity(self.num_results_per_batch);
201+
while batch.len() < self.num_results_per_batch {
202+
let Some(item) = stream.next().await else {
203+
break;
204+
};
205+
batch.push(item?);
206+
}
207+
208+
if batch.is_empty() {
209+
return Ok(None);
210+
}
211+
drop(stream);
179212

180-
for (room_id, event) in events {
213+
let mut results = Vec::with_capacity(batch.len());
214+
for (room_id, event) in batch {
181215
let Some(room) = self.sdk_client.get_room(&room_id) else {
182216
continue;
183217
};

0 commit comments

Comments
 (0)