aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--atuin-client/src/record/sqlite_store.rs6
-rw-r--r--atuin-client/src/record/store.rs2
-rw-r--r--atuin/src/command/client/store.rs9
-rw-r--r--atuin/src/command/client/store/pull.rs86
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(())
+ }
+}