From 9d9ace145298954ae2701eb76af8c33f3e1f2623 Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Sat, 27 Sep 2025 02:41:51 +0000 Subject: [PATCH] Add timeline interface to get next or prev pdu nearest to a pduid. Signed-off-by: Jason Volk --- src/service/rooms/timeline/mod.rs | 88 ++++++++++++++++++++++++++++++- 1 file changed, 86 insertions(+), 2 deletions(-) diff --git a/src/service/rooms/timeline/mod.rs b/src/service/rooms/timeline/mod.rs index c494f1cd..f48e41a8 100644 --- a/src/service/rooms/timeline/mod.rs +++ b/src/service/rooms/timeline/mod.rs @@ -34,7 +34,7 @@ use tuwunel_core::{ }; use tuwunel_database::{Database, Deserialized, Json, KeyVal, Map}; -use crate::rooms::short::ShortRoomId; +use crate::rooms::short::{ShortRoomId, ShortStateHash}; pub struct Service { services: Arc, @@ -163,7 +163,91 @@ pub async fn latest_item_in_room( .try_next() .await? .map(at!(1)) - .ok_or_else(|| err!(Request(NotFound("no PDU's found in room")))) + .ok_or_else(|| err!(Request(NotFound("No PDU's found in room")))) +} + +/// Returns the shortstatehash of the room at the event directly preceding the +/// exclusive `before` param. `before` does not have to be a valid shorteventid +/// or in the room. +#[implement(Service)] +#[tracing::instrument(skip(self), level = "debug")] +pub async fn prev_shortstatehash( + &self, + room_id: &RoomId, + before: PduCount, +) -> Result { + let prev = self.prev_timeline_count(room_id, before).await?; + + self.services + .state_accessor + .get_shortstatehash(prev.into_unsigned()) + .await +} + +/// Returns the shortstatehash of the room at the event directly following the +/// exclusive `after` param. `after` does not have to be a valid shorteventid or +/// in the room. +#[implement(Service)] +#[tracing::instrument(skip(self), level = "debug")] +pub async fn next_shortstatehash( + &self, + room_id: &RoomId, + after: PduCount, +) -> Result { + let next = self.next_timeline_count(room_id, after).await?; + + self.services + .state_accessor + .get_shortstatehash(next.into_unsigned()) + .await +} + +/// Returns the shorteventid in the room preceding the exclusive `before` param. +/// `before` does not have to be a valid shorteventid or in the room. +#[implement(Service)] +#[tracing::instrument(skip(self), level = "debug")] +pub async fn prev_timeline_count(&self, room_id: &RoomId, before: PduCount) -> Result { + let before = self + .count_to_id(room_id, before, Direction::Backward) + .await?; + + let pdu_ids = self + .db + .pduid_pdu + .rev_keys_raw_from(&before) + .ready_try_take_while(|pdu_id: &RawPduId| Ok(pdu_id.is_room_eq(before))) + .ready_and_then(|pdu_id: RawPduId| Ok(pdu_id.pdu_count())); + + pin_mut!(pdu_ids); + pdu_ids + .try_next() + .await + .log_err()? + .ok_or_else(|| err!(Request(NotFound("No earlier PDU's found in room")))) +} + +/// Returns the next shorteventid in the room after the exclusive `after` param. +/// `after` does not have to be a valid shorteventid or in the room. +#[implement(Service)] +#[tracing::instrument(skip(self), level = "debug")] +pub async fn next_timeline_count(&self, room_id: &RoomId, after: PduCount) -> Result { + let after = self + .count_to_id(room_id, after, Direction::Forward) + .await?; + + let pdu_ids = self + .db + .pduid_pdu + .keys_raw_from(&after) + .ready_try_take_while(|pdu_id: &RawPduId| Ok(pdu_id.is_room_eq(after))) + .ready_and_then(|pdu_id: RawPduId| Ok(pdu_id.pdu_count())); + + pin_mut!(pdu_ids); + pdu_ids + .try_next() + .await + .log_err()? + .ok_or(err!(Request(NotFound("No more PDU's found in room")))) } #[implement(Service)]