split AccountStorage into reader & writer
Signed-off-by: kernelkind <kernelkind@gmail.com>
This commit is contained in:
@@ -3,6 +3,7 @@ use tracing::{debug, info};
|
||||
use crate::account::cache::AccountCache;
|
||||
use crate::account::mute::AccountMutedData;
|
||||
use crate::account::relay::{AccountRelayData, RelayDefaults};
|
||||
use crate::storage::AccountStorageWriter;
|
||||
use crate::user_account::UserAccountSerializable;
|
||||
use crate::{AccountStorage, MuteFun, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount};
|
||||
use enostr::{ClientMessage, FilledKeypair, Keypair, Pubkey, RelayPool};
|
||||
@@ -16,7 +17,7 @@ use std::sync::Arc;
|
||||
/// Represents all user-facing operations related to account management.
|
||||
pub struct Accounts {
|
||||
pub cache: AccountCache,
|
||||
key_store: Option<AccountStorage>,
|
||||
storage_writer: Option<AccountStorageWriter>,
|
||||
relay_defaults: RelayDefaults,
|
||||
needs_relay_config: bool,
|
||||
}
|
||||
@@ -40,8 +41,10 @@ impl Accounts {
|
||||
|
||||
unknown_id.process_action(unknown_ids, ndb, txn);
|
||||
|
||||
if let Some(keystore) = &key_store {
|
||||
match keystore.get_accounts() {
|
||||
let mut storage_writer = None;
|
||||
if let Some(keystore) = key_store {
|
||||
let (reader, writer) = keystore.rw();
|
||||
match reader.get_accounts() {
|
||||
Ok(accounts) => {
|
||||
for account in accounts {
|
||||
add_account_from_storage(&mut cache, ndb, txn, account).process_action(
|
||||
@@ -55,16 +58,18 @@ impl Accounts {
|
||||
tracing::error!("could not get keys: {e}");
|
||||
}
|
||||
}
|
||||
if let Some(selected) = keystore.get_selected_key().ok().flatten() {
|
||||
if let Some(selected) = reader.get_selected_key().ok().flatten() {
|
||||
cache.select(selected);
|
||||
}
|
||||
|
||||
storage_writer = Some(writer);
|
||||
};
|
||||
|
||||
let relay_defaults = RelayDefaults::new(forced_relays);
|
||||
|
||||
Accounts {
|
||||
cache,
|
||||
key_store,
|
||||
storage_writer,
|
||||
relay_defaults,
|
||||
needs_relay_config: true,
|
||||
}
|
||||
@@ -75,7 +80,7 @@ impl Accounts {
|
||||
return;
|
||||
};
|
||||
|
||||
if let Some(key_store) = &self.key_store {
|
||||
if let Some(key_store) = &self.storage_writer {
|
||||
if let Err(e) = key_store.remove_key(&removed.key) {
|
||||
tracing::error!("Could not remove account {pk}: {e}");
|
||||
}
|
||||
@@ -118,7 +123,7 @@ impl Accounts {
|
||||
)
|
||||
};
|
||||
|
||||
if let Some(key_store) = &self.key_store {
|
||||
if let Some(key_store) = &self.storage_writer {
|
||||
if let Err(e) = key_store.write_account(&acc.get_acc().into()) {
|
||||
tracing::error!("Could not add key for {:?}: {e}", kp.pubkey);
|
||||
}
|
||||
@@ -139,7 +144,7 @@ impl Accounts {
|
||||
|
||||
let cur_acc = self.get_selected_account();
|
||||
|
||||
let Some(key_store) = &self.key_store else {
|
||||
let Some(key_store) = &self.storage_writer else {
|
||||
return false;
|
||||
};
|
||||
|
||||
@@ -190,7 +195,7 @@ impl Accounts {
|
||||
return;
|
||||
}
|
||||
|
||||
if let Some(key_store) = &self.key_store {
|
||||
if let Some(key_store) = &self.storage_writer {
|
||||
if let Err(e) = key_store.select_key(Some(*pk)) {
|
||||
tracing::error!("Could not select key {:?}: {e}", pk);
|
||||
}
|
||||
|
||||
@@ -7,7 +7,7 @@ use super::file_storage::{delete_file, write_file, Directory};
|
||||
static SELECTED_PUBKEY_FILE_NAME: &str = "selected_pubkey";
|
||||
|
||||
/// An OS agnostic file key storage implementation
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct AccountStorage {
|
||||
accounts_directory: Directory,
|
||||
selected_key_directory: Directory,
|
||||
@@ -21,18 +21,73 @@ impl AccountStorage {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn rw(self) -> (AccountStorageReader, AccountStorageWriter) {
|
||||
(
|
||||
AccountStorageReader::new(self.clone()),
|
||||
AccountStorageWriter::new(self),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccountStorageWriter {
|
||||
storage: AccountStorage,
|
||||
}
|
||||
|
||||
impl AccountStorageWriter {
|
||||
pub fn new(storage: AccountStorage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
|
||||
pub fn write_account(&self, account: &UserAccountSerializable) -> Result<()> {
|
||||
let mut writer = TokenWriter::new("\t");
|
||||
account.serialize_tokens(&mut writer);
|
||||
write_file(
|
||||
&self.accounts_directory.file_path,
|
||||
&self.storage.accounts_directory.file_path,
|
||||
account.key.pubkey.hex(),
|
||||
writer.str(),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn remove_key(&self, key: &Keypair) -> Result<()> {
|
||||
delete_file(&self.storage.accounts_directory.file_path, key.pubkey.hex())
|
||||
}
|
||||
|
||||
pub fn select_key(&self, pubkey: Option<Pubkey>) -> Result<()> {
|
||||
if let Some(pubkey) = pubkey {
|
||||
write_file(
|
||||
&self.storage.selected_key_directory.file_path,
|
||||
SELECTED_PUBKEY_FILE_NAME.to_owned(),
|
||||
&serde_json::to_string(&pubkey.hex())?,
|
||||
)
|
||||
} else if self
|
||||
.storage
|
||||
.selected_key_directory
|
||||
.get_file(SELECTED_PUBKEY_FILE_NAME.to_owned())
|
||||
.is_ok()
|
||||
{
|
||||
// Case where user chose to have no selected pubkey, but one already exists
|
||||
Ok(delete_file(
|
||||
&self.storage.selected_key_directory.file_path,
|
||||
SELECTED_PUBKEY_FILE_NAME.to_owned(),
|
||||
)?)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AccountStorageReader {
|
||||
storage: AccountStorage,
|
||||
}
|
||||
|
||||
impl AccountStorageReader {
|
||||
pub fn new(storage: AccountStorage) -> Self {
|
||||
Self { storage }
|
||||
}
|
||||
|
||||
pub fn get_accounts(&self) -> Result<Vec<UserAccountSerializable>> {
|
||||
let keys = self
|
||||
.storage
|
||||
.accounts_directory
|
||||
.get_files()?
|
||||
.values()
|
||||
@@ -41,12 +96,9 @@ impl AccountStorage {
|
||||
Ok(keys)
|
||||
}
|
||||
|
||||
pub fn remove_key(&self, key: &Keypair) -> Result<()> {
|
||||
delete_file(&self.accounts_directory.file_path, key.pubkey.hex())
|
||||
}
|
||||
|
||||
pub fn get_selected_key(&self) -> Result<Option<Pubkey>> {
|
||||
match self
|
||||
.storage
|
||||
.selected_key_directory
|
||||
.get_file(SELECTED_PUBKEY_FILE_NAME.to_owned())
|
||||
{
|
||||
@@ -55,28 +107,6 @@ impl AccountStorage {
|
||||
Err(e) => Err(e),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn select_key(&self, pubkey: Option<Pubkey>) -> Result<()> {
|
||||
if let Some(pubkey) = pubkey {
|
||||
write_file(
|
||||
&self.selected_key_directory.file_path,
|
||||
SELECTED_PUBKEY_FILE_NAME.to_owned(),
|
||||
&serde_json::to_string(&pubkey.hex())?,
|
||||
)
|
||||
} else if self
|
||||
.selected_key_directory
|
||||
.get_file(SELECTED_PUBKEY_FILE_NAME.to_owned())
|
||||
.is_ok()
|
||||
{
|
||||
// Case where user chose to have no selected pubkey, but one already exists
|
||||
Ok(delete_file(
|
||||
&self.selected_key_directory.file_path,
|
||||
SELECTED_PUBKEY_FILE_NAME.to_owned(),
|
||||
)?)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn deserialize_storage(serialized: &str) -> Result<UserAccountSerializable> {
|
||||
@@ -119,14 +149,14 @@ mod tests {
|
||||
#[test]
|
||||
fn test_basic() {
|
||||
let kp = enostr::FullKeypair::generate().to_keypair();
|
||||
let storage = AccountStorage::mock().unwrap();
|
||||
let resp = storage.write_account(&UserAccountSerializable::new(kp.clone()));
|
||||
let (reader, writer) = AccountStorage::mock().unwrap().rw();
|
||||
let resp = writer.write_account(&UserAccountSerializable::new(kp.clone()));
|
||||
|
||||
assert!(resp.is_ok());
|
||||
assert_num_storage(&storage.get_accounts(), 1);
|
||||
assert_num_storage(&reader.get_accounts(), 1);
|
||||
|
||||
assert!(storage.remove_key(&kp).is_ok());
|
||||
assert_num_storage(&storage.get_accounts(), 0);
|
||||
assert!(writer.remove_key(&kp).is_ok());
|
||||
assert_num_storage(&reader.get_accounts(), 0);
|
||||
}
|
||||
|
||||
fn assert_num_storage(keys_response: &Result<Vec<UserAccountSerializable>>, n: usize) {
|
||||
@@ -144,21 +174,21 @@ mod tests {
|
||||
fn test_select_key() {
|
||||
let kp = enostr::FullKeypair::generate().to_keypair();
|
||||
|
||||
let storage = AccountStorage::mock().unwrap();
|
||||
let _ = storage.write_account(&UserAccountSerializable::new(kp.clone()));
|
||||
assert_num_storage(&storage.get_accounts(), 1);
|
||||
let (reader, writer) = AccountStorage::mock().unwrap().rw();
|
||||
let _ = writer.write_account(&UserAccountSerializable::new(kp.clone()));
|
||||
assert_num_storage(&reader.get_accounts(), 1);
|
||||
|
||||
let resp = storage.select_key(Some(kp.pubkey));
|
||||
let resp = writer.select_key(Some(kp.pubkey));
|
||||
assert!(resp.is_ok());
|
||||
|
||||
let resp = storage.get_selected_key();
|
||||
let resp = reader.get_selected_key();
|
||||
|
||||
assert!(resp.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_selected_key_when_no_file() {
|
||||
let storage = AccountStorage::mock().unwrap();
|
||||
let storage = AccountStorage::mock().unwrap().rw().0;
|
||||
|
||||
// Should return Ok(None) when no key has been selected
|
||||
match storage.get_selected_key() {
|
||||
|
||||
@@ -59,7 +59,7 @@ pub enum DataPathType {
|
||||
Cache,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
#[derive(Debug, PartialEq, Clone)]
|
||||
pub struct Directory {
|
||||
pub file_path: PathBuf,
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
mod account_storage;
|
||||
mod file_storage;
|
||||
|
||||
pub use account_storage::AccountStorage;
|
||||
pub use account_storage::{AccountStorage, AccountStorageReader, AccountStorageWriter};
|
||||
pub use file_storage::{delete_file, write_file, DataPath, DataPathType, Directory};
|
||||
|
||||
Reference in New Issue
Block a user