Merge branch 'main' into mauroromito/ss_once
# Conflicts: # crates/matrix-sdk/src/sliding_sync/mod.rs
This commit is contained in:
commit
9c0e604247
|
@ -15,8 +15,9 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-05-06
|
||||
components: rustfmt
|
||||
|
||||
- name: Run Benchmarks
|
||||
|
|
|
@ -34,9 +34,9 @@ jobs:
|
|||
matrix:
|
||||
name:
|
||||
- no-encryption
|
||||
- no-sled
|
||||
- no-encryption-and-sled
|
||||
- sled-cryptostore
|
||||
- no-sqlite
|
||||
- no-encryption-and-sqlite
|
||||
- sqlite-cryptostore
|
||||
- rustls-tls
|
||||
- markdown
|
||||
- socks
|
||||
|
@ -327,8 +327,9 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-05-06
|
||||
components: rustfmt
|
||||
|
||||
- name: Cargo fmt
|
||||
|
@ -363,8 +364,9 @@ jobs:
|
|||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-05-06
|
||||
components: clippy
|
||||
|
||||
- name: Load cache
|
||||
|
|
|
@ -20,7 +20,9 @@ jobs:
|
|||
uses: actions/checkout@v3
|
||||
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-05-06
|
||||
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
|
|
|
@ -78,8 +78,9 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
if: "${{ !inputs.tag }}"
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-05-06
|
||||
targets: ${{ matrix.target }}
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
|
@ -114,7 +115,9 @@ jobs:
|
|||
- uses: actions/checkout@v3
|
||||
if: "${{ !inputs.tag }}"
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@nightly
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: nightly-2023-05-06
|
||||
- name: Install Node.js
|
||||
uses: actions/setup-node@v3
|
||||
- name: Build lib
|
||||
|
|
|
@ -538,7 +538,7 @@ version = "1.0.0"
|
|||
dependencies = [
|
||||
"criterion",
|
||||
"matrix-sdk-crypto",
|
||||
"matrix-sdk-sled",
|
||||
"matrix-sdk-sqlite",
|
||||
"matrix-sdk-test",
|
||||
"pprof",
|
||||
"ruma",
|
||||
|
@ -2633,7 +2633,7 @@ dependencies = [
|
|||
"matrix-sdk-base",
|
||||
"matrix-sdk-common",
|
||||
"matrix-sdk-indexeddb",
|
||||
"matrix-sdk-sled",
|
||||
"matrix-sdk-sqlite",
|
||||
"matrix-sdk-test",
|
||||
"mime",
|
||||
"mime2ext",
|
||||
|
@ -2859,8 +2859,6 @@ dependencies = [
|
|||
"futures-util",
|
||||
"log-panics",
|
||||
"matrix-sdk",
|
||||
"matrix-sdk-sled",
|
||||
"matrix-sdk-sqlite",
|
||||
"mime",
|
||||
"once_cell",
|
||||
"opentelemetry",
|
||||
|
|
|
@ -10,7 +10,7 @@ publish = false
|
|||
[dependencies]
|
||||
criterion = { version = "0.4.0", features = ["async", "async_tokio", "html_reports"] }
|
||||
matrix-sdk-crypto = { path = "../crates/matrix-sdk-crypto", version = "0.6.0"}
|
||||
matrix-sdk-sled = { path = "../crates/matrix-sdk-sled", version = "0.2.0", default-features = false, features = ["crypto-store"] }
|
||||
matrix-sdk-sqlite = { path = "../crates/matrix-sdk-sqlite", version = "0.1.0", default-features = false, features = ["crypto-store"] }
|
||||
matrix-sdk-test = { path = "../testing/matrix-sdk-test", version = "0.6.0"}
|
||||
ruma = { workspace = true }
|
||||
serde_json = { workspace = true }
|
||||
|
|
|
@ -2,7 +2,7 @@ use std::{ops::Deref, sync::Arc};
|
|||
|
||||
use criterion::*;
|
||||
use matrix_sdk_crypto::{EncryptionSettings, OlmMachine};
|
||||
use matrix_sdk_sled::SledCryptoStore;
|
||||
use matrix_sdk_sqlite::SqliteCryptoStore;
|
||||
use matrix_sdk_test::response_from_file;
|
||||
use ruma::{
|
||||
api::{
|
||||
|
@ -71,11 +71,11 @@ pub fn keys_query(c: &mut Criterion) {
|
|||
});
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
let store = Arc::new(runtime.block_on(SqliteCryptoStore::open(dir.path(), None)).unwrap());
|
||||
let machine =
|
||||
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("sled store", &name), &response, |b, response| {
|
||||
group.bench_with_input(BenchmarkId::new("sqlite store", &name), &response, |b, response| {
|
||||
b.to_async(&runtime)
|
||||
.iter(|| async { machine.mark_request_as_sent(&txn_id, response).await.unwrap() })
|
||||
});
|
||||
|
@ -114,12 +114,12 @@ pub fn keys_claiming(c: &mut Criterion) {
|
|||
)
|
||||
});
|
||||
|
||||
group.bench_with_input(BenchmarkId::new("sled store", &name), &response, |b, response| {
|
||||
group.bench_with_input(BenchmarkId::new("sqlite store", &name), &response, |b, response| {
|
||||
b.iter_batched(
|
||||
|| {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store =
|
||||
Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
Arc::new(runtime.block_on(SqliteCryptoStore::open(dir.path(), None)).unwrap());
|
||||
|
||||
let machine = runtime
|
||||
.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store))
|
||||
|
@ -181,14 +181,14 @@ pub fn room_key_sharing(c: &mut Criterion) {
|
|||
})
|
||||
});
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
let store = Arc::new(runtime.block_on(SqliteCryptoStore::open(dir.path(), None)).unwrap());
|
||||
|
||||
let machine =
|
||||
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
|
||||
runtime.block_on(machine.mark_request_as_sent(&txn_id, &keys_query_response)).unwrap();
|
||||
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
|
||||
|
||||
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
|
||||
group.bench_function(BenchmarkId::new("sqlite store", &name), |b| {
|
||||
b.to_async(&runtime).iter(|| async {
|
||||
let requests = machine
|
||||
.share_room_key(
|
||||
|
@ -236,14 +236,14 @@ pub fn devices_missing_sessions_collecting(c: &mut Criterion) {
|
|||
});
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let store = Arc::new(runtime.block_on(SledCryptoStore::open(dir.path(), None)).unwrap());
|
||||
let store = Arc::new(runtime.block_on(SqliteCryptoStore::open(dir.path(), None)).unwrap());
|
||||
|
||||
let machine =
|
||||
runtime.block_on(OlmMachine::with_store(alice_id(), alice_device_id(), store)).unwrap();
|
||||
|
||||
runtime.block_on(machine.mark_request_as_sent(&txn_id, &response)).unwrap();
|
||||
|
||||
group.bench_function(BenchmarkId::new("sled store", &name), |b| {
|
||||
group.bench_function(BenchmarkId::new("sqlite store", &name), |b| {
|
||||
b.to_async(&runtime).iter(|| async {
|
||||
machine.get_missing_sessions(users.iter().map(Deref::deref)).await.unwrap()
|
||||
})
|
||||
|
|
|
@ -14,7 +14,7 @@ crate-type = ["cdylib", "staticlib"]
|
|||
|
||||
[features]
|
||||
default = ["bundled-sqlite"]
|
||||
bundled-sqlite = ["matrix-sdk-sqlite/bundled"]
|
||||
bundled-sqlite = ["matrix-sdk/bundled-sqlite"]
|
||||
|
||||
[build-dependencies]
|
||||
uniffi = { workspace = true, features = ["build"] }
|
||||
|
@ -27,8 +27,6 @@ eyeball-im = { workspace = true }
|
|||
extension-trait = "1.0.1"
|
||||
futures-core = "0.3.17"
|
||||
futures-util = { version = "0.3.17", default-features = false }
|
||||
matrix-sdk-sqlite = { path = "../../crates/matrix-sdk-sqlite", features = ["crypto-store"] }
|
||||
matrix-sdk-sled = { path = "../../crates/matrix-sdk-sled" }
|
||||
mime = "0.3.16"
|
||||
# FIXME: we currently can't feature flag anything in the api.udl, therefore we must enforce experimental-sliding-sync being exposed here..
|
||||
# see https://github.com/matrix-org/matrix-rust-sdk/issues/1014
|
||||
|
@ -68,6 +66,7 @@ features = [
|
|||
"markdown",
|
||||
"socks",
|
||||
"rustls-tls",
|
||||
"sqlite",
|
||||
]
|
||||
|
||||
[target.'cfg(not(target_os = "android"))'.dependencies.matrix-sdk]
|
||||
|
@ -81,4 +80,5 @@ features = [
|
|||
"markdown",
|
||||
"native-tls",
|
||||
"socks",
|
||||
"sqlite",
|
||||
]
|
||||
|
|
|
@ -91,6 +91,12 @@ callback interface SlidingSyncListRoomItemsObserver {
|
|||
void did_receive_update();
|
||||
};
|
||||
|
||||
interface SlidingSyncList {};
|
||||
|
||||
callback interface SlidingSyncListOnceBuilt {
|
||||
SlidingSyncList update_list(SlidingSyncList list);
|
||||
};
|
||||
|
||||
dictionary CreateRoomParameters {
|
||||
string? name;
|
||||
string? topic = null;
|
||||
|
|
|
@ -2,7 +2,6 @@ use std::{fs, path::PathBuf, sync::Arc};
|
|||
|
||||
use anyhow::anyhow;
|
||||
use matrix_sdk::{
|
||||
config::StoreConfig,
|
||||
ruma::{
|
||||
api::{error::UnknownVersionError, MatrixVersion},
|
||||
ServerName, UserId,
|
||||
|
@ -98,24 +97,7 @@ impl ClientBuilder {
|
|||
let data_path = PathBuf::from(base_path).join(sanitize(username));
|
||||
fs::create_dir_all(&data_path)?;
|
||||
|
||||
let mut state_store =
|
||||
matrix_sdk_sled::SledStateStore::builder().path(data_path.to_owned());
|
||||
|
||||
if let Some(passphrase) = builder.passphrase.as_deref() {
|
||||
state_store = state_store.passphrase(passphrase.to_owned());
|
||||
}
|
||||
|
||||
let state_store = state_store.build()?;
|
||||
|
||||
let crypto_store = RUNTIME.block_on(matrix_sdk_sqlite::SqliteCryptoStore::open(
|
||||
&data_path,
|
||||
builder.passphrase.as_deref(),
|
||||
))?;
|
||||
|
||||
let store_config =
|
||||
StoreConfig::new().state_store(state_store).crypto_store(crypto_store);
|
||||
|
||||
inner_builder = inner_builder.store_config(store_config)
|
||||
inner_builder = inner_builder.sqlite_store(&data_path, builder.passphrase.as_deref());
|
||||
}
|
||||
|
||||
// Determine server either from URL, server name or user ID.
|
||||
|
|
|
@ -107,20 +107,17 @@ pub enum SlidingSyncError {
|
|||
/// The response we've received from the server can't be parsed or doesn't
|
||||
/// match up with the current expectations on the client side. A
|
||||
/// `sync`-restart might be required.
|
||||
BadResponse {
|
||||
msg: String,
|
||||
},
|
||||
BadResponse { msg: String },
|
||||
/// Called `.build()` on a builder type, but the given required field was
|
||||
/// missing.
|
||||
BuildMissingField { msg: String },
|
||||
/// A `SlidingSyncListRequestGenerator` has been used without having been
|
||||
/// initialized. It happens when a response is handled before a request has
|
||||
/// been sent. It usually happens when testing.
|
||||
RequestGeneratorHasNotBeenInitialized {
|
||||
msg: String,
|
||||
},
|
||||
RequestGeneratorHasNotBeenInitialized { msg: String },
|
||||
/// Someone has tried to modify a sliding sync list's ranges, but the
|
||||
/// selected sync mode doesn't allow that.
|
||||
CannotModifyRanges {
|
||||
msg: String,
|
||||
},
|
||||
CannotModifyRanges { msg: String },
|
||||
/// Ranges have a `start` bound greater than `end`.
|
||||
InvalidRange {
|
||||
/// Start bound.
|
||||
|
@ -128,9 +125,10 @@ pub enum SlidingSyncError {
|
|||
/// End bound.
|
||||
end: u32,
|
||||
},
|
||||
Unknown {
|
||||
error: String,
|
||||
},
|
||||
/// The SlidingSync internal channel is broken.
|
||||
InternalChannelIsBroken,
|
||||
/// Unknown or other error.
|
||||
Unknown { error: String },
|
||||
}
|
||||
|
||||
impl From<matrix_sdk::sliding_sync::Error> for SlidingSyncError {
|
||||
|
@ -144,6 +142,7 @@ impl From<matrix_sdk::sliding_sync::Error> for SlidingSyncError {
|
|||
}
|
||||
E::CannotModifyRanges(msg) => Self::CannotModifyRanges { msg },
|
||||
E::InvalidRange { start, end } => Self::InvalidRange { start, end },
|
||||
E::InternalChannelIsBroken => Self::InternalChannelIsBroken,
|
||||
error => Self::Unknown { error: error.to_string() },
|
||||
}
|
||||
}
|
||||
|
@ -220,10 +219,10 @@ impl SlidingSyncRoom {
|
|||
let (items, mut stoppable_spawn) = self.add_timeline_listener_inner(listener)?;
|
||||
let room_id = self.inner.room_id().clone();
|
||||
|
||||
self.runner.subscribe(room_id.clone(), settings.map(Into::into));
|
||||
self.runner.subscribe(room_id.clone(), settings.map(Into::into))?;
|
||||
|
||||
let runner = self.runner.clone();
|
||||
stoppable_spawn.set_finalizer(Box::new(move || runner.unsubscribe(room_id)));
|
||||
stoppable_spawn.set_finalizer(Box::new(move || runner.unsubscribe(room_id).unwrap()));
|
||||
|
||||
Ok(SlidingSyncSubscribeResult { items, task_handle: Arc::new(stoppable_spawn) })
|
||||
}
|
||||
|
@ -478,11 +477,6 @@ impl SlidingSyncListBuilder {
|
|||
Arc::new(builder)
|
||||
}
|
||||
|
||||
pub fn build(self: Arc<Self>) -> Arc<SlidingSyncList> {
|
||||
let builder = unwrap_or_clone_arc(self);
|
||||
Arc::new(builder.inner.build().into())
|
||||
}
|
||||
|
||||
pub fn sort(self: Arc<Self>, sort: Vec<String>) -> Arc<Self> {
|
||||
let mut builder = unwrap_or_clone_arc(self);
|
||||
builder.inner = builder.inner.sort(sort);
|
||||
|
@ -550,9 +544,26 @@ impl SlidingSyncListBuilder {
|
|||
builder.inner = builder.inner.reset_ranges();
|
||||
Arc::new(builder)
|
||||
}
|
||||
|
||||
pub fn once_built(self: Arc<Self>, callback: Box<dyn SlidingSyncListOnceBuilt>) -> Arc<Self> {
|
||||
let mut builder = unwrap_or_clone_arc(self);
|
||||
|
||||
builder.inner = builder.inner.once_built(
|
||||
move |list: matrix_sdk::SlidingSyncList| -> matrix_sdk::SlidingSyncList {
|
||||
let list = callback.update_list(Arc::new(list.into()));
|
||||
|
||||
unwrap_or_clone_arc(list).inner
|
||||
},
|
||||
);
|
||||
Arc::new(builder)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, uniffi::Object)]
|
||||
pub trait SlidingSyncListOnceBuilt: Sync + Send {
|
||||
fn update_list(&self, list: Arc<SlidingSyncList>) -> Arc<SlidingSyncList>;
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct SlidingSyncList {
|
||||
inner: matrix_sdk::SlidingSyncList,
|
||||
}
|
||||
|
@ -685,12 +696,14 @@ impl SlidingSync {
|
|||
room_id: String,
|
||||
settings: Option<RoomSubscription>,
|
||||
) -> Result<(), ClientError> {
|
||||
self.inner.subscribe(room_id.try_into()?, settings.map(Into::into));
|
||||
self.inner.subscribe(room_id.try_into()?, settings.map(Into::into))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn unsubscribe(&self, room_id: String) -> Result<(), ClientError> {
|
||||
self.inner.unsubscribe(room_id.try_into()?);
|
||||
self.inner.unsubscribe(room_id.try_into()?)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -732,27 +745,18 @@ impl SlidingSync {
|
|||
.collect())
|
||||
}
|
||||
|
||||
#[allow(clippy::significant_drop_in_scrutinee)]
|
||||
pub fn get_list(&self, name: String) -> Option<Arc<SlidingSyncList>> {
|
||||
self.inner.list(&name).map(|inner| Arc::new(SlidingSyncList { inner }))
|
||||
pub fn add_list(&self, list_builder: Arc<SlidingSyncListBuilder>) {
|
||||
self.inner.add_list(unwrap_or_clone_arc(list_builder).inner).unwrap();
|
||||
}
|
||||
|
||||
pub fn add_list(&self, list: Arc<SlidingSyncList>) -> Option<Arc<SlidingSyncList>> {
|
||||
self.inner.add_list(list.inner.clone()).map(|inner| Arc::new(SlidingSyncList { inner }))
|
||||
}
|
||||
|
||||
pub fn pop_list(&self, name: String) -> Option<Arc<SlidingSyncList>> {
|
||||
self.inner.pop_list(&name).map(|inner| Arc::new(SlidingSyncList { inner }))
|
||||
pub fn reset_lists(&self) -> Result<(), SlidingSyncError> {
|
||||
self.inner.reset_lists().map_err(Into::into)
|
||||
}
|
||||
|
||||
pub fn add_common_extensions(&self) {
|
||||
self.inner.add_common_extensions();
|
||||
}
|
||||
|
||||
pub fn reset_lists(&self) {
|
||||
self.inner.reset_lists()
|
||||
}
|
||||
|
||||
pub fn sync(&self) -> Arc<TaskHandle> {
|
||||
let inner = self.inner.clone();
|
||||
let client = self.client.clone();
|
||||
|
@ -809,10 +813,9 @@ impl SlidingSyncBuilder {
|
|||
Arc::new(builder)
|
||||
}
|
||||
|
||||
pub fn add_list(self: Arc<Self>, v: Arc<SlidingSyncList>) -> Arc<Self> {
|
||||
pub fn add_list(self: Arc<Self>, list_builder: Arc<SlidingSyncListBuilder>) -> Arc<Self> {
|
||||
let mut builder = unwrap_or_clone_arc(self);
|
||||
let list = unwrap_or_clone_arc(v);
|
||||
builder.inner = builder.inner.add_list(list.inner);
|
||||
builder.inner = builder.inner.add_list(unwrap_or_clone_arc(list_builder).inner);
|
||||
Arc::new(builder)
|
||||
}
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@ coverage:
|
|||
- "crates/matrix-sdk-crypto/"
|
||||
- "crates/matrix-sdk-qrcode/"
|
||||
- "crates/matrix-sdk-sled/"
|
||||
- "crates/matrix-sdk-sqlite/"
|
||||
- "crates/matrix-sdk-store-encryption/"
|
||||
# Coverage of wasm tests isn't supported at the moment,
|
||||
# see rustwasm/wasm-bindgen#2276
|
||||
|
|
|
@ -19,7 +19,7 @@ e2e-encryption = [
|
|||
"matrix-sdk/e2e-encryption"
|
||||
]
|
||||
eyre = ["matrix-sdk/eyre"]
|
||||
sled = ["matrix-sdk/sled"]
|
||||
sqlite = ["matrix-sdk/sqlite"]
|
||||
|
||||
markdown = ["matrix-sdk/markdown"]
|
||||
native-tls = ["matrix-sdk/native-tls"]
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
//! The storage layer for the [`OlmMachine`] can be customized using a trait.
|
||||
//! Implementing your own [`CryptoStore`]
|
||||
//!
|
||||
//! An in-memory only store is provided as well as a Sled based one, depending
|
||||
//! An in-memory only store is provided as well as a SQLite-based one, depending
|
||||
//! on your needs and targets a custom store may be implemented, e.g. for
|
||||
//! `wasm-unknown-unknown` an indexeddb store would be needed
|
||||
//!
|
||||
|
|
|
@ -23,6 +23,10 @@ crypto-store = [
|
|||
"matrix-sdk-base?/e2e-encryption",
|
||||
]
|
||||
|
||||
docsrs = [
|
||||
"crypto-store",
|
||||
]
|
||||
|
||||
[dependencies]
|
||||
async-trait = { workspace = true }
|
||||
fs_extra = "1.2.0"
|
||||
|
|
|
@ -20,7 +20,7 @@ use matrix_sdk_crypto::CryptoStoreError;
|
|||
use thiserror::Error;
|
||||
use tokio::io;
|
||||
|
||||
/// All the errors that can occur when opening a sled store.
|
||||
/// All the errors that can occur when opening a SQLite store.
|
||||
#[derive(Error, Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum OpenStoreError {
|
||||
|
|
|
@ -16,7 +16,10 @@
|
|||
allow(dead_code, unused_imports)
|
||||
)]
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
use deadpool_sqlite::Object as SqliteConn;
|
||||
use matrix_sdk_base::store::StoreConfig;
|
||||
use matrix_sdk_store_encryption::StoreCipher;
|
||||
|
||||
#[cfg(feature = "crypto-store")]
|
||||
|
@ -63,3 +66,26 @@ fn init_logging() {
|
|||
.with(tracing_subscriber::fmt::layer().with_test_writer())
|
||||
.init();
|
||||
}
|
||||
|
||||
/// Create a [`StoreConfig`] with an opened [`SqliteStateStore`] in the given
|
||||
/// directory and using the given passphrase. If the `crypto-store` feature is
|
||||
/// enabled, a [`SqliteCryptoStore`] with the same parameters is also opened.
|
||||
#[cfg(feature = "state-store")]
|
||||
pub async fn make_store_config(
|
||||
path: &Path,
|
||||
passphrase: Option<&str>,
|
||||
) -> Result<StoreConfig, OpenStoreError> {
|
||||
let state_store = SqliteStateStore::open(path, passphrase).await?;
|
||||
let config = StoreConfig::new().state_store(state_store);
|
||||
|
||||
#[cfg(feature = "crypto-store")]
|
||||
{
|
||||
let crypto_store = SqliteCryptoStore::open(path, passphrase).await?;
|
||||
Ok(config.crypto_store(crypto_store))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "crypto-store"))]
|
||||
{
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
- `Common::members` and `Common::members_no_sync` take a `RoomMemberships` to be able to filter the
|
||||
results by any membership state.
|
||||
- `Common::active_members(_no_sync)` and `Common::joined_members(_no_sync)` are deprecated.
|
||||
- `matrix-sdk-sqlite` is the new default store implementation outside of WASM, behind the `sqlite` feature.
|
||||
- The `sled` feature was removed. It is still possible to use `matrix-sdk-sled` as a custom store.
|
||||
|
||||
# 0.6.2
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ rustdoc-args = ["--cfg", "docsrs"]
|
|||
default = [
|
||||
"e2e-encryption",
|
||||
"automatic-room-key-forwarding",
|
||||
"sled",
|
||||
"sqlite",
|
||||
"native-tls",
|
||||
]
|
||||
testing = []
|
||||
|
@ -27,12 +27,13 @@ testing = []
|
|||
e2e-encryption = [
|
||||
"matrix-sdk-base/e2e-encryption",
|
||||
"matrix-sdk-base/automatic-room-key-forwarding",
|
||||
"matrix-sdk-sled?/crypto-store", # activate crypto-store on sled if given
|
||||
"matrix-sdk-sqlite?/crypto-store", # activate crypto-store on sqlite if given
|
||||
"matrix-sdk-indexeddb?/e2e-encryption", # activate on indexeddb if given
|
||||
]
|
||||
js = ["matrix-sdk-common/js", "matrix-sdk-base/js"]
|
||||
|
||||
sled = ["dep:matrix-sdk-sled", "matrix-sdk-sled?/state-store"]
|
||||
sqlite = ["dep:matrix-sdk-sqlite", "matrix-sdk-sqlite?/state-store"]
|
||||
bundled-sqlite = ["sqlite", "matrix-sdk-sqlite?/bundled"]
|
||||
indexeddb = ["dep:matrix-sdk-indexeddb"]
|
||||
|
||||
qrcode = ["e2e-encryption", "matrix-sdk-base/qrcode"]
|
||||
|
@ -57,7 +58,7 @@ experimental-sliding-sync = [
|
|||
|
||||
docsrs = [
|
||||
"e2e-encryption",
|
||||
"sled",
|
||||
"sqlite",
|
||||
"sso-login",
|
||||
"qrcode",
|
||||
"image-proc",
|
||||
|
@ -85,7 +86,7 @@ hyper = { version = "0.14.20", features = ["http1", "http2", "server"], optional
|
|||
matrix-sdk-base = { version = "0.6.0", path = "../matrix-sdk-base", default_features = false }
|
||||
matrix-sdk-common = { version = "0.6.0", path = "../matrix-sdk-common" }
|
||||
matrix-sdk-indexeddb = { version = "0.2.0", path = "../matrix-sdk-indexeddb", default-features = false, optional = true }
|
||||
matrix-sdk-sled = { version = "0.2.0", path = "../matrix-sdk-sled", default-features = false, optional = true }
|
||||
matrix-sdk-sqlite = { version = "0.1.0", path = "../matrix-sdk-sqlite", default-features = false, optional = true }
|
||||
mime = "0.3.16"
|
||||
mime2ext = "0.1.52"
|
||||
once_cell = { workspace = true }
|
||||
|
@ -130,7 +131,7 @@ tokio = { workspace = true }
|
|||
|
||||
[target.'cfg(not(target_arch = "wasm32"))'.dependencies]
|
||||
backoff = { version = "0.4.0", features = ["tokio"] }
|
||||
tokio = { workspace = true, features = ["fs", "rt"] }
|
||||
tokio = { workspace = true, features = ["fs", "rt", "macros"] }
|
||||
|
||||
[dev-dependencies]
|
||||
anyhow = { workspace = true }
|
||||
|
|
|
@ -65,7 +65,8 @@ The following crate feature flags are available:
|
|||
| `js` | No | Enables JavaScript API usage for things like the current system time on WASM (does nothing on other targets) |
|
||||
| `markdown` | No | Support for sending Markdown-formatted messages |
|
||||
| `qrcode` | Yes | QR code verification support |
|
||||
| `sled` | Yes | Persistent storage of state and E2EE data (optionally, if feature `e2e-encryption` is enabled), via Sled |
|
||||
| `sqlite` | Yes | Persistent storage of state and E2EE data (optionally, if feature `e2e-encryption` is enabled), via SQLite available on system |
|
||||
| `bundled-sqlite` | No | Persistent storage of state and E2EE data (optionally, if feature `e2e-encryption` is enabled), via SQLite compiled and bundled with the binary |
|
||||
| `indexeddb` | No | Persistent storage of state and E2EE data (optionally, if feature `e2e-encryption` is enabled) for browsers, via IndexedDB |
|
||||
| `socks` | No | SOCKS support in the default HTTP client, [`reqwest`] |
|
||||
| `sso-login` | No | Support for SSO login with a local HTTP server |
|
||||
|
|
|
@ -118,19 +118,19 @@ impl ClientBuilder {
|
|||
self
|
||||
}
|
||||
|
||||
/// Set up the store configuration for a sled store.
|
||||
/// Set up the store configuration for a SQLite store.
|
||||
///
|
||||
/// This is the same as
|
||||
/// <code>.[store_config](Self::store_config)([matrix_sdk_sled]::[make_store_config](matrix_sdk_sled::make_store_config)(path, passphrase)?)</code>.
|
||||
/// <code>.[store_config](Self::store_config)([matrix_sdk_sqlite]::[make_store_config](matrix_sdk_sqlite::make_store_config)(path, passphrase)?)</code>.
|
||||
/// except it delegates the actual store config creation to when
|
||||
/// `.build().await` is called.
|
||||
#[cfg(feature = "sled")]
|
||||
pub fn sled_store(
|
||||
#[cfg(feature = "sqlite")]
|
||||
pub fn sqlite_store(
|
||||
mut self,
|
||||
path: impl AsRef<std::path::Path>,
|
||||
passphrase: Option<&str>,
|
||||
) -> Self {
|
||||
self.store_config = BuilderStoreConfig::Sled {
|
||||
self.store_config = BuilderStoreConfig::Sqlite {
|
||||
path: path.as_ref().to_owned(),
|
||||
passphrase: passphrase.map(ToOwned::to_owned),
|
||||
};
|
||||
|
@ -342,9 +342,9 @@ impl ClientBuilder {
|
|||
|
||||
#[allow(clippy::infallible_destructuring_match)]
|
||||
let store_config = match self.store_config {
|
||||
#[cfg(feature = "sled")]
|
||||
BuilderStoreConfig::Sled { path, passphrase } => {
|
||||
matrix_sdk_sled::make_store_config(&path, passphrase.as_deref()).await?
|
||||
#[cfg(feature = "sqlite")]
|
||||
BuilderStoreConfig::Sqlite { path, passphrase } => {
|
||||
matrix_sdk_sqlite::make_store_config(&path, passphrase.as_deref()).await?
|
||||
}
|
||||
#[cfg(feature = "indexeddb")]
|
||||
BuilderStoreConfig::IndexedDb { name, passphrase } => {
|
||||
|
@ -480,8 +480,8 @@ impl Default for HttpConfig {
|
|||
|
||||
#[derive(Clone)]
|
||||
enum BuilderStoreConfig {
|
||||
#[cfg(feature = "sled")]
|
||||
Sled {
|
||||
#[cfg(feature = "sqlite")]
|
||||
Sqlite {
|
||||
path: std::path::PathBuf,
|
||||
passphrase: Option<String>,
|
||||
},
|
||||
|
@ -498,9 +498,9 @@ impl fmt::Debug for BuilderStoreConfig {
|
|||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
#[allow(clippy::infallible_destructuring_match)]
|
||||
match self {
|
||||
#[cfg(feature = "sled")]
|
||||
Self::Sled { path, .. } => {
|
||||
f.debug_struct("Sled").field("path", path).finish_non_exhaustive()
|
||||
#[cfg(feature = "sqlite")]
|
||||
Self::Sqlite { path, .. } => {
|
||||
f.debug_struct("Sqlite").field("path", path).finish_non_exhaustive()
|
||||
}
|
||||
#[cfg(feature = "indexeddb")]
|
||||
Self::IndexedDb { name, .. } => {
|
||||
|
@ -535,10 +535,10 @@ pub enum ClientBuildError {
|
|||
#[error(transparent)]
|
||||
IndexeddbStore(#[from] matrix_sdk_indexeddb::OpenStoreError),
|
||||
|
||||
/// Error opening the sled store.
|
||||
#[cfg(feature = "sled")]
|
||||
/// Error opening the sqlite store.
|
||||
#[cfg(feature = "sqlite")]
|
||||
#[error(transparent)]
|
||||
SledStore(#[from] matrix_sdk_sled::OpenStoreError),
|
||||
SqliteStore(#[from] matrix_sdk_sqlite::OpenStoreError),
|
||||
}
|
||||
|
||||
impl ClientBuildError {
|
||||
|
|
|
@ -1672,6 +1672,10 @@ impl Client {
|
|||
/// use [`create_dm`][Self::create_dm], which is more convenient than
|
||||
/// assembling the [`create_room::v3::Request`] yourself.
|
||||
///
|
||||
/// If the `is_direct` field of the request is set to `true` and at least
|
||||
/// one user is invited, the room will be automatically added to the direct
|
||||
/// rooms in the account data.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```no_run
|
||||
|
|
|
@ -216,7 +216,7 @@ is **not** supported using the default store.
|
|||
| Failure | Cause | Fix |
|
||||
| ------------------- | ----- | ----------- |
|
||||
| No messages get encrypted nor decrypted | The `e2e-encryption` feature is disabled | [Enable the feature in your `Cargo.toml` file] |
|
||||
| Messages that were decryptable aren't after a restart | Storage isn't setup to be persistent | Ensure you've activated the persistent storage backend feature, e.g. `sled` |
|
||||
| Messages that were decryptable aren't after a restart | Storage isn't setup to be persistent | Ensure you've activated the persistent storage backend feature, e.g. `sqlite` |
|
||||
| Messages are encrypted but can't be decrypted | The access token that the client is using is tied to another device | Clear storage to create a new device, read the [Restoring a Client] section |
|
||||
| Messages don't get encrypted but get decrypted | The `m.room.encryption` event is missing | Make sure encryption is [enabled] for the room and the event isn't [filtered] out, otherwise it might be a deserialization bug |
|
||||
|
||||
|
|
|
@ -386,6 +386,122 @@ impl Device {
|
|||
self.inner.is_verified()
|
||||
}
|
||||
|
||||
/// Is the device considered to be verified with cross-signing.
|
||||
///
|
||||
/// A device is considered to be verified if it's signed by the appropriate
|
||||
/// cross-signing key.
|
||||
///
|
||||
/// ## Cross-signing verification
|
||||
///
|
||||
/// Cross-signing verification uses signatures over devices and user
|
||||
/// identities to check if a device is considered to be verified. The
|
||||
/// signatures can be uploaded to the homeserver, this allows us to
|
||||
/// share the verification state with other devices. Devices only need to
|
||||
/// verify a user identity, if the user identity has verified and signed
|
||||
/// the device we can consider the device to be verified as well.
|
||||
///
|
||||
/// Devices are usually cross-signing verified using interactive
|
||||
/// verification, which can be started using the
|
||||
/// [`Device::request_verification()`] method.
|
||||
///
|
||||
/// A [`Device`] can also be manually signed using the [`Device::verify()`]
|
||||
/// method, this works only for devices belonging to our own user.
|
||||
///
|
||||
/// Do note that the device that is being manually signed will not trust our
|
||||
/// own user identity like it would if we interactively verify the device.
|
||||
/// Such a device can mark our own user as verified using the
|
||||
/// [`UserIdentity::verify()`] method.
|
||||
///
|
||||
/// ### Verification of devices belonging to our own user.
|
||||
///
|
||||
/// If the device belongs to our own user, the device will be considered to
|
||||
/// be verified if:
|
||||
///
|
||||
/// * The device has been signed by our self-signing key
|
||||
/// * Our own user identity is considered to be [verified]
|
||||
///
|
||||
/// In other words we need to find a valid signature chain from our user
|
||||
/// identity to the device:
|
||||
///
|
||||
///```text
|
||||
/// ┌─────────────────────────────────────┐ ┌─────────────┐
|
||||
/// │ Own User Identity │ │ Device │
|
||||
/// ├──────────────────┬──────────────────┤───►├─────────────┤
|
||||
/// │ Master Key │ Self-signing Key │ │ Device Keys │
|
||||
/// └──────────────────┴──────────────────┘ └─────────────┘
|
||||
/// ```
|
||||
///
|
||||
/// ### Verification of devices belonging to other users.
|
||||
///
|
||||
/// If the device belongs to some other user it will be considered to be
|
||||
/// verified if:
|
||||
///
|
||||
/// * The device has been signed by the user's self-signing key
|
||||
/// * The user's master-signing key has been signed by our own user-signing
|
||||
/// key, i.e. our own identity trusts the other users identity.
|
||||
/// * Our own user identity is considered to be [verified]
|
||||
///
|
||||
/// ```text
|
||||
/// ┌─────────────────────────────────────┐
|
||||
/// │ Own User Identity │
|
||||
/// ├──────────────────┬──────────────────┤─────┐
|
||||
/// │ Master Key │ User-signing Key │ │
|
||||
/// └──────────────────┴──────────────────┘ │
|
||||
/// ┌───────────────────────────────────────────────────┘
|
||||
/// │
|
||||
/// │ ┌─────────────────────────────────────┐ ┌─────────────┐
|
||||
/// │ │ User Identity │ │ Device │
|
||||
/// └──────►├──────────────────┬──────────────────┤───►│─────────────│
|
||||
/// │ Master Key │ Self-signing Key │ │ Device Keys │
|
||||
/// └──────────────────┴──────────────────┘ └─────────────┘
|
||||
/// ```
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// Let's check if a device is verified:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use matrix_sdk::{
|
||||
/// # Client,
|
||||
/// # ruma::{
|
||||
/// # device_id, user_id,
|
||||
/// # events::key::verification::VerificationMethod,
|
||||
/// # }
|
||||
/// # };
|
||||
/// # use url::Url;
|
||||
/// # use futures::executor::block_on;
|
||||
/// # block_on(async {
|
||||
/// # let alice = user_id!("@alice:example.org");
|
||||
/// # let homeserver = Url::parse("http://example.com")?;
|
||||
/// # let client = Client::new(homeserver).await?;
|
||||
/// let device =
|
||||
/// client.encryption().get_device(alice, device_id!("DEVICEID")).await?;
|
||||
///
|
||||
/// if let Some(device) = device {
|
||||
/// if device.is_verified_with_cross_signing() {
|
||||
/// println!(
|
||||
/// "Device {} of user {} is verified with cross-signing",
|
||||
/// device.device_id(),
|
||||
/// device.user_id()
|
||||
/// );
|
||||
/// } else {
|
||||
/// println!(
|
||||
/// "Device {} of user {} is not verified with cross-signing",
|
||||
/// device.device_id(),
|
||||
/// device.user_id()
|
||||
/// );
|
||||
/// }
|
||||
/// }
|
||||
/// # anyhow::Ok(()) });
|
||||
/// ```
|
||||
///
|
||||
/// [`UserIdentity::verify()`]:
|
||||
/// crate::encryption::identities::UserIdentity::verify
|
||||
/// [verified]: crate::encryption::identities::UserIdentity::is_verified
|
||||
pub fn is_verified_with_cross_signing(&self) -> bool {
|
||||
self.inner.is_cross_signing_trusted()
|
||||
}
|
||||
|
||||
/// Set the local trust state of the device to the given state.
|
||||
///
|
||||
/// This won't affect any cross signing verification state, this only sets
|
||||
|
|
|
@ -434,8 +434,7 @@ let full_sync_list = SlidingSyncList::builder(&full_sync_list_name)
|
|||
(StateEventType::RoomEncryption, "".to_owned())
|
||||
]) // only want to know if the room is encrypted
|
||||
.full_sync_batch_size(50) // grow the window by 50 items at a time
|
||||
.full_sync_maximum_number_of_rooms_to_fetch(500) // only sync up the top 500 rooms
|
||||
.build();
|
||||
.full_sync_maximum_number_of_rooms_to_fetch(500); // only sync up the top 500 rooms
|
||||
|
||||
let active_list = SlidingSyncList::builder(&active_list_name) // the active window
|
||||
.sync_mode(SlidingSyncMode::Selective) // sync up the specific range only
|
||||
|
@ -446,8 +445,7 @@ let active_list = SlidingSyncList::builder(&active_list_name) // the active wind
|
|||
(StateEventType::RoomEncryption, "".to_owned()), // is it encrypted
|
||||
(StateEventType::RoomTopic, "".to_owned()), // any topic if known
|
||||
(StateEventType::RoomAvatar, "".to_owned()), // avatar if set
|
||||
])
|
||||
.build();
|
||||
]);
|
||||
|
||||
let sliding_sync = sliding_sync_builder
|
||||
.add_list(active_list)
|
||||
|
@ -457,10 +455,9 @@ let sliding_sync = sliding_sync_builder
|
|||
|
||||
// subscribe to the list APIs for updates
|
||||
|
||||
let active_list = sliding_sync.list(&active_list_name).unwrap();
|
||||
let list_state_stream = active_list.state_stream();
|
||||
let list_count_stream = active_list.maximum_number_of_rooms_stream();
|
||||
let list_stream = active_list.room_list_stream();
|
||||
let (list_state_stream, list_count_stream, list_stream) = sliding_sync.on_list(&active_list_name, |list| {
|
||||
(list.state_stream(), list.maximum_number_of_rooms_stream(), list.room_list_stream())
|
||||
}).unwrap();
|
||||
|
||||
tokio::spawn(async move {
|
||||
pin_mut!(list_state_stream);
|
||||
|
|
|
@ -13,10 +13,11 @@ use ruma::{
|
|||
events::TimelineEventType,
|
||||
OwnedRoomId,
|
||||
};
|
||||
use tokio::sync::{mpsc::channel, RwLock as AsyncRwLock};
|
||||
use url::Url;
|
||||
|
||||
use super::{
|
||||
cache::restore_sliding_sync_state, SlidingSync, SlidingSyncInner, SlidingSyncList,
|
||||
cache::restore_sliding_sync_state, SlidingSync, SlidingSyncInner, SlidingSyncListBuilder,
|
||||
SlidingSyncPositionMarkers, SlidingSyncRoom,
|
||||
};
|
||||
use crate::{Client, Result};
|
||||
|
@ -25,12 +26,12 @@ use crate::{Client, Result};
|
|||
///
|
||||
/// Get a new builder with methods like [`crate::Client::sliding_sync`], or
|
||||
/// [`crate::SlidingSync::builder`].
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct SlidingSyncBuilder {
|
||||
storage_key: Option<String>,
|
||||
homeserver: Option<Url>,
|
||||
client: Client,
|
||||
lists: BTreeMap<String, SlidingSyncList>,
|
||||
lists: Vec<SlidingSyncListBuilder>,
|
||||
bump_event_types: Vec<TimelineEventType>,
|
||||
extensions: Option<ExtensionsConfig>,
|
||||
subscriptions: BTreeMap<OwnedRoomId, v4::RoomSubscription>,
|
||||
|
@ -42,7 +43,7 @@ impl SlidingSyncBuilder {
|
|||
storage_key: None,
|
||||
homeserver: None,
|
||||
client,
|
||||
lists: BTreeMap::new(),
|
||||
lists: Vec::new(),
|
||||
bump_event_types: Vec::new(),
|
||||
extensions: None,
|
||||
subscriptions: BTreeMap::new(),
|
||||
|
@ -64,9 +65,8 @@ impl SlidingSyncBuilder {
|
|||
/// Add the given list to the lists.
|
||||
///
|
||||
/// Replace any list with the name.
|
||||
pub fn add_list(mut self, list: SlidingSyncList) -> Self {
|
||||
self.lists.insert(list.name().to_owned(), list);
|
||||
|
||||
pub fn add_list(mut self, list_builder: SlidingSyncListBuilder) -> Self {
|
||||
self.lists.push(list_builder);
|
||||
self
|
||||
}
|
||||
|
||||
|
@ -206,12 +206,22 @@ impl SlidingSyncBuilder {
|
|||
let mut delta_token = None;
|
||||
let mut rooms_found: BTreeMap<OwnedRoomId, SlidingSyncRoom> = BTreeMap::new();
|
||||
|
||||
let (internal_channel_sender, internal_channel_receiver) = channel(8);
|
||||
|
||||
let mut lists = BTreeMap::new();
|
||||
|
||||
for list_builder in self.lists {
|
||||
let list = list_builder.build(internal_channel_sender.clone());
|
||||
|
||||
lists.insert(list.name().to_owned(), list);
|
||||
}
|
||||
|
||||
// Load an existing state from the cache.
|
||||
if let Some(storage_key) = &self.storage_key {
|
||||
restore_sliding_sync_state(
|
||||
&client,
|
||||
storage_key,
|
||||
&mut self.lists,
|
||||
&mut lists,
|
||||
&mut delta_token,
|
||||
&mut rooms_found,
|
||||
&mut self.extensions,
|
||||
|
@ -220,7 +230,7 @@ impl SlidingSyncBuilder {
|
|||
}
|
||||
|
||||
let rooms = StdRwLock::new(rooms_found);
|
||||
let lists = StdRwLock::new(self.lists);
|
||||
let lists = StdRwLock::new(lists);
|
||||
|
||||
Ok(SlidingSync::new(SlidingSyncInner {
|
||||
homeserver: self.homeserver,
|
||||
|
@ -241,6 +251,11 @@ impl SlidingSyncBuilder {
|
|||
|
||||
subscriptions: StdRwLock::new(self.subscriptions),
|
||||
unsubscribe: Default::default(),
|
||||
|
||||
internal_channel: (
|
||||
internal_channel_sender,
|
||||
AsyncRwLock::new(internal_channel_receiver),
|
||||
),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -244,7 +244,7 @@ mod tests {
|
|||
.sliding_sync()
|
||||
.await
|
||||
.storage_key(Some("hello".to_owned()))
|
||||
.add_list(SlidingSyncList::builder("list_foo").build())
|
||||
.add_list(SlidingSyncList::builder("list_foo"))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
|
@ -278,7 +278,7 @@ mod tests {
|
|||
.sliding_sync()
|
||||
.await
|
||||
.storage_key(Some("hello".to_owned()))
|
||||
.add_list(SlidingSyncList::builder("list_foo").build())
|
||||
.add_list(SlidingSyncList::builder("list_foo"))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
|
|
|
@ -29,4 +29,7 @@ pub enum Error {
|
|||
/// End bound.
|
||||
end: u32,
|
||||
},
|
||||
/// The internal channel of `SlidingSync` seems to be broken.
|
||||
#[error("SlidingSync's internal channel is broken")]
|
||||
InternalChannelIsBroken,
|
||||
}
|
||||
|
|
|
@ -1,24 +1,26 @@
|
|||
//! Builder for [`SlidingSyncList`].
|
||||
|
||||
use std::{
|
||||
fmt::Debug,
|
||||
convert::identity,
|
||||
fmt,
|
||||
sync::{Arc, RwLock as StdRwLock},
|
||||
};
|
||||
|
||||
use eyeball::unique::Observable;
|
||||
use eyeball_im::ObservableVector;
|
||||
use ruma::{api::client::sync::sync_events::v4, events::StateEventType, UInt};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
|
||||
use super::{
|
||||
SlidingSyncList, SlidingSyncListInner, SlidingSyncListRequestGenerator, SlidingSyncMode,
|
||||
SlidingSyncState,
|
||||
super::SlidingSyncInternalMessage, SlidingSyncList, SlidingSyncListInner,
|
||||
SlidingSyncListRequestGenerator, SlidingSyncMode, SlidingSyncState,
|
||||
};
|
||||
|
||||
/// The default name for the full sync list.
|
||||
pub const FULL_SYNC_LIST_NAME: &str = "full-sync";
|
||||
|
||||
/// Builder for [`SlidingSyncList`].
|
||||
#[derive(Clone, Debug)]
|
||||
#[derive(Clone)]
|
||||
pub struct SlidingSyncListBuilder {
|
||||
sync_mode: SlidingSyncMode,
|
||||
sort: Vec<String>,
|
||||
|
@ -29,6 +31,28 @@ pub struct SlidingSyncListBuilder {
|
|||
timeline_limit: Option<UInt>,
|
||||
name: String,
|
||||
ranges: Vec<(UInt, UInt)>,
|
||||
once_built: Arc<Box<dyn Fn(SlidingSyncList) -> SlidingSyncList + Send + Sync>>,
|
||||
}
|
||||
|
||||
// Print debug values for the builder, except `once_built` which is ignored.
|
||||
impl fmt::Debug for SlidingSyncListBuilder {
|
||||
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
formatter
|
||||
.debug_struct("SlidingSyncListBuilder")
|
||||
.field("sync_mode", &self.sync_mode)
|
||||
.field("sort", &self.sort)
|
||||
.field("required_state", &self.required_state)
|
||||
.field("full_sync_batch_size", &self.full_sync_batch_size)
|
||||
.field(
|
||||
"full_sync_maximum_number_of_rooms_to_fetch",
|
||||
&self.full_sync_maximum_number_of_rooms_to_fetch,
|
||||
)
|
||||
.field("filters", &self.filters)
|
||||
.field("timeline_limit", &self.timeline_limit)
|
||||
.field("name", &self.name)
|
||||
.field("ranges", &self.ranges)
|
||||
.finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl SlidingSyncListBuilder {
|
||||
|
@ -46,9 +70,19 @@ impl SlidingSyncListBuilder {
|
|||
timeline_limit: None,
|
||||
name: name.into(),
|
||||
ranges: Vec::new(),
|
||||
once_built: Arc::new(Box::new(identity)),
|
||||
}
|
||||
}
|
||||
|
||||
/// foo
|
||||
pub fn once_built<C>(mut self, callback: C) -> Self
|
||||
where
|
||||
C: Fn(SlidingSyncList) -> SlidingSyncList + Send + Sync + 'static,
|
||||
{
|
||||
self.once_built = Arc::new(Box::new(callback));
|
||||
self
|
||||
}
|
||||
|
||||
/// Create a Builder set up for full sync.
|
||||
pub fn default_with_fullsync() -> Self {
|
||||
Self::new(FULL_SYNC_LIST_NAME).sync_mode(SlidingSyncMode::Paging)
|
||||
|
@ -133,7 +167,10 @@ impl SlidingSyncListBuilder {
|
|||
}
|
||||
|
||||
/// Build the list.
|
||||
pub fn build(self) -> SlidingSyncList {
|
||||
pub(in super::super) fn build(
|
||||
self,
|
||||
sliding_sync_internal_channel_sender: Sender<SlidingSyncInternalMessage>,
|
||||
) -> SlidingSyncList {
|
||||
let request_generator = match &self.sync_mode {
|
||||
SlidingSyncMode::Paging => SlidingSyncListRequestGenerator::new_paging(
|
||||
self.full_sync_batch_size,
|
||||
|
@ -148,7 +185,7 @@ impl SlidingSyncListBuilder {
|
|||
SlidingSyncMode::Selective => SlidingSyncListRequestGenerator::new_selective(),
|
||||
};
|
||||
|
||||
SlidingSyncList {
|
||||
let list = SlidingSyncList {
|
||||
inner: Arc::new(SlidingSyncListInner {
|
||||
// From the builder
|
||||
sync_mode: self.sync_mode,
|
||||
|
@ -166,7 +203,13 @@ impl SlidingSyncListBuilder {
|
|||
state: StdRwLock::new(Observable::new(SlidingSyncState::default())),
|
||||
maximum_number_of_rooms: StdRwLock::new(Observable::new(None)),
|
||||
room_list: StdRwLock::new(ObservableVector::new()),
|
||||
|
||||
sliding_sync_internal_channel_sender,
|
||||
}),
|
||||
}
|
||||
};
|
||||
|
||||
let once_built = self.once_built;
|
||||
|
||||
once_built(list)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,9 +22,10 @@ pub(super) use request_generator::*;
|
|||
pub use room_list_entry::RoomListEntry;
|
||||
use ruma::{api::client::sync::sync_events::v4, assign, events::StateEventType, OwnedRoomId, UInt};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::sync::mpsc::Sender;
|
||||
use tracing::{instrument, warn};
|
||||
|
||||
use super::{Error, FrozenSlidingSyncRoom, SlidingSyncRoom};
|
||||
use super::{Error, FrozenSlidingSyncRoom, SlidingSyncInternalMessage, SlidingSyncRoom};
|
||||
use crate::Result;
|
||||
|
||||
/// Holding a specific filtered list within the concept of sliding sync.
|
||||
|
@ -72,7 +73,7 @@ impl SlidingSyncList {
|
|||
}
|
||||
|
||||
self.inner.set_ranges(ranges);
|
||||
self.reset();
|
||||
self.reset()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -87,7 +88,7 @@ impl SlidingSyncList {
|
|||
}
|
||||
|
||||
self.inner.set_ranges(&[(start, end)]);
|
||||
self.reset();
|
||||
self.reset()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -102,7 +103,7 @@ impl SlidingSyncList {
|
|||
}
|
||||
|
||||
self.inner.add_range((start, end));
|
||||
self.reset();
|
||||
self.reset()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -119,7 +120,7 @@ impl SlidingSyncList {
|
|||
}
|
||||
|
||||
self.inner.set_ranges::<UInt>(&[]);
|
||||
self.reset();
|
||||
self.reset()?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
@ -219,8 +220,16 @@ impl SlidingSyncList {
|
|||
}
|
||||
|
||||
// Reset `Self`.
|
||||
pub(super) fn reset(&self) {
|
||||
pub(super) fn reset(&self) -> Result<(), Error> {
|
||||
self.inner.reset();
|
||||
|
||||
// When a list is reset, the sync loop must be “restarted”.
|
||||
self.inner
|
||||
.sliding_sync_internal_channel_sender
|
||||
.blocking_send(SlidingSyncInternalMessage::ContinueSyncLoop)
|
||||
.map_err(|_| Error::InternalChannelIsBroken)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -275,6 +284,8 @@ pub(super) struct SlidingSyncListInner {
|
|||
/// The request generator, i.e. a type that yields the appropriate list
|
||||
/// request. See [`SlidingSyncListRequestGenerator`] to learn more.
|
||||
request_generator: StdRwLock<SlidingSyncListRequestGenerator>,
|
||||
|
||||
sliding_sync_internal_channel_sender: Sender<SlidingSyncInternalMessage>,
|
||||
}
|
||||
|
||||
impl SlidingSyncListInner {
|
||||
|
@ -871,11 +882,19 @@ impl SlidingSyncMode {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use std::{
|
||||
cell::Cell,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use futures::StreamExt;
|
||||
use imbl::vector;
|
||||
use ruma::{api::client::sync::sync_events::v4::SlidingOp, room_id, uint};
|
||||
use serde_json::json;
|
||||
use tokio::{spawn, sync::mpsc::unbounded_channel};
|
||||
use tokio::{
|
||||
spawn,
|
||||
sync::mpsc::{channel, unbounded_channel},
|
||||
};
|
||||
|
||||
use super::*;
|
||||
|
||||
|
@ -902,10 +921,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sliding_sync_list_set_ranges() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 1), (2, 3)].to_vec())
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
{
|
||||
let lock = list.inner.ranges.read().unwrap();
|
||||
|
@ -926,12 +947,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sliding_sync_list_set_range() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
// Set range on `Selective`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 1), (2, 3)].to_vec())
|
||||
.build();
|
||||
.build(sender.clone());
|
||||
|
||||
{
|
||||
let lock = list.inner.ranges.read().unwrap();
|
||||
|
@ -952,14 +975,17 @@ mod tests {
|
|||
|
||||
// Set range on `Growing`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Growing).build();
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Growing)
|
||||
.build(sender.clone());
|
||||
|
||||
assert!(list.set_range(4u32, 5).is_err());
|
||||
}
|
||||
|
||||
// Set range on `Paging`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Paging).build();
|
||||
let list =
|
||||
SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Paging).build(sender);
|
||||
|
||||
assert!(list.set_range(4u32, 5).is_err());
|
||||
}
|
||||
|
@ -967,12 +993,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sliding_sync_list_add_range() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
// Add range on `Selective`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 1)].to_vec())
|
||||
.build();
|
||||
.build(sender.clone());
|
||||
|
||||
{
|
||||
let lock = list.inner.ranges.read().unwrap();
|
||||
|
@ -993,14 +1021,17 @@ mod tests {
|
|||
|
||||
// Add range on `Growing`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Growing).build();
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Growing)
|
||||
.build(sender.clone());
|
||||
|
||||
assert!(list.add_range((2u32, 3)).is_err());
|
||||
}
|
||||
|
||||
// Add range on `Paging`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Paging).build();
|
||||
let list =
|
||||
SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Paging).build(sender);
|
||||
|
||||
assert!(list.add_range((2u32, 3)).is_err());
|
||||
}
|
||||
|
@ -1008,12 +1039,14 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sliding_sync_list_reset_ranges() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
// Reset ranges on `Selective`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 1)].to_vec())
|
||||
.build();
|
||||
.build(sender.clone());
|
||||
|
||||
{
|
||||
let lock = list.inner.ranges.read().unwrap();
|
||||
|
@ -1034,14 +1067,17 @@ mod tests {
|
|||
|
||||
// Reset range on `Growing`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Growing).build();
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Growing)
|
||||
.build(sender.clone());
|
||||
|
||||
assert!(list.reset_ranges().is_err());
|
||||
}
|
||||
|
||||
// Reset range on `Paging`.
|
||||
{
|
||||
let list = SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Paging).build();
|
||||
let list =
|
||||
SlidingSyncList::builder("foo").sync_mode(SlidingSyncMode::Paging).build(sender);
|
||||
|
||||
assert!(list.reset_ranges().is_err());
|
||||
}
|
||||
|
@ -1049,11 +1085,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sliding_sync_list_timeline_limit() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 1)].to_vec())
|
||||
.timeline_limit(7u32)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
{
|
||||
let lock = list.inner.timeline_limit.read().unwrap();
|
||||
|
@ -1083,10 +1121,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_sliding_sync_get_room_id() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.add_range(0u32, 1)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
let room0 = room_id!("!room0:bar.org");
|
||||
let room1 = room_id!("!room1:bar.org");
|
||||
|
@ -1158,10 +1198,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_generator_paging_full_sync() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("testing")
|
||||
.sync_mode(crate::SlidingSyncMode::Paging)
|
||||
.full_sync_batch_size(10)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_ranges! {
|
||||
list = list,
|
||||
|
@ -1199,11 +1241,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_generator_paging_full_sync_with_a_maximum_number_of_rooms_to_fetch() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("testing")
|
||||
.sync_mode(crate::SlidingSyncMode::Paging)
|
||||
.full_sync_batch_size(10)
|
||||
.full_sync_maximum_number_of_rooms_to_fetch(22)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_ranges! {
|
||||
list = list,
|
||||
|
@ -1241,10 +1285,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_generator_growing_full_sync() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("testing")
|
||||
.sync_mode(crate::SlidingSyncMode::Growing)
|
||||
.full_sync_batch_size(10)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_ranges! {
|
||||
list = list,
|
||||
|
@ -1282,11 +1328,13 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_generator_growing_full_sync_with_a_maximum_number_of_rooms_to_fetch() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("testing")
|
||||
.sync_mode(crate::SlidingSyncMode::Growing)
|
||||
.full_sync_batch_size(10)
|
||||
.full_sync_maximum_number_of_rooms_to_fetch(22)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_ranges! {
|
||||
list = list,
|
||||
|
@ -1324,10 +1372,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_generator_selective() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("testing")
|
||||
.sync_mode(crate::SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 10), (42, 153)].to_vec())
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_ranges! {
|
||||
list = list,
|
||||
|
@ -1355,10 +1405,12 @@ mod tests {
|
|||
|
||||
#[test]
|
||||
fn test_generator_selective_with_modifying_ranges_on_the_fly() {
|
||||
let (sender, _receiver) = channel(4);
|
||||
|
||||
let mut list = SlidingSyncList::builder("testing")
|
||||
.sync_mode(crate::SlidingSyncMode::Selective)
|
||||
.ranges(ranges![(0, 10), (42, 153)].to_vec())
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_ranges! {
|
||||
list = list,
|
||||
|
@ -1439,10 +1491,12 @@ mod tests {
|
|||
#[tokio::test]
|
||||
#[allow(clippy::await_holding_lock)]
|
||||
async fn test_sliding_sync_inner_update_state_room_list_and_maximum_number_of_rooms() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let mut list = SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.add_range(0u32, 3)
|
||||
.build();
|
||||
.build(sender);
|
||||
|
||||
assert_eq!(**list.inner.maximum_number_of_rooms.read().unwrap(), None);
|
||||
assert_eq!(list.inner.room_list.read().unwrap().len(), 0);
|
||||
|
@ -2093,4 +2147,24 @@ mod tests {
|
|||
room_list = [F("!r0:x.y"), F("!r1:x.y"), F("!r2:x.y")],
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_once_built() {
|
||||
let (sender, _receiver) = channel(1);
|
||||
|
||||
let probe = Arc::new(Mutex::new(Cell::new(false)));
|
||||
let probe_clone = probe.clone();
|
||||
|
||||
let _list = SlidingSyncList::builder("testing")
|
||||
.once_built(move |list| {
|
||||
let mut probe_lock = probe.lock().unwrap();
|
||||
*probe_lock.get_mut() = true;
|
||||
|
||||
list
|
||||
})
|
||||
.build(sender);
|
||||
|
||||
let probe_lock = probe_clone.lock().unwrap();
|
||||
assert!(probe_lock.get());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ mod list;
|
|||
mod room;
|
||||
|
||||
use std::{
|
||||
borrow::BorrowMut,
|
||||
collections::BTreeMap,
|
||||
fmt::Debug,
|
||||
mem,
|
||||
|
@ -34,6 +33,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use async_stream::stream;
|
||||
pub use builder::*;
|
||||
pub use client::*;
|
||||
pub use error::*;
|
||||
|
@ -52,7 +52,13 @@ use ruma::{
|
|||
OwnedRoomId, RoomId,
|
||||
};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tokio::{spawn, sync::Mutex as AsyncMutex};
|
||||
use tokio::{
|
||||
select, spawn,
|
||||
sync::{
|
||||
mpsc::{Receiver, Sender},
|
||||
Mutex as AsyncMutex, RwLock as AsyncRwLock,
|
||||
},
|
||||
};
|
||||
use tracing::{debug, error, info_span, instrument, warn, Instrument, Span};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
@ -113,6 +119,9 @@ pub(super) struct SlidingSyncInner {
|
|||
/// The intended state of the extensions being supplied to sliding /sync
|
||||
/// calls. May contain the latest next_batch for to_devices, etc.
|
||||
extensions: Mutex<Option<ExtensionsConfig>>,
|
||||
|
||||
internal_channel:
|
||||
(Sender<SlidingSyncInternalMessage>, AsyncRwLock<Receiver<SlidingSyncInternalMessage>>),
|
||||
}
|
||||
|
||||
impl SlidingSync {
|
||||
|
@ -130,23 +139,35 @@ impl SlidingSync {
|
|||
}
|
||||
|
||||
/// Subscribe to a given room.
|
||||
///
|
||||
/// Note: this does not cancel any pending request, so make sure to only
|
||||
/// poll the stream after you've altered this. If you do that during, it
|
||||
/// might take one round trip to take effect.
|
||||
pub fn subscribe(&self, room_id: OwnedRoomId, settings: Option<v4::RoomSubscription>) {
|
||||
pub fn subscribe(
|
||||
&self,
|
||||
room_id: OwnedRoomId,
|
||||
settings: Option<v4::RoomSubscription>,
|
||||
) -> Result<()> {
|
||||
self.inner.subscriptions.write().unwrap().insert(room_id, settings.unwrap_or_default());
|
||||
|
||||
self.inner
|
||||
.internal_channel
|
||||
.0
|
||||
.blocking_send(SlidingSyncInternalMessage::ContinueSyncLoop)
|
||||
.map_err(|_| Error::InternalChannelIsBroken)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Unsubscribe from a given room.
|
||||
///
|
||||
/// Note: this does not cancel any pending request, so make sure to only
|
||||
/// poll the stream after you've altered this. If you do that during, it
|
||||
/// might take one round trip to take effect.
|
||||
pub fn unsubscribe(&self, room_id: OwnedRoomId) {
|
||||
pub fn unsubscribe(&self, room_id: OwnedRoomId) -> Result<()> {
|
||||
if self.inner.subscriptions.write().unwrap().remove(&room_id).is_some() {
|
||||
self.inner.unsubscribe.write().unwrap().push(room_id);
|
||||
|
||||
self.inner
|
||||
.internal_channel
|
||||
.0
|
||||
.blocking_send(SlidingSyncInternalMessage::ContinueSyncLoop)
|
||||
.map_err(|_| Error::InternalChannelIsBroken)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Add the common extensions if not already configured.
|
||||
|
@ -190,23 +211,14 @@ impl SlidingSync {
|
|||
.since = Some(since);
|
||||
}
|
||||
|
||||
/// Get access to the SlidingSyncList named `list_name`.
|
||||
///
|
||||
/// Note: Remember that this list might have been changed since you started
|
||||
/// listening to the stream and is therefor not necessarily up to date
|
||||
/// with the lists used for the stream.
|
||||
pub fn list(&self, list_name: &str) -> Option<SlidingSyncList> {
|
||||
self.inner.lists.read().unwrap().get(list_name).cloned()
|
||||
}
|
||||
/// Find a list by its name, and do something on it if it exists.
|
||||
pub fn on_list<F, R>(&self, list_name: &str, f: F) -> Option<R>
|
||||
where
|
||||
F: FnOnce(&SlidingSyncList) -> R,
|
||||
{
|
||||
let lists = self.inner.lists.read().unwrap();
|
||||
|
||||
/// Remove the SlidingSyncList named `list_name` from the lists list if
|
||||
/// found.
|
||||
///
|
||||
/// Note: Remember that this change will only be applicable for any new
|
||||
/// stream created after this. The old stream will still continue to use the
|
||||
/// previous set of lists.
|
||||
pub fn pop_list(&self, list_name: &String) -> Option<SlidingSyncList> {
|
||||
self.inner.lists.write().unwrap().remove(list_name)
|
||||
lists.get(list_name).map(f)
|
||||
}
|
||||
|
||||
/// Add the list to the list of lists.
|
||||
|
@ -214,12 +226,13 @@ impl SlidingSync {
|
|||
/// As lists need to have a unique `.name`, if a list with the same name
|
||||
/// is found the new list will replace the old one and the return it or
|
||||
/// `None`.
|
||||
///
|
||||
/// Note: Remember that this change will only be applicable for any new
|
||||
/// stream created after this. The old stream will still continue to use the
|
||||
/// previous set of lists.
|
||||
pub fn add_list(&self, list: SlidingSyncList) -> Option<SlidingSyncList> {
|
||||
self.inner.lists.write().unwrap().insert(list.name().to_owned(), list)
|
||||
pub fn add_list(
|
||||
&self,
|
||||
list_builder: SlidingSyncListBuilder,
|
||||
) -> Result<Option<SlidingSyncList>> {
|
||||
let list = list_builder.build(self.inner.internal_channel.0.clone());
|
||||
|
||||
Ok(self.inner.lists.write().unwrap().insert(list.name().to_owned(), list))
|
||||
}
|
||||
|
||||
/// Lookup a set of rooms
|
||||
|
@ -277,12 +290,11 @@ impl SlidingSync {
|
|||
}
|
||||
|
||||
/// Handle the HTTP response.
|
||||
#[instrument(skip_all, fields(lists = lists.len()))]
|
||||
#[instrument(skip_all, fields(lists = self.inner.lists.read().unwrap().len()))]
|
||||
fn handle_response(
|
||||
&self,
|
||||
sliding_sync_response: v4::Response,
|
||||
mut sync_response: SyncResponse,
|
||||
lists: &mut BTreeMap<String, SlidingSyncList>,
|
||||
) -> Result<UpdateSummary, crate::Error> {
|
||||
{
|
||||
debug!(
|
||||
|
@ -338,6 +350,8 @@ impl SlidingSync {
|
|||
// Update the lists.
|
||||
let mut updated_lists = Vec::with_capacity(sliding_sync_response.lists.len());
|
||||
|
||||
let mut lists = self.inner.lists.write().unwrap();
|
||||
|
||||
for (name, updates) in sliding_sync_response.lists {
|
||||
let Some(list) = lists.get_mut(&name) else {
|
||||
error!("Response for list `{name}` - unknown to us; skipping");
|
||||
|
@ -367,65 +381,68 @@ impl SlidingSync {
|
|||
/// Sync once just to update the E2E encryption keys.
|
||||
pub async fn sync_once_for_keys(&self) -> Result<()> {
|
||||
let stream_id = Uuid::new_v4().to_string();
|
||||
let keys_list = SlidingSyncList::builder("sync_once_for_keys").build();
|
||||
let keys_list = SlidingSyncListBuilder::default_with_fullsync();
|
||||
self.add_list(keys_list);
|
||||
let lists = Arc::new(Mutex::new(self.inner.lists.read().unwrap().clone()));
|
||||
self.sync_once(&stream_id, lists).await?;
|
||||
self.sync_once(&stream_id).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn sync_once(
|
||||
&self,
|
||||
stream_id: &str,
|
||||
lists: Arc<Mutex<BTreeMap<String, SlidingSyncList>>>,
|
||||
) -> Result<Option<UpdateSummary>> {
|
||||
let mut requests_lists = BTreeMap::new();
|
||||
async fn sync_once(&self, stream_id: &str) -> Result<Option<UpdateSummary>> {
|
||||
let (request, request_config) = {
|
||||
// Collect requests for lists.
|
||||
let mut requests_lists = BTreeMap::new();
|
||||
|
||||
{
|
||||
let mut lists_lock = lists.lock().unwrap();
|
||||
let lists = lists_lock.borrow_mut();
|
||||
{
|
||||
let mut lists = self.inner.lists.write().unwrap();
|
||||
|
||||
if lists.is_empty() {
|
||||
return Ok(None);
|
||||
if lists.is_empty() {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
for (name, list) in lists.iter_mut() {
|
||||
requests_lists.insert(name.clone(), list.next_request()?);
|
||||
}
|
||||
}
|
||||
|
||||
for (name, list) in lists.iter_mut() {
|
||||
requests_lists.insert(name.clone(), list.next_request()?);
|
||||
}
|
||||
}
|
||||
// Collect the `pos` and `delta_token`.
|
||||
let (pos, delta_token) = {
|
||||
let position_lock = self.inner.position.read().unwrap();
|
||||
|
||||
let (pos, delta_token) = {
|
||||
let position_lock = self.inner.position.read().unwrap();
|
||||
(position_lock.pos.clone(), position_lock.delta_token.clone())
|
||||
};
|
||||
|
||||
(position_lock.pos.clone(), position_lock.delta_token.clone())
|
||||
// Collect other data.
|
||||
let room_subscriptions = self.inner.subscriptions.read().unwrap().clone();
|
||||
let unsubscribe_rooms = mem::take(&mut *self.inner.unsubscribe.write().unwrap());
|
||||
let timeout = Duration::from_secs(30);
|
||||
let extensions = self.prepare_extension_config(pos.as_deref());
|
||||
|
||||
(
|
||||
// Build the request itself.
|
||||
assign!(v4::Request::new(), {
|
||||
pos,
|
||||
delta_token,
|
||||
// We want to track whether the incoming response maps to this
|
||||
// request. We use the (optional) `txn_id` field for that.
|
||||
txn_id: Some(stream_id.to_owned()),
|
||||
timeout: Some(timeout),
|
||||
lists: requests_lists,
|
||||
bump_event_types: self.inner.bump_event_types.clone(),
|
||||
room_subscriptions,
|
||||
unsubscribe_rooms,
|
||||
extensions,
|
||||
}),
|
||||
// Configure long-polling. We need 30 seconds for the long-poll itself, in
|
||||
// addition to 30 more extra seconds for the network delays.
|
||||
RequestConfig::default().timeout(timeout + Duration::from_secs(30)),
|
||||
)
|
||||
};
|
||||
|
||||
let room_subscriptions = self.inner.subscriptions.read().unwrap().clone();
|
||||
let unsubscribe_rooms = mem::take(&mut *self.inner.unsubscribe.write().unwrap());
|
||||
let timeout = Duration::from_secs(30);
|
||||
let extensions = self.prepare_extension_config(pos.as_deref());
|
||||
|
||||
debug!("Sending the sliding sync request");
|
||||
|
||||
// Configure long-polling. We need 30 seconds for the long-poll itself, in
|
||||
// addition to 30 more extra seconds for the network delays.
|
||||
let request_config = RequestConfig::default().timeout(timeout + Duration::from_secs(30));
|
||||
|
||||
// Prepare the request.
|
||||
let request = self.inner.client.send_with_homeserver(
|
||||
assign!(v4::Request::new(), {
|
||||
pos,
|
||||
delta_token,
|
||||
// We want to track whether the incoming response maps to this
|
||||
// request. We use the (optional) `txn_id` field for that.
|
||||
txn_id: Some(stream_id.to_owned()),
|
||||
timeout: Some(timeout),
|
||||
lists: requests_lists,
|
||||
bump_event_types: self.inner.bump_event_types.clone(),
|
||||
room_subscriptions,
|
||||
unsubscribe_rooms,
|
||||
extensions,
|
||||
}),
|
||||
request,
|
||||
Some(request_config),
|
||||
self.inner.homeserver.as_ref().map(ToString::to_string),
|
||||
);
|
||||
|
@ -508,7 +525,7 @@ impl SlidingSync {
|
|||
|
||||
debug!(?sync_response, "Sliding Sync response has been handled by the client");
|
||||
|
||||
let updates = this.handle_response(response, sync_response, lists.lock().unwrap().borrow_mut())?;
|
||||
let updates = this.handle_response(response, sync_response)?;
|
||||
|
||||
this.cache_to_storage().await?;
|
||||
|
||||
|
@ -528,9 +545,6 @@ impl SlidingSync {
|
|||
#[allow(unknown_lints, clippy::let_with_type_underscore)] // triggered by instrument macro
|
||||
#[instrument(name = "sync_stream", skip_all)]
|
||||
pub fn stream(&self) -> impl Stream<Item = Result<UpdateSummary, crate::Error>> + '_ {
|
||||
// Copy all the lists.
|
||||
let lists = Arc::new(Mutex::new(self.inner.lists.read().unwrap().clone()));
|
||||
|
||||
// Define a stream ID.
|
||||
let stream_id = Uuid::new_v4().to_string();
|
||||
|
||||
|
@ -538,7 +552,7 @@ impl SlidingSync {
|
|||
|
||||
let instrument_span = Span::current();
|
||||
|
||||
async_stream::stream! {
|
||||
stream! {
|
||||
loop {
|
||||
let sync_span = info_span!(parent: &instrument_span, "sync_once");
|
||||
|
||||
|
@ -546,49 +560,72 @@ impl SlidingSync {
|
|||
debug!(?self.inner.extensions, "Sync stream loop is running");
|
||||
});
|
||||
|
||||
match self.sync_once(&stream_id, lists.clone()).instrument(sync_span.clone()).await {
|
||||
Ok(Some(updates)) => {
|
||||
self.inner.reset_counter.store(0, Ordering::SeqCst);
|
||||
let mut internal_channel_receiver_lock = self.inner.internal_channel.1.write().await;
|
||||
|
||||
yield Ok(updates);
|
||||
}
|
||||
select! {
|
||||
biased;
|
||||
|
||||
Ok(None) => {
|
||||
break;
|
||||
}
|
||||
|
||||
Err(error) => {
|
||||
if error.client_api_error_kind() == Some(&ErrorKind::UnknownPos) {
|
||||
// The session has expired.
|
||||
|
||||
// Has it expired too many times?
|
||||
if self.inner.reset_counter.fetch_add(1, Ordering::SeqCst) >= MAXIMUM_SLIDING_SYNC_SESSION_EXPIRATION {
|
||||
sync_span.in_scope(|| error!("Session expired {MAXIMUM_SLIDING_SYNC_SESSION_EXPIRATION} times in a row"));
|
||||
|
||||
// The session has expired too many times, let's raise an error!
|
||||
yield Err(error);
|
||||
internal_message = internal_channel_receiver_lock.recv() => {
|
||||
use SlidingSyncInternalMessage::*;
|
||||
|
||||
match internal_message {
|
||||
None | Some(BreakSyncLoop) => {
|
||||
break;
|
||||
}
|
||||
|
||||
// Let's reset the Sliding Sync session.
|
||||
sync_span.in_scope(|| {
|
||||
warn!("Session expired. Restarting Sliding Sync.");
|
||||
Some(ContinueSyncLoop) => {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// To “restart” a Sliding Sync session, we set `pos` to its initial value.
|
||||
{
|
||||
let mut position_lock = self.inner.position.write().unwrap();
|
||||
update_summary = self.sync_once(&stream_id).instrument(sync_span.clone()) => {
|
||||
match update_summary {
|
||||
Ok(Some(updates)) => {
|
||||
self.inner.reset_counter.store(0, Ordering::SeqCst);
|
||||
|
||||
Observable::set(&mut position_lock.pos, None);
|
||||
yield Ok(updates);
|
||||
}
|
||||
|
||||
Ok(None) => {
|
||||
break;
|
||||
}
|
||||
|
||||
Err(error) => {
|
||||
if error.client_api_error_kind() == Some(&ErrorKind::UnknownPos) {
|
||||
// The session has expired.
|
||||
|
||||
// Has it expired too many times?
|
||||
if self.inner.reset_counter.fetch_add(1, Ordering::SeqCst) >= MAXIMUM_SLIDING_SYNC_SESSION_EXPIRATION {
|
||||
sync_span.in_scope(|| error!("Session expired {MAXIMUM_SLIDING_SYNC_SESSION_EXPIRATION} times in a row"));
|
||||
|
||||
// The session has expired too many times, let's raise an error!
|
||||
yield Err(error);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
// Let's reset the Sliding Sync session.
|
||||
sync_span.in_scope(|| {
|
||||
warn!("Session expired. Restarting Sliding Sync.");
|
||||
|
||||
// To “restart” a Sliding Sync session, we set `pos` to its initial value.
|
||||
{
|
||||
let mut position_lock = self.inner.position.write().unwrap();
|
||||
|
||||
Observable::set(&mut position_lock.pos, None);
|
||||
}
|
||||
|
||||
debug!(?self.inner.extensions, "Sliding Sync has been reset");
|
||||
});
|
||||
}
|
||||
|
||||
debug!(?self.inner.extensions, "Sliding Sync has been reset");
|
||||
});
|
||||
yield Err(error);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
yield Err(error);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -596,15 +633,24 @@ impl SlidingSync {
|
|||
}
|
||||
|
||||
/// Resets the lists.
|
||||
pub fn reset_lists(&self) {
|
||||
pub fn reset_lists(&self) -> Result<(), Error> {
|
||||
let lists = self.inner.lists.read().unwrap();
|
||||
|
||||
for (_, list) in lists.iter() {
|
||||
list.reset();
|
||||
list.reset()?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
enum SlidingSyncInternalMessage {
|
||||
#[allow(unused)] // temporary
|
||||
BreakSyncLoop,
|
||||
ContinueSyncLoop,
|
||||
}
|
||||
|
||||
#[cfg(any(test, feature = "testing"))]
|
||||
impl SlidingSync {
|
||||
/// Get a copy of the `pos` value.
|
||||
|
|
|
@ -92,7 +92,7 @@ async fn restore_session(session_file: &Path) -> anyhow::Result<(Client, Option<
|
|||
// Build the client with the previous settings from the session.
|
||||
let client = Client::builder()
|
||||
.homeserver_url(client_session.homeserver)
|
||||
.sled_store(client_session.db_path, Some(&client_session.passphrase))
|
||||
.sqlite_store(client_session.db_path, Some(&client_session.passphrase))
|
||||
.build()
|
||||
.await?;
|
||||
|
||||
|
@ -165,7 +165,7 @@ async fn build_client(data_dir: &Path) -> anyhow::Result<(Client, ClientSession)
|
|||
|
||||
// Generating a subfolder for the database is not mandatory, but it is useful if
|
||||
// you allow several clients to run at the same time. Each one must have a
|
||||
// separate database, which is a different folder with the sled store.
|
||||
// separate database, which is a different folder with the SQLite store.
|
||||
let db_subfolder: String =
|
||||
(&mut rng).sample_iter(Alphanumeric).take(7).map(char::from).collect();
|
||||
let db_path = data_dir.join(db_subfolder);
|
||||
|
@ -186,10 +186,10 @@ async fn build_client(data_dir: &Path) -> anyhow::Result<(Client, ClientSession)
|
|||
|
||||
match Client::builder()
|
||||
.homeserver_url(&homeserver)
|
||||
// We use the sled store, which is enabled by default. This is the crucial part to
|
||||
// We use the SQLite store, which is enabled by default. This is the crucial part to
|
||||
// persist the encryption setup.
|
||||
// Note that other store backends are available and you an even implement your own.
|
||||
.sled_store(&db_path, Some(&passphrase))
|
||||
// Note that other store backends are available and you can even implement your own.
|
||||
.sqlite_store(&db_path, Some(&passphrase))
|
||||
.build()
|
||||
.await
|
||||
{
|
||||
|
|
|
@ -18,5 +18,5 @@ url = "2.2.2"
|
|||
|
||||
[dependencies.matrix-sdk]
|
||||
path = "../../crates/matrix-sdk"
|
||||
features = ["experimental-timeline", "sled"]
|
||||
features = ["experimental-timeline"]
|
||||
version = "0.6.0"
|
||||
|
|
|
@ -36,7 +36,7 @@ pub fn test_server_conf() -> (String, String) {
|
|||
)
|
||||
}
|
||||
|
||||
pub async fn get_client_for_user(username: String, use_sled_store: bool) -> Result<Client> {
|
||||
pub async fn get_client_for_user(username: String, use_sqlite_store: bool) -> Result<Client> {
|
||||
let mut users = USERS.lock().await;
|
||||
if let Some((client, _)) = users.get(&username) {
|
||||
return Ok(client.clone());
|
||||
|
@ -50,8 +50,8 @@ pub async fn get_client_for_user(username: String, use_sled_store: bool) -> Resu
|
|||
.user_agent("matrix-sdk-integation-tests")
|
||||
.homeserver_url(homeserver_url)
|
||||
.request_config(RequestConfig::short_retry());
|
||||
let client = if use_sled_store {
|
||||
client_builder.sled_store(tmp_dir.path(), None).build().await?
|
||||
let client = if use_sqlite_store {
|
||||
client_builder.sqlite_store(tmp_dir.path(), None).build().await?
|
||||
} else {
|
||||
client_builder.build().await?
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ use crate::helpers::get_client_for_user;
|
|||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||
async fn test_repeated_join_leave() -> Result<()> {
|
||||
let peter = get_client_for_user("peter".to_owned(), true).await?;
|
||||
// FIXME: Run once with memory, once with sled
|
||||
// FIXME: Run once with memory, once with SQLite
|
||||
let karl = get_client_for_user("karl".to_owned(), false).await?;
|
||||
let karl_id = karl.user_id().expect("karl has a userid!").to_owned();
|
||||
|
||||
|
|
|
@ -1,36 +1,17 @@
|
|||
#![cfg(test)]
|
||||
|
||||
use std::{
|
||||
iter::{once, repeat},
|
||||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use anyhow::{bail, Context};
|
||||
use assert_matches::assert_matches;
|
||||
use eyeball_im::VectorDiff;
|
||||
use anyhow::Context;
|
||||
use futures::{pin_mut, stream::StreamExt};
|
||||
use matrix_sdk::{
|
||||
ruma::{
|
||||
api::client::{
|
||||
error::ErrorKind as RumaError,
|
||||
receipt::create_receipt::v3::ReceiptType as CreateReceiptType,
|
||||
room::create_room::v3::Request as CreateRoomRequest,
|
||||
sync::sync_events::v4::ReceiptsConfig,
|
||||
},
|
||||
events::{
|
||||
receipt::{ReceiptThread, ReceiptType},
|
||||
room::message::RoomMessageEventContent,
|
||||
},
|
||||
uint,
|
||||
},
|
||||
Client, RoomListEntry, SlidingSyncBuilder, SlidingSyncList, SlidingSyncMode, SlidingSyncState,
|
||||
};
|
||||
use matrix_sdk::{Client, RoomListEntry, SlidingSyncBuilder, SlidingSyncList, SlidingSyncMode};
|
||||
use matrix_sdk_integration_testing::helpers::get_client_for_user;
|
||||
|
||||
async fn setup(name: String, use_sled_store: bool) -> anyhow::Result<(Client, SlidingSyncBuilder)> {
|
||||
async fn setup(
|
||||
name: String,
|
||||
use_sqlite_store: bool,
|
||||
) -> anyhow::Result<(Client, SlidingSyncBuilder)> {
|
||||
let sliding_sync_proxy_url =
|
||||
option_env!("SLIDING_SYNC_PROXY_URL").unwrap_or("http://localhost:8338").to_owned();
|
||||
let client = get_client_for_user(name, use_sled_store).await?;
|
||||
let client = get_client_for_user(name, use_sqlite_store).await?;
|
||||
let sliding_sync_builder = client
|
||||
.sliding_sync()
|
||||
.await
|
||||
|
@ -39,33 +20,6 @@ async fn setup(name: String, use_sled_store: bool) -> anyhow::Result<(Client, Sl
|
|||
Ok((client, sliding_sync_builder))
|
||||
}
|
||||
|
||||
async fn random_setup_with_rooms(
|
||||
number_of_rooms: usize,
|
||||
) -> anyhow::Result<(Client, SlidingSyncBuilder)> {
|
||||
random_setup_with_rooms_opt_store(number_of_rooms, false).await
|
||||
}
|
||||
|
||||
async fn random_setup_with_rooms_opt_store(
|
||||
number_of_rooms: usize,
|
||||
use_sled_store: bool,
|
||||
) -> anyhow::Result<(Client, SlidingSyncBuilder)> {
|
||||
let namespace = uuid::Uuid::new_v4().to_string();
|
||||
let (client, sliding_sync_builder) = setup(namespace.clone(), use_sled_store).await?;
|
||||
|
||||
for room_num in 0..number_of_rooms {
|
||||
make_room(&client, format!("{namespace}-{room_num}")).await?
|
||||
}
|
||||
|
||||
Ok((client, sliding_sync_builder))
|
||||
}
|
||||
|
||||
async fn make_room(client: &Client, room_name: String) -> anyhow::Result<()> {
|
||||
let mut request = CreateRoomRequest::new();
|
||||
request.name = Some(room_name);
|
||||
let _event_id = client.create_room(request).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[derive(PartialEq, Eq, Clone, Debug)]
|
||||
enum RoomListEntryEasy {
|
||||
Empty,
|
||||
|
@ -91,8 +45,7 @@ async fn it_works_smoke_test() -> anyhow::Result<()> {
|
|||
SlidingSyncList::builder("foo")
|
||||
.sync_mode(SlidingSyncMode::Selective)
|
||||
.add_range(0u32, 10)
|
||||
.timeline_limit(0u32)
|
||||
.build(),
|
||||
.timeline_limit(0u32),
|
||||
)
|
||||
.build()
|
||||
.await?;
|
||||
|
@ -105,6 +58,7 @@ async fn it_works_smoke_test() -> anyhow::Result<()> {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
/*
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 4)]
|
||||
async fn modifying_timeline_limit() -> anyhow::Result<()> {
|
||||
let (client, sync_builder) = random_setup_with_rooms(1).await?;
|
||||
|
@ -443,9 +397,9 @@ async fn live_lists() -> anyhow::Result<()> {
|
|||
// we only heard about the ones we had asked for
|
||||
assert_eq!(summary.lists, [list_name_1, list_name_2, list_name_3]);
|
||||
|
||||
let Some(list_2) = sync_proxy.pop_list(&list_name_2.to_owned()) else {
|
||||
bail!("Room exists");
|
||||
};
|
||||
let Some(list_2) = sync_proxy.get_list(&list_name_2.to_owned()) else {
|
||||
bail!("Room exists");
|
||||
};
|
||||
|
||||
// we need to restart the stream after every list listing update
|
||||
let stream = sync_proxy.stream();
|
||||
|
@ -1305,3 +1259,4 @@ async fn receipts_extension_works() -> anyhow::Result<()> {
|
|||
assert!(found_receipt);
|
||||
Ok(())
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -5,15 +5,16 @@ use xshell::{cmd, pushd};
|
|||
|
||||
use crate::{build_docs, workspace, DenyWarnings, Result};
|
||||
|
||||
const NIGHTLY: &str = "nightly-2023-05-06";
|
||||
const WASM_TIMEOUT_ENV_KEY: &str = "WASM_BINDGEN_TEST_TIMEOUT";
|
||||
const WASM_TIMEOUT_VALUE: &str = "120";
|
||||
|
||||
#[derive(Args)]
|
||||
pub struct CiArgs {
|
||||
#[clap(subcommand)]
|
||||
cmd: Option<CiCommand>,
|
||||
}
|
||||
|
||||
const WASM_TIMEOUT_ENV_KEY: &str = "WASM_BINDGEN_TEST_TIMEOUT";
|
||||
const WASM_TIMEOUT_VALUE: &str = "120";
|
||||
|
||||
#[derive(Subcommand)]
|
||||
enum CiCommand {
|
||||
/// Check style
|
||||
|
@ -62,9 +63,9 @@ enum CiCommand {
|
|||
#[derive(Subcommand, PartialEq, Eq, PartialOrd, Ord)]
|
||||
enum FeatureSet {
|
||||
NoEncryption,
|
||||
NoSled,
|
||||
NoEncryptionAndSled,
|
||||
SledCryptostore,
|
||||
NoSqlite,
|
||||
NoEncryptionAndSqlite,
|
||||
SqliteCryptostore,
|
||||
RustlsTls,
|
||||
Markdown,
|
||||
Socks,
|
||||
|
@ -156,7 +157,7 @@ fn check_examples() -> Result<()> {
|
|||
}
|
||||
|
||||
fn check_style() -> Result<()> {
|
||||
cmd!("rustup run nightly cargo fmt -- --check").run()?;
|
||||
cmd!("rustup run {NIGHTLY} cargo fmt -- --check").run()?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
@ -168,9 +169,9 @@ fn check_typos() -> Result<()> {
|
|||
}
|
||||
|
||||
fn check_clippy() -> Result<()> {
|
||||
cmd!("rustup run nightly cargo clippy --all-targets -- -D warnings").run()?;
|
||||
cmd!("rustup run {NIGHTLY} cargo clippy --all-targets -- -D warnings").run()?;
|
||||
cmd!(
|
||||
"rustup run nightly cargo clippy --workspace --all-targets
|
||||
"rustup run {NIGHTLY} cargo clippy --workspace --all-targets
|
||||
--exclude matrix-sdk-crypto --exclude xtask
|
||||
--no-default-features
|
||||
--features native-tls,experimental-sliding-sync,sso-login,experimental-timeline
|
||||
|
@ -178,7 +179,7 @@ fn check_clippy() -> Result<()> {
|
|||
)
|
||||
.run()?;
|
||||
cmd!(
|
||||
"rustup run nightly cargo clippy --all-targets -p matrix-sdk-crypto
|
||||
"rustup run {NIGHTLY} cargo clippy --all-targets -p matrix-sdk-crypto
|
||||
--no-default-features -- -D warnings"
|
||||
)
|
||||
.run()?;
|
||||
|
@ -193,13 +194,13 @@ fn run_feature_tests(cmd: Option<FeatureSet>) -> Result<()> {
|
|||
let args = BTreeMap::from([
|
||||
(
|
||||
FeatureSet::NoEncryption,
|
||||
"--no-default-features --features sled,native-tls,experimental-sliding-sync",
|
||||
"--no-default-features --features sqlite,native-tls,experimental-sliding-sync",
|
||||
),
|
||||
(FeatureSet::NoSled, "--no-default-features --features e2e-encryption,native-tls"),
|
||||
(FeatureSet::NoEncryptionAndSled, "--no-default-features --features native-tls"),
|
||||
(FeatureSet::NoSqlite, "--no-default-features --features e2e-encryption,native-tls"),
|
||||
(FeatureSet::NoEncryptionAndSqlite, "--no-default-features --features native-tls"),
|
||||
(
|
||||
FeatureSet::SledCryptostore,
|
||||
"--no-default-features --features e2e-encryption,sled,native-tls",
|
||||
FeatureSet::SqliteCryptostore,
|
||||
"--no-default-features --features e2e-encryption,sqlite,native-tls",
|
||||
),
|
||||
(FeatureSet::RustlsTls, "--no-default-features --features rustls-tls"),
|
||||
(FeatureSet::Markdown, "--features markdown"),
|
||||
|
|
Loading…
Reference in New Issue