file_key_storage.rs (5770B)
1 use eframe::Result; 2 use enostr::{Keypair, Pubkey, SerializableKeypair}; 3 4 use crate::Error; 5 6 use super::{ 7 file_storage::{delete_file, write_file, Directory}, 8 key_storage_impl::{KeyStorageError, KeyStorageResponse}, 9 }; 10 11 static SELECTED_PUBKEY_FILE_NAME: &str = "selected_pubkey"; 12 13 /// An OS agnostic file key storage implementation 14 #[derive(Debug, PartialEq)] 15 pub struct FileKeyStorage { 16 keys_directory: Directory, 17 selected_key_directory: Directory, 18 } 19 20 impl FileKeyStorage { 21 pub fn new(keys_directory: Directory, selected_key_directory: Directory) -> Self { 22 Self { 23 keys_directory, 24 selected_key_directory, 25 } 26 } 27 28 fn add_key_internal(&self, key: &Keypair) -> Result<(), KeyStorageError> { 29 write_file( 30 &self.keys_directory.file_path, 31 key.pubkey.hex(), 32 &serde_json::to_string(&SerializableKeypair::from_keypair(key, "", 7)) 33 .map_err(|e| KeyStorageError::Addition(Error::Generic(e.to_string())))?, 34 ) 35 .map_err(KeyStorageError::Addition) 36 } 37 38 fn get_keys_internal(&self) -> Result<Vec<Keypair>, KeyStorageError> { 39 let keys = self 40 .keys_directory 41 .get_files() 42 .map_err(KeyStorageError::Retrieval)? 43 .values() 44 .filter_map(|str_key| serde_json::from_str::<SerializableKeypair>(str_key).ok()) 45 .map(|serializable_keypair| serializable_keypair.to_keypair("")) 46 .collect(); 47 Ok(keys) 48 } 49 50 fn remove_key_internal(&self, key: &Keypair) -> Result<(), KeyStorageError> { 51 delete_file(&self.keys_directory.file_path, key.pubkey.hex()) 52 .map_err(KeyStorageError::Removal) 53 } 54 55 fn get_selected_pubkey(&self) -> Result<Option<Pubkey>, KeyStorageError> { 56 let pubkey_str = self 57 .selected_key_directory 58 .get_file(SELECTED_PUBKEY_FILE_NAME.to_owned()) 59 .map_err(KeyStorageError::Selection)?; 60 61 serde_json::from_str(&pubkey_str) 62 .map_err(|e| KeyStorageError::Selection(Error::Generic(e.to_string()))) 63 } 64 65 fn select_pubkey(&self, pubkey: Option<Pubkey>) -> Result<(), KeyStorageError> { 66 if let Some(pubkey) = pubkey { 67 write_file( 68 &self.selected_key_directory.file_path, 69 SELECTED_PUBKEY_FILE_NAME.to_owned(), 70 &serde_json::to_string(&pubkey.hex()) 71 .map_err(|e| KeyStorageError::Selection(Error::Generic(e.to_string())))?, 72 ) 73 .map_err(KeyStorageError::Selection) 74 } else if self 75 .selected_key_directory 76 .get_file(SELECTED_PUBKEY_FILE_NAME.to_owned()) 77 .is_ok() 78 { 79 // Case where user chose to have no selected pubkey, but one already exists 80 delete_file( 81 &self.selected_key_directory.file_path, 82 SELECTED_PUBKEY_FILE_NAME.to_owned(), 83 ) 84 .map_err(KeyStorageError::Selection) 85 } else { 86 Ok(()) 87 } 88 } 89 } 90 91 impl FileKeyStorage { 92 pub fn get_keys(&self) -> KeyStorageResponse<Vec<enostr::Keypair>> { 93 KeyStorageResponse::ReceivedResult(self.get_keys_internal()) 94 } 95 96 pub fn add_key(&self, key: &enostr::Keypair) -> KeyStorageResponse<()> { 97 KeyStorageResponse::ReceivedResult(self.add_key_internal(key)) 98 } 99 100 pub fn remove_key(&self, key: &enostr::Keypair) -> KeyStorageResponse<()> { 101 KeyStorageResponse::ReceivedResult(self.remove_key_internal(key)) 102 } 103 104 pub fn get_selected_key(&self) -> KeyStorageResponse<Option<Pubkey>> { 105 KeyStorageResponse::ReceivedResult(self.get_selected_pubkey()) 106 } 107 108 pub fn select_key(&self, key: Option<Pubkey>) -> KeyStorageResponse<()> { 109 KeyStorageResponse::ReceivedResult(self.select_pubkey(key)) 110 } 111 } 112 113 #[cfg(test)] 114 mod tests { 115 use std::path::PathBuf; 116 117 use super::*; 118 use enostr::Keypair; 119 static CREATE_TMP_DIR: fn() -> Result<PathBuf, Error> = 120 || Ok(tempfile::TempDir::new()?.path().to_path_buf()); 121 122 impl FileKeyStorage { 123 fn mock() -> Result<Self, Error> { 124 Ok(Self { 125 keys_directory: Directory::new(CREATE_TMP_DIR()?), 126 selected_key_directory: Directory::new(CREATE_TMP_DIR()?), 127 }) 128 } 129 } 130 131 #[test] 132 fn test_basic() { 133 let kp = enostr::FullKeypair::generate().to_keypair(); 134 let storage = FileKeyStorage::mock().unwrap(); 135 let resp = storage.add_key(&kp); 136 137 assert_eq!(resp, KeyStorageResponse::ReceivedResult(Ok(()))); 138 assert_num_storage(&storage.get_keys(), 1); 139 140 assert_eq!( 141 storage.remove_key(&kp), 142 KeyStorageResponse::ReceivedResult(Ok(())) 143 ); 144 assert_num_storage(&storage.get_keys(), 0); 145 } 146 147 fn assert_num_storage(keys_response: &KeyStorageResponse<Vec<Keypair>>, n: usize) { 148 match keys_response { 149 KeyStorageResponse::ReceivedResult(Ok(keys)) => { 150 assert_eq!(keys.len(), n); 151 } 152 KeyStorageResponse::ReceivedResult(Err(_e)) => { 153 panic!("could not get keys"); 154 } 155 KeyStorageResponse::Waiting => { 156 panic!("did not receive result"); 157 } 158 } 159 } 160 161 #[test] 162 fn test_select_key() { 163 let kp = enostr::FullKeypair::generate().to_keypair(); 164 165 let storage = FileKeyStorage::mock().unwrap(); 166 let _ = storage.add_key(&kp); 167 assert_num_storage(&storage.get_keys(), 1); 168 169 let resp = storage.select_pubkey(Some(kp.pubkey)); 170 assert!(resp.is_ok()); 171 172 let resp = storage.get_selected_pubkey(); 173 174 assert!(resp.is_ok()); 175 } 176 }