diff --git a/matrix_sdk_crypto/src/store/memorystore.rs b/matrix_sdk_crypto/src/store/memorystore.rs index c06614b7..05746102 100644 --- a/matrix_sdk_crypto/src/store/memorystore.rs +++ b/matrix_sdk_crypto/src/store/memorystore.rs @@ -172,8 +172,9 @@ impl CryptoStore for MemoryStore { Ok(()) } - async fn remove_value(&self, key: &str) -> Result> { - Ok(self.values.remove(key).map(|(_, v)| v)) + async fn remove_value(&self, key: &str) -> Result<()> { + self.values.remove(key); + Ok(()) } async fn get_value(&self, key: &str) -> Result> { diff --git a/matrix_sdk_crypto/src/store/mod.rs b/matrix_sdk_crypto/src/store/mod.rs index b3d098fb..c545d0fd 100644 --- a/matrix_sdk_crypto/src/store/mod.rs +++ b/matrix_sdk_crypto/src/store/mod.rs @@ -300,7 +300,7 @@ pub trait CryptoStore: Debug { async fn save_value(&self, key: String, value: String) -> Result<()>; /// Remove a value from the store. - async fn remove_value(&self, key: &str) -> Result>; + async fn remove_value(&self, key: &str) -> Result<()>; /// Load a serializeable object from the store. async fn get_value(&self, key: &str) -> Result>; diff --git a/matrix_sdk_crypto/src/store/sqlite.rs b/matrix_sdk_crypto/src/store/sqlite.rs index 2ea7d6c1..6e0782e4 100644 --- a/matrix_sdk_crypto/src/store/sqlite.rs +++ b/matrix_sdk_crypto/src/store/sqlite.rs @@ -442,6 +442,24 @@ impl SqliteStore { ) .await?; + connection + .execute( + r#" + CREATE TABLE IF NOT EXISTS key_value ( + "id" INTEGER NOT NULL PRIMARY KEY, + "account_id" INTEGER NOT NULL, + "key" TEXT NOT NULL, + "value" TEXT NOT NULL, + FOREIGN KEY ("account_id") REFERENCES "accounts" ("id") + ON DELETE CASCADE + UNIQUE(account_id,key) + ); + + CREATE INDEX IF NOT EXISTS "key_values_index" ON "key_value" ("account_id"); + "#, + ) + .await?; + Ok(()) } @@ -1372,16 +1390,49 @@ impl CryptoStore for SqliteStore { Ok(()) } - async fn save_value(&self, _key: String, _value: String) -> Result<()> { - todo!() + async fn save_value(&self, key: String, value: String) -> Result<()> { + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; + let mut connection = self.connection.lock().await; + + query("REPLACE INTO key_value (account_id, key, value) VALUES (?1, ?2, ?3)") + .bind(account_id) + .bind(&key) + .bind(&value) + .execute(&mut *connection) + .await?; + + Ok(()) } - async fn remove_value(&self, _key: &str) -> Result> { - todo!() + async fn remove_value(&self, key: &str) -> Result<()> { + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; + let mut connection = self.connection.lock().await; + + query( + "DELETE FROM key_value + WHERE account_id = ?1 and key = ?2 + ", + ) + .bind(account_id) + .bind(key) + .execute(&mut *connection) + .await?; + + Ok(()) } - async fn get_value(&self, _key: &str) -> Result> { - todo!() + async fn get_value(&self, key: &str) -> Result> { + let account_id = self.account_id().ok_or(CryptoStoreError::AccountUnset)?; + let mut connection = self.connection.lock().await; + + let row: Option<(String,)> = + query_as("SELECT value FROM key_value WHERE account_id = ? and key = ?") + .bind(account_id) + .bind(key) + .fetch_optional(&mut *connection) + .await?; + + Ok(row.map(|r| r.0)) } } @@ -1881,4 +1932,19 @@ mod test { let loaded_user = store.load_user(&user_id).await.unwrap().unwrap(); assert!(loaded_user.own().unwrap().is_verified()) } + + #[tokio::test] + async fn key_value_saving() { + let (_, store, _dir) = get_loaded_store().await; + let key = "test_key".to_string(); + let value = "secret value".to_string(); + + store.save_value(key.clone(), value.clone()).await.unwrap(); + let stored_value = store.get_value(&key).await.unwrap().unwrap(); + + assert_eq!(value, stored_value); + + store.remove_value(&key).await.unwrap(); + assert!(store.get_value(&key).await.unwrap().is_none()); + } }