Select the types of activity you want to include in your feed.
feat: environments now working
You can set specific environments that store the KV pairs under a specific name. That way, when you list them all / search, all you get is those in your environment. Should declutter the list view
···11-- Add migration script here
22create table if not exists safir (
33 key text not null primary key,
44- value text not null
44+ value text not null,
55+ environment text not null
56);
6778create index if not exists idx_key on safir(key);
+7
src/cli.rs
···63636464 /// Purges the .safirstore directory, removing it and its contents
6565 Purge,
6666+6767+ /// Use / create an environment to store key / value pairs
6868+ Use {
6969+ /// Name of the environment to use / create
7070+ #[arg(default_value_t = String::from("default"))]
7171+ environment: String,
7272+ },
6673}
···4949#[async_trait]
5050impl SafirStore for SqliteStore {
5151 async fn add(&mut self, key: String, value: String) -> Result<()> {
5252- sqlx::query("insert into safir(key, value) values(?1, ?2)")
5252+ sqlx::query("insert into safir(key, value, environment) values(?1, ?2, ?3)")
5353 .bind(&key)
5454 .bind(&value)
5555+ .bind(&self.config.environment)
5556 .execute(&self.pool)
5657 .await
5758 .with_context(|| format!("insert {key} - {value} into database"))?;
···6566 .map(|k| format!("'{k}'"))
6667 .collect::<Vec<String>>();
67686868- let query = format!("select * from safir where key in ({})", keys.join(", "));
6969+ let query = format!(
7070+ "select * from safir where environment = '{}' and key in ({})",
7171+ &self.config.environment,
7272+ keys.join(", ")
7373+ );
6974 let results: Vec<KVPair> = sqlx::query_as::<_, KVPair>(&query)
7075 .fetch_all(&self.pool)
7176 .await?;
···7479 }
75807681 async fn list(&self) -> Result<Vec<KVPair>> {
7777- let results: Vec<KVPair> = sqlx::query_as::<_, KVPair>("select * from safir")
8282+ let query = format!(
8383+ "select * from safir where environment = '{}'",
8484+ self.config.environment
8585+ );
8686+ let results: Vec<KVPair> = sqlx::query_as::<_, KVPair>(&query)
7887 .fetch_all(&self.pool)
7988 .await?;
8089···8796 .map(|k| format!("'{k}'"))
8897 .collect::<Vec<String>>();
89989090- let query = format!("delete from safir where key in ({})", keys.join(", "));
9999+ let query = format!(
100100+ "delete from safir where environment = '{}' and key in ({})",
101101+ self.config.environment,
102102+ keys.join(", ")
103103+ );
91104 let _ = sqlx::query_as::<_, KVPair>(&query)
92105 .fetch_all(&self.pool)
93106 .await?;
···9711098111 async fn clear(&mut self) -> Result<()> {
99112 if confirm_entry("Are you sure you want to clear the safirstore?") {
100100- let _ = sqlx::query_as::<_, KVPair>("delete from safir")
113113+ let query = format!(
114114+ "delete from safir where environment = '{}'",
115115+ self.config.environment
116116+ );
117117+ let _ = sqlx::query_as::<_, KVPair>(&query)
101118 .fetch_all(&self.pool)
102119 .await?;
103120 }
+100-23
src/store/file_store.rs
···5566use anyhow::Result;
77use async_trait::async_trait;
88+use serde_json::Value;
8999-use std::{collections::HashMap, path::PathBuf};
1010+use std::{
1111+ collections::HashMap,
1212+ path::{Path, PathBuf},
1313+};
10141115#[derive(Debug, Clone)]
1216pub struct KVStore {
1317 loc: PathBuf,
1414- store: HashMap<String, String>,
1818+ environment: String,
1919+ store: HashMap<String, HashMap<String, String>>,
1520 config: SafirConfig,
1621}
17221823impl KVStore {
1924 pub fn load(ws: PathBuf, config: SafirConfig) -> Self {
2025 let store_path = ws.join("safirstore.json");
2121- let store = if store_path.exists() {
2222- utils::load_store(&store_path)
2626+ let safir = if store_path.exists() {
2727+ let store = Self::load_store(&store_path, &config);
2828+ Self {
2929+ loc: store_path,
3030+ environment: config.environment.clone(),
3131+ config,
3232+ store,
3333+ }
2334 } else {
2424- let store = HashMap::new();
2525- utils::write_store(&store, &store_path);
2626- store
3535+ let mut store = HashMap::new();
3636+ store.insert(config.environment.clone(), HashMap::new());
3737+3838+ Self {
3939+ loc: store_path,
4040+ store,
4141+ environment: config.environment.clone(),
4242+ config,
4343+ }
2744 };
28452929- Self {
3030- loc: store_path,
3131- config,
3232- store,
4646+ safir.write_store();
4747+ safir
4848+ }
4949+5050+ /// Loads the store from disk
5151+ /// This is stupid having to reload the map but it will allow users that had
5252+ /// the old format to port over to the new format seamlessly
5353+ pub fn load_store(
5454+ path: impl AsRef<Path>,
5555+ config: &SafirConfig,
5656+ ) -> HashMap<String, HashMap<String, String>> {
5757+ let contents = std::fs::read_to_string(path.as_ref()).expect("unable to store contents");
5858+5959+ let store: HashMap<String, Value> =
6060+ serde_json::from_str(&contents).expect("unable to deserialize contents");
6161+6262+ // If they're all objects then this is the new format, load appropriately
6363+ if store.values().all(|v| v.is_object()) {
6464+ return serde_json::from_str::<HashMap<String, HashMap<String, String>>>(&contents)
6565+ .unwrap();
6666+ }
6767+6868+ // Old map (no environment) - Load and create new environment
6969+ let old_map = serde_json::from_str::<HashMap<String, String>>(&contents).unwrap();
7070+ let mut new_map = HashMap::new();
7171+ new_map.insert(config.environment.clone(), HashMap::new());
7272+7373+ let env = new_map.get_mut(config.environment.as_str()).unwrap();
7474+ for (key, value) in old_map.into_iter() {
7575+ env.insert(key, value);
3376 }
7777+7878+ new_map
7979+ }
8080+8181+ /// Writes the store to disk
8282+ pub fn write_store(&self) {
8383+ use std::io::Write;
8484+ let str_store =
8585+ serde_json::to_string_pretty(&self.store).expect("unable to serialize store contents");
8686+8787+ let mut file = std::fs::File::create(&self.loc).expect("unable to get file handle");
8888+8989+ file.write_all(str_store.as_bytes())
9090+ .expect("unable to write store out to disk");
9191+ }
9292+9393+ pub fn get_environment(&mut self) -> &mut HashMap<String, String> {
9494+ self.store
9595+ .entry(self.environment.clone())
9696+ .or_insert(HashMap::new());
9797+9898+ self.store.get_mut(&self.environment).unwrap()
3499 }
35100}
3610137102#[async_trait]
38103impl SafirStore for KVStore {
39104 async fn add(&mut self, key: String, value: String) -> Result<()> {
4040- if let Some(v) = self.store.get(&key) {
105105+ let env = self.get_environment();
106106+ if let Some(v) = env.get(&key) {
41107 let confirm_msg = format!("Key {key} already exists ({v}), Replace?");
42108 if utils::confirm_entry(&confirm_msg) {
4343- self.store.insert(key, value);
109109+ env.insert(key, value);
44110 }
45111 } else {
4646- self.store.insert(key, value);
112112+ env.insert(key, value);
47113 }
481144949- utils::write_store(&self.store, &self.loc);
115115+ self.write_store();
5011651117 Ok(())
52118 }
5311954120 async fn get(&self, keys: Vec<String>) -> Result<Vec<KVPair>> {
121121+ let inner = match self.store.get(&self.environment) {
122122+ Some(inner) => inner,
123123+ None => return Ok(vec![]),
124124+ };
125125+55126 let kvs: Vec<KVPair> = keys
56127 .into_iter()
5757- .filter_map(|key| match self.store.get(&key) {
128128+ .filter_map(|key| match inner.get(&key) {
58129 Some(value) => Some((key, value.clone())),
59130 None => None,
60131 })
···64135 }
6513666137 async fn list(&self) -> Result<Vec<KVPair>> {
6767- let kvs: Vec<KVPair> = self
6868- .store
138138+ let inner = match self.store.get(&self.environment) {
139139+ Some(inner) => inner,
140140+ None => return Ok(vec![]),
141141+ };
142142+143143+ let kvs: Vec<KVPair> = inner
69144 .iter()
70145 .map(|(key, value)| (key.clone(), value.clone()))
71146 .collect();
···74149 }
7515076151 async fn remove(&mut self, keys: Vec<String>) -> Result<()> {
152152+ let inner = self.get_environment();
77153 for key in keys.iter() {
7878- if let Some(v) = self.store.get(key) {
154154+ if let Some(v) = inner.get(key) {
79155 let confirm_msg = format!("Remove {key} ({v}) from the store?");
80156 if utils::confirm_entry(&confirm_msg) {
8181- self.store.remove(key);
157157+ inner.remove(key);
82158 }
83159 }
84160 }
851618686- utils::write_store(&self.store, &self.loc);
162162+ self.write_store();
8716388164 Ok(())
89165 }
90166 async fn clear(&mut self) -> Result<()> {
167167+ let inner = self.get_environment();
91168 let confirm_msg = "Are you sure you want to clear the cache of all contents?";
92169 if utils::confirm_entry(&confirm_msg) {
9393- self.store.clear();
170170+ inner.clear();
94171 }
951729696- utils::write_store(&self.store, &self.loc);
173173+ self.write_store();
9717498175 Ok(())
99176 }
-20
src/utils.rs
···11use std::{
22- collections::HashMap,
32 fs,
43 io::Write,
54 path::{Path, PathBuf},
···3736 let (key, value) = kv;
3837 println!("{display_cmd} {key}=\"{value}\"");
3938 }
4040-}
4141-4242-/// Loads the store from disk
4343-pub fn load_store(path: impl AsRef<Path>) -> HashMap<String, String> {
4444- let contents = std::fs::read_to_string(path.as_ref()).expect("unable to store contents");
4545-4646- return serde_json::from_str::<HashMap<String, String>>(&contents)
4747- .expect("unable to deserialize store contents");
4848-}
4949-5050-/// Writes the store to disk
5151-pub fn write_store(store: &HashMap<String, String>, path: impl AsRef<Path>) {
5252- let str_store =
5353- serde_json::to_string_pretty(store).expect("unable to serialize store contents");
5454-5555- let mut file = std::fs::File::create(&path).expect("unable to get file handle");
5656-5757- file.write_all(str_store.as_bytes())
5858- .expect("unable to write store out to disk");
5939}
60406141/// Remove the .safirstore directory