diff options
author | Ellie Huxtable <ellie@elliehuxtable.com> | 2023-12-09 17:36:50 +0000 |
---|---|---|
committer | Ellie Huxtable <ellie@elliehuxtable.com> | 2023-12-12 07:53:40 +0000 |
commit | b99de3c4d7d46a7b4ec9d9f699876bccc6da5690 (patch) | |
tree | 58b2822fc14244599f11da64418f644dc9e9b959 | |
parent | eadce83a573c0a0442426abccc8af83a1a10e3ce (diff) |
wip
-rw-r--r-- | atuin-client/src/history.rs | 3 | ||||
-rw-r--r-- | atuin-client/src/kv.rs | 25 | ||||
-rw-r--r-- | atuin-client/src/record/sqlite_store.rs | 34 | ||||
-rw-r--r-- | atuin-client/src/record/store.rs | 4 | ||||
-rw-r--r-- | atuin/src/command/client/record.rs | 27 |
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!(); |