aboutsummaryrefslogtreecommitdiffstats
path: root/atuin-client/src/kv.rs
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@elliehuxtable.com>2023-06-14 21:18:24 +0100
committerGitHub <noreply@github.com>2023-06-14 21:18:24 +0100
commitae1709dafd22ac3c64441472e90df8799253292e (patch)
tree88d1cb17af6af9948d44ffb7242d69be5743785d /atuin-client/src/kv.rs
parentBump debian from bullseye-20230502-slim to bullseye-20230612-slim (#1047) (diff)
downloadatuin-ae1709dafd22ac3c64441472e90df8799253292e.zip
Key values (#1038)
* wip * Start testing * Store host IDs, not hostnames Why? Hostnames can change a lot, and therefore host filtering can be funky. Really, all we want is a unique ID per machine + do not care what it might be. * Mostly just write a fuckload of tests * Add a v0 kv store I can push to * Appending works * Add next() and iterate, test the pointer chain * Fix sig * Make clippy happy and thaw the ICE * Fix tests' * Fix tests * typed builder and cleaner db trait --------- Co-authored-by: Conrad Ludgate <conrad.ludgate@truelayer.com>
Diffstat (limited to 'atuin-client/src/kv.rs')
-rw-r--r--atuin-client/src/kv.rs103
1 files changed, 103 insertions, 0 deletions
diff --git a/atuin-client/src/kv.rs b/atuin-client/src/kv.rs
new file mode 100644
index 00000000..87149275
--- /dev/null
+++ b/atuin-client/src/kv.rs
@@ -0,0 +1,103 @@
+use eyre::Result;
+use serde::{Deserialize, Serialize};
+
+use crate::record::store::Store;
+use crate::settings::Settings;
+
+const KV_VERSION: &str = "v0";
+const KV_TAG: &str = "kv";
+
+#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
+pub struct KvRecord {
+ pub key: String,
+ pub value: String,
+}
+
+impl KvRecord {
+ pub fn serialize(&self) -> Result<Vec<u8>> {
+ let buf = rmp_serde::to_vec(self)?;
+
+ Ok(buf)
+ }
+}
+
+pub struct KvStore;
+
+impl Default for KvStore {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl KvStore {
+ // will want to init the actual kv store when that is done
+ pub fn new() -> KvStore {
+ KvStore {}
+ }
+
+ pub async fn set(
+ &self,
+ store: &mut (impl Store + Send + Sync),
+ key: &str,
+ value: &str,
+ ) -> Result<()> {
+ let host_id = Settings::host_id().expect("failed to get host_id");
+
+ let record = KvRecord {
+ key: key.to_string(),
+ value: value.to_string(),
+ };
+
+ let bytes = record.serialize()?;
+
+ let parent = store
+ .last(host_id.as_str(), KV_TAG)
+ .await?
+ .map(|entry| entry.id);
+
+ let record = atuin_common::record::Record::builder()
+ .host(host_id)
+ .version(KV_VERSION.to_string())
+ .tag(KV_TAG.to_string())
+ .parent(parent)
+ .data(bytes)
+ .build();
+
+ store.push(&record).await?;
+
+ Ok(())
+ }
+
+ // TODO: setup an actual kv store, rebuild func, and do not pass the main store in here as
+ // well.
+ pub async fn get(&self, store: &impl Store, key: &str) -> Result<Option<KvRecord>> {
+ // TODO: don't load this from disk so much
+ let host_id = Settings::host_id().expect("failed to get host_id");
+
+ // Currently, this is O(n). When we have an actual KV store, it can be better
+ // Just a poc for now!
+
+ // iterate records to find the value we want
+ // start at the end, so we get the most recent version
+ let Some(mut record) = store.last(host_id.as_str(), KV_TAG).await? else {
+ return Ok(None);
+ };
+ let kv: KvRecord = rmp_serde::from_slice(&record.data)?;
+
+ if kv.key == key {
+ return Ok(Some(kv));
+ }
+
+ while let Some(parent) = record.parent {
+ record = store.get(parent.as_str()).await?;
+ let kv: KvRecord = rmp_serde::from_slice(&record.data)?;
+
+ if kv.key == key {
+ return Ok(Some(kv));
+ }
+ }
+
+ // if we get here, then... we didn't find the record with that key :(
+ Ok(None)
+ }
+}