use std::{borrow::Borrow, iter::once}; use axum::extract::State; use futures::{ FutureExt, StreamExt, TryFutureExt, TryStreamExt, future::{join, try_join}, }; use ruma::{OwnedEventId, api::federation::event::get_room_state}; use tuwunel_core::{ Result, at, err, utils::{ future::TryExtExt, stream::{IterStream, TryBroadbandExt}, }, }; use super::AccessCheck; use crate::Ruma; /// # `GET /_matrix/federation/v1/state/{roomId}` /// /// Retrieves a snapshot of a room's state at a given event. pub(crate) async fn get_room_state_route( State(services): State, body: Ruma, ) -> Result { AccessCheck { services: &services, origin: body.origin(), room_id: &body.room_id, event_id: None, } .check() .await?; let shortstatehash = services .state .pdu_shortstatehash(&body.event_id) .await .map_err(|_| err!(Request(NotFound("PDU state not found."))))?; let state_ids = services .state_accessor .state_full_ids(shortstatehash) .map(at!(1)) .collect::>(); let room_version = services .state .get_room_version(&body.room_id) .ok(); let (room_version, state_ids) = join(room_version, state_ids).await; let into_federation_format = |pdu| { services .federation .format_pdu_into(pdu, room_version.as_ref()) .map(Ok) }; let auth_chain = services .auth_chain .event_ids_iter(&body.room_id, once(body.event_id.borrow())) .broad_and_then(async |id| { services .timeline .get_pdu_json(&id) .and_then(into_federation_format) .await }) .try_collect(); let pdus = state_ids .iter() .try_stream() .broad_and_then(|id| { services .timeline .get_pdu_json(id) .and_then(into_federation_format) }) .try_collect(); let (auth_chain, pdus) = try_join(auth_chain, pdus).await?; Ok(get_room_state::v1::Response { auth_chain, pdus }) }