mirror of
https://github.com/Dummi26/rembackup.git
synced 2025-03-10 05:13:54 +01:00
show sizes in list of changes; sort; progress bar
in the list of changes, "add file" and "add dir" type changes now show the size of the file to be copied or the sum of the directory content sizes. changes are now sorted from largest to smallest progress bar now shows the smaller and larger progress of change-count or transferred size, where the larger progress is before the '>' and the smaller one is on the last '=' before the '-'s start (which indicate larger-not-smaller) [========----------> ]
This commit is contained in:
parent
20f37de854
commit
6172ffe248
@ -37,7 +37,7 @@ fn eprint_status(
|
|||||||
changes_len_width: usize,
|
changes_len_width: usize,
|
||||||
gib_len_width: usize,
|
gib_len_width: usize,
|
||||||
) {
|
) {
|
||||||
let leftpad = prog_width.min(
|
let leftpad_min = prog_width.min(
|
||||||
(prog_width as f64
|
(prog_width as f64
|
||||||
* f64::min(
|
* f64::min(
|
||||||
changes_applied as f64 / changes_total as f64,
|
changes_applied as f64 / changes_total as f64,
|
||||||
@ -45,15 +45,24 @@ fn eprint_status(
|
|||||||
))
|
))
|
||||||
.round() as usize,
|
.round() as usize,
|
||||||
);
|
);
|
||||||
|
let leftpad_max = prog_width.min(
|
||||||
|
(prog_width as f64
|
||||||
|
* f64::max(
|
||||||
|
changes_applied as f64 / changes_total as f64,
|
||||||
|
gib_transferred / gib_total,
|
||||||
|
))
|
||||||
|
.round() as usize,
|
||||||
|
);
|
||||||
let changes_applied = changes_applied.to_string();
|
let changes_applied = changes_applied.to_string();
|
||||||
let changes_pad = " ".repeat(changes_len_width - changes_applied.len());
|
let changes_pad = " ".repeat(changes_len_width - changes_applied.len());
|
||||||
let gib_transferred = format!("{gib_transferred:.1}");
|
let gib_transferred = format!("{gib_transferred:.1}");
|
||||||
let gib_pad = " ".repeat(gib_len_width - gib_transferred.len());
|
let gib_pad = " ".repeat(gib_len_width - gib_transferred.len());
|
||||||
let rightpad = prog_width - leftpad;
|
let rightpad = prog_width - leftpad_max;
|
||||||
let completed_prog = "-".repeat(leftpad);
|
let completed_prog_min = "=".repeat(leftpad_min);
|
||||||
|
let completed_prog_max = "-".repeat(leftpad_max - leftpad_min);
|
||||||
let pending_prog = " ".repeat(rightpad);
|
let pending_prog = " ".repeat(rightpad);
|
||||||
eprint!(
|
eprint!(
|
||||||
"\r{changes_pad}{changes_applied}/{changes_total} | {gib_pad}{gib_transferred}/{gib_total:.1}GiB [{completed_prog}>{pending_prog}]",
|
"\r{changes_pad}{changes_applied}/{changes_total} | {gib_pad}{gib_transferred}/{gib_total:.1}GiB [{completed_prog_min}{completed_prog_max}>{pending_prog}]",
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -91,16 +100,10 @@ pub fn apply_indexchanges_int(
|
|||||||
);
|
);
|
||||||
for (i, change) in changes.iter().enumerate() {
|
for (i, change) in changes.iter().enumerate() {
|
||||||
match change {
|
match change {
|
||||||
IndexChange::AddDir(dir) => {
|
IndexChange::AddDir(dir, _) => {
|
||||||
let ok = if let Some(target) = target {
|
let ok = if let Some(target) = target {
|
||||||
let t = target.join(dir);
|
let t = target.join(dir);
|
||||||
if let Some(e) = fs::create_dir(&t).err().and_then(|e| {
|
if let Err(e) = fs::create_dir_all(&t) {
|
||||||
if matches!(e.kind(), io::ErrorKind::AlreadyExists) {
|
|
||||||
None
|
|
||||||
} else {
|
|
||||||
Some(e)
|
|
||||||
}
|
|
||||||
}) {
|
|
||||||
eprintln!("\n[warn] couldn't create directory {t:?}: {e}");
|
eprintln!("\n[warn] couldn't create directory {t:?}: {e}");
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@ -110,7 +113,7 @@ pub fn apply_indexchanges_int(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if ok {
|
if ok {
|
||||||
fs::create_dir(&index.join(dir))?;
|
fs::create_dir_all(&index.join(dir))?;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IndexChange::AddFile(file, index_file) => {
|
IndexChange::AddFile(file, index_file) => {
|
||||||
|
@ -5,7 +5,7 @@ use crate::indexfile::IndexFile;
|
|||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum IndexChange {
|
pub enum IndexChange {
|
||||||
/// Ensure a directory with this path exists (at least if all its parent directories exist).
|
/// Ensure a directory with this path exists (at least if all its parent directories exist).
|
||||||
AddDir(PathBuf),
|
AddDir(PathBuf, u64),
|
||||||
/// Add or update a file
|
/// Add or update a file
|
||||||
AddFile(PathBuf, IndexFile),
|
AddFile(PathBuf, IndexFile),
|
||||||
/// Remove a file
|
/// Remove a file
|
||||||
|
61
src/main.rs
61
src/main.rs
@ -71,12 +71,17 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
Ignore(vec![])
|
Ignore(vec![])
|
||||||
};
|
};
|
||||||
let changes = match perform_index_diff(
|
let (total_size, changes) = match perform_index_diff(
|
||||||
&source,
|
&source,
|
||||||
&index,
|
&index,
|
||||||
target.as_ref().map(|v| v.as_path()),
|
target.as_ref().map(|v| v.as_path()),
|
||||||
ignore,
|
ignore,
|
||||||
&args.settings,
|
&args.settings,
|
||||||
|
if args.settings.dont_sort {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(!args.settings.smallest_first)
|
||||||
|
},
|
||||||
) {
|
) {
|
||||||
Ok(c) => c,
|
Ok(c) => c,
|
||||||
Err((what, path, err)) => {
|
Err((what, path, err)) => {
|
||||||
@ -92,12 +97,42 @@ fn main() {
|
|||||||
} else {
|
} else {
|
||||||
eprintln!("done! found {} changes:", changes.len());
|
eprintln!("done! found {} changes:", changes.len());
|
||||||
// display the changes
|
// display the changes
|
||||||
for change in &changes {
|
if args.settings.dont_reverse_output {
|
||||||
|
for change in &changes {
|
||||||
|
show_change(change, false);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for change in changes.iter().rev() {
|
||||||
|
show_change(change, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fn show_change(change: &IndexChange, rev: bool) {
|
||||||
match change {
|
match change {
|
||||||
IndexChange::AddDir(v) => eprintln!(" >> {}", v.display()),
|
IndexChange::AddDir(v, s) => {
|
||||||
IndexChange::AddFile(v, _) => eprintln!(" + {}", v.display()),
|
let mut path_str = v.display().to_string();
|
||||||
|
if !path_str.ends_with(['/', '\\']) {
|
||||||
|
path_str.push('/');
|
||||||
|
}
|
||||||
|
eprintln!(
|
||||||
|
"{}>> {} [{:.2} GiB]",
|
||||||
|
if rev { "^" } else { "v" },
|
||||||
|
path_str,
|
||||||
|
*s as f64 / (1024 * 1024 * 1024) as f64
|
||||||
|
);
|
||||||
|
}
|
||||||
|
IndexChange::AddFile(v, f) => eprintln!(
|
||||||
|
" + {} ({:.3} GiB)",
|
||||||
|
v.display(),
|
||||||
|
f.size as f64 / (1024 * 1024 * 1024) as f64
|
||||||
|
),
|
||||||
IndexChange::RemoveFile(v) => eprintln!(" - {}", v.display()),
|
IndexChange::RemoveFile(v) => eprintln!(" - {}", v.display()),
|
||||||
IndexChange::RemoveDir(v) => eprintln!(" [-] {}", v.display()),
|
IndexChange::RemoveDir(v) => {
|
||||||
|
let mut path_str = v.display().to_string();
|
||||||
|
if !path_str.ends_with(['/', '\\']) {
|
||||||
|
path_str.push('/');
|
||||||
|
}
|
||||||
|
eprintln!(" [-] {}", path_str);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
eprintln!(" - - - - -");
|
eprintln!(" - - - - -");
|
||||||
@ -105,8 +140,15 @@ fn main() {
|
|||||||
.iter()
|
.iter()
|
||||||
.filter(|c| matches!(c, IndexChange::AddDir(..)))
|
.filter(|c| matches!(c, IndexChange::AddDir(..)))
|
||||||
.count();
|
.count();
|
||||||
eprintln!(" >> add directory | {add_dir_count}x");
|
eprintln!(
|
||||||
let (add_file_count, add_file_total_size_gib) = changes
|
" {}>> add directory | {add_dir_count}x",
|
||||||
|
if args.settings.dont_reverse_output {
|
||||||
|
"v"
|
||||||
|
} else {
|
||||||
|
"^"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
let add_file_count = changes
|
||||||
.iter()
|
.iter()
|
||||||
.filter_map(|c| {
|
.filter_map(|c| {
|
||||||
if let IndexChange::AddFile(_, f) = c {
|
if let IndexChange::AddFile(_, f) = c {
|
||||||
@ -115,9 +157,8 @@ fn main() {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.fold((0, 0.0f64), |(c, s), f| {
|
.count();
|
||||||
(c + 1, s + f.size as f64 / (1024 * 1024 * 1024) as f64)
|
let add_file_total_size_gib = total_size as f64 / (1024 * 1024 * 1024) as f64;
|
||||||
});
|
|
||||||
eprintln!(" + add/update file | {add_file_count}x ({add_file_total_size_gib:.1} GiB)");
|
eprintln!(" + add/update file | {add_file_count}x ({add_file_total_size_gib:.1} GiB)");
|
||||||
let remove_file_count = changes
|
let remove_file_count = changes
|
||||||
.iter()
|
.iter()
|
||||||
|
@ -14,6 +14,16 @@ use crate::{
|
|||||||
|
|
||||||
#[derive(Clone, Default, Args)]
|
#[derive(Clone, Default, Args)]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
|
/// don't sort the changes that form the backup. disables sort options.
|
||||||
|
#[arg(long)]
|
||||||
|
pub dont_sort: bool,
|
||||||
|
/// start with smaller directories rather than larger ones
|
||||||
|
#[arg(long)]
|
||||||
|
pub smallest_first: bool,
|
||||||
|
/// show changes in the order in which they will be applied, not reversed
|
||||||
|
#[arg(long)]
|
||||||
|
pub dont_reverse_output: bool,
|
||||||
|
|
||||||
/// don't update files just because their timestamp is different
|
/// don't update files just because their timestamp is different
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub ignore_timestamp: bool,
|
pub ignore_timestamp: bool,
|
||||||
@ -37,8 +47,8 @@ pub fn perform_index_diff<'a>(
|
|||||||
target: Option<&'a Path>,
|
target: Option<&'a Path>,
|
||||||
mut ignore: Ignore,
|
mut ignore: Ignore,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Result<Vec<IndexChange>, (String, PathBuf, io::Error)> {
|
sort_by_size_largest: Option<bool>,
|
||||||
let mut changes = Vec::new();
|
) -> Result<(u64, Vec<IndexChange>), (String, PathBuf, io::Error)> {
|
||||||
if let Ok(inner_index) = index.strip_prefix(source) {
|
if let Ok(inner_index) = index.strip_prefix(source) {
|
||||||
eprintln!("[info] source contains index at {inner_index:?}, but index will not be part of the backup.");
|
eprintln!("[info] source contains index at {inner_index:?}, but index will not be part of the backup.");
|
||||||
ignore.0.push(Specifier::InDir {
|
ignore.0.push(Specifier::InDir {
|
||||||
@ -55,15 +65,15 @@ pub fn perform_index_diff<'a>(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
rec(
|
let (total_size, changes) = rec(
|
||||||
source.as_ref(),
|
source.as_ref(),
|
||||||
Path::new(""),
|
Path::new(""),
|
||||||
index,
|
index,
|
||||||
&mut changes,
|
|
||||||
&ignore,
|
&ignore,
|
||||||
settings,
|
settings,
|
||||||
|
sort_by_size_largest,
|
||||||
)?;
|
)?;
|
||||||
Ok(changes)
|
Ok((total_size, changes))
|
||||||
}
|
}
|
||||||
fn rec(
|
fn rec(
|
||||||
// location of source files
|
// location of source files
|
||||||
@ -72,18 +82,17 @@ fn rec(
|
|||||||
rel_path: &Path,
|
rel_path: &Path,
|
||||||
// location of the index
|
// location of the index
|
||||||
index_files: &Path,
|
index_files: &Path,
|
||||||
// list of changes to be made
|
|
||||||
changes: &mut Vec<IndexChange>,
|
|
||||||
ignore: &Ignore,
|
ignore: &Ignore,
|
||||||
settings: &Settings,
|
settings: &Settings,
|
||||||
) -> Result<(), (String, PathBuf, io::Error)> {
|
sort_by_size_largest: Option<bool>,
|
||||||
|
) -> Result<(u64, Vec<IndexChange>), (String, PathBuf, io::Error)> {
|
||||||
|
let mut removals = vec![];
|
||||||
|
let mut ichanges = vec![];
|
||||||
|
let mut total_size = 0;
|
||||||
// used to find removals
|
// used to find removals
|
||||||
let index_rel_path = index_files.join(rel_path);
|
let index_rel_path = index_files.join(rel_path);
|
||||||
let mut index_entries = match fs::read_dir(&index_rel_path) {
|
let mut index_entries = match fs::read_dir(&index_rel_path) {
|
||||||
Err(_) => {
|
Err(_) => HashMap::new(),
|
||||||
changes.push(IndexChange::AddDir(rel_path.to_path_buf()));
|
|
||||||
HashMap::new()
|
|
||||||
}
|
|
||||||
Ok(e) => e
|
Ok(e) => e
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter_map(|v| v.ok())
|
.filter_map(|v| v.ok())
|
||||||
@ -128,29 +137,55 @@ fn rec(
|
|||||||
if metadata.is_dir() {
|
if metadata.is_dir() {
|
||||||
if let Some(false) = in_index_and_is_dir {
|
if let Some(false) = in_index_and_is_dir {
|
||||||
// is dir, but was file -> remove file
|
// is dir, but was file -> remove file
|
||||||
changes.push(IndexChange::RemoveFile(rel_path.clone()));
|
removals.push(IndexChange::RemoveFile(rel_path.clone()));
|
||||||
}
|
}
|
||||||
rec(source, &rel_path, index_files, changes, ignore, settings)?;
|
let (rec_size, rec_changes) = rec(
|
||||||
|
source,
|
||||||
|
&rel_path,
|
||||||
|
index_files,
|
||||||
|
ignore,
|
||||||
|
settings,
|
||||||
|
sort_by_size_largest,
|
||||||
|
)?;
|
||||||
|
total_size += rec_size;
|
||||||
|
ichanges.push((rec_size, rec_changes));
|
||||||
} else {
|
} else {
|
||||||
if let Some(true) = in_index_and_is_dir {
|
if let Some(true) = in_index_and_is_dir {
|
||||||
// is file, but was dir -> remove dir
|
// is file, but was dir -> remove dir
|
||||||
changes.push(IndexChange::RemoveDir(rel_path.clone()));
|
removals.push(IndexChange::RemoveDir(rel_path.clone()));
|
||||||
}
|
}
|
||||||
let newif = IndexFile::new_from_metadata(&metadata);
|
let newif = IndexFile::new_from_metadata(&metadata);
|
||||||
let oldif = IndexFile::from_path(&index_files.join(&rel_path));
|
let oldif = IndexFile::from_path(&index_files.join(&rel_path));
|
||||||
match oldif {
|
match oldif {
|
||||||
Ok(Ok(oldif)) if !newif.should_be_updated(&oldif, settings) => {}
|
Ok(Ok(oldif)) if !newif.should_be_updated(&oldif, settings) => {}
|
||||||
_ => changes.push(IndexChange::AddFile(rel_path, newif)),
|
_ => {
|
||||||
|
total_size += newif.size;
|
||||||
|
ichanges.push((newif.size, vec![IndexChange::AddFile(rel_path, newif)]));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// removals
|
// removals
|
||||||
for (removed_file, is_dir) in index_entries {
|
for (removed_file, is_dir) in index_entries {
|
||||||
changes.push(if is_dir {
|
removals.push(if is_dir {
|
||||||
IndexChange::RemoveDir(rel_path.join(removed_file))
|
IndexChange::RemoveDir(rel_path.join(removed_file))
|
||||||
} else {
|
} else {
|
||||||
IndexChange::RemoveFile(rel_path.join(removed_file))
|
IndexChange::RemoveFile(rel_path.join(removed_file))
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
Ok(())
|
// sorting
|
||||||
|
if let Some(sort_largest_first) = sort_by_size_largest {
|
||||||
|
if sort_largest_first {
|
||||||
|
ichanges.sort_by(|a, b| b.0.cmp(&a.0));
|
||||||
|
} else {
|
||||||
|
ichanges.sort_by_key(|v| v.0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// combine everything
|
||||||
|
let changes = [IndexChange::AddDir(rel_path.to_path_buf(), total_size)]
|
||||||
|
.into_iter()
|
||||||
|
.chain(removals.into_iter())
|
||||||
|
.chain(ichanges.into_iter().flat_map(|(_, v)| v))
|
||||||
|
.collect();
|
||||||
|
Ok((total_size, changes))
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user