notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

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 }