atagen

matrix-rust-sdk

public
Star0
Watch0
ssh://brewery@pls.lobotomise.me:33/atagen/matrix-rust-sdk.git

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

7ff8adafe9e4atagen2026-10-22 12:30 UTC1
// Copyright 2026 The Matrix.org Foundation C.I.C.
7ff8adafe9e4atagen2026-10-22 12:30 UTC2
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC3
// Licensed under the Apache License, Version 2.0 (the "License");
7ff8adafe9e4atagen2026-10-22 12:30 UTC4
// you may not use this file except in compliance with the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC5
// You may obtain a copy of the License at
7ff8adafe9e4atagen2026-10-22 12:30 UTC6
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC7
//     http://www.apache.org/licenses/LICENSE-2.0
7ff8adafe9e4atagen2026-10-22 12:30 UTC8
//
7ff8adafe9e4atagen2026-10-22 12:30 UTC9
// Unless required by applicable law or agreed to in writing, software
7ff8adafe9e4atagen2026-10-22 12:30 UTC10
// distributed under the License is distributed on an "AS IS" BASIS,
7ff8adafe9e4atagen2026-10-22 12:30 UTC11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7ff8adafe9e4atagen2026-10-22 12:30 UTC12
// See the License for that specific language governing permissions and
7ff8adafe9e4atagen2026-10-22 12:30 UTC13
// limitations under the License.
7ff8adafe9e4atagen2026-10-22 12:30 UTC14
7ff8adafe9e4atagen2026-10-22 12:30 UTC15
use matrix_sdk::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC16
    deserialized_responses::TimelineEvent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC17
    message_search::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC18
        GlobalSearchIterator as SdkGlobalSearchIterator,
7ff8adafe9e4atagen2026-10-22 12:30 UTC19
        RoomSearchIterator as SdkRoomSearchIterator, SearchError as SdkSearchError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC20
    },
7ff8adafe9e4atagen2026-10-22 12:30 UTC21
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC22
use matrix_sdk_ui::timeline::TimelineDetails;
7ff8adafe9e4atagen2026-10-22 12:30 UTC23
use tokio::sync::Mutex;
7ff8adafe9e4atagen2026-10-22 12:30 UTC24
7ff8adafe9e4atagen2026-10-22 12:30 UTC25
use crate::{
7ff8adafe9e4atagen2026-10-22 12:30 UTC26
    client::Client,
7ff8adafe9e4atagen2026-10-22 12:30 UTC27
    error::ClientError,
7ff8adafe9e4atagen2026-10-22 12:30 UTC28
    room::Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC29
    timeline::{ProfileDetails, TimelineItemContent},
7ff8adafe9e4atagen2026-10-22 12:30 UTC30
    utils::Timestamp,
7ff8adafe9e4atagen2026-10-22 12:30 UTC31
};
7ff8adafe9e4atagen2026-10-22 12:30 UTC32
7ff8adafe9e4atagen2026-10-22 12:30 UTC33
#[derive(uniffi::Error, thiserror::Error, Debug)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC34
pub enum SearchError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC35
    #[error("Failed to search through the index: {0}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC36
    IndexError(String),
7ff8adafe9e4atagen2026-10-22 12:30 UTC37
    #[error("Failed to load event content for search result: {0}")]
7ff8adafe9e4atagen2026-10-22 12:30 UTC38
    EventLoadError(String),
7ff8adafe9e4atagen2026-10-22 12:30 UTC39
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC40
7ff8adafe9e4atagen2026-10-22 12:30 UTC41
impl From<SdkSearchError> for SearchError {
7ff8adafe9e4atagen2026-10-22 12:30 UTC42
    fn from(err: SdkSearchError) -> Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC43
        match err {
7ff8adafe9e4atagen2026-10-22 12:30 UTC44
            SdkSearchError::IndexError(err) => SearchError::IndexError(err.to_string()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC45
            SdkSearchError::EventLoadError(err) => SearchError::EventLoadError(err.to_string()),
7ff8adafe9e4atagen2026-10-22 12:30 UTC46
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC47
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC48
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC49
7ff8adafe9e4atagen2026-10-22 12:30 UTC50
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC51
impl Room {
7ff8adafe9e4atagen2026-10-22 12:30 UTC52
    /// Search for messages in this room matching the given query, returning an
7ff8adafe9e4atagen2026-10-22 12:30 UTC53
    /// iterator over the results that yields `num_results_per_batch` results at
7ff8adafe9e4atagen2026-10-22 12:30 UTC54
    /// a time.
7ff8adafe9e4atagen2026-10-22 12:30 UTC55
    pub fn search_messages(&self, query: String, num_results_per_batch: u32) -> RoomSearchIterator {
7ff8adafe9e4atagen2026-10-22 12:30 UTC56
        RoomSearchIterator {
7ff8adafe9e4atagen2026-10-22 12:30 UTC57
            sdk_room: (*self.inner).clone(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC58
            inner: Mutex::new(self.inner.search_messages(query, num_results_per_batch as usize)),
7ff8adafe9e4atagen2026-10-22 12:30 UTC59
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC60
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC61
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC62
7ff8adafe9e4atagen2026-10-22 12:30 UTC63
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC64
pub struct RoomSearchIterator {
7ff8adafe9e4atagen2026-10-22 12:30 UTC65
    sdk_room: matrix_sdk::room::Room,
7ff8adafe9e4atagen2026-10-22 12:30 UTC66
    inner: Mutex<SdkRoomSearchIterator>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC67
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC68
7ff8adafe9e4atagen2026-10-22 12:30 UTC69
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC70
impl RoomSearchIterator {
7ff8adafe9e4atagen2026-10-22 12:30 UTC71
    /// Return a list of events for the next batch of search results, or `None`
7ff8adafe9e4atagen2026-10-22 12:30 UTC72
    /// if there are no more results.
7ff8adafe9e4atagen2026-10-22 12:30 UTC73
    pub async fn next_events(&self) -> Result<Option<Vec<RoomSearchResult>>, SearchError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC74
        let Some(events) = self.inner.lock().await.next_events().await? else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC75
            return Ok(None);
7ff8adafe9e4atagen2026-10-22 12:30 UTC76
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC77
7ff8adafe9e4atagen2026-10-22 12:30 UTC78
        let mut results = Vec::with_capacity(events.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC79
7ff8adafe9e4atagen2026-10-22 12:30 UTC80
        for event in events {
7ff8adafe9e4atagen2026-10-22 12:30 UTC81
            if let Some(result) = RoomSearchResult::from(&self.sdk_room, event).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC82
                results.push(result);
7ff8adafe9e4atagen2026-10-22 12:30 UTC83
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC84
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC85
7ff8adafe9e4atagen2026-10-22 12:30 UTC86
        results.shrink_to_fit();
7ff8adafe9e4atagen2026-10-22 12:30 UTC87
7ff8adafe9e4atagen2026-10-22 12:30 UTC88
        Ok(Some(results))
7ff8adafe9e4atagen2026-10-22 12:30 UTC89
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC90
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC91
7ff8adafe9e4atagen2026-10-22 12:30 UTC92
#[derive(Clone, uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC93
pub struct RoomSearchResult {
7ff8adafe9e4atagen2026-10-22 12:30 UTC94
    event_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC95
    sender: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC96
    sender_profile: ProfileDetails,
7ff8adafe9e4atagen2026-10-22 12:30 UTC97
    content: TimelineItemContent,
7ff8adafe9e4atagen2026-10-22 12:30 UTC98
    timestamp: Timestamp,
7ff8adafe9e4atagen2026-10-22 12:30 UTC99
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC100
7ff8adafe9e4atagen2026-10-22 12:30 UTC101
impl RoomSearchResult {
7ff8adafe9e4atagen2026-10-22 12:30 UTC102
    async fn from(room: &matrix_sdk::room::Room, event: TimelineEvent) -> Option<Self> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC103
        let sender = event.sender()?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC104
7ff8adafe9e4atagen2026-10-22 12:30 UTC105
        let event_id = event.event_id().unwrap().to_string();
7ff8adafe9e4atagen2026-10-22 12:30 UTC106
        let timestamp =
7ff8adafe9e4atagen2026-10-22 12:30 UTC107
            event.timestamp().unwrap_or_else(ruma::MilliSecondsSinceUnixEpoch::now).into();
7ff8adafe9e4atagen2026-10-22 12:30 UTC108
7ff8adafe9e4atagen2026-10-22 12:30 UTC109
        let content = matrix_sdk_ui::timeline::TimelineItemContent::from_event(room, event).await?;
7ff8adafe9e4atagen2026-10-22 12:30 UTC110
        let profile = TimelineDetails::from_initial_value(
7ff8adafe9e4atagen2026-10-22 12:30 UTC111
            matrix_sdk_ui::timeline::Profile::load(room, &sender).await,
7ff8adafe9e4atagen2026-10-22 12:30 UTC112
        );
7ff8adafe9e4atagen2026-10-22 12:30 UTC113
7ff8adafe9e4atagen2026-10-22 12:30 UTC114
        Some(Self {
7ff8adafe9e4atagen2026-10-22 12:30 UTC115
            event_id,
7ff8adafe9e4atagen2026-10-22 12:30 UTC116
            sender: sender.to_string(),
7ff8adafe9e4atagen2026-10-22 12:30 UTC117
            sender_profile: ProfileDetails::from(profile),
7ff8adafe9e4atagen2026-10-22 12:30 UTC118
            content: TimelineItemContent::from(content),
7ff8adafe9e4atagen2026-10-22 12:30 UTC119
            timestamp,
7ff8adafe9e4atagen2026-10-22 12:30 UTC120
        })
7ff8adafe9e4atagen2026-10-22 12:30 UTC121
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC122
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC123
7ff8adafe9e4atagen2026-10-22 12:30 UTC124
#[derive(Clone, uniffi::Enum)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC125
pub enum SearchRoomFilter {
7ff8adafe9e4atagen2026-10-22 12:30 UTC126
    /// All the joined rooms (= DMs + non-DMs).
7ff8adafe9e4atagen2026-10-22 12:30 UTC127
    Rooms,
7ff8adafe9e4atagen2026-10-22 12:30 UTC128
    /// Only joined DM rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC129
    Dms,
7ff8adafe9e4atagen2026-10-22 12:30 UTC130
    /// Only joined non-DM (group) rooms.
7ff8adafe9e4atagen2026-10-22 12:30 UTC131
    NonDms,
7ff8adafe9e4atagen2026-10-22 12:30 UTC132
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC133
7ff8adafe9e4atagen2026-10-22 12:30 UTC134
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC135
impl Client {
7ff8adafe9e4atagen2026-10-22 12:30 UTC136
    /// Search across all all rooms for the given query, returning an iterator
7ff8adafe9e4atagen2026-10-22 12:30 UTC137
    /// over the results.
7ff8adafe9e4atagen2026-10-22 12:30 UTC138
    pub async fn search_messages(
7ff8adafe9e4atagen2026-10-22 12:30 UTC139
        &self,
7ff8adafe9e4atagen2026-10-22 12:30 UTC140
        query: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC141
        filter: SearchRoomFilter,
7ff8adafe9e4atagen2026-10-22 12:30 UTC142
        num_results_per_batch: u32,
7ff8adafe9e4atagen2026-10-22 12:30 UTC143
    ) -> Result<GlobalSearchIterator, ClientError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC144
        let sdk_client = (*self.inner).clone();
7ff8adafe9e4atagen2026-10-22 12:30 UTC145
        let mut search = sdk_client.search_messages(query, num_results_per_batch as usize);
7ff8adafe9e4atagen2026-10-22 12:30 UTC146
7ff8adafe9e4atagen2026-10-22 12:30 UTC147
        match filter {
7ff8adafe9e4atagen2026-10-22 12:30 UTC148
            SearchRoomFilter::Rooms => {}
7ff8adafe9e4atagen2026-10-22 12:30 UTC149
            SearchRoomFilter::Dms => search = search.only_dm_rooms().await?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC150
            SearchRoomFilter::NonDms => search = search.no_dms().await?,
7ff8adafe9e4atagen2026-10-22 12:30 UTC151
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC152
7ff8adafe9e4atagen2026-10-22 12:30 UTC153
        Ok(GlobalSearchIterator { sdk_client, inner: Mutex::new(search.build()) })
7ff8adafe9e4atagen2026-10-22 12:30 UTC154
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC155
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC156
7ff8adafe9e4atagen2026-10-22 12:30 UTC157
#[derive(uniffi::Record)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC158
pub struct GlobalSearchResult {
7ff8adafe9e4atagen2026-10-22 12:30 UTC159
    room_id: String,
7ff8adafe9e4atagen2026-10-22 12:30 UTC160
    result: RoomSearchResult,
7ff8adafe9e4atagen2026-10-22 12:30 UTC161
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC162
7ff8adafe9e4atagen2026-10-22 12:30 UTC163
#[derive(uniffi::Object)]
7ff8adafe9e4atagen2026-10-22 12:30 UTC164
pub struct GlobalSearchIterator {
7ff8adafe9e4atagen2026-10-22 12:30 UTC165
    sdk_client: matrix_sdk::Client,
7ff8adafe9e4atagen2026-10-22 12:30 UTC166
    inner: Mutex<SdkGlobalSearchIterator>,
7ff8adafe9e4atagen2026-10-22 12:30 UTC167
}
7ff8adafe9e4atagen2026-10-22 12:30 UTC168
7ff8adafe9e4atagen2026-10-22 12:30 UTC169
#[matrix_sdk_ffi_macros::export]
7ff8adafe9e4atagen2026-10-22 12:30 UTC170
impl GlobalSearchIterator {
7ff8adafe9e4atagen2026-10-22 12:30 UTC171
    /// Return a list of events for the next batch of search results, or `None`
7ff8adafe9e4atagen2026-10-22 12:30 UTC172
    /// if there are no more results.
7ff8adafe9e4atagen2026-10-22 12:30 UTC173
    pub async fn next_events(&self) -> Result<Option<Vec<GlobalSearchResult>>, SearchError> {
7ff8adafe9e4atagen2026-10-22 12:30 UTC174
        let Some(events) = self.inner.lock().await.next_events().await? else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC175
            return Ok(None);
7ff8adafe9e4atagen2026-10-22 12:30 UTC176
        };
7ff8adafe9e4atagen2026-10-22 12:30 UTC177
7ff8adafe9e4atagen2026-10-22 12:30 UTC178
        let mut results = Vec::with_capacity(events.len());
7ff8adafe9e4atagen2026-10-22 12:30 UTC179
7ff8adafe9e4atagen2026-10-22 12:30 UTC180
        for (room_id, event) in events {
7ff8adafe9e4atagen2026-10-22 12:30 UTC181
            let Some(room) = self.sdk_client.get_room(&room_id) else {
7ff8adafe9e4atagen2026-10-22 12:30 UTC182
                continue;
7ff8adafe9e4atagen2026-10-22 12:30 UTC183
            };
7ff8adafe9e4atagen2026-10-22 12:30 UTC184
            if let Some(result) = RoomSearchResult::from(&room, event).await {
7ff8adafe9e4atagen2026-10-22 12:30 UTC185
                results.push(GlobalSearchResult { room_id: room_id.to_string(), result });
7ff8adafe9e4atagen2026-10-22 12:30 UTC186
            }
7ff8adafe9e4atagen2026-10-22 12:30 UTC187
        }
7ff8adafe9e4atagen2026-10-22 12:30 UTC188
7ff8adafe9e4atagen2026-10-22 12:30 UTC189
        results.shrink_to_fit();
7ff8adafe9e4atagen2026-10-22 12:30 UTC190
7ff8adafe9e4atagen2026-10-22 12:30 UTC191
        Ok(Some(results))
7ff8adafe9e4atagen2026-10-22 12:30 UTC192
    }
7ff8adafe9e4atagen2026-10-22 12:30 UTC193
}