refactor(sdk-ui): remove expect() with better borrow (#6513)

<!-- description of the changes in this PR -->

Follow up on review on this PR
https://github.com/matrix-org/matrix-rust-sdk/pull/6494#discussion_r3146315397

Remove the usage of if/expect that was introduced because of a borrowing
problem.
Instead we use `ok_or_else`, but we clone the debug_string early (to
avoid needing to borrow event again in the `ok_or_else`), it is not
ideal either but better that the expect/panic

I also removed the `is_rtc_notification` and `is_live_location` that was
created I believe just to avoid the borrowing problem? They were only
used in test and it just a shortcut for `matches!()`

- [ ] I've documented the public API Changes in the appropriate
`CHANGELOG.md` files.
- [ ] This PR was made with the help of AI.

<!-- Sign-off, if not part of the commits -->
<!-- See CONTRIBUTING.md if you don't know what this is -->
Signed-off-by:
This commit is contained in:
Valere Fedronic
2026-05-01 11:40:15 +02:00
committed by GitHub
parent 8a0dcdd550
commit 4e8ceaac82
4 changed files with 34 additions and 59 deletions

View File

@@ -39,7 +39,6 @@
use std::{borrow::Cow, collections::HashMap, sync::Arc};
use as_variant::as_variant;
use matrix_sdk::{check_validity_of_replacement_events, deserialized_responses::EncryptionInfo};
use ruma::{
MilliSecondsSinceUnixEpoch, OwnedEventId, OwnedTransactionId, OwnedUserId,
@@ -183,18 +182,16 @@ pub(crate) struct Aggregation {
fn poll_state_from_item<'a>(
event: &'a mut Cow<'_, EventTimelineItem>,
) -> Result<&'a mut PollState, AggregationError> {
if event.content().is_poll() {
// It was a poll! Now return the state as mutable.
let state = as_variant!(
event.to_mut().content_mut(),
TimelineItemContent::MsgLike(MsgLikeContent { kind: MsgLikeKind::Poll(s), ..}) => s
)
.expect("it was a poll just above");
let content = event.to_mut().content_mut();
if let TimelineItemContent::MsgLike(MsgLikeContent { kind: MsgLikeKind::Poll(state), .. }) =
content
{
Ok(state)
} else {
Err(AggregationError::InvalidType {
expected: "a poll".to_owned(),
actual: event.content().debug_string().to_owned(),
actual: content.debug_string().to_owned(),
})
}
}
@@ -203,18 +200,18 @@ fn poll_state_from_item<'a>(
fn live_location_state_from_item<'a>(
event: &'a mut Cow<'_, EventTimelineItem>,
) -> Result<&'a mut LiveLocationState, AggregationError> {
if event.content().is_live_location() {
// It was a live location! Now return the state as mutable.
let state = event
.to_mut()
.content_mut()
.as_live_location_state_mut()
.expect("it was a live location just above");
let content = event.to_mut().content_mut();
if let TimelineItemContent::MsgLike(MsgLikeContent {
kind: MsgLikeKind::LiveLocation(state),
..
}) = content
{
Ok(state)
} else {
Err(AggregationError::InvalidType {
expected: "a live location".to_owned(),
actual: event.content().debug_string().to_owned(),
actual: content.debug_string().to_owned(),
})
}
}
@@ -223,13 +220,16 @@ fn live_location_state_from_item<'a>(
fn rtc_notification_declinations_from_item<'a>(
event: &'a mut Cow<'_, EventTimelineItem>,
) -> Result<&'a mut Vec<OwnedUserId>, AggregationError> {
let debug_string = event.content().debug_string().to_owned();
event.to_mut().content_mut().as_rtc_notification_mut().ok_or_else(|| {
AggregationError::InvalidType {
let content = event.to_mut().content_mut();
if let TimelineItemContent::RtcNotification { declined_by, .. } = content {
Ok(declined_by)
} else {
Err(AggregationError::InvalidType {
expected: "an rtc notification".to_owned(),
actual: debug_string,
}
})
actual: content.debug_string().to_owned(),
})
}
}
impl Aggregation {

View File

@@ -223,38 +223,6 @@ impl TimelineItemContent {
}) => state)
}
/// If `self` is of the [`MsgLike`][Self::MsgLike] variant with a
/// [`LiveLocation`][MsgLikeKind::LiveLocation] kind, return the inner
/// [`LiveLocationState`] mutably.
pub(in crate::timeline) fn as_live_location_state_mut(
&mut self,
) -> Option<&mut LiveLocationState> {
as_variant!(self, Self::MsgLike(MsgLikeContent {
kind: MsgLikeKind::LiveLocation(state),
..
}) => state)
}
pub(in crate::timeline) fn as_rtc_notification_mut(&mut self) -> Option<&mut Vec<OwnedUserId>> {
if let TimelineItemContent::RtcNotification { declined_by, .. } = self {
Some(declined_by)
} else {
None
}
}
/// Check whether this item's content is a
/// [`LiveLocation`][MsgLikeKind::LiveLocation].
pub fn is_live_location(&self) -> bool {
matches!(self, Self::MsgLike(MsgLikeContent { kind: MsgLikeKind::LiveLocation(_), .. }))
}
/// Check whether this item's content is an
/// [`RtcNotification`][Self::RtcNotification].
pub fn is_rtc_notification(&self) -> bool {
matches!(self, Self::RtcNotification { .. })
}
/// If `self` is of the [`MsgLike`][Self::MsgLike] variant, return the
/// inner [`Message`].
pub fn as_message(&self) -> Option<&Message> {

View File

@@ -22,7 +22,8 @@ use ruma::{
use stream_assert::{assert_next_matches, assert_pending};
use crate::timeline::{
EventSendState, TimelineEventShieldState, TimelineEventShieldStateCode,
EventSendState, MsgLikeContent, MsgLikeKind, TimelineEventShieldState,
TimelineEventShieldStateCode, TimelineItemContent,
tests::{TestTimeline, TestTimelineBuilder},
};
@@ -156,7 +157,13 @@ async fn test_live_location_no_sent_in_clear_shield() {
// No beacons yet → shield should be None.
let item = assert_next_matches!(stream, VectorDiff::PushBack { value } => value);
assert!(item.content().is_live_location(), "timeline item should be a live location");
assert!(
matches!(
item.content(),
TimelineItemContent::MsgLike(MsgLikeContent { kind: MsgLikeKind::LiveLocation(_), .. })
),
"timeline item should be a live location"
);
let shield = item.get_shield(false);
assert_eq!(shield, TimelineEventShieldState::None);

View File

@@ -52,7 +52,7 @@ async fn test_decline_call() {
assert_let!(VectorDiff::PushBack { value: message } = &timeline_updates[0]);
let event_item = message.as_event().unwrap();
assert!(event_item.content().is_rtc_notification());
assert!(matches!(event_item.content(), TimelineItemContent::RtcNotification { .. }));
assert_let!(
TimelineItemContent::RtcNotification { call_intent: _, declined_by } = event_item.content()
);
@@ -116,7 +116,7 @@ async fn test_multiple_decline_call() {
assert_let!(VectorDiff::PushBack { value: message } = &timeline_updates[0]);
let event_item = message.as_event().unwrap();
assert!(event_item.content().is_rtc_notification());
assert!(matches!(event_item.content(), TimelineItemContent::RtcNotification { .. }));
assert_let!(
TimelineItemContent::RtcNotification { call_intent: _, declined_by } = event_item.content()
);