use crate::cli::{DEFAULT_REMOTE_NAME, REMOTE_NAME_ENV, Switch, TRANSPORT_ENV};
use crate::commands;
use crate::session::Master;
use sefy_core::Vault;
use zeroize::Zeroizing;
pub struct AutoSync {
on: bool,
master: Master,
synced_at: u64,
}
impl AutoSync {
pub fn new(switch: Option<Switch>, master: &Master) -> Self {
Self {
on: Self::is_wanted(switch),
master: master.clone(),
synced_at: 0,
}
}
pub fn off() -> Self {
Self::new(Some(Switch::Off), &Master::Typed(Zeroizing::default()))
}
pub fn is_wanted(switch: Option<Switch>) -> bool {
switch == Some(Switch::On)
}
pub fn catch_up(&mut self, vault: &mut Vault) {
if !self.on || vault.writes() == self.synced_at {
return;
}
let outcome = self.sync(vault);
self.synced_at = vault.writes();
match outcome {
Ok(lines) => {
for line in lines {
eprintln!("{line}");
}
}
Err(error) => {
eprintln!(
"warning: the change is saved here but did not reach the remote: {error:#}\n\
it goes up with the next `sefy sync`."
);
}
}
}
fn sync(&self, vault: &mut Vault) -> anyhow::Result<Vec<String>> {
let plugin = commands::transport(remote_setting(TRANSPORT_ENV).as_deref())?;
let name = remote_setting(REMOTE_NAME_ENV).unwrap_or_else(|| DEFAULT_REMOTE_NAME.into());
let report = match sefy_core::sync(vault, &plugin, &name, self.master.opener()) {
Ok(report) => report,
Err(sefy_core::Error::WrongPasswordOrNotAVault) => anyhow::bail!(
"the remote copy does not open under this vault's password\n\
sync it once with `sefy sync --ask-remote-password`"
),
Err(other) => return Err(other.into()),
};
let mut lines = vec![format!("synced {name:?} through {}", plugin.name())];
if report.pulled.merged.changed() || report.pulled.merged.unsupported > 0 {
lines.extend(commands::merge_lines(
&report.pulled.merged,
commands::NOTHING_CAME_BACK,
));
}
Ok(lines)
}
}
fn remote_setting(variable: &str) -> Option<String> {
std::env::var(variable)
.ok()
.filter(|value| !value.is_empty())
}