diff options
| -rw-r--r-- | atuin-client/src/record/sqlite_store.rs | 6 | ||||
| -rw-r--r-- | atuin-client/src/record/store.rs | 2 | ||||
| -rw-r--r-- | atuin/src/command/client/store.rs | 9 | ||||
| -rw-r--r-- | atuin/src/command/client/store/pull.rs | 86 |
4 files changed, 103 insertions, 0 deletions
diff --git a/atuin-client/src/record/sqlite_store.rs b/atuin-client/src/record/sqlite_store.rs index 4e4a3756..e5e3b9cd 100644 --- a/atuin-client/src/record/sqlite_store.rs +++ b/atuin-client/src/record/sqlite_store.rs @@ -154,6 +154,12 @@ impl Store for SqliteStore { Ok(()) } + async fn delete_all(&self) -> Result<()> { + sqlx::query("delete from store").execute(&self.pool).await?; + + Ok(()) + } + async fn last(&self, host: HostId, tag: &str) -> Result<Option<Record<EncryptedData>>> { let res = sqlx::query("select * from store where host=?1 and tag=?2 order by idx desc limit 1") diff --git a/atuin-client/src/record/store.rs b/atuin-client/src/record/store.rs index 1d812549..ae8ea356 100644 --- a/atuin-client/src/record/store.rs +++ b/atuin-client/src/record/store.rs @@ -21,7 +21,9 @@ pub trait Store { ) -> Result<()>; async fn get(&self, id: RecordId) -> Result<Record<EncryptedData>>; + async fn delete(&self, id: RecordId) -> Result<()>; + async fn delete_all(&self) -> Result<()>; async fn len(&self, host: HostId, tag: &str) -> Result<u64>; async fn len_tag(&self, tag: &str) -> Result<u64>; diff --git a/atuin/src/command/client/store.rs b/atuin/src/command/client/store.rs index 16618b04..8e53954d 100644 --- a/atuin/src/command/client/store.rs +++ b/atuin/src/command/client/store.rs @@ -11,6 +11,9 @@ use time::OffsetDateTime; #[cfg(feature = "sync")] mod push; +#[cfg(feature = "sync")] +mod pull; + mod purge; mod rebuild; mod rekey; @@ -27,6 +30,9 @@ pub enum Cmd { #[cfg(feature = "sync")] Push(push::Push), + + #[cfg(feature = "sync")] + Pull(pull::Pull), } impl Cmd { @@ -45,6 +51,9 @@ impl Cmd { #[cfg(feature = "sync")] Self::Push(push) => push.run(settings, store).await, + + #[cfg(feature = "sync")] + Self::Pull(pull) => pull.run(settings, store, database).await, } } diff --git a/atuin/src/command/client/store/pull.rs b/atuin/src/command/client/store/pull.rs new file mode 100644 index 00000000..d920dd21 --- /dev/null +++ b/atuin/src/command/client/store/pull.rs @@ -0,0 +1,86 @@ +use clap::Args; +use eyre::{Result, WrapErr}; + +use atuin_client::{ + database::Database, + encryption, + history::store::HistoryStore, + record::store::Store, + record::sync::Operation, + record::{sqlite_store::SqliteStore, sync}, + settings::Settings, +}; + +#[derive(Args, Debug)] +pub struct Pull { + /// The tag to push (eg, 'history'). Defaults to all tags + #[arg(long, short)] + pub tag: Option<String>, + + /// Force push records + /// This will first wipe the local store, and then download all records from the remote + #[arg(long, default_value = "false")] + pub force: bool, +} + +impl Pull { + pub async fn run( + &self, + settings: &Settings, + store: SqliteStore, + db: &dyn Database, + ) -> Result<()> { + if self.force { + println!("Forcing local overwrite!"); + println!("Clearing local store"); + + store.delete_all().await?; + } + + // We can actually just use the existing diff/etc to push + // 1. Diff + // 2. Get operations + // 3. Filter operations by + // a) are they a download op? + // b) are they for the host/tag we are pushing here? + let (diff, _) = sync::diff(settings, &store).await?; + let operations = sync::operations(diff, &store).await?; + + let operations = operations + .into_iter() + .filter(|op| match op { + // No noops or downloads thx + Operation::Noop { .. } | Operation::Upload { .. } => false, + + // pull, so yes plz to downloads! + Operation::Download { tag, .. } => { + if self.force { + return true; + } + + if let Some(t) = self.tag.clone() { + if t != *tag { + return false; + } + } + + true + } + }) + .collect(); + + let (_, downloaded) = sync::sync_remote(operations, &store, settings).await?; + + println!("Downloaded {} records", downloaded.len()); + + let encryption_key: [u8; 32] = encryption::load_key(settings) + .context("could not load encryption key")? + .into(); + + let host_id = Settings::host_id().expect("failed to get host_id"); + let history_store = HistoryStore::new(store.clone(), host_id, encryption_key); + history_store.incremental_build(db, &downloaded).await?; + + Ok(()) + } +} |
