mirror of
https://github.com/matrix-org/matrix-rust-sdk.git
synced 2026-05-08 16:04:13 -04:00
chore!: bump the MSRV to 1.88
let-chains ftw
This commit is contained in:
@@ -16,7 +16,7 @@ default-members = ["benchmarks", "crates/*", "labs/*"]
|
||||
resolver = "2"
|
||||
|
||||
[workspace.package]
|
||||
rust-version = "1.85"
|
||||
rust-version = "1.88"
|
||||
|
||||
[workspace.dependencies]
|
||||
anyhow = "1.0.95"
|
||||
|
||||
@@ -25,6 +25,9 @@ All notable changes to this project will be documented in this file.
|
||||
- `ClientBuilder::build_with_qr_code` has been removed. Instead, the Client should be built by passing
|
||||
`QrCodeData::server_name` to `ClientBuilder::server_name_or_homeserver_url`, after which QR login can be performed by
|
||||
calling `Client::login_with_qr_code`. ([#5388](https://github.com/matrix-org/matrix-rust-sdk/pull/5388))
|
||||
- The MSRV has been bumped to Rust 1.88.
|
||||
([#5431](https://github.com/matrix-org/matrix-rust-sdk/pull/5431))
|
||||
|
||||
|
||||
## [0.13.0] - 2025-07-10
|
||||
|
||||
|
||||
@@ -6,6 +6,12 @@ All notable changes to this project will be documented in this file.
|
||||
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
### Refactor
|
||||
|
||||
- [**breaking**] The MSRV has been bumped to Rust 1.88.
|
||||
([#5431](https://github.com/matrix-org/matrix-rust-sdk/pull/5431))
|
||||
|
||||
|
||||
## [0.13.0] - 2025-07-10
|
||||
|
||||
### Features
|
||||
|
||||
@@ -747,10 +747,10 @@ impl NotificationClient {
|
||||
timeline_event = decrypted_event;
|
||||
}
|
||||
|
||||
if let Some(actions) = timeline_event.push_actions() {
|
||||
if !actions.iter().any(|a| a.should_notify()) {
|
||||
return Ok(NotificationStatus::EventFilteredOut);
|
||||
}
|
||||
if let Some(actions) = timeline_event.push_actions()
|
||||
&& !actions.iter().any(|a| a.should_notify())
|
||||
{
|
||||
return Ok(NotificationStatus::EventFilteredOut);
|
||||
}
|
||||
|
||||
let push_actions = timeline_event.push_actions().map(ToOwned::to_owned);
|
||||
|
||||
@@ -377,13 +377,12 @@ impl Aggregations {
|
||||
|
||||
// If there was any redaction among the current aggregation, adding a new one
|
||||
// should be a noop.
|
||||
if let Some(previous_aggregations) = self.related_events.get(&related_to) {
|
||||
if previous_aggregations
|
||||
if let Some(previous_aggregations) = self.related_events.get(&related_to)
|
||||
&& previous_aggregations
|
||||
.iter()
|
||||
.any(|agg| matches!(agg.kind, AggregationKind::Redaction))
|
||||
{
|
||||
return;
|
||||
}
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.inverted_map.insert(aggregation.own_id.clone(), related_to.clone());
|
||||
@@ -553,26 +552,26 @@ impl Aggregations {
|
||||
return false;
|
||||
};
|
||||
|
||||
if let Some(aggregations) = self.related_events.get_mut(&target) {
|
||||
if let Some(found) = aggregations.iter_mut().find(|agg| agg.own_id == from) {
|
||||
found.own_id = to.clone();
|
||||
if let Some(aggregations) = self.related_events.get_mut(&target)
|
||||
&& let Some(found) = aggregations.iter_mut().find(|agg| agg.own_id == from)
|
||||
{
|
||||
found.own_id = to.clone();
|
||||
|
||||
match &mut found.kind {
|
||||
AggregationKind::PollResponse { .. }
|
||||
| AggregationKind::PollEnd { .. }
|
||||
| AggregationKind::Edit(..)
|
||||
| AggregationKind::Redaction => {
|
||||
// Nothing particular to do.
|
||||
}
|
||||
match &mut found.kind {
|
||||
AggregationKind::PollResponse { .. }
|
||||
| AggregationKind::PollEnd { .. }
|
||||
| AggregationKind::Edit(..)
|
||||
| AggregationKind::Redaction => {
|
||||
// Nothing particular to do.
|
||||
}
|
||||
|
||||
AggregationKind::Reaction { reaction_status, .. } => {
|
||||
// Mark the reaction as becoming remote, and signal that update to the
|
||||
// caller.
|
||||
*reaction_status = ReactionStatus::RemoteToRemote(event_id);
|
||||
AggregationKind::Reaction { reaction_status, .. } => {
|
||||
// Mark the reaction as becoming remote, and signal that update to the
|
||||
// caller.
|
||||
*reaction_status = ReactionStatus::RemoteToRemote(event_id);
|
||||
|
||||
let found = found.clone();
|
||||
find_item_and_apply_aggregation(self, items, &target, found, room_version);
|
||||
}
|
||||
let found = found.clone();
|
||||
find_item_and_apply_aggregation(self, items, &target, found, room_version);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -754,16 +753,14 @@ pub(crate) fn find_item_and_apply_aggregation(
|
||||
Some(new_event_item)
|
||||
}
|
||||
ApplyAggregationResult::Edit => {
|
||||
if let Some(aggregations) = aggregations.related_events.get(target) {
|
||||
if resolve_edits(aggregations, items, &mut cowed) {
|
||||
let new_event_item = cowed.into_owned();
|
||||
let new_item = TimelineItem::new(
|
||||
new_event_item.clone(),
|
||||
event_item.internal_id.to_owned(),
|
||||
);
|
||||
items.replace(idx, new_item);
|
||||
return Some(new_event_item);
|
||||
}
|
||||
if let Some(aggregations) = aggregations.related_events.get(target)
|
||||
&& resolve_edits(aggregations, items, &mut cowed)
|
||||
{
|
||||
let new_event_item = cowed.into_owned();
|
||||
let new_item =
|
||||
TimelineItem::new(new_event_item.clone(), event_item.internal_id.to_owned());
|
||||
items.replace(idx, new_item);
|
||||
return Some(new_event_item);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -318,21 +318,21 @@ impl TimelineMetadata {
|
||||
timeline_items: &Vector<Arc<TimelineItem>>,
|
||||
is_thread_focus: bool,
|
||||
) -> (Option<InReplyToDetails>, Option<OwnedEventId>) {
|
||||
if let AnySyncTimelineEvent::MessageLike(ev) = event {
|
||||
if let Some(content) = ev.original_content() {
|
||||
let remote_ctx = Some(RemoteEventContext {
|
||||
event_id: ev.event_id(),
|
||||
raw_event,
|
||||
relations: ev.relations(),
|
||||
bundled_edit_encryption_info,
|
||||
});
|
||||
return self.process_content_relations(
|
||||
&content,
|
||||
remote_ctx,
|
||||
timeline_items,
|
||||
is_thread_focus,
|
||||
);
|
||||
}
|
||||
if let AnySyncTimelineEvent::MessageLike(ev) = event
|
||||
&& let Some(content) = ev.original_content()
|
||||
{
|
||||
let remote_ctx = Some(RemoteEventContext {
|
||||
event_id: ev.event_id(),
|
||||
raw_event,
|
||||
relations: ev.relations(),
|
||||
bundled_edit_encryption_info,
|
||||
});
|
||||
return self.process_content_relations(
|
||||
&content,
|
||||
remote_ctx,
|
||||
timeline_items,
|
||||
is_thread_focus,
|
||||
);
|
||||
}
|
||||
(None, None)
|
||||
}
|
||||
|
||||
@@ -850,12 +850,11 @@ impl<P: RoomDataProvider, D: Decryptor> TimelineController<P, D> {
|
||||
.await;
|
||||
}
|
||||
|
||||
if track_read_markers {
|
||||
if let Some(fully_read_event_id) =
|
||||
if track_read_markers
|
||||
&& let Some(fully_read_event_id) =
|
||||
self.room_data_provider.load_fully_read_marker().await
|
||||
{
|
||||
state.handle_fully_read_marker(fully_read_event_id);
|
||||
}
|
||||
{
|
||||
state.handle_fully_read_marker(fully_read_event_id);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1561,14 +1560,13 @@ impl TimelineController {
|
||||
|
||||
SendReceiptType::FullyRead => {
|
||||
if let Some(prev_event_id) = self.room_data_provider.load_fully_read_marker().await
|
||||
{
|
||||
if let Some(relative_pos) = state.meta.compare_events_positions(
|
||||
&& let Some(relative_pos) = state.meta.compare_events_positions(
|
||||
&prev_event_id,
|
||||
event_id,
|
||||
state.items.all_remote_events(),
|
||||
) {
|
||||
return relative_pos == RelativePosition::After;
|
||||
}
|
||||
)
|
||||
{
|
||||
return relative_pos == RelativePosition::After;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2008,10 +2008,10 @@ impl AllRemoteEvents {
|
||||
) {
|
||||
self.increment_all_timeline_item_index_after(new_timeline_item_index);
|
||||
|
||||
if let Some(event_index) = event_index {
|
||||
if let Some(event_meta) = self.0.get_mut(event_index) {
|
||||
event_meta.timeline_item_index = Some(new_timeline_item_index);
|
||||
}
|
||||
if let Some(event_index) = event_index
|
||||
&& let Some(event_meta) = self.0.get_mut(event_index)
|
||||
{
|
||||
event_meta.timeline_item_index = Some(new_timeline_item_index);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -850,15 +850,14 @@ impl<'a, P: RoomDataProvider> TimelineStateTransaction<'a, P> {
|
||||
TimelineItemPosition::UpdateAt { .. } => {
|
||||
if let Some(event) =
|
||||
self.items.get_remote_event_by_event_id_mut(&event_meta.event_id)
|
||||
&& event.visible != event_meta.visible
|
||||
{
|
||||
if event.visible != event_meta.visible {
|
||||
event.visible = event_meta.visible;
|
||||
event.visible = event_meta.visible;
|
||||
|
||||
if settings.track_read_receipts {
|
||||
// Since the event's visibility changed, we need to update the read
|
||||
// receipts of the previous visible event.
|
||||
self.maybe_update_read_receipts_of_prev_event(&event_meta.event_id);
|
||||
}
|
||||
if settings.track_read_receipts {
|
||||
// Since the event's visibility changed, we need to update the read
|
||||
// receipts of the previous visible event.
|
||||
self.maybe_update_read_receipts_of_prev_event(&event_meta.event_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -297,16 +297,16 @@ impl DateDividerAdjuster {
|
||||
if timestamp_to_date(*prev_ts) != event_date {
|
||||
// The date divider is wrong. Should we replace it with the correct value, or
|
||||
// remove it entirely?
|
||||
if let Some(last_event_ts) = latest_event_ts {
|
||||
if timestamp_to_date(last_event_ts) == event_date {
|
||||
// There's a previous event with the same date: remove the divider.
|
||||
trace!(
|
||||
"removed date divider @ {item_index} between two events \
|
||||
if let Some(last_event_ts) = latest_event_ts
|
||||
&& timestamp_to_date(last_event_ts) == event_date
|
||||
{
|
||||
// There's a previous event with the same date: remove the divider.
|
||||
trace!(
|
||||
"removed date divider @ {item_index} between two events \
|
||||
that have the same date"
|
||||
);
|
||||
self.ops.insert(insert_op_at, DateDividerOperation::Remove(item_index));
|
||||
return;
|
||||
}
|
||||
);
|
||||
self.ops.insert(insert_op_at, DateDividerOperation::Remove(item_index));
|
||||
return;
|
||||
}
|
||||
|
||||
// There's no previous event or there's one with a different date: replace
|
||||
@@ -440,10 +440,10 @@ impl DateDividerAdjuster {
|
||||
};
|
||||
|
||||
// 3. There's no trailing date divider.
|
||||
if let Some(last) = items.last() {
|
||||
if last.is_date_divider() {
|
||||
report.errors.push(DateDividerInsertError::TrailingDateDivider);
|
||||
}
|
||||
if let Some(last) = items.last()
|
||||
&& last.is_date_divider()
|
||||
{
|
||||
report.errors.push(DateDividerInsertError::TrailingDateDivider);
|
||||
}
|
||||
|
||||
// 4. Items are properly separated with date dividers.
|
||||
@@ -456,12 +456,12 @@ impl DateDividerAdjuster {
|
||||
let ts = ev.timestamp();
|
||||
|
||||
// We have the same date as the previous event we've seen.
|
||||
if let Some(prev_ts) = prev_event_ts {
|
||||
if !self.is_same_date_divider_group_as(prev_ts, ts) {
|
||||
report.errors.push(
|
||||
DateDividerInsertError::MissingDateDividerBetweenEvents { at: i },
|
||||
);
|
||||
}
|
||||
if let Some(prev_ts) = prev_event_ts
|
||||
&& !self.is_same_date_divider_group_as(prev_ts, ts)
|
||||
{
|
||||
report.errors.push(
|
||||
DateDividerInsertError::MissingDateDividerBetweenEvents { at: i },
|
||||
);
|
||||
}
|
||||
|
||||
// There is a date divider before us, and it's the same date as our timestamp.
|
||||
@@ -484,12 +484,10 @@ impl DateDividerAdjuster {
|
||||
item.kind()
|
||||
{
|
||||
// The previous date divider is for a different date.
|
||||
if let Some(prev_ts) = prev_date_divider_ts {
|
||||
if self.is_same_date_divider_group_as(prev_ts, *ts) {
|
||||
report
|
||||
.errors
|
||||
.push(DateDividerInsertError::DuplicateDateDivider { at: i });
|
||||
}
|
||||
if let Some(prev_ts) = prev_date_divider_ts
|
||||
&& self.is_same_date_divider_group_as(prev_ts, *ts)
|
||||
{
|
||||
report.errors.push(DateDividerInsertError::DuplicateDateDivider { at: i });
|
||||
}
|
||||
|
||||
prev_event_ts = None;
|
||||
@@ -500,15 +498,14 @@ impl DateDividerAdjuster {
|
||||
|
||||
// 5. If there was a read marker at the beginning, there should be one at the
|
||||
// end.
|
||||
if let Some(state) = &report.initial_state {
|
||||
if state.iter().any(|item| item.is_read_marker())
|
||||
&& !report
|
||||
.final_state
|
||||
.iter_remotes_and_locals_regions()
|
||||
.any(|(_i, item)| item.is_read_marker())
|
||||
{
|
||||
report.errors.push(DateDividerInsertError::ReadMarkerDisappeared);
|
||||
}
|
||||
if let Some(state) = &report.initial_state
|
||||
&& state.iter().any(|item| item.is_read_marker())
|
||||
&& !report
|
||||
.final_state
|
||||
.iter_remotes_and_locals_regions()
|
||||
.any(|(_i, item)| item.is_read_marker())
|
||||
{
|
||||
report.errors.push(DateDividerInsertError::ReadMarkerDisappeared);
|
||||
}
|
||||
|
||||
if report.errors.is_empty() { None } else { Some(report) }
|
||||
|
||||
@@ -773,14 +773,14 @@ impl ReactionsByKeyBySender {
|
||||
sender: &UserId,
|
||||
annotation: &str,
|
||||
) -> Option<ReactionInfo> {
|
||||
if let Some(by_user) = self.0.get_mut(annotation) {
|
||||
if let Some(info) = by_user.swap_remove(sender) {
|
||||
// If this was the last reaction, remove the annotation entry.
|
||||
if by_user.is_empty() {
|
||||
self.0.swap_remove(annotation);
|
||||
}
|
||||
return Some(info);
|
||||
if let Some(by_user) = self.0.get_mut(annotation)
|
||||
&& let Some(info) = by_user.swap_remove(sender)
|
||||
{
|
||||
// If this was the last reaction, remove the annotation entry.
|
||||
if by_user.is_empty() {
|
||||
self.0.swap_remove(annotation);
|
||||
}
|
||||
return Some(info);
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -614,8 +614,8 @@ impl Timeline {
|
||||
/// This also unsets the unread marker of the room if necessary.
|
||||
#[instrument(skip(self))]
|
||||
pub async fn send_multiple_receipts(&self, mut receipts: Receipts) -> Result<()> {
|
||||
if let Some(fully_read) = &receipts.fully_read {
|
||||
if !self
|
||||
if let Some(fully_read) = &receipts.fully_read
|
||||
&& !self
|
||||
.controller
|
||||
.should_send_receipt(
|
||||
&ReceiptType::FullyRead,
|
||||
@@ -623,23 +623,21 @@ impl Timeline {
|
||||
fully_read,
|
||||
)
|
||||
.await
|
||||
{
|
||||
receipts.fully_read = None;
|
||||
}
|
||||
{
|
||||
receipts.fully_read = None;
|
||||
}
|
||||
|
||||
if let Some(read_receipt) = &receipts.public_read_receipt {
|
||||
if !self
|
||||
if let Some(read_receipt) = &receipts.public_read_receipt
|
||||
&& !self
|
||||
.controller
|
||||
.should_send_receipt(&ReceiptType::Read, &ReceiptThread::Unthreaded, read_receipt)
|
||||
.await
|
||||
{
|
||||
receipts.public_read_receipt = None;
|
||||
}
|
||||
{
|
||||
receipts.public_read_receipt = None;
|
||||
}
|
||||
|
||||
if let Some(private_read_receipt) = &receipts.private_read_receipt {
|
||||
if !self
|
||||
if let Some(private_read_receipt) = &receipts.private_read_receipt
|
||||
&& !self
|
||||
.controller
|
||||
.should_send_receipt(
|
||||
&ReceiptType::ReadPrivate,
|
||||
@@ -647,9 +645,8 @@ impl Timeline {
|
||||
private_read_receipt,
|
||||
)
|
||||
.await
|
||||
{
|
||||
receipts.private_read_receipt = None;
|
||||
}
|
||||
{
|
||||
receipts.private_read_receipt = None;
|
||||
}
|
||||
|
||||
let room = self.room();
|
||||
|
||||
@@ -171,13 +171,12 @@ impl PinnedEventsRoom for Room {
|
||||
related_event_filters: Option<Vec<RelationType>>,
|
||||
) -> BoxFuture<'a, Result<(TimelineEvent, Vec<TimelineEvent>), matrix_sdk::Error>> {
|
||||
Box::pin(async move {
|
||||
if let Ok((cache, _handles)) = self.event_cache().await {
|
||||
if let Some(ret) =
|
||||
if let Ok((cache, _handles)) = self.event_cache().await
|
||||
&& let Some(ret) =
|
||||
cache.find_event_with_relations(event_id, related_event_filters).await
|
||||
{
|
||||
debug!("Loaded pinned event {event_id} and related events from cache");
|
||||
return Ok(ret);
|
||||
}
|
||||
{
|
||||
debug!("Loaded pinned event {event_id} and related events from cache");
|
||||
return Ok(ret);
|
||||
}
|
||||
|
||||
debug!("Loading pinned event {event_id} from HS");
|
||||
|
||||
@@ -110,22 +110,22 @@ async fn test_sync_service_state() -> anyhow::Result<()> {
|
||||
|
||||
let mut json_value = serde_json::from_slice::<serde_json::Value>(&request.body).unwrap();
|
||||
|
||||
if let Some(root) = json_value.as_object_mut() {
|
||||
if let Some(conn_id) = root.get("conn_id").and_then(|obj| obj.as_str()) {
|
||||
if conn_id == "encryption" {
|
||||
num_encryption_sync_requests += 1;
|
||||
} else if conn_id == "room-list" {
|
||||
num_room_list_requests += 1;
|
||||
if let Some(root) = json_value.as_object_mut()
|
||||
&& let Some(conn_id) = root.get("conn_id").and_then(|obj| obj.as_str())
|
||||
{
|
||||
if conn_id == "encryption" {
|
||||
num_encryption_sync_requests += 1;
|
||||
} else if conn_id == "room-list" {
|
||||
num_room_list_requests += 1;
|
||||
|
||||
// Retrieve the position used in the query.
|
||||
for (key, val) in request.url.query_pairs() {
|
||||
if key == "pos" {
|
||||
latest_room_list_pos = Some(val.to_string());
|
||||
}
|
||||
// Retrieve the position used in the query.
|
||||
for (key, val) in request.url.query_pairs() {
|
||||
if key == "pos" {
|
||||
latest_room_list_pos = Some(val.to_string());
|
||||
}
|
||||
} else {
|
||||
panic!("unexpected conn id seen server side: {conn_id}");
|
||||
}
|
||||
} else {
|
||||
panic!("unexpected conn id seen server side: {conn_id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,29 +167,29 @@ async fn test_sync_service_state() -> anyhow::Result<()> {
|
||||
|
||||
let mut json_value = serde_json::from_slice::<serde_json::Value>(&request.body).unwrap();
|
||||
|
||||
if let Some(root) = json_value.as_object_mut() {
|
||||
if let Some(conn_id) = root.get("conn_id").and_then(|obj| obj.as_str()) {
|
||||
if conn_id == "encryption" {
|
||||
num_encryption_sync_requests += 1;
|
||||
} else if conn_id == "room-list" {
|
||||
if num_room_list_requests == 0 {
|
||||
// Either it's the same pos, or it's the next one if the request could be
|
||||
// processed by the client.
|
||||
let mut current_pos = None;
|
||||
for (key, val) in request.url.query_pairs() {
|
||||
if key == "pos" {
|
||||
current_pos = Some(val);
|
||||
}
|
||||
if let Some(root) = json_value.as_object_mut()
|
||||
&& let Some(conn_id) = root.get("conn_id").and_then(|obj| obj.as_str())
|
||||
{
|
||||
if conn_id == "encryption" {
|
||||
num_encryption_sync_requests += 1;
|
||||
} else if conn_id == "room-list" {
|
||||
if num_room_list_requests == 0 {
|
||||
// Either it's the same pos, or it's the next one if the request could be
|
||||
// processed by the client.
|
||||
let mut current_pos = None;
|
||||
for (key, val) in request.url.query_pairs() {
|
||||
if key == "pos" {
|
||||
current_pos = Some(val);
|
||||
}
|
||||
let current_pos: i32 = current_pos.unwrap().parse()?;
|
||||
let prev_pos: i32 = latest_room_list_pos.take().unwrap().parse()?;
|
||||
assert!((current_pos - prev_pos).abs() <= 1);
|
||||
}
|
||||
|
||||
num_room_list_requests += 1;
|
||||
} else {
|
||||
panic!("unexpected conn id seen server side: {conn_id}");
|
||||
let current_pos: i32 = current_pos.unwrap().parse()?;
|
||||
let prev_pos: i32 = latest_room_list_pos.take().unwrap().parse()?;
|
||||
assert!((current_pos - prev_pos).abs() <= 1);
|
||||
}
|
||||
|
||||
num_room_list_requests += 1;
|
||||
} else {
|
||||
panic!("unexpected conn id seen server side: {conn_id}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,11 @@ All notable changes to this project will be documented in this file.
|
||||
- [**breaking**] `OAuth::login` now allows requesting additional scopes for the authorization code grant.
|
||||
([#5395](https://github.com/matrix-org/matrix-rust-sdk/pull/5395))
|
||||
|
||||
### Refactor
|
||||
|
||||
- [**breaking**] The MSRV has been bumped to Rust 1.88.
|
||||
([#5431](https://github.com/matrix-org/matrix-rust-sdk/pull/5431))
|
||||
|
||||
## [0.13.0] - 2025-07-10
|
||||
|
||||
### Security Fixes
|
||||
|
||||
Reference in New Issue
Block a user