use std::num::NonZeroU64;
use atuin_client::database::Sqlite;
use atuin_client::record::sqlite_store::SqliteStore;
use atuin_client::record::sync::{ClientSource, Operation, SyncSession};
use atuin_client::settings::Settings;
use atuin_common::encryption::paseto_v4;
use atuin_domain::record::RecordTag;
use clap::Args;
use eyre::{Context as _, Result};
#[derive(Args, Debug)]
pub struct Pull {
#[arg(long, short)]
pub tag: Option<RecordTag>,
#[arg(long, default_value = "false")]
pub force: bool,
#[arg(long, default_value = "100")]
pub page: NonZeroU64,
}
impl Pull {
pub async fn run(&self, settings: &Settings, store: SqliteStore, db: &Sqlite) -> Result<()> {
if self.force {
println!("Forcing local overwrite!");
println!("Clearing local store");
store.delete_all().await?;
}
let key = paseto_v4::Key::try_load_from_path(&settings.key_path)
.context("could not load encryption key")?;
let session = SyncSession::builder()
.store(store.clone())
.client_source(ClientSource::FromSettings {
settings,
caps: None,
})
.build()
.connect()
.await?
.with_page_size(self.page);
let keyed = session.keyed(&key);
let (diff, remote_index) = session.diff().await?;
if !self.force
&& let Some(err) = keyed.key_valid_against(&remote_index).await
{
return Err(crate::print_error::format_sync_error(err));
}
let operations = SyncSession::operations(diff)?;
let operations = operations
.into_iter()
.filter(|op| match op {
Operation::Noop { .. } | Operation::Upload { .. } => false,
Operation::Download { series, .. } => {
if self.force {
return true;
}
if let Some(t) = self.tag.clone()
&& t != series.tag
{
return false;
}
true
}
})
.collect();
let (_, downloaded) = keyed.sync_remote(operations).await?;
println!("Downloaded {} records", downloaded.len());
crate::sync::build(settings, &store, db, Some(&downloaded)).await?;
Ok(())
}
}