Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
a2f79f2
feat(sdk): use MSC4482 bookmark Ruma types and begin implementation
IT-ess Jun 7, 2026
b925e5e
feat(sdk): define bookmarks index and setup background task for indexing
IT-ess Jun 11, 2026
2ffdbd2
feat(search): filter bookmark index by room_id
IT-ess Jun 11, 2026
decc12d
feat(sdk): higher level apis to manage bookmarks
IT-ess Jun 12, 2026
55c4e10
enable experimental-search and encrypt room if activated
IT-ess Jun 12, 2026
277d50d
expose parameter to store index locally
IT-ess Jun 15, 2026
3a4ded7
store room_id in account data and crawl the room on launch
IT-ess Jun 16, 2026
be441dd
get_bookmark_id_for_event
IT-ess Jun 16, 2026
23a59d5
implement timeline methods and boolean
IT-ess Jun 16, 2026
653b1d7
fix bookmark redaction
IT-ess Jun 17, 2026
6e0a0bc
add more tests
IT-ess Jun 18, 2026
8b2b7fa
fix: remove some stored fields
IT-ess Jun 19, 2026
1f41dd7
fix: no longer inspect every element of every timeline
IT-ess Jun 19, 2026
605b0fa
fix: do not create a separate task to index bookmarks
IT-ess Jun 19, 2026
835e021
fix: adjustments after rebase
IT-ess Jun 19, 2026
858da7c
feat: rework index structure to support bookmarks edits
IT-ess Jun 22, 2026
4b5ccca
feat: handle target event edits and redactions
IT-ess Jun 22, 2026
8078c35
fix: tests and swapped id after index refacto
IT-ess Jun 23, 2026
2e52f82
cleanup unecessary functions and errors
IT-ess Jun 23, 2026
c4e5dc8
fix: return latest replacement of bookmarked event
IT-ess Jun 23, 2026
92fee3d
fix: make indexed body mandatory
IT-ess Jun 24, 2026
6ce4363
feat: global bookmarks search iterator
IT-ess Jun 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ rand = { version = "0.10.1", default-features = false, features = ["std", "std_r
regex = { version = "1.12.2", default-features = false }
reqwest = { version = "0.13.1", default-features = false }
rmp-serde = { version = "1.3.0", default-features = false }
ruma = { version = "0.16.0", features = [
ruma = { git = "https://github.com/IT-ess/ruma", branch = "msc4482-bookmarks", features = [
"client-api-c",
"compat-unset-avatar",
"compat-upload-signatures",
Expand Down
3 changes: 3 additions & 0 deletions bindings/matrix-sdk-ffi/src/room_preview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,8 @@ pub enum RoomType {
Room,
/// It's a space that can group several rooms.
Space,
/// It's a bookmark room that shouldn't be displayed in the regular room list
Bookmarks,
/// It's a custom implementation.
Custom { value: String },
}
Expand All @@ -171,6 +173,7 @@ impl From<Option<RumaRoomType>> for RoomType {
fn from(value: Option<RumaRoomType>) -> Self {
match value {
Some(RumaRoomType::Space) => RoomType::Space,
Some(RumaRoomType::Bookmarks) => RoomType::Bookmarks,
Some(RumaRoomType::_Custom(_)) => RoomType::Custom {
// SAFETY: this was checked in the match branch above
value: value.unwrap().to_string(),
Expand Down
2 changes: 2 additions & 0 deletions crates/matrix-sdk-base/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ unstable-msc4274 = []

experimental-element-recent-emojis = []

experimental-bookmarks = ["ruma/unstable-msc4482"]

[dependencies]
as_variant.workspace = true
assert_matches = { workspace = true, optional = true }
Expand Down
8 changes: 8 additions & 0 deletions crates/matrix-sdk-base/src/room/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,14 @@ impl Room {
self.info.read().room_type().is_some_and(|t| *t == RoomType::Call)
}

/// Whether this room is a Bookmarks room as defined by [MSC4482].
///
/// [MSC4482]: <https://github.com/matrix-org/matrix-spec-proposals/pull/4482>
#[cfg(feature = "experimental-bookmarks")]
pub fn is_bookmarks(&self) -> bool {
self.info.read().room_type().is_some_and(|t| *t == RoomType::Bookmarks)
}

/// Returns the room's type as defined in its creation event
/// (`m.room.create`).
pub fn room_type(&self) -> Option<RoomType> {
Expand Down
4 changes: 4 additions & 0 deletions crates/matrix-sdk-search/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ version = "0.18.0"
all-features = true
rustdoc-args = ["--cfg", "docsrs", "--generate-link-to-definition"]

[features]
experimental-bookmarks = ["ruma/unstable-msc4482"]
unstable-msc4274 = ["ruma/unstable-msc4274"]

[dependencies]
aes = { version = "0.8.4", default-features = false }
byteorder.workspace = true
Expand Down
150 changes: 150 additions & 0 deletions crates/matrix-sdk-search/src/bookmarks/builder.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Copyright 2026 The Matrix.org Foundation C.I.C.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! A module for building a [`BookmarkIndex`]

use std::{fs, path::PathBuf, sync::Arc};

use tantivy::{
Index,
directory::{MmapDirectory, error::OpenDirectoryError},
};
use zeroize::Zeroizing;

use crate::{
bookmarks::{
BookmarkIndex,
schema::{BookmarkSchema, MatrixBookmarkIndexSchema},
},
encrypted::encrypted_dir::{EncryptedMmapDirectory, PBKDF_COUNT},
error::IndexError,
};

/// Builder for [`BookmarkIndex`].
pub struct BookmarkIndexBuilder {}

impl BookmarkIndexBuilder {
/// Make an index on disk
pub fn new_on_disk(path: PathBuf) -> PhysicalBookmarkIndexBuilder {
PhysicalBookmarkIndexBuilder::new(path)
}

/// Make an index in memory
pub fn new_in_memory() -> MemoryBookmarkIndexBuilder {
MemoryBookmarkIndexBuilder::new()
}
}

/// Incomplete builder for [`BookmarkIndex`] on disk.
pub struct PhysicalBookmarkIndexBuilder {
path: PathBuf,
}

impl PhysicalBookmarkIndexBuilder {
/// Make an new [`PhysicalBookmarkIndexBuilder`]
pub(crate) fn new(path: PathBuf) -> PhysicalBookmarkIndexBuilder {
PhysicalBookmarkIndexBuilder { path }
}

/// Make an unencrypted index
pub fn unencrypted(&self) -> UnencryptedPhysicalBookmarkIndexBuilder {
UnencryptedPhysicalBookmarkIndexBuilder { path: self.path.clone() }
}

/// Make an encrypted index
pub fn encrypted<P: Into<String>>(&self, password: P) -> EncryptedPhysicalBookmarkIndexBuilder {
EncryptedPhysicalBookmarkIndexBuilder {
path: self.path.clone(),
password: Zeroizing::new(password.into()),
}
}
}

/// Complete builder for [`BookmarkIndex`] on disk.
pub struct UnencryptedPhysicalBookmarkIndexBuilder {
path: PathBuf,
}

impl UnencryptedPhysicalBookmarkIndexBuilder {
/// Build the [`BookmarkIndex`]
pub fn build(&self) -> Result<BookmarkIndex, IndexError> {
let path = self.path.join("bookmarks_unencrypted");
let mmap_dir = match MmapDirectory::open(path) {
Ok(dir) => Ok(dir),
Err(err) => match err {
OpenDirectoryError::DoesNotExist(path) => {
fs::create_dir_all(path.clone()).map_err(|err| {
OpenDirectoryError::IoError {
io_error: Arc::new(err),
directory_path: path.to_path_buf(),
}
})?;
MmapDirectory::open(path)
}
_ => Err(err),
},
}?;
let schema = BookmarkSchema::new();
let index = Index::open_or_create(mmap_dir, schema.as_tantivy_schema())?;
Ok(BookmarkIndex::new_with(index, schema))
}
}

/// Complete builder for [`BookmarkIndex`] on disk.
pub struct EncryptedPhysicalBookmarkIndexBuilder {
path: PathBuf,
password: Zeroizing<String>,
}

impl EncryptedPhysicalBookmarkIndexBuilder {
/// Build the [`BookmarkIndex`]
pub fn build(&self) -> Result<BookmarkIndex, IndexError> {
let path = self.path.join("bookmarks_encrypted");
let mmap_dir =
match EncryptedMmapDirectory::open_or_create(path, &self.password, PBKDF_COUNT) {
Ok(dir) => Ok(dir),
Err(err) => match err {
OpenDirectoryError::DoesNotExist(path) => {
fs::create_dir_all(path.clone()).map_err(|err| {
OpenDirectoryError::IoError {
io_error: Arc::new(err),
directory_path: path.to_path_buf(),
}
})?;
EncryptedMmapDirectory::open_or_create(path, &self.password, PBKDF_COUNT)
}
_ => Err(err),
},
}?;
let schema = BookmarkSchema::new();
let index = Index::open_or_create(mmap_dir, schema.as_tantivy_schema())?;
Ok(BookmarkIndex::new_with(index, schema))
}
}

/// Builder for [`BookmarkIndex`] in memory
pub struct MemoryBookmarkIndexBuilder {}

impl MemoryBookmarkIndexBuilder {
/// Make an new [`MemoryIndexBuilder`]
pub(crate) fn new() -> MemoryBookmarkIndexBuilder {
MemoryBookmarkIndexBuilder {}
}

/// Build the [`BookmarkIndex`]
pub fn build(&self) -> BookmarkIndex {
let schema = BookmarkSchema::new();
let index = Index::create_in_ram(schema.as_tantivy_schema());
BookmarkIndex::new_with(index, schema)
}
}
Loading
Loading