commit 6003ef5aecb3228b9c93a7aa160e2ad4a3219600
parent d9f92ef54fb676fa39a8d35bbf9dc792f63a9423
Author: kernelkind <kernelkind@gmail.com>
Date: Mon, 17 Mar 2025 19:36:23 -0400
`FileKeyStorage` -> `AccountStorage`
Signed-off-by: kernelkind <kernelkind@gmail.com>
Diffstat:
6 files changed, 183 insertions(+), 183 deletions(-)
diff --git a/crates/notedeck/src/accounts.rs b/crates/notedeck/src/accounts.rs
@@ -1,7 +1,7 @@
use tracing::{debug, error, info};
use crate::{
- FileKeyStorage, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount,
+ AccountStorage, MuteFun, Muted, RelaySpec, SingleUnkIdAction, UnknownIds, UserAccount,
};
use enostr::{ClientMessage, FilledKeypair, Keypair, Pubkey, RelayPool};
use nostrdb::{Filter, Ndb, Note, NoteBuilder, NoteKey, Subscription, Transaction};
@@ -308,7 +308,7 @@ pub struct AccountData {
pub struct Accounts {
currently_selected_account: Option<usize>,
accounts: Vec<UserAccount>,
- key_store: Option<FileKeyStorage>,
+ key_store: Option<AccountStorage>,
account_data: BTreeMap<[u8; 32], AccountData>,
forced_relays: BTreeSet<RelaySpec>,
bootstrap_relays: BTreeSet<RelaySpec>,
@@ -316,10 +316,10 @@ pub struct Accounts {
}
impl Accounts {
- pub fn new(key_store: Option<FileKeyStorage>, forced_relays: Vec<String>) -> Self {
+ pub fn new(key_store: Option<AccountStorage>, forced_relays: Vec<String>) -> Self {
let accounts = match &key_store {
- Some(keystore) => match keystore.get_keys() {
- Ok(k) => k.into_iter().map(|key| UserAccount { key }).collect(),
+ Some(keystore) => match keystore.get_accounts() {
+ Ok(k) => k,
Err(e) => {
tracing::error!("could not get keys: {e}");
Vec::new()
@@ -434,22 +434,22 @@ impl Accounts {
}
#[must_use = "UnknownIdAction's must be handled. Use .process_unknown_id_action()"]
- pub fn add_account(&mut self, account: Keypair) -> AddAccountAction {
- let pubkey = account.pubkey;
+ pub fn add_account(&mut self, key: Keypair) -> AddAccountAction {
+ let pubkey = key.pubkey;
let switch_to_index = if let Some(contains_acc) = self.contains_account(pubkey.bytes()) {
- if account.secret_key.is_some() && !contains_acc.has_nsec {
+ if key.secret_key.is_some() && !contains_acc.has_nsec {
info!(
"user provided nsec, but we already have npub {}. Upgrading to nsec",
pubkey
);
if let Some(key_store) = &self.key_store {
- if let Err(e) = key_store.add_key(&account) {
- tracing::error!("Could not add key for {:?}: {e}", account.pubkey);
+ if let Err(e) = key_store.write_account(&UserAccount::new(key.clone())) {
+ tracing::error!("Could not add key for {:?}: {e}", key.pubkey);
}
}
- self.accounts[contains_acc.index].key = account;
+ self.accounts[contains_acc.index].key = key;
} else {
info!("already have account, not adding {}", pubkey);
}
@@ -457,11 +457,11 @@ impl Accounts {
} else {
info!("adding new account {}", pubkey);
if let Some(key_store) = &self.key_store {
- if let Err(e) = key_store.add_key(&account) {
- tracing::error!("Could not add key for {:?}: {e}", account.pubkey);
+ if let Err(e) = key_store.write_account(&UserAccount::new(key.clone())) {
+ tracing::error!("Could not add key for {:?}: {e}", key.pubkey);
}
}
- self.accounts.push(UserAccount::new(account));
+ self.accounts.push(UserAccount::new(key));
self.accounts.len() - 1
};
@@ -821,7 +821,7 @@ enum RelayAction {
Remove,
}
-fn get_selected_index(accounts: &[UserAccount], keystore: &FileKeyStorage) -> Option<usize> {
+fn get_selected_index(accounts: &[UserAccount], keystore: &AccountStorage) -> Option<usize> {
match keystore.get_selected_key() {
Ok(Some(pubkey)) => {
return accounts
diff --git a/crates/notedeck/src/app.rs b/crates/notedeck/src/app.rs
@@ -1,6 +1,6 @@
use crate::persist::{AppSizeHandler, ZoomHandler};
use crate::{
- Accounts, AppContext, Args, DataPath, DataPathType, Directory, FileKeyStorage, Images,
+ AccountStorage, Accounts, AppContext, Args, DataPath, DataPathType, Directory, Images,
NoteCache, RelayDebugView, ThemeHandler, UnknownIds,
};
use egui::ThemePreference;
@@ -149,7 +149,7 @@ impl Notedeck {
let keystore = if parsed_args.use_keystore {
let keys_path = path.path(DataPathType::Keys);
let selected_key_path = path.path(DataPathType::SelectedKey);
- Some(FileKeyStorage::new(
+ Some(AccountStorage::new(
Directory::new(keys_path),
Directory::new(selected_key_path),
))
diff --git a/crates/notedeck/src/lib.rs b/crates/notedeck/src/lib.rs
@@ -44,7 +44,7 @@ pub use persist::*;
pub use relay_debug::RelayDebugView;
pub use relayspec::RelaySpec;
pub use result::Result;
-pub use storage::{DataPath, DataPathType, Directory, FileKeyStorage};
+pub use storage::{AccountStorage, DataPath, DataPathType, Directory};
pub use style::NotedeckTextStyle;
pub use theme::ColorTheme;
pub use time::time_ago_since;
diff --git a/crates/notedeck/src/storage/account_storage.rs b/crates/notedeck/src/storage/account_storage.rs
@@ -0,0 +1,163 @@
+use crate::{Result, UserAccount};
+use enostr::{Keypair, Pubkey, SerializableKeypair};
+use tokenator::{TokenParser, TokenSerializable, TokenWriter};
+
+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)]
+pub struct AccountStorage {
+ accounts_directory: Directory,
+ selected_key_directory: Directory,
+}
+
+impl AccountStorage {
+ pub fn new(accounts_directory: Directory, selected_key_directory: Directory) -> Self {
+ Self {
+ accounts_directory,
+ selected_key_directory,
+ }
+ }
+
+ pub fn write_account(&self, account: &UserAccount) -> Result<()> {
+ let mut writer = TokenWriter::new("\t");
+ account.serialize_tokens(&mut writer);
+ write_file(
+ &self.accounts_directory.file_path,
+ account.key.pubkey.hex(),
+ writer.str(),
+ )
+ }
+
+ pub fn get_accounts(&self) -> Result<Vec<UserAccount>> {
+ let keys = self
+ .accounts_directory
+ .get_files()?
+ .values()
+ .filter_map(|str_key| deserialize_kp(str_key).ok())
+ .map(UserAccount::new)
+ .collect();
+ 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
+ .selected_key_directory
+ .get_file(SELECTED_PUBKEY_FILE_NAME.to_owned())
+ {
+ Ok(pubkey_str) => Ok(Some(serde_json::from_str(&pubkey_str)?)),
+ Err(crate::Error::Io(_)) => Ok(None),
+ 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_kp(serialized: &str) -> Result<Keypair> {
+ let data = serialized.split("\t").collect::<Vec<&str>>();
+ let mut parser = TokenParser::new(&data);
+
+ if let Ok(kp) = Keypair::parse_from_tokens(&mut parser) {
+ return Ok(kp);
+ }
+
+ Ok(serde_json::from_str::<SerializableKeypair>(serialized)?.to_keypair(""))
+}
+
+#[cfg(test)]
+mod tests {
+ use std::path::PathBuf;
+
+ use super::Result;
+ use super::*;
+
+ static CREATE_TMP_DIR: fn() -> Result<PathBuf> =
+ || Ok(tempfile::TempDir::new()?.path().to_path_buf());
+
+ impl AccountStorage {
+ fn mock() -> Result<Self> {
+ Ok(Self {
+ accounts_directory: Directory::new(CREATE_TMP_DIR()?),
+ selected_key_directory: Directory::new(CREATE_TMP_DIR()?),
+ })
+ }
+ }
+
+ #[test]
+ fn test_basic() {
+ let kp = enostr::FullKeypair::generate().to_keypair();
+ let storage = AccountStorage::mock().unwrap();
+ let resp = storage.write_account(&UserAccount::new(kp.clone()));
+
+ assert!(resp.is_ok());
+ assert_num_storage(&storage.get_accounts(), 1);
+
+ assert!(storage.remove_key(&kp).is_ok());
+ assert_num_storage(&storage.get_accounts(), 0);
+ }
+
+ fn assert_num_storage(keys_response: &Result<Vec<UserAccount>>, n: usize) {
+ match keys_response {
+ Ok(keys) => {
+ assert_eq!(keys.len(), n);
+ }
+ Err(_e) => {
+ panic!("could not get keys");
+ }
+ }
+ }
+
+ #[test]
+ fn test_select_key() {
+ let kp = enostr::FullKeypair::generate().to_keypair();
+
+ let storage = AccountStorage::mock().unwrap();
+ let _ = storage.write_account(&UserAccount::new(kp.clone()));
+ assert_num_storage(&storage.get_accounts(), 1);
+
+ let resp = storage.select_key(Some(kp.pubkey));
+ assert!(resp.is_ok());
+
+ let resp = storage.get_selected_key();
+
+ assert!(resp.is_ok());
+ }
+
+ #[test]
+ fn test_get_selected_key_when_no_file() {
+ let storage = AccountStorage::mock().unwrap();
+
+ // Should return Ok(None) when no key has been selected
+ match storage.get_selected_key() {
+ Ok(None) => (), // This is what we expect
+ other => panic!("Expected Ok(None), got {:?}", other),
+ }
+ }
+}
diff --git a/crates/notedeck/src/storage/file_key_storage.rs b/crates/notedeck/src/storage/file_key_storage.rs
@@ -1,163 +0,0 @@
-use crate::Result;
-use enostr::{Keypair, Pubkey, SerializableKeypair};
-use tokenator::{TokenParser, TokenSerializable, TokenWriter};
-
-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)]
-pub struct FileKeyStorage {
- keys_directory: Directory,
- selected_key_directory: Directory,
-}
-
-impl FileKeyStorage {
- pub fn new(keys_directory: Directory, selected_key_directory: Directory) -> Self {
- Self {
- keys_directory,
- selected_key_directory,
- }
- }
-
- pub fn add_key(&self, key: &Keypair) -> Result<()> {
- let mut writer = TokenWriter::new("\t");
- key.serialize_tokens(&mut writer);
- write_file(
- &self.keys_directory.file_path,
- key.pubkey.hex(),
- writer.str(),
- )
- }
-
- pub fn get_keys(&self) -> Result<Vec<Keypair>> {
- let keys = self
- .keys_directory
- .get_files()?
- .values()
- .filter_map(|str_key| deserialize_kp(str_key).ok())
- .collect();
- Ok(keys)
- }
-
- pub fn remove_key(&self, key: &Keypair) -> Result<()> {
- delete_file(&self.keys_directory.file_path, key.pubkey.hex())
- }
-
- pub fn get_selected_key(&self) -> Result<Option<Pubkey>> {
- match self
- .selected_key_directory
- .get_file(SELECTED_PUBKEY_FILE_NAME.to_owned())
- {
- Ok(pubkey_str) => Ok(Some(serde_json::from_str(&pubkey_str)?)),
- Err(crate::Error::Io(_)) => Ok(None),
- 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_kp(serialized: &str) -> Result<Keypair> {
- let data = serialized.split("\t").collect::<Vec<&str>>();
- let mut parser = TokenParser::new(&data);
-
- if let Ok(kp) = Keypair::parse_from_tokens(&mut parser) {
- return Ok(kp);
- }
-
- Ok(serde_json::from_str::<SerializableKeypair>(serialized)?.to_keypair(""))
-}
-
-#[cfg(test)]
-mod tests {
- use std::path::PathBuf;
-
- use super::Result;
- use super::*;
-
- use enostr::Keypair;
- static CREATE_TMP_DIR: fn() -> Result<PathBuf> =
- || Ok(tempfile::TempDir::new()?.path().to_path_buf());
-
- impl FileKeyStorage {
- fn mock() -> Result<Self> {
- Ok(Self {
- keys_directory: Directory::new(CREATE_TMP_DIR()?),
- selected_key_directory: Directory::new(CREATE_TMP_DIR()?),
- })
- }
- }
-
- #[test]
- fn test_basic() {
- let kp = enostr::FullKeypair::generate().to_keypair();
- let storage = FileKeyStorage::mock().unwrap();
- let resp = storage.add_key(&kp);
-
- assert!(resp.is_ok());
- assert_num_storage(&storage.get_keys(), 1);
-
- assert!(storage.remove_key(&kp).is_ok());
- assert_num_storage(&storage.get_keys(), 0);
- }
-
- fn assert_num_storage(keys_response: &Result<Vec<Keypair>>, n: usize) {
- match keys_response {
- Ok(keys) => {
- assert_eq!(keys.len(), n);
- }
- Err(_e) => {
- panic!("could not get keys");
- }
- }
- }
-
- #[test]
- fn test_select_key() {
- let kp = enostr::FullKeypair::generate().to_keypair();
-
- let storage = FileKeyStorage::mock().unwrap();
- let _ = storage.add_key(&kp);
- assert_num_storage(&storage.get_keys(), 1);
-
- let resp = storage.select_key(Some(kp.pubkey));
- assert!(resp.is_ok());
-
- let resp = storage.get_selected_key();
-
- assert!(resp.is_ok());
- }
-
- #[test]
- fn test_get_selected_key_when_no_file() {
- let storage = FileKeyStorage::mock().unwrap();
-
- // Should return Ok(None) when no key has been selected
- match storage.get_selected_key() {
- Ok(None) => (), // This is what we expect
- other => panic!("Expected Ok(None), got {:?}", other),
- }
- }
-}
diff --git a/crates/notedeck/src/storage/mod.rs b/crates/notedeck/src/storage/mod.rs
@@ -1,5 +1,5 @@
-mod file_key_storage;
+mod account_storage;
mod file_storage;
-pub use file_key_storage::FileKeyStorage;
+pub use account_storage::AccountStorage;
pub use file_storage::{delete_file, write_file, DataPath, DataPathType, Directory};