···11+# Change Log
22+33+Documenting changes between versions beginning from v0.3.0
44+55+## v0.3.0
66+77+No breaking changes, just an update of the backend used for file creation and maintenance.
88+Due to this allowing more extended work going forward, bumped up to v0.3.0
99+1010+* Changed backend file handling to use the [Rubin](https://crates.io/crates/rubin) crates instead of a custom solution
1111+
···88//!
99//! Safir gives you the option to add / get / remove items from the store
1010//! and to clear / purge when you're finished with them.
1111-use std::{
1212- collections::HashMap,
1313- fs::{File, OpenOptions},
1414- io::{BufWriter, Read, Result, Write},
1515- path::PathBuf,
1616-};
1111+use std::io::{Result, Write};
17121813use colored::*;
1414+1515+use rubin::store::persistence::PersistentStore;
19162017/// Formats and prints the message to stdout
2118fn print_output(msg: &str) {
···2825}
29263027/// Safir Store (fancy wrapper around reading and writing to a JSON file)
3131-#[derive(Default)]
3228pub struct Safir {
3333- pub path: PathBuf,
3434- pub store: HashMap<String, String>,
2929+ pub store: PersistentStore,
3530}
36313732impl Safir {
3833 /// Initialises the Safirstore if not already initialised
3939- pub fn init() -> Result<Self> {
4040- let mut safir = Self::default();
4141- if let Some(home_dir) = dirs::home_dir() {
4242- let safir_path = home_dir.join(".safirstore");
4343- std::fs::create_dir_all(&safir_path)?;
4444- safir.path = safir_path.join("safirstore.json");
4545- safir.load()?;
4646- }
3434+ pub async fn init() -> Result<Self> {
3535+ let home_dir = dirs::home_dir().unwrap();
3636+ let store_path = home_dir.join(".safirstore/safirstore.json");
3737+ let mut ps = if store_path.exists() {
3838+ PersistentStore::from_existing(store_path).await?
3939+ } else {
4040+ PersistentStore::new(store_path).await?
4141+ };
47424848- Ok(safir)
4343+ ps.set_write_on_update(true);
4444+ Ok(Self { store: ps })
4945 }
50465147 /// Add an entry to the store and write it out to disk
5252- pub fn add_entry(&mut self, key: String, value: String) {
5353- self.store
5454- .entry(key)
5555- .and_modify(|entry| *entry = value.clone())
5656- .or_insert(value);
5757-5858- self.write().expect("unable to write store out to file!");
4848+ pub async fn add_entry(&mut self, key: String, value: String) -> Result<String> {
4949+ self.store.insert_string(&key, &value).await
5950 }
60516152 /// Get an entry form the store by loading it from disk and displaying it
6262- pub fn get_entry(&self, key: String) {
5353+ pub fn get_entry(&self, key: String) -> Result<()> {
6354 print_header();
6464- let output = if let Some(val) = self.store.get(&key) {
5555+ let output = if let Ok(val) = self.store.get_string(&key) {
6556 format!("{}: \"{}\"", key.bold().yellow(), val)
6657 } else {
6758 format!("{}: ", key.bold().yellow())
6859 };
69607061 print_output(&output);
6262+6363+ Ok(())
7164 }
72657366 /// Display all key/values in the store
7467 pub fn display_all(&self) {
7568 print_header();
7669 let mut output: String;
7777- for (key, value) in self.store.iter() {
7070+ let strings = self.store.get_string_store_ref();
7171+ for (key, value) in strings.iter() {
7872 output = format!("{}: \"{}\"", key.bold().yellow(), value);
7973 print_output(&output);
8074 }
8175 }
82768377 /// Remove a key/value pair from the store and update onto disk
8484- pub fn remove_entry(&mut self, keys: Vec<String>) {
7878+ pub async fn remove_entry(&mut self, keys: Vec<String>) -> Result<()> {
8579 for key in &keys {
8686- self.store.remove_entry(key);
8080+ self.store.remove_string(key).await?;
8781 }
8888- self.write().expect("unable to update safirstore");
8282+8383+ Ok(())
8984 }
90859186 /// Outputs the key/value pair as a command with the prefix
···10196 };
1029710398 for key in keys {
104104- if let Some(value) = self.store.get(key) {
9999+ if let Ok(value) = self.store.get_string(key) {
105100 println!("{} {}=\"{}\"\n", prefix, key.bold().yellow(), value);
106101 }
107102 }
108103 }
109104110105 /// Clear the the contents of the store and update onto disk
111111- pub fn clear_entries(&mut self) {
106106+ pub async fn clear_entries(&mut self) -> Result<()> {
112107 if self.confirm_entry("Are you sure you want to clear the store?") {
113113- self.store.clear();
114114- self.write().expect("unable to clear safirstore");
108108+ self.store.clear_strings().await?;
115109 }
110110+111111+ Ok(())
116112 }
117113118118- /// Remove the `.safirstore` directory and all contents
114114+ /// Remove the store directory and all contents
119115 pub fn purge(&mut self) {
120116 if self.confirm_entry("Are you sure you want to purge Safirstore?") {
121121- self.path.pop();
122122- std::fs::remove_dir_all(&self.path).expect("unable to remove safirstore directory");
117117+ std::fs::remove_dir_all(&self.store.path)
118118+ .expect("unable to remove safirstore directory");
123119 }
124120 }
125121···139135 }
140136141137 false
142142- }
143143-144144- /// Load the contents of the store off of disk
145145- fn load(&mut self) -> Result<()> {
146146- let mut f = OpenOptions::new()
147147- .create(true)
148148- .read(true)
149149- .write(true)
150150- .open(&self.path)?;
151151-152152- let mut contents = String::new();
153153- f.read_to_string(&mut contents)?;
154154- if !contents.is_empty() {
155155- let store = serde_json::from_str(&contents)?;
156156- self.store = store;
157157- } else {
158158- self.write()?;
159159- }
160160-161161- Ok(())
162162- }
163163-164164- /// Write the contents of the store out to disk in the .safirstore/ directory
165165- fn write(&self) -> Result<()> {
166166- let mut writer = BufWriter::new(File::create(&self.path)?);
167167- serde_json::to_writer_pretty(&mut writer, &self.store)?;
168168- writer.write_all(b"\n")?;
169169- writer.flush()?;
170170- Ok(())
171138 }
172139}