Merge remote-tracking branch 'origin/main' into gnunicorn/issue756

This commit is contained in:
Benjamin Kampmann
2022-07-27 12:04:56 +02:00
71 changed files with 681 additions and 387 deletions
+120
View File
@@ -0,0 +1,120 @@
# Conventional Commits
This project uses [Conventional
Commits](https://www.conventionalcommits.org/). Read the
[Summary](https://www.conventionalcommits.org/en/v1.0.0/#summary) or
the [Full
Specification](https://www.conventionalcommits.org/en/v1.0.0/#specification)
to learn more.
## Types
Conventional Commits defines _type_ (as in `type(scope):
message`). This section aims at listing the types used inside this
project:
| Type | Definition |
|-|-|
| `feat` | About a new feature. |
| `fix` | About a bug fix. |
| `test` | About a test (suite, case, runner…). |
| `doc` | About a documentation modification. |
| `refactor` | About a refactoring. |
| `ci` | About a Continuous Integration modification. |
| `chore` | About some cleanup, or regular tasks. |
## Scopes
Conventional Commits defines _scope_ (as in `type(scope): message`). This
section aims at listing all the scopes used inside this project:
<table>
<thead>
<tr>
<th>Group</th>
<th>Scope</th>
<th>Definition</th>
</tr>
</thead>
<tbody>
<tr>
<td rowspan="10">Crates</td>
<td><code>sdk</code></td>
<td>About the <code>matrix-sdk</code> crate.</td>
</tr>
<tr>
<td><code>appservice</code></td>
<td>About the <code>matrix-sdk-appservice</code> crate.</td>
</tr>
<tr>
<td><code>base</code></td>
<td>About the <code>matrix-sdk-base</code> crate.</td>
</tr>
<tr>
<td><code>common</code></td>
<td>About the <code>matrix-sdk-common</code> crate.</td>
</tr>
<tr>
<td><code>crypto</code></td>
<td>About the <code>matrix-sdk-crypto</code> crate.</td>
</tr>
<tr>
<td><code>indexeddb</code></td>
<td>About the <code>matrix-sdk-indexeddb</code> crate.</td>
</tr>
<tr>
<td><code>qrcode</code></td>
<td>About the <code>matrix-sdk-qrcode</code> crate.</td>
</tr>
<tr>
<td><code>sled</code></td>
<td>About the <code>matrix-sdk-sled</code> crate.</td>
</tr>
<tr>
<td><code>store-encryption</code></td>
<td>About the <code>matrix-sdk-store-encryption</code> crate.</td>
</tr>
<tr>
<td><code>test</code></td>
<td>About the <code>matrix-sdk-test</code> and <code>matrix-sdk-test-macros</code> crate.</td>
</tr>
<tr>
<td rowspan="4">Bindings</td>
<td><code>apple</code></td>
<td>About the <code>matrix-rust-components-swift</code> binding.</td>
</tr>
<tr>
<td><code>crypto-nodejs</code></td>
<td>About the <code>matrix-sdk-crypto-nodejs</code> binding.</td>
</tr>
<tr>
<td><code>crypto-js</code></td>
<td>About the <code>matrix-sdk-crypto-js</code> binding.</td>
</tr>
<tr>
<td><code>crypto-ffi</code></td>
<td>About the <code>matrix-sdk-crypto-ffi</code> binding.</td>
</tr>
<tr>
<td>Labs</td>
<td><code>sled-state-inspector</code></td>
<td>About the <code>sled-state-inspector</code> project.</td>
</tr>
<tr>
<td>Continuous Integration</td>
<td><code>xtask</code></td>
<td>About the <code>xtask</code> project.</td>
</tr>
</tbody>
</table>
## Generating `CHANGELOG.md`
The [`git-cliff`](https://github.com/orhun/git-cliff) project is used
to generate `CHANGELOG.md` automatically. Hence the various
`cliff.toml` files that are present in this project, or the
`package.metadata.git-cliff` sections in various `Cargo.toml` files.
Its companion,
[`git-cliff-action`](https://github.com/orhun/git-cliff-action)
project, is used inside Github Action workflows.
+4 -4
View File
@@ -63,7 +63,7 @@ pub fn keys_query(c: &mut Criterion) {
let mut group = c.benchmark_group("Keys querying");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} device and cross signing keys", count);
let name = format!("{count} device and cross signing keys");
group.bench_with_input(BenchmarkId::new("memory store", &name), &response, |b, response| {
b.to_async(&runtime)
@@ -96,7 +96,7 @@ pub fn keys_claiming(c: &mut Criterion) {
let mut group = c.benchmark_group("Olm session creation");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} one-time keys", count);
let name = format!("{count} one-time keys");
group.bench_with_input(BenchmarkId::new("memory store", &name), &response, |b, response| {
b.iter_batched(
@@ -158,7 +158,7 @@ pub fn room_key_sharing(c: &mut Criterion) {
let mut group = c.benchmark_group("Room key sharing");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} devices", count);
let name = format!("{count} devices");
group.bench_function(BenchmarkId::new("memory store", &name), |b| {
b.to_async(&runtime).iter(|| async {
@@ -225,7 +225,7 @@ pub fn devices_missing_sessions_collecting(c: &mut Criterion) {
let mut group = c.benchmark_group("Devices missing sessions collecting");
group.throughput(Throughput::Elements(count as u64));
let name = format!("{} devices", count);
let name = format!("{count} devices");
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
+1 -1
View File
@@ -2,7 +2,7 @@
name = "matrix-sdk-crypto-ffi"
version = "0.1.0"
authors = ["Damir Jelić <poljar@termina.org.uk>"]
edition = "2018"
edition = "2021"
rust-version = "1.60"
description = "Uniffi based bindings for the Rust SDK crypto crate"
repository = "https://github.com/matrix-org/matrix-rust-sdk"
+1 -1
View File
@@ -14,7 +14,7 @@ mod responses;
mod users;
mod verification;
use std::{borrow::Borrow, collections::HashMap, convert::TryFrom, str::FromStr, sync::Arc};
use std::{borrow::Borrow, collections::HashMap, str::FromStr, sync::Arc};
pub use backup_recovery_key::{
BackupRecoveryKey, DecodeError, MegolmV1BackupKey, PassphraseInfo, PkDecryptionError,
@@ -1,6 +1,5 @@
use std::{
collections::{BTreeMap, HashMap},
convert::TryInto,
io::Cursor,
ops::Deref,
sync::Arc,
+1 -2
View File
@@ -62,8 +62,7 @@ where
Ok(unsafe { T::ref_from_abi(pointer) })
} else {
Err(JsError::new(&format!(
"Expect an `{}` instance, received `{}` instead",
classname, constructor_name,
"Expect an `{classname}` instance, received `{constructor_name}` instead",
)))
}
}
+3 -3
View File
@@ -207,7 +207,7 @@ mod inner {
let origin = metadata
.file()
.and_then(|file| metadata.line().map(|ln| format!("{}:{}", file, ln)))
.and_then(|file| metadata.line().map(|ln| format!("{file}:{ln}")))
.unwrap_or_default();
let message = format!("{level} {origin}{recorder}");
@@ -240,11 +240,11 @@ mod inner {
self.string.push('\n');
}
let _ = write!(self.string, "{:?}", value);
let _ = write!(self.string, "{value:?}");
}
field_name => {
let _ = write!(self.string, "\n{} = {:?}", field_name, value);
let _ = write!(self.string, "\n{field_name} = {value:?}");
}
}
}
+4
View File
@@ -159,6 +159,7 @@ interface MediaSource {
[Error]
enum AuthenticationError {
"ClientMissing",
"SessionMissing",
"Generic",
};
@@ -178,6 +179,9 @@ interface AuthenticationService {
[Throws=AuthenticationError]
Client login(string username, string password);
[Throws=AuthenticationError]
Client restore_with_access_token(string token, string device_id);
};
interface SessionVerificationEmoji {
@@ -1,6 +1,10 @@
use std::sync::Arc;
use futures_util::future::join3;
use matrix_sdk::{
ruma::{OwnedDeviceId, UserId},
Session,
};
use parking_lot::RwLock;
use super::{client::Client, client_builder::ClientBuilder, RUNTIME};
@@ -15,6 +19,8 @@ pub struct AuthenticationService {
pub enum AuthenticationError {
#[error("A successful call to use_server must be made first.")]
ClientMissing,
#[error("Login was successful but is missing a valid Session to configure the file store.")]
SessionMissing,
#[error("An error occurred: {message}")]
Generic { message: String },
}
@@ -66,14 +72,12 @@ impl AuthenticationService {
/// Updates the service to authenticate with the homeserver for the
/// specified address.
pub fn configure_homeserver(&self, server_name: String) -> Result<(), AuthenticationError> {
// Construct a username as the builder currently requires one.
let username = format!("@auth:{}", server_name);
let mut builder =
Arc::new(ClientBuilder::new()).base_path(self.base_path.clone()).username(username);
let mut builder = Arc::new(ClientBuilder::new()).base_path(self.base_path.clone());
if server_name.starts_with("http://") || server_name.starts_with("https://") {
builder = builder.homeserver_url(server_name)
} else {
builder = builder.server_name(server_name);
}
let client = builder.build().map_err(AuthenticationError::from)?;
@@ -96,18 +100,74 @@ impl AuthenticationService {
) -> Result<Arc<Client>, AuthenticationError> {
match self.client.read().as_ref() {
Some(client) => {
let homeserver_url = client.homeserver();
// Login and ask the server for the full user ID as this could be different from
// the username that was entered.
client.login(username, password).map_err(AuthenticationError::from)?;
let whoami = client.whoami()?;
// Create a new client to setup the store path for the username
// Create a new client to setup the store path now the user ID is known.
let homeserver_url = client.homeserver();
let session = client.session().ok_or(AuthenticationError::SessionMissing)?;
let client = Arc::new(ClientBuilder::new())
.base_path(self.base_path.clone())
.homeserver_url(homeserver_url)
.username(username.clone())
.username(whoami.user_id.to_string())
.build()
.map_err(AuthenticationError::from)?;
// Restore the client using the session from the login request.
client
.login(username, password)
.restore_session(session.clone())
.map(|_| client.clone())
.map_err(AuthenticationError::from)
}
None => Err(AuthenticationError::ClientMissing),
}
}
/// Restore an existing session on the current homeserver using an access
/// token issued by an authentication server.
/// # Arguments
///
/// * `token` - The access token issued by the authentication server.
///
/// * `device_id` - The device ID that the access token was scoped for.
pub fn restore_with_access_token(
&self,
token: String,
device_id: String,
) -> Result<Arc<Client>, AuthenticationError> {
match self.client.read().as_ref() {
Some(client) => {
// Restore the client and ask the server for the full user ID as this
// could be different from the username that was entered.
let discovery_user_id = UserId::parse("@unknown:unknown")
.map_err(|e| AuthenticationError::Generic { message: e.to_string() })?;
let device_id: OwnedDeviceId = device_id.as_str().into();
let discovery_session = Session {
access_token: token.clone(),
user_id: discovery_user_id,
device_id: device_id.clone(),
};
client.restore_session(discovery_session).map_err(AuthenticationError::from)?;
let whoami = client.whoami()?;
// Create the actual client with a store path from the user ID.
let homeserver_url = client.homeserver();
let session =
Session { access_token: token, user_id: whoami.user_id.clone(), device_id };
let client = Arc::new(ClientBuilder::new())
.base_path(self.base_path.clone())
.homeserver_url(homeserver_url)
.username(whoami.user_id.to_string())
.build()
.map_err(AuthenticationError::from)?;
// Restore the client using the session.
client
.restore_session(session)
.map(|_| client.clone())
.map_err(AuthenticationError::from)
}
+16 -1
View File
@@ -1,10 +1,12 @@
use std::sync::Arc;
use anyhow::anyhow;
use matrix_sdk::{
config::SyncSettings,
media::{MediaFormat, MediaRequest},
ruma::{
api::client::{
account::whoami,
filter::{FilterDefinition, LazyLoadOptions, RoomEventFilter, RoomFilter},
session::get_login_types,
sync::sync_events::v3::Filter,
@@ -12,7 +14,7 @@ use matrix_sdk::{
events::room::MediaSource,
TransactionId,
},
Client as MatrixClient, LoopCtrl,
Client as MatrixClient, LoopCtrl, Session,
};
use parking_lot::RwLock;
@@ -51,6 +53,7 @@ impl Client {
}
}
/// Login using a username and password.
pub fn login(&self, username: String, password: String) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
self.client.login_username(&username, &password).send().await?;
@@ -58,10 +61,16 @@ impl Client {
})
}
/// Restores the client from a `RestoreToken`.
pub fn restore_login(&self, restore_token: String) -> anyhow::Result<()> {
let RestoreToken { session, homeurl: _, is_guest: _ } =
serde_json::from_str(&restore_token)?;
self.restore_session(session)
}
/// Restores the client from a `Session`.
pub fn restore_session(&self, session: Session) -> anyhow::Result<()> {
RUNTIME.block_on(async move {
self.client.restore_login(session).await?;
Ok(())
@@ -97,6 +106,12 @@ impl Client {
Ok(supports_password)
}
/// Gets information about the owner of a given access token.
pub fn whoami(&self) -> anyhow::Result<whoami::v3::Response> {
RUNTIME
.block_on(async move { self.client.whoami().await.map_err(|e| anyhow!(e.to_string())) })
}
pub fn start_sync(&self) {
let client = self.client.clone();
let state = self.state.clone();
+29 -15
View File
@@ -1,9 +1,10 @@
use std::{fs, path::PathBuf, sync::Arc};
use anyhow::Context;
use anyhow::anyhow;
use matrix_sdk::{
ruma::UserId, store::make_store_config, Client as MatrixClient,
ClientBuilder as MatrixClientBuilder,
ruma::{ServerName, UserId},
store::make_store_config,
Client as MatrixClient, ClientBuilder as MatrixClientBuilder,
};
use sanitize_filename_reader_friendly::sanitize;
@@ -13,6 +14,7 @@ use super::{client::Client, ClientState, RUNTIME};
pub struct ClientBuilder {
base_path: Option<String>,
username: Option<String>,
server_name: Option<String>,
homeserver_url: Option<String>,
inner: MatrixClientBuilder,
}
@@ -22,6 +24,7 @@ impl ClientBuilder {
Self {
base_path: None,
username: None,
server_name: None,
homeserver_url: None,
inner: MatrixClient::builder().user_agent("rust-sdk-ios"),
}
@@ -39,6 +42,12 @@ impl ClientBuilder {
Arc::new(builder)
}
pub fn server_name(self: Arc<Self>, server_name: String) -> Arc<Self> {
let mut builder = unwrap_or_clone_arc(self);
builder.server_name = Some(server_name);
Arc::new(builder)
}
pub fn homeserver_url(self: Arc<Self>, url: String) -> Arc<Self> {
let mut builder = unwrap_or_clone_arc(self);
builder.homeserver_url = Some(url);
@@ -47,25 +56,30 @@ impl ClientBuilder {
pub fn build(self: Arc<Self>) -> anyhow::Result<Arc<Client>> {
let builder = unwrap_or_clone_arc(self);
let mut inner_builder = builder.inner;
let base_path = builder.base_path.context("Base path was not set")?;
let username = builder
.username
.context("Username to determine homeserver and home path was not set")?;
if let (Some(base_path), Some(username)) = (builder.base_path, &builder.username) {
// Determine store path
let data_path = PathBuf::from(base_path).join(sanitize(username));
fs::create_dir_all(&data_path)?;
let store_config = make_store_config(&data_path, None)?;
// Determine store path
let data_path = PathBuf::from(base_path).join(sanitize(&username));
fs::create_dir_all(&data_path)?;
let store_config = make_store_config(&data_path, None)?;
inner_builder = inner_builder.store_config(store_config);
}
let mut inner_builder = builder.inner.store_config(store_config);
// Determine server either from explicitly set homeserver or from userId
// Determine server either from URL, server name or user ID.
if let Some(homeserver_url) = builder.homeserver_url {
inner_builder = inner_builder.homeserver_url(homeserver_url);
} else {
} else if let Some(server_name) = builder.server_name {
let server_name = ServerName::parse(server_name)?;
inner_builder = inner_builder.server_name(&server_name);
} else if let Some(username) = builder.username {
let user = UserId::parse(username)?;
inner_builder = inner_builder.server_name(user.server_name());
} else {
return Err(anyhow!(
"Failed to build: One of homeserver_url, server_name or username must be called."
));
}
RUNTIME.block_on(async move {
+1 -1
View File
@@ -1,6 +1,6 @@
[package]
authors = ["Johannes Becker <j.becker@famedly.com>"]
edition = "2018"
edition = "2021"
homepage = "https://github.com/matrix-org/matrix-rust-sdk"
repository = "https://github.com/matrix-org/matrix-rust-sdk"
description = "Appservice SDK based on the matrix-sdk"
@@ -60,10 +60,11 @@ pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
let appservice = AppService::new(homeserver_url, server_name, registration).await?;
appservice.register_user_query(Box::new(|_, _| Box::pin(async { true }))).await;
appservice
.virtual_user(None)
.await?
.register_event_handler_context(appservice.clone())
let virtual_user = appservice.virtual_user(None).await?;
virtual_user.register_event_handler_context(appservice.clone());
virtual_user
.register_event_handler(
move |event: OriginalSyncRoomMemberEvent,
room: Room,
+4 -4
View File
@@ -84,7 +84,7 @@
//! [matrix-org/matrix-rust-sdk#228]: https://github.com/matrix-org/matrix-rust-sdk/issues/228
//! [examples directory]: https://github.com/matrix-org/matrix-rust-sdk/tree/main/crates/matrix-sdk-appservice/examples
use std::{convert::TryInto, sync::Arc};
use std::sync::Arc;
use dashmap::DashMap;
pub use error::Error;
@@ -122,7 +122,7 @@ pub use virtual_user::VirtualUserBuilder;
pub type Result<T, E = Error> = std::result::Result<T, E>;
const USER_KEY: &[u8] = b"appservice.users.";
pub const USER_MEMBER: &[u8] = b"appservice.users.membership.";
const USER_MEMBER: &[u8] = b"appservice.users.membership.";
type Localpart = String;
@@ -492,7 +492,7 @@ impl AppService {
}
for task in tasks {
if let Err(e) = task.await {
warn!("Joining sync task failed: {}", e);
warn!("Joining sync task failed: {e}");
}
}
Ok(())
@@ -505,7 +505,7 @@ impl AppService {
pub async fn run(&self, host: impl Into<String>, port: impl Into<u16>) -> Result<()> {
let host = host.into();
let port = port.into();
info!("Starting AppService on {}:{}", &host, &port);
info!("Starting AppService on {host}:{port}");
webserver::run_server(self.clone(), host, port).await?;
Ok(())
@@ -14,7 +14,7 @@
//! AppService Registration.
use std::{convert::TryFrom, fs::File, ops::Deref, path::PathBuf};
use std::{fs::File, ops::Deref, path::PathBuf};
use http::Uri;
use regex::Regex;
+10 -6
View File
@@ -145,7 +145,7 @@ mod filters {
.and(warp::body::bytes())
.and_then(|method, path: FullPath, query, headers, bytes| async move {
let uri = http::uri::Builder::new()
.path_and_query(format!("{}?{}", path.as_str(), query))
.path_and_query(format!("{}?{query}", path.as_str()))
.build()
.map_err(Error::from)?;
@@ -164,9 +164,13 @@ mod filters {
mod handlers {
use percent_encoding::percent_decode_str;
use serde::Serialize;
use super::*;
#[derive(Serialize)]
struct EmptyObject {}
pub async fn user(
user_id: String,
appservice: AppService,
@@ -177,12 +181,12 @@ mod handlers {
let request = query_user::IncomingRequest::try_from_http_request(request, &[user_id])
.map_err(Error::from)?;
return if user_exists(appservice.clone(), request).await {
Ok(warp::reply::json(&String::from("{}")))
Ok(warp::reply::json(&EmptyObject {}))
} else {
Err(warp::reject::not_found())
};
}
Ok(warp::reply::json(&String::from("{}")))
Ok(warp::reply::json(&EmptyObject {}))
}
pub async fn room(
@@ -195,12 +199,12 @@ mod handlers {
let request = query_room::IncomingRequest::try_from_http_request(request, &[room_id])
.map_err(Error::from)?;
return if room_exists(appservice.clone(), request).await {
Ok(warp::reply::json(&String::from("{}")))
Ok(warp::reply::json(&EmptyObject {}))
} else {
Err(warp::reject::not_found())
};
}
Ok(warp::reply::json(&String::from("{}")))
Ok(warp::reply::json(&EmptyObject {}))
}
pub async fn transaction(
@@ -213,7 +217,7 @@ mod handlers {
.map_err(Error::from)?;
appservice.receive_transaction(incoming_transaction).await?;
Ok(warp::reply::json(&String::from("{}")))
Ok(warp::reply::json(&EmptyObject {}))
}
}
+5 -5
View File
@@ -400,8 +400,8 @@ impl BaseClient {
}
Err(err) => {
warn!(
"Couldn't deserialize stripped state event for room {}: {:?}",
room_info.room_id, err
"Couldn't deserialize stripped state event for room {}: {err:?}",
room_info.room_id,
);
}
}
@@ -430,8 +430,8 @@ impl BaseClient {
Ok(e) => e,
Err(e) => {
warn!(
"Couldn't deserialize state event for room {}: {:?} {:#?}",
room_id, e, raw_event
"Couldn't deserialize state event for room {room_id}: \
{e:?} {raw_event:#?}",
);
continue;
}
@@ -831,7 +831,7 @@ impl BaseClient {
.filter_map(|event| match event.deserialize() {
Ok(ev) => Some(ev),
Err(e) => {
debug!(?event, "Failed to deserialize m.room.member event: {}", e);
debug!(?event, "Failed to deserialize m.room.member event: {e}");
None
}
})
+2 -2
View File
@@ -47,9 +47,9 @@ impl fmt::Display for DisplayName {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
DisplayName::Named(s) | DisplayName::Calculated(s) | DisplayName::Aliased(s) => {
write!(f, "{}", s)
write!(f, "{s}")
}
DisplayName::EmptyWas(s) => write!(f, "Empty Room (was {})", s),
DisplayName::EmptyWas(s) => write!(f, "Empty Room (was {s})"),
DisplayName::Empty => write!(f, "Empty Room"),
}
}
+6 -3
View File
@@ -44,6 +44,7 @@ use ruma::{
RoomVersionId, UserId,
};
use serde::{Deserialize, Serialize};
use tracing::debug;
use super::{BaseRoomInfo, DisplayName, RoomMember};
use crate::{
@@ -396,7 +397,7 @@ impl Room {
_ => (summary.joined_member_count, summary.invited_member_count),
};
tracing::debug!(
debug!(
room_id = self.room_id().as_str(),
own_user = self.own_user_id.as_str(),
joined, invited,
@@ -589,6 +590,8 @@ impl Room {
/// Add a new timeline slice to the timeline streams.
#[cfg(feature = "experimental-timeline")]
pub async fn add_timeline_slice(&self, timeline: &TimelineSlice) {
use tracing::warn;
if timeline.sync {
let mut streams = self.forward_timeline_streams.lock().await;
let mut remaining_streams = Vec::with_capacity(streams.len());
@@ -596,7 +599,7 @@ impl Room {
if !forward.is_closed() {
if let Err(error) = forward.try_send(timeline.clone()) {
if error.is_full() {
tracing::warn!("Drop timeline slice because the limit of the buffer for the forward stream is reached");
warn!("Drop timeline slice because the limit of the buffer for the forward stream is reached");
}
} else {
remaining_streams.push(forward);
@@ -611,7 +614,7 @@ impl Room {
if !backward.is_closed() {
if let Err(error) = backward.try_send(timeline.clone()) {
if error.is_full() {
tracing::warn!("Drop timeline slice because the limit of the buffer for the backward stream is reached");
warn!("Drop timeline slice because the limit of the buffer for the backward stream is reached");
}
} else {
remaining_streams.push(backward);
@@ -123,7 +123,7 @@ impl AmbiguityCache {
member_ambiguous: ambiguous,
};
trace!("Handling display name ambiguity for {}: {:#?}", member_event.state_key(), change);
trace!("Handling display name ambiguity for {}: {change:#?}", member_event.state_key());
self.add_change(room_id, member_event.event_id().to_owned(), change);
@@ -775,13 +775,14 @@ macro_rules! statestore_integration_tests {
.zip(stored_events.iter())
.enumerate()
{
assert_eq!(a.expect("not a value").event_id(), b.event_id(), "pos {} not equal - expected: {:#?}, but found {:#?}", idx, expected, found);
assert_eq!(
a.expect("not a value").event_id(),
b.event_id(),
"pos {idx} not equal - expected: {expected:#?}, but found {found:#?}",
);
}
}
}
)*
}
}
@@ -43,6 +43,7 @@ use ruma::{
serde::Raw,
EventId, MxcUri, OwnedEventId, OwnedRoomId, OwnedUserId, RoomId, UserId,
};
use tracing::info;
#[cfg(feature = "experimental-timeline")]
use super::BoxStream;
@@ -343,25 +344,24 @@ impl MemoryStore {
#[cfg(feature = "experimental-timeline")]
for (room, timeline) in &changes.timeline {
use tracing::warn;
if timeline.sync {
tracing::info!("Save new timeline batch from sync response for {}", room);
info!("Save new timeline batch from sync response for {room}");
} else {
tracing::info!("Save new timeline batch from messages response for {}", room);
info!("Save new timeline batch from messages response for {room}");
}
let mut delete_timeline = false;
if timeline.limited {
tracing::info!(
"Delete stored timeline for {} because the sync response was limited",
room
);
info!("Delete stored timeline for {room} because the sync response was limited");
delete_timeline = true;
} else if let Some(mut data) = self.room_timeline.get_mut(room) {
if !timeline.sync && Some(&timeline.start) != data.end.as_ref() {
// This should only happen when a developer adds a wrong timeline
// batch to the `StateChanges` or the server returns a wrong response
// to our request.
tracing::warn!("Drop unexpected timeline batch for {}", room);
warn!("Drop unexpected timeline batch for {room}");
return Ok(());
}
@@ -385,7 +385,7 @@ impl MemoryStore {
}
if delete_timeline {
tracing::info!("Delete stored timeline for {} because of duplicated events", room);
info!("Delete stored timeline for {room} because of duplicated events");
self.room_timeline.remove(room);
}
@@ -401,10 +401,7 @@ impl MemoryStore {
.get(room)
.and_then(|info| info.room_version().cloned())
.unwrap_or_else(|| {
tracing::warn!(
"Unable to find the room version for {}, assume version 9",
room
);
warn!("Unable to find the room version for {room}, assume version 9");
RoomVersionId::V9
})
};
@@ -455,7 +452,7 @@ impl MemoryStore {
}
}
tracing::info!("Saved changes in {:?}", now.elapsed());
info!("Saved changes in {:?}", now.elapsed());
Ok(())
}
@@ -676,7 +673,7 @@ impl MemoryStore {
let (events, end_token) = if let Some(data) = self.room_timeline.get(room_id) {
(data.events.clone(), data.end.clone())
} else {
tracing::info!("No timeline for {} was previously stored", room_id);
info!("No timeline for {room_id} was previously stored");
return Ok(None);
};
@@ -686,11 +683,7 @@ impl MemoryStore {
}
};
tracing::info!(
"Found previously stored timeline for {}, with end token {:?}",
room_id,
end_token
);
info!("Found previously stored timeline for {room_id}, with end token {end_token:?}");
Ok(Some((Box::pin(stream), end_token)))
}
+1 -1
View File
@@ -17,7 +17,7 @@ The state machine works in a push/pull manner:
state machine
```rust,no_run
use std::{collections::BTreeMap, convert::TryFrom};
use std::collections::BTreeMap;
use matrix_sdk_crypto::{OlmMachine, OlmError};
use ruma::{
@@ -13,7 +13,6 @@
// limitations under the License.
use std::{
convert::TryFrom,
io::{Cursor, Read},
ops::DerefMut,
};
@@ -369,7 +369,7 @@ mod tests {
)]),
);
assert_eq!(machine.import_keys(export, false, |_, _| {}).await?, keys,);
assert_eq!(machine.import_keys(export, false, |_, _| {}).await?, keys);
let export = vec![session.export_at_index(10).await];
assert_eq!(
@@ -379,7 +379,7 @@ mod tests {
let better_export = vec![session.export().await];
assert_eq!(machine.import_keys(better_export, false, |_, _| {}).await?, keys,);
assert_eq!(machine.import_keys(better_export, false, |_, _| {}).await?, keys);
let another_session = machine.create_inbound_session(room_id).await?;
let export = vec![another_session.export_at_index(10).await];
@@ -396,7 +396,7 @@ mod tests {
)]),
);
assert_eq!(machine.import_keys(export, false, |_, _| {}).await?, keys,);
assert_eq!(machine.import_keys(export, false, |_, _| {}).await?, keys);
Ok(())
}
@@ -14,7 +14,6 @@
use std::{
collections::{BTreeMap, BTreeSet, HashSet},
convert::TryFrom,
ops::Deref,
sync::Arc,
time::Duration,
@@ -623,7 +622,7 @@ impl IdentityManager {
}
if let Err(e) = self.store.update_tracked_user(user, true).await {
warn!("Error storing users for tracking {}", e);
warn!("Error storing users for tracking: {e}");
}
}
}
+1 -3
View File
@@ -1211,7 +1211,6 @@ impl OlmMachine {
/// # Example
///
/// ```
/// # use std::convert::TryFrom;
/// # use matrix_sdk_crypto::OlmMachine;
/// # use ruma::{device_id, user_id};
/// # use futures::executor::block_on;
@@ -1269,7 +1268,6 @@ impl OlmMachine {
/// # Example
///
/// ```
/// # use std::convert::TryFrom;
/// # use matrix_sdk_crypto::OlmMachine;
/// # use ruma::{device_id, user_id};
/// # use futures::executor::block_on;
@@ -1554,7 +1552,7 @@ pub(crate) mod testing {
#[cfg(test)]
pub(crate) mod tests {
use std::{collections::BTreeMap, convert::TryInto, iter, sync::Arc};
use std::{collections::BTreeMap, iter, sync::Arc};
use matrix_sdk_test::{async_test, test_json};
use ruma::{
+1 -3
View File
@@ -14,7 +14,6 @@
use std::{
collections::{BTreeMap, HashMap},
convert::TryInto,
fmt,
ops::Deref,
sync::{
@@ -577,9 +576,8 @@ impl ReadOnlyAccount {
// so.
if count != old_count {
debug!(
"Updated uploaded one-time key count {} -> {}.",
"Updated uploaded one-time key count {} -> {count}.",
self.uploaded_key_count(),
count
);
}
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{collections::BTreeMap, convert::TryInto};
use std::collections::BTreeMap;
use ruma::{
events::forwarded_room_key::{
@@ -717,12 +717,12 @@ mod tests {
assert!(master_key
.public_key
.verify_subkey(&identity.self_signing_key.lock().await.as_ref().unwrap().public_key,)
.verify_subkey(&identity.self_signing_key.lock().await.as_ref().unwrap().public_key)
.is_ok());
assert!(master_key
.public_key
.verify_subkey(&identity.user_signing_key.lock().await.as_ref().unwrap().public_key,)
.verify_subkey(&identity.user_signing_key.lock().await.as_ref().unwrap().public_key)
.is_ok());
}
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::TryInto;
use ruma::{CanonicalJsonValue, DeviceKeyAlgorithm, DeviceKeyId, UserId};
use serde::Serialize;
use serde_json::Value;
@@ -445,7 +445,7 @@ impl GroupSessionManager {
users: impl Iterator<Item = &UserId>,
encryption_settings: impl Into<EncryptionSettings>,
) -> OlmResult<Vec<Arc<ToDeviceRequest>>> {
trace!(room_id = room_id.as_str(), "Checking if a room key needs to be shared",);
trace!(room_id = room_id.as_str(), "Checking if a room key needs to be shared");
let encryption_settings = encryption_settings.into();
let history_visibility = encryption_settings.history_visibility.clone();
@@ -338,10 +338,7 @@ impl SessionManager {
self.key_request_machine.retry_keyshare(user_id, device_id);
if let Err(e) = self.check_if_unwedged(user_id, device_id).await {
error!(
"Error while treating an unwedged device {} {} {:?}",
user_id, device_id, e
);
error!("Error while treating an unwedged device {user_id} {device_id} {e:?}");
}
changes.sessions.push(session);
@@ -753,7 +753,7 @@ impl TryFrom<ToDeviceRequest> for OutgoingContent {
serde_json::from_value(json).map_err(|e| e.to_string())?,
)
}
e => return Err(format!("Unsupported event type {}", e)),
e => return Err(format!("Unsupported event type {e}")),
};
Ok(content.into())
@@ -506,7 +506,7 @@ impl VerificationMachine {
#[cfg(test)]
mod tests {
use std::{convert::TryFrom, sync::Arc, time::Duration};
use std::{sync::Arc, time::Duration};
use matrix_sdk_common::{instant::Instant, locks::Mutex};
use matrix_sdk_test::async_test;
@@ -501,10 +501,9 @@ impl IdentitiesBeingVerified {
}
Err(e) => {
error!(
"Error signing device keys for {} {} {:?}",
"Error signing device keys for {} {}: {e:?}",
device.user_id(),
device.device_id(),
e
);
None
}
@@ -535,9 +534,8 @@ impl IdentitiesBeingVerified {
}
Err(e) => {
error!(
"Error signing the public cross signing keys for {} {:?}",
"Error signing the public cross signing keys for {} {e:?}",
i.user_id(),
e
);
None
}
@@ -707,7 +705,6 @@ impl IdentitiesBeingVerified {
#[cfg(test)]
pub(crate) mod tests {
use std::convert::TryInto;
use ruma::{
events::{AnyToDeviceEventContent, ToDeviceEvent},
@@ -787,7 +787,7 @@ impl QrState<Reciprocated> {
#[cfg(test)]
mod tests {
use std::{convert::TryFrom, sync::Arc};
use std::sync::Arc;
use matrix_sdk_common::locks::Mutex;
use matrix_sdk_qrcode::QrVerificationData;
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{collections::BTreeMap, convert::TryInto};
use std::collections::BTreeMap;
use ruma::{
events::{
@@ -196,19 +196,14 @@ pub fn receive_mac_event(
let info = extra_mac_info_receive(ids, flow_id);
trace!(
"Received a key.verification.mac event from {} {}",
sender,
ids.other_device.device_id()
);
trace!("Received a key.verification.mac event from {sender} {}", ids.other_device.device_id());
let mut keys = content.mac().keys().map(|k| k.as_str()).collect::<Vec<_>>();
keys.sort_unstable();
let keys = Base64::parse(
sas.calculate_mac_invalid_base64(&keys.join(","), &format!("{}KEY_IDS", &info)),
)
.expect("Can't base64-decode SAS MAC");
let keys =
Base64::parse(sas.calculate_mac_invalid_base64(&keys.join(","), &format!("{info}KEY_IDS")))
.expect("Can't base64-decode SAS MAC");
if keys != *content.keys() {
return Err(CancelCode::KeyMismatch);
@@ -216,9 +211,7 @@ pub fn receive_mac_event(
for (key_id, key_mac) in content.mac() {
trace!(
"Checking MAC for the key id {} from {} {}",
key_id,
sender,
"Checking MAC for the key id {key_id} from {sender} {}",
ids.other_device.device_id()
);
@@ -234,7 +227,7 @@ pub fn receive_mac_event(
.expect("Can't base64-decode SAS MAC");
if *key_mac == calculated_mac {
trace!("Successfully verified the device key {} from {}", key_id, sender);
trace!("Successfully verified the device key {key_id} from {sender}");
verified_devices.push(ids.other_device.clone());
} else {
return Err(CancelCode::KeyMismatch);
@@ -243,14 +236,13 @@ pub fn receive_mac_event(
if let Some(key) = identity.master_key().get_key(&key_id) {
// TODO we should check that the master key signs the device,
// this way we know the master key also trusts the device
let calculated_mac = Base64::parse(sas.calculate_mac_invalid_base64(
&key.to_base64(),
&format!("{}{}", info, key_id),
))
let calculated_mac = Base64::parse(
sas.calculate_mac_invalid_base64(&key.to_base64(), &format!("{info}{key_id}")),
)
.expect("Can't base64-decode SAS MAC");
if *key_mac == calculated_mac {
trace!("Successfully verified the master key {} from {}", key_id, sender);
trace!("Successfully verified the master key {key_id} from {sender}");
verified_identities.push(identity.clone())
} else {
return Err(CancelCode::KeyMismatch);
@@ -258,10 +250,8 @@ pub fn receive_mac_event(
}
} else {
warn!(
"Key ID {} in MAC event from {} {} doesn't belong to any device \
"Key ID {key_id} in MAC event from {sender} {} doesn't belong to any device \
or user identity",
key_id,
sender,
ids.other_device.device_id()
);
}
@@ -312,7 +302,7 @@ pub fn get_mac_content(sas: &EstablishedSas, ids: &SasIds, flow_id: &FlowId) ->
mac.insert(
key_id.to_string(),
Base64::parse(sas.calculate_mac_invalid_base64(&key, &format!("{}{}", info, key_id)))
Base64::parse(sas.calculate_mac_invalid_base64(&key, &format!("{info}{key_id}")))
.expect("Can't base64-decode SAS MAC"),
);
@@ -321,10 +311,9 @@ pub fn get_mac_content(sas: &EstablishedSas, ids: &SasIds, flow_id: &FlowId) ->
if let Some(key) = own_identity.master_key().get_first_key() {
let key_id = format!("{}:{}", DeviceKeyAlgorithm::Ed25519, key.to_base64());
let calculated_mac = Base64::parse(sas.calculate_mac_invalid_base64(
&key.to_base64(),
&format!("{}{}", info, &key_id),
))
let calculated_mac = Base64::parse(
sas.calculate_mac_invalid_base64(&key.to_base64(), &format!("{info}{key_id}")),
)
.expect("Can't base64-decode SAS Master key MAC");
mac.insert(key_id, calculated_mac);
@@ -508,7 +508,7 @@ impl AcceptSettings {
#[cfg(test)]
mod tests {
use std::{convert::TryFrom, sync::Arc};
use std::sync::Arc;
use matrix_sdk_common::locks::Mutex;
use matrix_sdk_test::async_test;
@@ -13,7 +13,6 @@
// limitations under the License.
use std::{
convert::{TryFrom, TryInto},
matches,
sync::{Arc, Mutex},
time::Duration,
@@ -546,10 +545,8 @@ impl SasState<Started> {
let commitment = calculate_commitment(our_public_key, content);
info!(
"Calculated commitment for pubkey {} and content {:?} {}",
"Calculated commitment for pubkey {} and content {content:?} {commitment}",
our_public_key.to_base64(),
content,
commitment
);
if let Ok(accepted_protocols) = AcceptedProtocols::try_from(method_content) {
@@ -1224,8 +1221,6 @@ impl SasState<Cancelled> {
#[cfg(test)]
mod tests {
use std::convert::TryFrom;
use matrix_sdk_test::async_test;
use ruma::{
device_id,
@@ -72,8 +72,7 @@ pub trait SafeEncode {
store_cipher: &StoreCipher,
i: usize,
) -> JsValue {
format!("{}{}{:016x}", self.as_secure_string(table_name, store_cipher), KEY_SEPARATOR, i,)
.into()
format!("{}{KEY_SEPARATOR}{i:016x}", self.as_secure_string(table_name, store_cipher)).into()
}
/// Encode self into a IdbKeyRange for searching all keys that are
+23 -31
View File
@@ -54,6 +54,8 @@ use ruma::{
RoomVersionId,
};
use serde::{de::DeserializeOwned, Deserialize, Serialize};
#[cfg(feature = "experimental-timeline")]
use tracing::{info, warn};
use wasm_bindgen::JsValue;
use web_sys::IdbKeyRange;
@@ -111,12 +113,10 @@ impl From<IndexeddbStoreError> for StoreError {
EncryptionError::Serialization(e) => StoreError::Json(e),
EncryptionError::Encryption(e) => StoreError::Encryption(e.to_string()),
EncryptionError::Version(found, expected) => StoreError::Encryption(format!(
"Bad Database Encryption Version: expected {} found {}",
expected, found
"Bad Database Encryption Version: expected {expected}, found {found}",
)),
EncryptionError::Length(found, expected) => StoreError::Encryption(format!(
"The database key an invalid length: expected {} found {}",
expected, found
"The database key an invalid length: expected {expected}, found {found}",
)),
},
_ => StoreError::backend(e),
@@ -274,9 +274,11 @@ async fn backup(source: &IdbDatabase, meta: &IdbDatabase) -> Result<()> {
pub struct IndexeddbStoreBuilderConfig {
/// The name for the indexeddb store to use, `state` is none given
name: String,
/// The password the indexeddb should be encrypted with. If not given, the DB is not encrypted
/// The password the indexeddb should be encrypted with. If not given, the
/// DB is not encrypted
passphrase: String,
/// The strategy to use when a merge conflict is found, see [`MigrationConflictStrategy`] for details
/// The strategy to use when a merge conflict is found, see
/// [`MigrationConflictStrategy`] for details
#[builder(default = "MigrationConflictStrategy::BackupAndDrop")]
migration_conflict_strategy: MigrationConflictStrategy,
}
@@ -851,17 +853,14 @@ impl IndexeddbStore {
for (room_id, timeline) in &changes.timeline {
if timeline.sync {
tracing::info!("Save new timeline batch from sync response for {}", room_id);
info!("Save new timeline batch from sync response for {room_id}");
} else {
tracing::info!(
"Save new timeline batch from messages response for {}",
room_id
);
info!("Save new timeline batch from messages response for {room_id}");
}
let metadata: Option<TimelineMetadata> = if timeline.limited {
tracing::info!(
"Delete stored timeline for {} because the sync response was limited",
room_id
info!(
"Delete stored timeline for {room_id} because the sync response was \
limited",
);
let stores = &[
@@ -888,7 +887,7 @@ impl IndexeddbStore {
// This should only happen when a developer adds a wrong timeline
// batch to the `StateChanges` or the server returns a wrong response
// to our request.
tracing::warn!("Drop unexpected timeline batch for {}", room_id);
warn!("Drop unexpected timeline batch for {room_id}");
return Ok(());
}
@@ -912,9 +911,8 @@ impl IndexeddbStore {
}
if delete_timeline {
tracing::info!(
"Delete stored timeline for {} because of duplicated events",
room_id
info!(
"Delete stored timeline for {room_id} because of duplicated events",
);
let stores = &[
@@ -961,9 +959,8 @@ impl IndexeddbStore {
.transpose()?
.and_then(|info| info.room_version().cloned())
.unwrap_or_else(|| {
tracing::warn!(
"Unable to find the room version for {}, assume version 9",
room_id
warn!(
"Unable to find the room version for {room_id}, assume version 9",
);
RoomVersionId::V9
});
@@ -1466,7 +1463,7 @@ impl IndexeddbStore {
{
Some(tl) => tl,
_ => {
tracing::info!("No timeline for {} was previously stored", room_id);
info!("No timeline for {room_id} was previously stored");
return Ok(None);
}
};
@@ -1482,11 +1479,7 @@ impl IndexeddbStore {
let stream = Box::pin(stream::iter(timeline.into_iter()));
tracing::info!(
"Found previously stored timeline for {}, with end token {:?}",
room_id,
end_token
);
info!("Found previously stored timeline for {room_id}, with end token {end_token:?}");
Ok(Some((stream, end_token)))
}
@@ -1682,7 +1675,7 @@ mod tests {
use super::{IndexeddbStore, IndexeddbStoreBuilder, Result};
async fn get_store() -> Result<IndexeddbStore> {
let db_name = format!("test-state-plain-{}", Uuid::new_v4().as_hyphenated().to_string());
let db_name = format!("test-state-plain-{}", Uuid::new_v4().as_hyphenated());
Ok(IndexeddbStoreBuilder::default().name(db_name).build().await?)
}
@@ -1700,9 +1693,8 @@ mod encrypted_tests {
use super::{IndexeddbStore, IndexeddbStoreBuilder, Result};
async fn get_store() -> Result<IndexeddbStore> {
let db_name =
format!("test-state-encrypted-{}", Uuid::new_v4().as_hyphenated().to_string());
let passphrase = format!("some_passphrase-{}", Uuid::new_v4().as_hyphenated().to_string());
let db_name = format!("test-state-encrypted-{}", Uuid::new_v4().as_hyphenated());
let passphrase = format!("some_passphrase-{}", Uuid::new_v4().as_hyphenated());
Ok(IndexeddbStoreBuilder::default().name(db_name).passphrase(passphrase).build().await?)
}
+1 -1
View File
@@ -33,7 +33,7 @@ pub use types::{
#[cfg(test)]
mod tests {
#[cfg(feature = "decode_image")]
use std::{convert::TryFrom, io::Cursor};
use std::io::Cursor;
#[cfg(feature = "decode_image")]
use image::{ImageFormat, Luma};
-2
View File
@@ -12,8 +12,6 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::convert::TryInto;
#[cfg(feature = "decode_image")]
use image::{GenericImage, GenericImageView, Luma};
use qrcode::{bits::Bits, EcLevel, QrCode, Version};
@@ -14,7 +14,6 @@
use std::{
collections::{HashMap, HashSet},
convert::TryInto,
path::{Path, PathBuf},
sync::{Arc, RwLock},
};
+22 -35
View File
@@ -59,6 +59,7 @@ use sled::{
Config, Db, Transactional, Tree,
};
use tokio::task::spawn_blocking;
use tracing::{debug, info};
#[cfg(feature = "crypto-store")]
use super::OpenStoreError;
@@ -126,12 +127,10 @@ impl Into<StoreError> for SledStoreError {
KeyEncryptionError::Serialization(e) => StoreError::Json(e),
KeyEncryptionError::Encryption(e) => StoreError::Encryption(e.to_string()),
KeyEncryptionError::Version(found, expected) => StoreError::Encryption(format!(
"Bad Database Encryption Version: expected {} found {}",
expected, found
"Bad Database Encryption Version: expected {expected}, found {found}",
)),
KeyEncryptionError::Length(found, expected) => StoreError::Encryption(format!(
"The database key an invalid length: expected {} found {}",
expected, found
"The database key an invalid length: expected {expected}, found {found}",
)),
},
SledStoreError::StoreError(e) => e,
@@ -215,7 +214,8 @@ pub struct SledStoreBuilderConfig {
path: PathBuf,
/// Set the password the sled store is encrypted with (if any)
passphrase: String,
/// The strategy to use when a merge conflict is found, see [`MigrationConflictStrategy`] for details
/// The strategy to use when a merge conflict is found, see
/// [`MigrationConflictStrategy`] for details
#[builder(default = "MigrationConflictStrategy::BackupAndDrop")]
migration_conflict_strategy: MigrationConflictStrategy,
}
@@ -476,11 +476,7 @@ impl SledStore {
Some(version) => version,
};
tracing::debug!(
old_version,
new_version = DATABASE_VERSION,
"Upgrading the Sled state store"
);
debug!(old_version, new_version = DATABASE_VERSION, "Upgrading the Sled state store");
if old_version == 1 {
if self.store_cipher.is_some() {
@@ -839,7 +835,7 @@ impl SledStore {
self.inner.flush_async().await?;
tracing::info!("Saved changes in {:?}", now.elapsed());
info!("Saved changes in {:?}", now.elapsed());
Ok(())
}
@@ -1308,7 +1304,7 @@ impl SledStore {
let metadata = match metadata {
Some(m) => m,
None => {
tracing::info!("No timeline for {} was previously stored", r_id);
info!("No timeline for {r_id} was previously stored");
return Ok(None);
}
};
@@ -1316,16 +1312,14 @@ impl SledStore {
let mut position = metadata.start_position;
let end_token = metadata.end;
tracing::info!(
"Found previously stored timeline for {}, with end token {:?}",
r_id,
end_token
);
info!("Found previously stored timeline for {r_id}, with end token {end_token:?}");
let stream = stream! {
while let Ok(Some(item)) = db.room_timeline.get(&db.encode_key_with_counter(TIMELINE, &r_id, position)) {
while let Ok(Some(item)) =
db.room_timeline.get(&db.encode_key_with_counter(TIMELINE, &r_id, position))
{
position += 1;
yield db.deserialize_value(&item).map_err(SledStoreError::from).map_err(|e| e.into());
yield db.deserialize_value(&item).map_err(|e| SledStoreError::from(e).into());
}
};
@@ -1334,7 +1328,7 @@ impl SledStore {
#[cfg(feature = "experimental-timeline")]
async fn remove_room_timeline(&self, room_id: &RoomId) -> Result<()> {
tracing::info!("Remove stored timeline for {}", room_id);
info!("Remove stored timeline for {room_id}");
let mut timeline_batch = sled::Batch::default();
for key in self.room_timeline.scan_prefix(self.encode_key(TIMELINE, &room_id)).keys() {
@@ -1371,22 +1365,21 @@ impl SledStore {
#[cfg(feature = "experimental-timeline")]
async fn save_room_timeline(&self, changes: &StateChanges) -> Result<()> {
use tracing::warn;
let mut timeline_batch = sled::Batch::default();
let mut event_id_to_position_batch = sled::Batch::default();
let mut timeline_metadata_batch = sled::Batch::default();
for (room_id, timeline) in &changes.timeline {
if timeline.sync {
tracing::info!("Save new timeline batch from sync response for {}", room_id);
info!("Save new timeline batch from sync response for {room_id}");
} else {
tracing::info!("Save new timeline batch from messages response for {}", room_id);
info!("Save new timeline batch from messages response for {room_id}");
}
let metadata: Option<TimelineMetadata> = if timeline.limited {
tracing::info!(
"Delete stored timeline for {} because the sync response was limited",
room_id
);
info!("Delete stored timeline for {room_id} because the sync response was limited");
self.remove_room_timeline(room_id).await?;
None
} else {
@@ -1400,7 +1393,7 @@ impl SledStore {
// This should only happen when a developer adds a wrong timeline
// batch to the `StateChanges` or the server returns a wrong response
// to our request.
tracing::warn!("Drop unexpected timeline batch for {}", room_id);
warn!("Drop unexpected timeline batch for {room_id}");
return Ok(());
}
@@ -1418,10 +1411,7 @@ impl SledStore {
}
if delete_timeline {
tracing::info!(
"Delete stored timeline for {} because of duplicated events",
room_id
);
info!("Delete stored timeline for {room_id} because of duplicated events");
self.remove_room_timeline(room_id).await?;
None
} else if timeline.sync {
@@ -1453,10 +1443,7 @@ impl SledStore {
.transpose()?
.and_then(|info| info.room_version().cloned())
.unwrap_or_else(|| {
tracing::warn!(
"Unable to find the room version for {}, assume version 9",
room_id
);
warn!("Unable to find the room version for {room_id}, assume version 9");
RoomVersionId::V9
});
-2
View File
@@ -1,5 +1,3 @@
use std::convert::TryFrom;
use ruma::{events::AnyRoomEvent, serde::Raw};
use serde_json::Value;
+1 -1
View File
@@ -19,7 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"]
default = [
"e2e-encryption",
"sled",
"native-tls"
"native-tls",
]
e2e-encryption = [
-1
View File
@@ -25,7 +25,6 @@ some event handlers and then syncing.
This is demonstrated in the example below.
```rust,no_run
use std::convert::TryFrom;
use matrix_sdk::{
Client, config::SyncSettings,
ruma::{user_id, events::room::message::SyncRoomMessageEvent},
+3 -3
View File
@@ -22,13 +22,13 @@ async fn on_stripped_state_member(
// retry autojoin due to synapse sending invites, before the
// invited user can join for more information see
// https://github.com/matrix-org/synapse/issues/4345
eprintln!("Failed to join room {} ({:?}), retrying in {}s", room.room_id(), err, delay);
eprintln!("Failed to join room {} ({err:?}), retrying in {delay}s", room.room_id());
sleep(Duration::from_secs(delay)).await;
delay *= 2;
if delay > 3600 {
eprintln!("Can't join room {} ({:?})", room.room_id(), err);
eprintln!("Can't join room {} ({err:?})", room.room_id());
break;
}
}
@@ -67,7 +67,7 @@ async fn login_and_sync(
.send()
.await?;
println!("logged in as {}", username);
println!("logged in as {username}");
client.register_event_handler(on_stripped_state_member).await;
+1 -1
View File
@@ -61,7 +61,7 @@ async fn login_and_sync(
.send()
.await?;
println!("logged in as {}", username);
println!("logged in as {username}");
// An initial sync to set up state and so our bot doesn't respond to old
// messages. If the `StateStore` finds saved state in the location given the
+1 -1
View File
@@ -92,7 +92,7 @@ async fn main() -> anyhow::Result<()> {
}
};
println!("helloooo {} {} {} {:#?}", homeserver_url, username, password, image_path);
println!("helloooo {homeserver_url} {username} {password} {image_path:#?}");
let path = PathBuf::from(image_path);
let image = File::open(path).expect("Can't open image file.");
+1 -1
View File
@@ -25,7 +25,7 @@ async fn on_room_message(event: OriginalSyncRoomMessageEvent, room: Room) {
{
let member = room.get_member(&sender).await.unwrap().unwrap();
let name = member.display_name().unwrap_or_else(|| member.user_id().as_str());
println!("{}: {}", name, msg_body);
println!("{name}: {msg_body}");
}
}
}
+1 -1
View File
@@ -54,7 +54,7 @@ async fn print_timeline(room: Room) {
while let Some(event) = backward_stream.next().await {
let event = event.unwrap();
if let Some(content) = event_content(event.event.deserialize().unwrap()) {
println!("{}", content);
println!("{content}");
}
}
}
+2 -4
View File
@@ -65,7 +65,7 @@ impl Account {
/// client.login(user, "password", None, None).await?;
///
/// if let Some(name) = client.account().get_display_name().await? {
/// println!("Logged in as user '{}' with display name '{}'", user, name);
/// println!("Logged in as user '{user}' with display name '{name}'");
/// }
/// # anyhow::Ok(()) });
/// ```
@@ -113,7 +113,7 @@ impl Account {
/// client.login(user, "password", None, None).await?;
///
/// if let Some(url) = client.account().get_avatar_url().await? {
/// println!("Your avatar's mxc url is {}", url);
/// println!("Your avatar's mxc url is {url}");
/// }
/// # anyhow::Ok(()) });
/// ```
@@ -258,7 +258,6 @@ impl Account {
///
/// # Example
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{
/// # api::client::{
@@ -307,7 +306,6 @@ impl Account {
///
/// # Example
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{
/// # api::client::{
+3 -2
View File
@@ -342,6 +342,7 @@ impl ClientBuilder {
typing_notice_times: Default::default(),
event_handlers: Default::default(),
event_handler_data: Default::default(),
event_handler_counter: Default::default(),
notification_handlers: Default::default(),
appservice_mode: self.appservice_mode,
respect_login_well_known: self.respect_login_well_known,
@@ -354,12 +355,12 @@ impl ClientBuilder {
fn homeserver_from_name(server_name: &ServerName) -> String {
#[cfg(not(test))]
return format!("https://{}", server_name);
return format!("https://{server_name}");
// Wiremock only knows how to test http endpoints:
// https://github.com/LukeMathWalker/wiremock-rs/issues/58
#[cfg(test)]
return format!("http://{}", server_name);
return format!("http://{server_name}");
}
#[derive(Clone, Debug)]
@@ -224,7 +224,7 @@ where
const SSO_SERVER_BIND_TRIES: u8 = 10;
let homeserver = self.client.homeserver().await;
info!("Logging in to {}", homeserver);
info!("Logging in to {homeserver}");
let (signal_tx, signal_rx) = oneshot::channel();
let (data_tx, data_rx) = oneshot::channel();
+123 -54
View File
@@ -1,5 +1,6 @@
// Copyright 2020 Damir Jelić
// Copyright 2020 The Matrix.org Foundation C.I.C.
// Copyright 2022 Famedly GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -19,7 +20,10 @@ use std::{
future::Future,
io::Read,
pin::Pin,
sync::{Arc, RwLock as StdRwLock},
sync::{
atomic::{AtomicU64, Ordering::SeqCst},
Arc, RwLock as StdRwLock,
},
};
use anymap2::any::CloneAnySendSync;
@@ -80,7 +84,10 @@ use crate::{
attachment::{AttachmentInfo, Thumbnail},
config::RequestConfig,
error::{HttpError, HttpResult},
event_handler::{EventHandler, EventHandlerData, EventHandlerResult, EventKind, SyncEvent},
event_handler::{
EventHandler, EventHandlerData, EventHandlerHandle, EventHandlerResult,
EventHandlerWrapper, EventKind, SyncEvent,
},
http_client::HttpClient,
room, Account, Error, Result,
};
@@ -101,8 +108,8 @@ const DEFAULT_UPLOAD_SPEED: u64 = 125_000;
const MIN_UPLOAD_REQUEST_TIMEOUT: Duration = Duration::from_secs(60 * 5);
type EventHandlerFut = Pin<Box<dyn Future<Output = ()> + Send>>;
type EventHandlerFn = Box<dyn Fn(EventHandlerData<'_>) -> EventHandlerFut + Send + Sync>;
type EventHandlerMap = BTreeMap<(EventKind, &'static str), Vec<EventHandlerFn>>;
pub(crate) type EventHandlerFn = dyn Fn(EventHandlerData<'_>) -> EventHandlerFut + Send + Sync;
type EventHandlerMap = BTreeMap<(EventKind, &'static str), Vec<EventHandlerWrapper>>;
type NotificationHandlerFut = EventHandlerFut;
type NotificationHandlerFn =
@@ -156,6 +163,9 @@ pub(crate) struct ClientInner {
event_handlers: RwLock<EventHandlerMap>,
/// Custom event handler context. See `register_event_handler_context`.
event_handler_data: StdRwLock<AnyMap>,
/// When registering a event handler, the current value is used for the
/// handlers identification, then the counter is incremented.
event_handler_counter: AtomicU64,
/// Notification handlers. See `register_notification_handler`.
notification_handlers: RwLock<Vec<NotificationHandlerFn>>,
/// Whether the client should operate in application service style mode.
@@ -346,9 +356,11 @@ impl Client {
/// "context" arguments: They have to implement [`EventHandlerContext`].
/// This trait is named that way because most of the types implementing it
/// give additional context about an event: The room it was in, its raw form
/// and other similar things. As an exception to this,
/// [`Client`] also implements the `EventHandlerContext` trait
/// so you don't have to clone your client into the event handler manually.
/// and other similar things. As two exceptions to this,
/// [`Client`] and [`EventHandlerHandle`] also implement the
/// `EventHandlerContext` trait so you don't have to clone your client
/// into the event handler manually and a handler can decide to remove
/// itself.
///
/// Some context arguments are not universally applicable. A context
/// argument that isn't available for the given event type will result in
@@ -388,13 +400,15 @@ impl Client {
/// # .build()
/// # .await
/// # .unwrap();
///
/// client
/// .register_event_handler(
/// |ev: SyncRoomMessageEvent, room: Room, client: Client| async move {
/// // Common usage: Room event plus room and client.
/// },
/// )
/// .await
/// .await;
/// client
/// .register_event_handler(
/// |ev: SyncRoomMessageEvent, room: Room, encryption_info: Option<EncryptionInfo>| {
/// async move {
@@ -403,7 +417,8 @@ impl Client {
/// }
/// },
/// )
/// .await
/// .await;
/// client
/// .register_event_handler(|ev: SyncRoomTopicEvent| async move {
/// // You can omit any or all arguments after the first.
/// })
@@ -431,54 +446,118 @@ impl Client {
/// move |ev: SyncRoomMessageEvent | {
/// let data = data.clone();
/// async move {
/// println!("Calling the handler with identifier {}", data);
/// println!("Calling the handler with identifier {data}");
/// }
/// }
/// }).await;
/// # });
/// ```
pub async fn register_event_handler<Ev, Ctx, H>(&self, handler: H) -> &Self
pub async fn register_event_handler<Ev, Ctx, H>(&self, handler: H) -> EventHandlerHandle
where
Ev: SyncEvent + DeserializeOwned + Send + 'static,
H: EventHandler<Ev, Ctx>,
<H::Future as Future>::Output: EventHandlerResult,
{
let event_type = H::ID.1;
self.inner.event_handlers.write().await.entry(H::ID).or_default().push(Box::new(
move |data| {
let maybe_fut = serde_json::from_str(data.raw.get())
.map(|ev| handler.clone().handle_event(ev, data));
let event_type = Ev::TYPE;
let key = (Ev::KIND, Ev::TYPE);
Box::pin(async move {
match maybe_fut {
Ok(Some(fut)) => {
fut.await.print_error(event_type);
}
Ok(None) => {
error!(
"Event handler for {} has an invalid context argument",
event_type
);
}
Err(e) => {
warn!(
"Failed to deserialize `{}` event, skipping event handler.\n\
Deserialization error: {}",
event_type, e,
);
}
let handler_fn: Box<EventHandlerFn> = Box::new(move |data| {
let maybe_fut = serde_json::from_str(data.raw.get())
.map(|ev| handler.clone().handle_event(ev, data));
Box::pin(async move {
match maybe_fut {
Ok(Some(fut)) => {
fut.await.print_error(event_type);
}
})
},
));
Ok(None) => {
error!("Event handler for {} has an invalid context argument", event_type);
}
Err(e) => {
warn!(
"Failed to deserialize `{}` event, skipping event handler.\n\
Deserialization error: {}",
event_type, e,
);
}
}
})
});
self
let handler_id = self.inner.event_handler_counter.fetch_add(1, SeqCst);
let handle = EventHandlerHandle { handler_id, ev_id: key };
self.inner
.event_handlers
.write()
.await
.entry(key)
.or_default()
.push(EventHandlerWrapper { handler_fn, handle });
handle
}
pub(crate) async fn event_handlers(&self) -> RwLockReadGuard<'_, EventHandlerMap> {
self.inner.event_handlers.read().await
}
/// Remove the event handler associated with the handle.
///
/// Note that handlers that remove themselves will still execute
/// with events received in the same sync cycle.
///
/// # Arguments
///
/// `handle` - The [`EventHandlerHandle`] that is returned when
/// registering the event handler with [`Client::register_event_handler`].
///
/// # Examples
///
/// ```
/// # use futures::executor::block_on;
/// # use url::Url;
/// # use tokio::sync::mpsc;
/// #
/// # let homeserver = Url::parse("http://localhost:8080").unwrap();
/// #
/// use matrix_sdk::{
/// ruma::events::room::member::SyncRoomMemberEvent,
/// Client, event_handler::EventHandlerHandle
/// };
/// #
/// # block_on(async {
/// # let client = matrix_sdk::Client::builder()
/// # .homeserver_url(homeserver)
/// # .server_versions([ruma::api::MatrixVersion::V1_0])
/// # .build()
/// # .await
/// # .unwrap();
///
/// client
/// .register_event_handler(
/// |ev: SyncRoomMemberEvent, client: Client, handle: EventHandlerHandle| async move {
/// // Common usage: Check arriving Event is the expected one
/// println!("Expected RoomMemberEvent received!");
/// client.remove_event_handler(handle);
/// },
/// )
/// .await;
/// # });
/// ```
pub async fn remove_event_handler(&self, handle: EventHandlerHandle) {
let mut event_handlers = self.inner.event_handlers.write().await;
if let Some(v) = event_handlers.get_mut(&handle.ev_id) {
v.retain(|e| e.handle.handler_id != handle.handler_id);
if v.is_empty() {
event_handlers.remove(&handle.ev_id);
}
}
}
/// Add an arbitrary value for use as event handler context.
///
/// The value can be obtained in an event handler by adding an argument of
@@ -511,8 +590,8 @@ impl Client {
/// // Handle used to send messages to the UI part of the app
/// let my_gui_handle: SomeType = obtain_gui_handle();
///
/// client.register_event_handler_context(my_gui_handle.clone());
/// client
/// .register_event_handler_context(my_gui_handle.clone())
/// .register_event_handler(
/// |ev: SyncRoomMessageEvent, room: Room, gui_handle: Ctx<SomeType>| async move {
/// // gui_handle.send(DisplayMessage { message: ev });
@@ -521,12 +600,11 @@ impl Client {
/// .await;
/// # });
/// ```
pub fn register_event_handler_context<T>(&self, ctx: T) -> &Self
pub fn register_event_handler_context<T>(&self, ctx: T)
where
T: Clone + Send + Sync + 'static,
{
self.inner.event_handler_data.write().unwrap().insert(ctx);
self
}
pub(crate) fn event_handler_context<T>(&self) -> Option<T>
@@ -725,7 +803,6 @@ impl Client {
/// # Example
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use futures::executor::block_on;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
@@ -742,8 +819,8 @@ impl Client {
/// .await?;
///
/// println!(
/// "Logged in as {}, got device_id {} and access_token {}",
/// user, response.device_id, response.access_token,
/// "Logged in as {user}, got device_id {} and access_token {}",
/// response.device_id, response.access_token,
/// );
/// # anyhow::Ok(()) });
/// ```
@@ -792,7 +869,6 @@ impl Client {
/// # Example
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{assign, DeviceId};
/// # use futures::executor::block_on;
@@ -1061,7 +1137,6 @@ impl Client {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{
/// # api::client::{
@@ -1092,7 +1167,7 @@ impl Client {
registration: impl Into<register::v3::Request<'_>>,
) -> HttpResult<register::v3::Response> {
let homeserver = self.homeserver().await;
info!("Registering to {}", homeserver);
info!("Registering to {homeserver}");
let config = if self.inner.appservice_mode {
Some(RequestConfig::short_retry().force_auth())
@@ -1224,7 +1299,6 @@ impl Client {
/// # Examples
/// ```no_run
/// use matrix_sdk::Client;
/// # use std::convert::TryInto;
/// # use url::Url;
/// # let homeserver = Url::parse("http://example.com").unwrap();
/// # let limit = Some(10);
@@ -1300,7 +1374,6 @@ impl Client {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use url::Url;
/// # use matrix_sdk::Client;
/// # use futures::executor::block_on;
@@ -1415,7 +1488,6 @@ impl Client {
/// # use matrix_sdk::{Client, config::SyncSettings};
/// # use futures::executor::block_on;
/// # use url::Url;
/// # use std::convert::TryFrom;
/// # block_on(async {
/// # let homeserver = Url::parse("http://localhost:8080")?;
/// # let mut client = Client::new(homeserver).await?;
@@ -1498,7 +1570,6 @@ impl Client {
/// # use matrix_sdk::{Client, config::SyncSettings};
/// # use futures::executor::block_on;
/// # use url::Url;
/// # use std::convert::TryFrom;
/// # block_on(async {
/// # let homeserver = Url::parse("http://localhost:8080")?;
/// # let mut client = Client::new(homeserver).await?;
@@ -1546,7 +1617,7 @@ impl Client {
/// # use futures::executor::block_on;
/// # use serde_json::json;
/// # use url::Url;
/// # use std::{collections::BTreeMap, convert::TryFrom};
/// # use std::collections::BTreeMap;
/// # block_on(async {
/// # let homeserver = Url::parse("http://localhost:8080")?;
/// # let mut client = Client::new(homeserver).await?;
@@ -1838,8 +1909,6 @@ impl Client {
if callback(r).await == LoopCtrl::Break {
return;
}
} else {
continue;
}
Client::delay_sync(&mut last_sync_time).await
@@ -81,7 +81,6 @@ impl Device {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::{device_id, user_id}};
/// # use url::Url;
/// # use futures::executor::block_on;
@@ -123,7 +122,6 @@ impl Device {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
@@ -171,7 +169,6 @@ impl Device {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::{device_id, user_id}};
/// # use url::Url;
/// # use futures::executor::block_on;
@@ -229,7 +226,6 @@ impl Device {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
@@ -344,7 +340,6 @@ impl Device {
/// Let's check if a device is verified:
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
@@ -35,7 +35,6 @@
//! Verifying a device is pretty straightforward:
//!
//! ```no_run
//! # use std::convert::TryFrom;
//! # use matrix_sdk::{Client, ruma::{device_id, user_id}};
//! # use url::Url;
//! # use futures::executor::block_on;
@@ -61,7 +60,6 @@
//! Verifying a user identity works largely the same:
//!
//! ```no_run
//! # use std::convert::TryFrom;
//! # use matrix_sdk::{Client, ruma::user_id};
//! # use url::Url;
//! # use futures::executor::block_on;
@@ -91,7 +91,6 @@ impl UserIdentity {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::user_id};
/// # use url::Url;
/// # let alice = user_id!("@alice:example.org");
@@ -143,7 +142,6 @@ impl UserIdentity {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::user_id};
/// # use url::Url;
/// # let alice = user_id!("@alice:example.org");
@@ -195,7 +193,6 @@ impl UserIdentity {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
@@ -274,7 +271,6 @@ impl UserIdentity {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
@@ -318,7 +314,6 @@ impl UserIdentity {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
@@ -358,7 +353,6 @@ impl UserIdentity {
/// # Examples
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{
/// # Client,
/// # ruma::{
+1 -4
View File
@@ -555,7 +555,6 @@ impl Encryption {
/// # Example
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::{device_id, user_id}};
/// # use url::Url;
/// # use futures::executor::block_on;
@@ -600,7 +599,6 @@ impl Encryption {
/// # Example
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::user_id};
/// # use url::Url;
/// # use futures::executor::block_on;
@@ -640,7 +638,6 @@ impl Encryption {
/// # Example
///
/// ```no_run
/// # use std::convert::TryFrom;
/// # use matrix_sdk::{Client, ruma::user_id};
/// # use url::Url;
/// # use futures::executor::block_on;
@@ -691,7 +688,7 @@ impl Encryption {
///
/// # Examples
/// ```no_run
/// # use std::{convert::TryFrom, collections::BTreeMap};
/// # use std::collections::BTreeMap;
/// # use matrix_sdk::{ruma::api::client::uiaa, Client};
/// # use url::Url;
/// # use futures::executor::block_on;
@@ -147,7 +147,7 @@ impl SasVerification {
/// .collect::<Vec<_>>()
/// .join("");
///
/// println!("Do the emojis match?\n{}\n{}", emoji_string, description);
/// println!("Do the emojis match?\n{emoji_string}\n{description}");
/// }
/// # anyhow::Ok(()) });
/// ```
+133 -43
View File
@@ -1,4 +1,5 @@
// Copyright 2021 Jonas Platte
// Copyright 2022 Famedly GmbH
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
@@ -38,8 +39,9 @@ use matrix_sdk_base::deserialized_responses::{EncryptionInfo, SyncRoomEvent};
use ruma::{events::AnySyncStateEvent, serde::Raw};
use serde::Deserialize;
use serde_json::value::RawValue as RawJsonValue;
use tracing::error;
use crate::{room, Client};
use crate::{client::EventHandlerFn, room, Client};
#[doc(hidden)]
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
@@ -80,7 +82,28 @@ impl EventKind {
/// A statically-known event kind/type that can be retrieved from an event sync.
pub trait SyncEvent {
#[doc(hidden)]
const ID: (EventKind, &'static str);
const KIND: EventKind;
#[doc(hidden)]
const TYPE: &'static str;
}
pub(crate) struct EventHandlerWrapper {
pub handler_fn: Box<EventHandlerFn>,
pub handle: EventHandlerHandle,
}
/// Handle to remove a registered event handler by passing it to
/// [`Client::remove_event_handler`].
#[derive(Clone, Copy, Debug)]
pub struct EventHandlerHandle {
pub(crate) ev_id: (EventKind, &'static str),
pub(crate) handler_id: u64,
}
impl EventHandlerContext for EventHandlerHandle {
fn from_data(data: &EventHandlerData<'_>) -> Option<Self> {
Some(data.handle)
}
}
/// Interface for event handlers.
@@ -121,11 +144,6 @@ pub trait EventHandler<Ev, Ctx>: Clone + Send + Sync + 'static {
#[doc(hidden)]
type Future: Future + Send + 'static;
/// The event type being handled, for example a message event of type
/// `m.room.message`.
#[doc(hidden)]
const ID: (EventKind, &'static str);
/// Create a future for handling the given event.
///
/// `data` provides additional data about the event, for example the room it
@@ -143,6 +161,7 @@ pub struct EventHandlerData<'a> {
pub room: Option<room::Room>,
pub raw: &'a RawJsonValue,
pub encryption_info: Option<&'a EncryptionInfo>,
pub handle: EventHandlerHandle,
}
/// Context for an event handler.
@@ -237,14 +256,14 @@ impl<E: fmt::Debug + fmt::Display + 'static> EventHandlerResult for Result<(), E
match self {
#[cfg(feature = "anyhow")]
Err(e) if TypeId::of::<E>() == TypeId::of::<anyhow::Error>() => {
tracing::error!("Event handler for `{}` failed: {:?}", event_type, e);
error!("Event handler for `{event_type}` failed: {e:?}");
}
#[cfg(feature = "eyre")]
Err(e) if TypeId::of::<E>() == TypeId::of::<eyre::Report>() => {
tracing::error!("Event handler for `{}` failed: {:?}", event_type, e);
error!("Event handler for `{event_type}` failed: {e:?}");
}
Err(e) => {
tracing::error!("Event handler for `{}` failed: {}", event_type, e);
error!("Event handler for `{event_type}` failed: {e}");
}
Ok(_) => {}
}
@@ -383,14 +402,15 @@ impl Client {
.get(&event_handler_id)
.into_iter()
.flatten()
.map(|handler| {
.map(|handler_wrapper| {
let data = EventHandlerData {
client: self.clone(),
room: room.clone(),
raw: raw_event.json(),
encryption_info,
handle: handler_wrapper.handle,
};
(handler)(data)
(handler_wrapper.handler_fn)(data)
})
.collect();
@@ -416,7 +436,6 @@ macro_rules! impl_event_handler {
$($ty: EventHandlerContext),*
{
type Future = Fut;
const ID: (EventKind, &'static str) = Ev::ID;
fn handle_event(&self, ev: Ev, _d: EventHandlerData<'_>) -> Option<Self::Future> {
Some((self)(ev, $($ty::from_data(&_d)?),*))
@@ -450,21 +469,24 @@ mod static_events {
where
C: StaticEventContent + GlobalAccountDataEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::GlobalAccountData, C::TYPE);
const KIND: EventKind = EventKind::GlobalAccountData;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::RoomAccountDataEvent<C>
where
C: StaticEventContent + RoomAccountDataEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::RoomAccountData, C::TYPE);
const KIND: EventKind = EventKind::RoomAccountData;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::SyncEphemeralRoomEvent<C>
where
C: StaticEventContent + EphemeralRoomEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::EphemeralRoomData, C::TYPE);
const KIND: EventKind = EventKind::EphemeralRoomData;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::SyncMessageLikeEvent<C>
@@ -472,40 +494,39 @@ mod static_events {
C: StaticEventContent + MessageLikeEventContent + RedactContent,
C::Redacted: MessageLikeEventContent + RedactedEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::MessageLike, C::TYPE);
const KIND: EventKind = EventKind::MessageLike;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::OriginalSyncMessageLikeEvent<C>
where
C: StaticEventContent + MessageLikeEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::OriginalMessageLike, C::TYPE);
const KIND: EventKind = EventKind::OriginalMessageLike;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::RedactedSyncMessageLikeEvent<C>
where
C: StaticEventContent + MessageLikeEventContent + RedactedEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::RedactedMessageLike, C::TYPE);
const KIND: EventKind = EventKind::RedactedMessageLike;
const TYPE: &'static str = C::TYPE;
}
impl SyncEvent for events::room::redaction::SyncRoomRedactionEvent {
const ID: (EventKind, &'static str) =
(EventKind::MessageLike, events::room::redaction::RoomRedactionEventContent::TYPE);
const KIND: EventKind = EventKind::MessageLike;
const TYPE: &'static str = events::room::redaction::RoomRedactionEventContent::TYPE;
}
impl SyncEvent for events::room::redaction::OriginalSyncRoomRedactionEvent {
const ID: (EventKind, &'static str) = (
EventKind::OriginalMessageLike,
events::room::redaction::RoomRedactionEventContent::TYPE,
);
const KIND: EventKind = EventKind::OriginalMessageLike;
const TYPE: &'static str = events::room::redaction::RoomRedactionEventContent::TYPE;
}
impl SyncEvent for events::room::redaction::RedactedSyncRoomRedactionEvent {
const ID: (EventKind, &'static str) = (
EventKind::RedactedMessageLike,
events::room::redaction::RoomRedactionEventContent::TYPE,
);
const KIND: EventKind = EventKind::RedactedMessageLike;
const TYPE: &'static str = events::room::redaction::RoomRedactionEventContent::TYPE;
}
impl<C> SyncEvent for events::SyncStateEvent<C>
@@ -513,46 +534,53 @@ mod static_events {
C: StaticEventContent + StateEventContent + RedactContent,
C::Redacted: StateEventContent + RedactedEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::State, C::TYPE);
const KIND: EventKind = EventKind::State;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::OriginalSyncStateEvent<C>
where
C: StaticEventContent + StateEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::OriginalState, C::TYPE);
const KIND: EventKind = EventKind::OriginalState;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::RedactedSyncStateEvent<C>
where
C: StaticEventContent + StateEventContent + RedactedEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::RedactedState, C::TYPE);
const KIND: EventKind = EventKind::RedactedState;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::StrippedStateEvent<C>
where
C: StaticEventContent + StateEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::StrippedState, C::TYPE);
const KIND: EventKind = EventKind::StrippedState;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::InitialStateEvent<C>
where
C: StaticEventContent + StateEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::InitialState, C::TYPE);
const KIND: EventKind = EventKind::InitialState;
const TYPE: &'static str = C::TYPE;
}
impl<C> SyncEvent for events::ToDeviceEvent<C>
where
C: StaticEventContent + ToDeviceEventContent,
{
const ID: (EventKind, &'static str) = (EventKind::ToDevice, C::TYPE);
const KIND: EventKind = EventKind::ToDevice;
const TYPE: &'static str = C::TYPE;
}
impl SyncEvent for PresenceEvent {
const ID: (EventKind, &'static str) = (EventKind::Presence, PresenceEventContent::TYPE);
const KIND: EventKind = EventKind::Presence;
const TYPE: &'static str = PresenceEventContent::TYPE;
}
}
@@ -567,7 +595,13 @@ mod tests {
EphemeralTestEvent, EventBuilder, StateTestEvent, StrippedStateTestEvent, TimelineTestEvent,
};
use ruma::{
events::room::member::{OriginalSyncRoomMemberEvent, StrippedRoomMemberEvent},
events::{
room::{
member::{OriginalSyncRoomMemberEvent, StrippedRoomMemberEvent},
power_levels::OriginalSyncRoomPowerLevelsEvent,
},
typing::SyncTypingEvent,
},
room_id,
};
use serde_json::json;
@@ -575,7 +609,7 @@ mod tests {
use crate::{room, Client};
#[async_test]
async fn event_handler() -> crate::Result<()> {
async fn register_event_handler() -> crate::Result<()> {
use std::sync::atomic::{AtomicU8, Ordering::SeqCst};
let client = crate::client::tests::logged_in_client(None).await;
@@ -593,23 +627,26 @@ mod tests {
future::ready(())
}
})
.await
.await;
client
.register_event_handler({
let typing_count = typing_count.clone();
move |_ev: OriginalSyncRoomMemberEvent| {
move |_ev: SyncTypingEvent| {
typing_count.fetch_add(1, SeqCst);
future::ready(())
}
})
.await
.await;
client
.register_event_handler({
let power_levels_count = power_levels_count.clone();
move |_ev: OriginalSyncRoomMemberEvent, _client: Client, _room: room::Room| {
move |_ev: OriginalSyncRoomPowerLevelsEvent, _client: Client, _room: room::Room| {
power_levels_count.fetch_add(1, SeqCst);
future::ready(())
}
})
.await
.await;
client
.register_event_handler({
let invited_member_count = invited_member_count.clone();
move |_ev: StrippedRoomMemberEvent| {
@@ -674,4 +711,57 @@ mod tests {
Ok(())
}
#[async_test]
async fn remove_event_handler() -> crate::Result<()> {
use std::sync::atomic::{AtomicU8, Ordering::SeqCst};
let client = crate::client::tests::logged_in_client(None).await;
let member_count = Arc::new(AtomicU8::new(0));
client
.register_event_handler({
let member_count = member_count.clone();
move |_ev: OriginalSyncRoomMemberEvent| {
member_count.fetch_add(1, SeqCst);
future::ready(())
}
})
.await;
let handle = client
.register_event_handler({
move |_ev: OriginalSyncRoomMemberEvent| {
panic!("handler should have been removed");
#[allow(unreachable_code)]
future::ready(())
}
})
.await;
client
.register_event_handler({
let member_count = member_count.clone();
move |_ev: OriginalSyncRoomMemberEvent| {
member_count.fetch_add(1, SeqCst);
future::ready(())
}
})
.await;
let response = EventBuilder::default()
.add_joined_room(
JoinedRoomBuilder::default().add_timeline_event(TimelineTestEvent::Member),
)
.build_sync_response();
client.remove_event_handler(handle).await;
client.process_sync(response).await?;
assert_eq!(member_count.load(SeqCst), 2);
Ok(())
}
}
+1 -2
View File
@@ -12,7 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use std::{any::type_name, convert::TryFrom, fmt::Debug, sync::Arc, time::Duration};
use std::{any::type_name, fmt::Debug, sync::Arc, time::Duration};
use async_trait::async_trait;
use bytes::{Bytes, BytesMut};
@@ -50,7 +50,6 @@ pub trait HttpSend: AsyncTraitDeps {
/// # Examples
///
/// ```
/// use std::convert::TryFrom;
/// use matrix_sdk::{HttpSend, async_trait, HttpError, config::RequestConfig, bytes::Bytes};
///
/// #[derive(Debug)]
+2 -2
View File
@@ -17,10 +17,10 @@
#![warn(missing_debug_implementations, missing_docs)]
#![cfg_attr(docsrs, feature(doc_auto_cfg))]
#[cfg(not(any(feature = "native-tls", feature = "rustls-tls",)))]
#[cfg(not(any(feature = "native-tls", feature = "rustls-tls")))]
compile_error!("one of 'native-tls' or 'rustls-tls' features must be enabled");
#[cfg(all(feature = "native-tls", feature = "rustls-tls",))]
#[cfg(all(feature = "native-tls", feature = "rustls-tls"))]
compile_error!("only one of 'native-tls' or 'rustls-tls' features can be enabled");
#[cfg(all(feature = "sso-login", target_arch = "wasm32"))]
+11 -9
View File
@@ -165,7 +165,6 @@ impl Common {
///
/// # Examples
/// ```no_run
/// # use std::convert::TryFrom;
/// use matrix_sdk::{room::MessagesOptions, Client};
/// # use matrix_sdk::ruma::{
/// # api::client::filter::RoomEventFilter,
@@ -263,7 +262,6 @@ impl Common {
///
/// # Examples
/// ```no_run
/// # use std::convert::TryFrom;
/// use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{
/// # api::client::filter::RoomEventFilter,
@@ -315,8 +313,10 @@ impl Common {
for await item in backward_store {
match item {
Ok(event) => yield Ok(event),
Err(TimelineStreamError::EndCache { fetch_more_token }) => if let Err(error) = room.request_messages(&fetch_more_token).await {
yield Err(error);
Err(TimelineStreamError::EndCache { fetch_more_token }) => {
if let Err(error) = room.request_messages(&fetch_more_token).await {
yield Err(error);
}
},
Err(TimelineStreamError::Store(error)) => yield Err(error.into()),
}
@@ -343,7 +343,6 @@ impl Common {
///
/// # Examples
/// ```no_run
/// # use std::convert::TryFrom;
/// use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{
/// # api::client::filter::RoomEventFilter,
@@ -402,7 +401,6 @@ impl Common {
///
/// # Examples
/// ```no_run
/// # use std::convert::TryFrom;
/// use matrix_sdk::Client;
/// # use matrix_sdk::ruma::{
/// # api::client::filter::RoomEventFilter,
@@ -445,8 +443,10 @@ impl Common {
for await item in backward_store {
match item {
Ok(event) => yield Ok(event),
Err(TimelineStreamError::EndCache { fetch_more_token }) => if let Err(error) = room.request_messages(&fetch_more_token).await {
yield Err(error);
Err(TimelineStreamError::EndCache { fetch_more_token }) => {
if let Err(error) = room.request_messages(&fetch_more_token).await {
yield Err(error);
}
},
Err(TimelineStreamError::Store(error)) => yield Err(error.into()),
}
@@ -1075,7 +1075,9 @@ impl Common {
/// Options for [`messages`][Common::messages].
///
/// See that method and <https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3roomsroomidmessages> for details.
/// See that method and
/// <https://spec.matrix.org/v1.3/client-server-api/#get_matrixclientv3roomsroomidmessages>
/// for details.
#[derive(Debug)]
#[non_exhaustive]
pub struct MessagesOptions<'a> {
+1 -3
View File
@@ -416,7 +416,6 @@ impl Joined {
/// # use url::Url;
/// # use futures::executor::block_on;
/// # use matrix_sdk::ruma::room_id;
/// # use std::convert::TryFrom;
/// # use serde::{Deserialize, Serialize};
/// use matrix_sdk::ruma::{
/// events::{
@@ -513,7 +512,6 @@ impl Joined {
/// # use url::Url;
/// # use futures::executor::block_on;
/// # use matrix_sdk::ruma::room_id;
/// # use std::convert::TryFrom;
/// # block_on(async {
/// # let homeserver = Url::parse("http://localhost:8080")?;
/// # let mut client = Client::new(homeserver).await?;
@@ -557,7 +555,7 @@ impl Joined {
if event_type == "m.reaction" {
debug!(
room_id = %self.room_id(),
"Sending plaintext event because the event type is {}", event_type
"Sending plaintext event because the event type is {event_type}",
);
(Raw::new(&content)?.cast(), event_type)
} else {
+5 -7
View File
@@ -36,7 +36,7 @@ impl Client {
for (room_id, room_info) in &rooms.join {
let room = self.get_room(room_id);
if room.is_none() {
error!("Can't call event handler, room {} not found", room_id);
error!("Can't call event handler, room {room_id} not found");
continue;
}
@@ -53,7 +53,7 @@ impl Client {
for (room_id, room_info) in &rooms.leave {
let room = self.get_room(room_id);
if room.is_none() {
error!("Can't call event handler, room {} not found", room_id);
error!("Can't call event handler, room {room_id} not found");
continue;
}
@@ -68,7 +68,7 @@ impl Client {
for (room_id, room_info) in &rooms.invite {
let room = self.get_room(room_id);
if room.is_none() {
error!("Can't call event handler, room {} not found", room_id);
error!("Can't call event handler, room {room_id} not found");
continue;
}
@@ -88,7 +88,7 @@ impl Client {
let room = match self.get_room(room_id) {
Some(room) => room,
None => {
warn!("Can't call notification handler, room {} not found", room_id);
warn!("Can't call notification handler, room {room_id} not found");
continue;
}
};
@@ -128,9 +128,7 @@ impl Client {
Ok(r)
}
Err(e) => {
error!("Received an invalid response: {}", e);
Self::sleep().await;
error!("Received an invalid response: {e}");
Err(e)
}
}
+6 -6
View File
@@ -1,4 +1,4 @@
use std::{convert::TryFrom, fmt::Debug, sync::Arc};
use std::{fmt::Debug, sync::Arc};
use atty::Stream;
use clap::{Arg, ArgMatches, Command as Argparse};
@@ -66,7 +66,7 @@ impl InspectorHelper {
fn complete_event_types(&self, arg: Option<&&str>) -> Vec<Pair> {
Self::EVENT_TYPES
.iter()
.map(|&t| Pair { display: t.to_owned(), replacement: format!("{} ", t) })
.map(|&t| Pair { display: t.to_owned(), replacement: format!("{t} ") })
.filter(|r| if let Some(arg) = arg { r.replacement.starts_with(arg) } else { true })
.collect()
}
@@ -105,7 +105,7 @@ impl Completer for InspectorHelper {
("get-members", "get all the membership events in the given room"),
]
.iter()
.map(|(r, d)| Pair { display: format!("{} ({})", r, d), replacement: format!("{} ", r) })
.map(|(r, d)| Pair { display: format!("{r} ({d})"), replacement: format!("{r} ") })
.collect();
if args.is_empty() {
@@ -188,13 +188,13 @@ impl Printer {
for line in LinesWithEndings::from(&data) {
let ranges: Vec<(Style, &str)> = h.highlight(line, &self.ps);
let escaped = as_24_bit_terminal_escaped(&ranges[..], false);
print!("{}", escaped);
print!("{escaped}");
}
// Clear the formatting
println!("\x1b[0m");
} else {
println!("{}", data);
println!("{data}");
}
}
}
@@ -318,7 +318,7 @@ impl Inspector {
self.run(m).await;
}
Err(e) => {
println!("{}", e);
println!("{e}");
}
}
}
+1 -1
View File
@@ -300,7 +300,7 @@ fn run_wasm_pack_tests(cmd: Option<WasmFeatureSet>) -> Result<()> {
]);
let run = |(folder, arg_set): (&str, &str)| {
let _p = pushd(format!("crates/{}", folder));
let _p = pushd(format!("crates/{folder}"));
cmd!("pwd").run()?; // print dir so we know what might have failed
cmd!("wasm-pack test --node -- ").args(arg_set.split_whitespace()).run()?;
cmd!("wasm-pack test --firefox --headless --").args(arg_set.split_whitespace()).run()