summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEllie Huxtable <ellie@elliehuxtable.com>2023-12-09 17:36:50 +0000
committerEllie Huxtable <ellie@elliehuxtable.com>2023-12-12 07:53:40 +0000
commitb99de3c4d7d46a7b4ec9d9f699876bccc6da5690 (patch)
tree58b2822fc14244599f11da64418f644dc9e9b959
parenteadce83a573c0a0442426abccc8af83a1a10e3ce (diff)
wip
-rw-r--r--atuin-client/src/history.rs3
-rw-r--r--atuin-client/src/kv.rs25
-rw-r--r--atuin-client/src/record/sqlite_store.rs34
-rw-r--r--atuin-client/src/record/store.rs4
-rw-r--r--atuin/src/command/client/record.rs27
5 files changed, 80 insertions, 13 deletions
diff --git a/atuin-client/src/history.rs b/atuin-client/src/history.rs
index 180e1c88..2b2c41ee 100644
--- a/atuin-client/src/history.rs
+++ b/atuin-client/src/history.rs
@@ -312,9 +312,8 @@ impl History {
#[cfg(test)]
mod tests {
- use atuin_common::record::DecryptedData;
use regex::RegexSet;
- use time::{macros::datetime, OffsetDateTime};
+ use time::macros::datetime;
use crate::{history::HISTORY_VERSION, settings::Settings};
diff --git a/atuin-client/src/kv.rs b/atuin-client/src/kv.rs
index 6876bb3b..3c97b8d4 100644
--- a/atuin-client/src/kv.rs
+++ b/atuin-client/src/kv.rs
@@ -151,11 +151,30 @@ impl KvStore {
// use as a write-through cache to avoid constant rebuilds.
pub async fn build_kv(
&self,
- _store: &impl Store,
- _encryption_key: &[u8; 32],
+ store: &impl Store,
+ encryption_key: &[u8; 32],
) -> Result<BTreeMap<String, BTreeMap<String, String>>> {
let map = BTreeMap::new();
- // TODO: implement
+
+ // get the status of all stores
+ let mut tagged = store.all_tagged(KV_TAG).await?;
+
+ // iterate through all tags and play each KV record at a time
+ for (host, record) in tagged {
+ let decrypted = match record.version.as_str() {
+ KV_VERSION => record.decrypt::<PASETO_V4>(encryption_key)?,
+ version => bail!("unknown version {version:?}"),
+ };
+
+ let kv = KvRecord::deserialize(&decrypted.data, &decrypted.version)?;
+
+ let next = store.next(host, KV_TAG, decrypted.idx, 1).await?;
+
+ write a function that iterates the kv records in order and builds a map
+ maybe next_record(tag) that returns the next of that tag in time, ignoring host?
+ then write some tests for this new sync
+ also see what happens if we run the new server but try and sync with v17 client. I imagine `atuin sync` breaks, but auto sync is fine. maybe version the api now.
+ }
Ok(map)
}
diff --git a/atuin-client/src/record/sqlite_store.rs b/atuin-client/src/record/sqlite_store.rs
index 245baefd..43bc2cb2 100644
--- a/atuin-client/src/record/sqlite_store.rs
+++ b/atuin-client/src/record/sqlite_store.rs
@@ -2,8 +2,8 @@
// Multiple stores of multiple types are all stored in one chonky table (for now), and we just index
// by tag/host
-use std::path::Path;
use std::str::FromStr;
+use std::{collections::HashMap, path::Path};
use async_trait::async_trait;
use eyre::{eyre, Result};
@@ -190,7 +190,7 @@ impl Store for SqliteStore {
) -> Result<Option<Record<EncryptedData>>> {
let res = sqlx::query("select * from store where idx = ?1 and host = ?2 and tag = ?3")
.bind(idx as i64)
- .bind(host)
+ .bind(host.0.as_hyphenated().to_string())
.bind(tag)
.map(Self::query_row)
.fetch_one(&self.pool)
@@ -227,14 +227,22 @@ impl Store for SqliteStore {
Ok(status)
}
- async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>> {
+ async fn all_tagged(&self, tag: &str) -> Result<HashMap<HostId, Record<EncryptedData>>> {
let res = sqlx::query("select * from store where idx = 0 and tag = ?1")
.bind(tag)
.map(Self::query_row)
.fetch_all(&self.pool)
.await?;
- Ok(res)
+ let mut ret = HashMap::new();
+
+ for i in res {
+ assert!(ret.get(&i.host.id).is_none());
+
+ ret.insert(i.host.id, i);
+ }
+
+ Ok(ret)
}
}
@@ -308,6 +316,24 @@ mod tests {
}
#[tokio::test]
+ async fn first() {
+ let db = SqliteStore::new(":memory:").await.unwrap();
+ let record = test_record();
+ db.push(&record).await.unwrap();
+
+ let first = db
+ .first(record.host.id, record.tag.as_str())
+ .await
+ .expect("failed to get store len");
+
+ assert_eq!(
+ first.unwrap().id,
+ record.id,
+ "expected to get back the same record that was inserted"
+ );
+ }
+
+ #[tokio::test]
async fn len() {
let db = SqliteStore::new(":memory:").await.unwrap();
let record = test_record();
diff --git a/atuin-client/src/record/store.rs b/atuin-client/src/record/store.rs
index 5e34312e..eb6cd604 100644
--- a/atuin-client/src/record/store.rs
+++ b/atuin-client/src/record/store.rs
@@ -1,3 +1,5 @@
+use std::collections::HashMap;
+
use async_trait::async_trait;
use eyre::Result;
@@ -48,5 +50,5 @@ pub trait Store {
/// Get every start record for a given tag, regardless of host.
/// Useful when actually operating on synchronized data, and will often have conflict
/// resolution applied.
- async fn all_tagged(&self, tag: &str) -> Result<Vec<Record<EncryptedData>>>;
+ async fn all_tagged(&self, tag: &str) -> Result<HashMap<HostId, Record<EncryptedData>>>;
}
diff --git a/atuin/src/command/client/record.rs b/atuin/src/command/client/record.rs
index 2250232c..6326bbd6 100644
--- a/atuin/src/command/client/record.rs
+++ b/atuin/src/command/client/record.rs
@@ -2,6 +2,7 @@ use clap::Subcommand;
use eyre::Result;
use atuin_client::{record::store::Store, settings::Settings};
+use time::OffsetDateTime;
#[derive(Subcommand, Debug)]
#[command(infer_subcommands = true)]
@@ -19,7 +20,8 @@ impl Cmd {
let status = store.status().await?;
- for (host, store) in &status.hosts {
+ // TODO: should probs build some data structure and then pretty-print it or smth
+ for (host, st) in &status.hosts {
let host_string = if host == &host_id {
format!("host: {} <- CURRENT HOST", host.0.as_hyphenated())
} else {
@@ -28,8 +30,27 @@ impl Cmd {
println!("{host_string}");
- for (tag, idx) in store {
- println!("\tstore: {tag} at {idx}");
+ for (tag, idx) in st {
+ println!("\tstore: {tag}");
+
+ let first = store.first(*host, tag).await?;
+ let last = store.last(*host, tag).await?;
+
+ println!("\t\tidx: {idx}");
+
+ if let Some(first) = first {
+ println!("\t\tfirst: {}", first.id.0.as_hyphenated().to_string());
+
+ let time = OffsetDateTime::from_unix_timestamp_nanos(first.timestamp as i128)?;
+ println!("\t\t\tcreated: {}", time.to_string());
+ }
+
+ if let Some(last) = last {
+ println!("\t\tlast: {}", last.id.0.as_hyphenated().to_string());
+
+ let time = OffsetDateTime::from_unix_timestamp_nanos(last.timestamp as i128)?;
+ println!("\t\t\tcreated: {:?}", time.to_string());
+ }
}
println!();