Do some small fixes & update deps
This commit is contained in:
@ -1,13 +1,13 @@
|
||||
use crate::{local, planner, remote, Opt};
|
||||
|
||||
pub fn run(opt: &Opt) -> anyhow::Result<()> {
|
||||
pub fn run(opt: &Opt, include_all: bool) -> anyhow::Result<()> {
|
||||
info!("showing backup plan");
|
||||
|
||||
let local_list = local::file_list(opt)?;
|
||||
let session = remote::connect(opt)?;
|
||||
let remote_list = remote::file_list(opt, &session)?;
|
||||
|
||||
let plan = planner::plan(&local_list, &remote_list);
|
||||
let plan = planner::plan(&local_list, &remote_list, include_all);
|
||||
let presence = planner::presence(&local_list, &remote_list);
|
||||
|
||||
println!(
|
||||
|
||||
@ -12,20 +12,13 @@ use std::time::Instant;
|
||||
|
||||
const TMP_FOLDER: &str = ".tmp";
|
||||
|
||||
pub fn run(opt: &Opt, sync_all: bool) -> anyhow::Result<()> {
|
||||
// TODO: currently we only sync the latest local files
|
||||
// --all will force a sync of ALL files on local which does not exist on remote
|
||||
if sync_all {
|
||||
error!("backup --all is not yet implemented");
|
||||
unimplemented!();
|
||||
}
|
||||
|
||||
pub fn run(opt: &Opt, include_all: bool) -> anyhow::Result<()> {
|
||||
info!("generating backup plan");
|
||||
let local_list = local::file_list(opt)?;
|
||||
let session = remote::connect(opt)?;
|
||||
let remote_list = remote::file_list(opt, &session)?;
|
||||
|
||||
let plan = planner::plan(&local_list, &remote_list);
|
||||
let plan = planner::plan(&local_list, &remote_list, include_all);
|
||||
|
||||
if plan.transfers.is_empty() {
|
||||
info!("nothing to do");
|
||||
@ -144,14 +137,14 @@ fn send_snapshot(
|
||||
.ok_or_else(|| anyhow::format_err!("failed to take stdout"))?;
|
||||
|
||||
// #### UPLOAD SNAPSHOT FILE ####
|
||||
const CHUNK_SIZE: usize = 1024 * 1024 * 100; // 100MB
|
||||
const CHUNK_SIZE: usize = 1024 * 1024 * 100; // 100MiB
|
||||
|
||||
let (data_tx, data_rx) = mpsc::sync_channel(10);
|
||||
let tmp_path = opt.remote.path.join(TMP_FOLDER);
|
||||
|
||||
// spawn a thread to stream data from btrfs send in chunks
|
||||
// spawn a thread to stream data from `btrfs send` in chunks
|
||||
thread::spawn(move || -> io::Result<()> {
|
||||
'outer: for _ in 0.. {
|
||||
'outer: for _chunk in 0.. {
|
||||
let mut buf: Vec<u8> = vec![0u8; CHUNK_SIZE];
|
||||
let mut len = 0;
|
||||
loop {
|
||||
|
||||
@ -16,7 +16,7 @@ pub fn file_list(opt: &Opt) -> anyhow::Result<FileList> {
|
||||
|
||||
let name = match entry.file_name().into_string() {
|
||||
Ok(name) => name,
|
||||
Err(_) => continue,
|
||||
Err(_) => continue, // ignore names that aren't valid utf-8
|
||||
};
|
||||
|
||||
let date = DateTime::parse_from_rfc3339(&name)?;
|
||||
|
||||
18
src/main.rs
18
src/main.rs
@ -5,12 +5,11 @@ mod actions;
|
||||
mod local;
|
||||
mod planner;
|
||||
mod remote;
|
||||
mod snapshot;
|
||||
mod util;
|
||||
|
||||
use actions::{list, show_plan, sync};
|
||||
use chrono::{DateTime, FixedOffset};
|
||||
use clap::{crate_version, AppSettings, Clap};
|
||||
use clap::{crate_version, Parser};
|
||||
use remote::Remote;
|
||||
use std::collections::BTreeMap;
|
||||
use std::path::PathBuf;
|
||||
@ -19,8 +18,8 @@ pub type TimeStamp = DateTime<FixedOffset>;
|
||||
pub type FileList = BTreeMap<TimeStamp, String>;
|
||||
|
||||
/// Backup btrfs snapshots over SSH
|
||||
#[derive(Clap)]
|
||||
#[clap(version = crate_version!(), setting = AppSettings::ColoredHelp)]
|
||||
#[derive(Parser)]
|
||||
#[clap(version = crate_version!())]
|
||||
pub struct Opt {
|
||||
/// The path of the backup directory on the local filesystem
|
||||
#[clap(short = 'l', long)]
|
||||
@ -42,16 +41,21 @@ pub struct Opt {
|
||||
action: Action,
|
||||
}
|
||||
|
||||
#[derive(Clap)]
|
||||
#[derive(Parser)]
|
||||
pub enum Action {
|
||||
/// Perform a backup
|
||||
Backup {
|
||||
/// Backup all files, not just the most recent ones
|
||||
#[clap(long)]
|
||||
all: bool,
|
||||
},
|
||||
|
||||
/// Generate and show a backup plan
|
||||
ShowPlan,
|
||||
ShowPlan {
|
||||
/// Backup all files, not just the most recent ones
|
||||
#[clap(long)]
|
||||
all: bool,
|
||||
},
|
||||
|
||||
/// List all backups, and where they reside
|
||||
List,
|
||||
@ -64,7 +68,7 @@ fn main() -> anyhow::Result<()> {
|
||||
|
||||
match opt.action {
|
||||
Action::Backup { all } => sync::run(&opt, all)?,
|
||||
Action::ShowPlan => show_plan::run(&opt)?,
|
||||
Action::ShowPlan { all } => show_plan::run(&opt, all)?,
|
||||
Action::List => list::run(&opt)?,
|
||||
}
|
||||
|
||||
|
||||
@ -8,7 +8,7 @@ pub enum Presence {
|
||||
LocalAndRemote,
|
||||
}
|
||||
|
||||
/// For every local file that is not in the remote, return a backup plan.
|
||||
/// Check which files exist on remote, local, or both
|
||||
pub fn presence(local: &FileList, remote: &FileList) -> BTreeMap<TimeStamp, Presence> {
|
||||
let mut presence = BTreeMap::new();
|
||||
|
||||
@ -41,17 +41,24 @@ pub struct Plan {
|
||||
pub transfers: BTreeMap<TimeStamp, TransferKind>,
|
||||
}
|
||||
|
||||
/// For every local file that is not in the remote, return a backup plan.
|
||||
/// For every trailing local file that is not in the remote, return a backup plan.
|
||||
///
|
||||
/// The backup plans may depend on each other, so they must be executed in order.
|
||||
pub fn plan(local: &FileList, remote: &FileList) -> Plan {
|
||||
// go through the local files in order, starting with the latest
|
||||
let upload_list: BTreeSet<_> = local
|
||||
.keys()
|
||||
.rev()
|
||||
// keep going while the file doesn't exist in the remote
|
||||
.take_while(|ts| !remote.contains_key(ts))
|
||||
.collect();
|
||||
///
|
||||
/// Set `include_all` to include all local files, not just the most recent.
|
||||
pub fn plan(local: &FileList, remote: &FileList, include_all: bool) -> Plan {
|
||||
let upload_list: BTreeSet<_> = {
|
||||
// go through the local files in order, starting with the most recent
|
||||
let local = local.keys().rev();
|
||||
|
||||
if include_all {
|
||||
// take all files that doesn't exist in the remote
|
||||
local.filter(|ts| !remote.contains_key(ts)).collect()
|
||||
} else {
|
||||
// take only the most recent files that doesn't exist in the remote
|
||||
local.take_while(|ts| !remote.contains_key(ts)).collect()
|
||||
}
|
||||
};
|
||||
|
||||
// find the closest parent file of the first planned upload
|
||||
let head_item = upload_list.iter().next().copied();
|
||||
|
||||
@ -41,7 +41,7 @@ pub fn connect(opt: &Opt) -> anyhow::Result<Session> {
|
||||
pub fn file_list(opt: &Opt, session: &Session) -> anyhow::Result<FileList> {
|
||||
let mut channel = session.channel_session()?;
|
||||
channel.exec(&format!(
|
||||
r#"ls -1N "{}""#,
|
||||
r#"ls -1NU "{}""#,
|
||||
opt.remote
|
||||
.path
|
||||
.to_str()
|
||||
@ -53,7 +53,6 @@ pub fn file_list(opt: &Opt, session: &Session) -> anyhow::Result<FileList> {
|
||||
let mut list = BTreeMap::new();
|
||||
for file in output.lines() {
|
||||
let date = DateTime::parse_from_rfc3339(file)?;
|
||||
//let path = PathBuf::from(file);
|
||||
list.insert(date, file.to_string());
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user