mirror of
https://github.com/Dummi26/rembackup.git
synced 2025-06-15 21:46:13 +02:00
Compare commits
No commits in common. "5ac3d9aee092a1d061d1635a117feaefd4464333" and "ed090e5962317ffdf5ac4032a62de754d47c6e89" have entirely different histories.
5ac3d9aee0
...
ed090e5962
@ -1,7 +1,7 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "rembackup"
|
name = "rembackup"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
edition = "2024"
|
edition = "2021"
|
||||||
|
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
use std::{
|
use std::{
|
||||||
fs,
|
fs, io,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -12,25 +12,12 @@ pub fn apply_indexchanges(
|
|||||||
source: &Path,
|
source: &Path,
|
||||||
index: &Path,
|
index: &Path,
|
||||||
target: &Option<PathBuf>,
|
target: &Option<PathBuf>,
|
||||||
changes: &[IndexChange],
|
changes: &Vec<IndexChange>,
|
||||||
gib_total: Option<f64>,
|
gib_total: Option<f64>,
|
||||||
) -> usize {
|
) -> io::Result<()> {
|
||||||
// do symlinks last, as they cd, which can fail,
|
let o = apply_indexchanges_int(source, index, target, changes, gib_total);
|
||||||
// and if it does, it would be fatal and stop the backup.
|
|
||||||
let (mut changes, symlink_additions) =
|
|
||||||
changes.into_iter().partition::<Vec<_>, _>(|c| match c {
|
|
||||||
IndexChange::AddDir(..)
|
|
||||||
| IndexChange::AddFile(..)
|
|
||||||
| IndexChange::RemoveFile(..)
|
|
||||||
| IndexChange::RemoveDir(..) => true,
|
|
||||||
IndexChange::AddSymlink(..) => false,
|
|
||||||
});
|
|
||||||
changes.extend(symlink_additions);
|
|
||||||
|
|
||||||
let mut failures = changes.len();
|
|
||||||
apply_indexchanges_int(source, index, target, &changes, gib_total, &mut failures);
|
|
||||||
eprintln!();
|
eprintln!();
|
||||||
failures
|
o
|
||||||
}
|
}
|
||||||
|
|
||||||
fn eprint_constants(changes_total: usize, gib_total: f64) -> (usize, usize, usize) {
|
fn eprint_constants(changes_total: usize, gib_total: f64) -> (usize, usize, usize) {
|
||||||
@ -76,6 +63,7 @@ fn eprint_status(
|
|||||||
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_min}{completed_prog_max}>{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}]",
|
||||||
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -83,10 +71,9 @@ pub fn apply_indexchanges_int(
|
|||||||
source: &Path,
|
source: &Path,
|
||||||
index: &Path,
|
index: &Path,
|
||||||
target: &Option<PathBuf>,
|
target: &Option<PathBuf>,
|
||||||
changes: &[&IndexChange],
|
changes: &Vec<IndexChange>,
|
||||||
gib_total: Option<f64>,
|
gib_total: Option<f64>,
|
||||||
failures: &mut usize,
|
) -> io::Result<()> {
|
||||||
) {
|
|
||||||
let changes_total = changes.len();
|
let changes_total = changes.len();
|
||||||
let gib_total = gib_total.unwrap_or_else(|| {
|
let gib_total = gib_total.unwrap_or_else(|| {
|
||||||
changes
|
changes
|
||||||
@ -100,7 +87,7 @@ pub fn apply_indexchanges_int(
|
|||||||
})
|
})
|
||||||
.sum()
|
.sum()
|
||||||
});
|
});
|
||||||
let (prog_width, changes_len_width, gib_len_width) = eprint_constants(changes_total, gib_total);
|
let (prog_width, changes_len_width, gib_len_width) = eprint_constants(changes.len(), gib_total);
|
||||||
let mut gib_transferred = 0.0;
|
let mut gib_transferred = 0.0;
|
||||||
eprint_status(
|
eprint_status(
|
||||||
0,
|
0,
|
||||||
@ -127,15 +114,9 @@ pub fn apply_indexchanges_int(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if ok {
|
if ok {
|
||||||
*failures -= 1;
|
fs::create_dir_all(&index.join(dir))?;
|
||||||
let t = index.join(dir);
|
|
||||||
if let Err(e) = fs::create_dir_all(&t) {
|
|
||||||
eprintln!("\n[warn] couldn't create index directory {t:?}: {e}");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
*failures -= 1;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
IndexChange::AddFile(file, index_file) => {
|
IndexChange::AddFile(file, index_file) => {
|
||||||
gib_transferred += index_file.size as f64 / (1024 * 1024 * 1024) as f64;
|
gib_transferred += index_file.size as f64 / (1024 * 1024 * 1024) as f64;
|
||||||
@ -152,29 +133,16 @@ pub fn apply_indexchanges_int(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if ok {
|
if ok {
|
||||||
*failures -= 1;
|
fs::write(&index.join(file), index_file.save())?;
|
||||||
let t = index.join(file);
|
|
||||||
if let Err(e) = fs::write(&t, index_file.save()) {
|
|
||||||
eprintln!("\n[warn] couldn't save index file {t:?}: {e}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IndexChange::AddSymlink(file, link_target) => {
|
IndexChange::AddSymlink(file, link_target) => {
|
||||||
let cwd = match std::env::current_dir() {
|
let cwd = std::env::current_dir()?;
|
||||||
Ok(cwd) => cwd,
|
|
||||||
Err(e) => {
|
|
||||||
eprintln!("\n[err] fatal: couldn't get cwd: {e}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
let ok = if let Some(target) = target {
|
let ok = if let Some(target) = target {
|
||||||
let t = target.join(file);
|
let t = target.join(file);
|
||||||
if let Some(p) = t.parent() {
|
if let Some(p) = t.parent() {
|
||||||
let t = t.file_name().expect("a file should always have a filename");
|
let t = t.file_name().expect("a file should always have a filename");
|
||||||
if let Err(e) = std::env::set_current_dir(&p) {
|
std::env::set_current_dir(&p)?;
|
||||||
eprintln!("\n[warn] couldn't cd to {p:?}: {e}");
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
let _ = std::fs::remove_file(t);
|
let _ = std::fs::remove_file(t);
|
||||||
if let Err(e) = std::os::unix::fs::symlink(&link_target, t) {
|
if let Err(e) = std::os::unix::fs::symlink(&link_target, t) {
|
||||||
eprintln!(
|
eprintln!(
|
||||||
@ -182,13 +150,9 @@ pub fn apply_indexchanges_int(
|
|||||||
);
|
);
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
if let Err(e) = std::env::set_current_dir(&cwd) {
|
std::env::set_current_dir(&cwd)?;
|
||||||
eprintln!("\n[err] fatal: couldn't cd back to {cwd:?}: {e}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!("\n[warn] symlink path was empty");
|
eprintln!("\n[warn] symlink path was empty");
|
||||||
false
|
false
|
||||||
@ -197,42 +161,27 @@ pub fn apply_indexchanges_int(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if ok {
|
if ok {
|
||||||
*failures -= 1;
|
|
||||||
let index_file = index.join(file);
|
let index_file = index.join(file);
|
||||||
if let Some(p) = index_file.parent() {
|
if let Some(p) = index_file.parent() {
|
||||||
if let Err(e) = std::env::set_current_dir(&p) {
|
std::env::set_current_dir(&p)?;
|
||||||
eprintln!("\n[warn] couldn't cd to {p:?}: {e}");
|
std::os::unix::fs::symlink(
|
||||||
} else {
|
link_target,
|
||||||
let index_file_name = index_file
|
index_file
|
||||||
.file_name()
|
.file_name()
|
||||||
.expect("a file should always have a filename");
|
.expect("a file should always have a filename"),
|
||||||
let _ = std::fs::remove_file(&index_file_name);
|
)?;
|
||||||
if let Err(e) = std::os::unix::fs::symlink(link_target, index_file_name)
|
|
||||||
{
|
|
||||||
eprintln!(
|
|
||||||
"\n[warn] couldn't set index file {index_file:?} to be a symlink to {link_target:?}: {e}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
eprintln!(
|
eprintln!("\n[warn] couldn't get parent for index file's path, so could not create the symlink");
|
||||||
"\n[warn] couldn't get parent for index file's path, so could not create the symlink"
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) = std::env::set_current_dir(&cwd) {
|
std::env::set_current_dir(&cwd)?;
|
||||||
eprintln!("\n[err] fatal: couldn't cd back to {cwd:?}: {e}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
IndexChange::RemoveFile(file) => {
|
IndexChange::RemoveFile(file) => {
|
||||||
let i = index.join(file);
|
let i = index.join(file);
|
||||||
let ok = if let Some(target) = target {
|
let ok = if let Some(target) = target {
|
||||||
let t = target.join(file);
|
let t = target.join(file);
|
||||||
if let Err(e) = fs::remove_file(&t) {
|
if let Err(e) = fs::remove_file(&t) {
|
||||||
eprintln!(
|
eprintln!("\n[warn] couldn't remove file {t:?}, keeping index file {i:?}: {e:?}\n If this error keeps appearing, check if the file was deleted on the target system but still exists in the index. if yes, consider manually deleting it.");
|
||||||
"\n[warn] couldn't remove file {t:?}, keeping index file {i:?}: {e:?}\n If this error keeps appearing, check if the file was deleted on the target system but still exists in the index. if yes, consider manually deleting it."
|
|
||||||
);
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
@ -241,10 +190,7 @@ pub fn apply_indexchanges_int(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if ok {
|
if ok {
|
||||||
*failures -= 1;
|
fs::remove_file(i)?;
|
||||||
if let Err(e) = fs::remove_file(&i) {
|
|
||||||
eprintln!("\n[warn] couldn't remove index file {i:?}: {e:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IndexChange::RemoveDir(dir) => {
|
IndexChange::RemoveDir(dir) => {
|
||||||
@ -252,9 +198,7 @@ pub fn apply_indexchanges_int(
|
|||||||
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 Err(e) = fs::remove_dir_all(&t) {
|
if let Err(e) = fs::remove_dir_all(&t) {
|
||||||
eprintln!(
|
eprintln!("\n[warn] couldn't remove directory {t:?}, keeping index files under {i:?}: {e:?}\n If this error keeps appearing, check if the directory was deleted on the target system but still exists in the index. if yes, consider manually deleting it.");
|
||||||
"\n[warn] couldn't remove directory {t:?}, keeping index files under {i:?}: {e:?}\n If this error keeps appearing, check if the directory was deleted on the target system but still exists in the index. if yes, consider manually deleting it."
|
|
||||||
);
|
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
true
|
true
|
||||||
@ -263,17 +207,14 @@ pub fn apply_indexchanges_int(
|
|||||||
true
|
true
|
||||||
};
|
};
|
||||||
if ok {
|
if ok {
|
||||||
*failures -= 1;
|
fs::remove_dir_all(i)?;
|
||||||
if let Err(e) = fs::remove_dir_all(&i) {
|
|
||||||
eprintln!("\n[warn] couldn't remove index directory {i:?}: {e:?}");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
eprint_status(
|
eprint_status(
|
||||||
i + 1,
|
i + 1,
|
||||||
changes_total,
|
changes.len(),
|
||||||
gib_transferred,
|
gib_transferred,
|
||||||
gib_total,
|
gib_total,
|
||||||
prog_width,
|
prog_width,
|
||||||
@ -282,4 +223,5 @@ pub fn apply_indexchanges_int(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -4,11 +4,6 @@ use clap::Parser;
|
|||||||
|
|
||||||
use crate::update_index::Settings;
|
use crate::update_index::Settings;
|
||||||
|
|
||||||
/// rembackup,
|
|
||||||
/// a simple backup tool for local or remote backups.
|
|
||||||
/// run with --help for more help.
|
|
||||||
///
|
|
||||||
/// rembackup copies files from <source> to <target> using and storing information in <index>.
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(author, version)]
|
#[command(author, version)]
|
||||||
pub struct Args {
|
pub struct Args {
|
||||||
|
22
src/main.rs
22
src/main.rs
@ -17,8 +17,7 @@ mod update_index;
|
|||||||
|
|
||||||
const EXIT_IGNORE_FAILED: u8 = 200;
|
const EXIT_IGNORE_FAILED: u8 = 200;
|
||||||
const EXIT_DIFF_FAILED: u8 = 20;
|
const EXIT_DIFF_FAILED: u8 = 20;
|
||||||
const EXIT_APPLY_FAILED_ONE: u8 = 100;
|
const EXIT_APPLY_FAILED: u8 = 30;
|
||||||
const EXIT_APPLY_FAILED_ALL: u8 = 200;
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
// get args
|
// get args
|
||||||
@ -28,7 +27,7 @@ fn main() {
|
|||||||
let cwd = match std::env::current_dir() {
|
let cwd = match std::env::current_dir() {
|
||||||
Ok(v) => Some(v),
|
Ok(v) => Some(v),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("[warn] Couldn't get current directory (CWD): {e}");
|
eprintln!("[WARN] Couldn't get current directory (CWD): {e}");
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -179,7 +178,7 @@ fn main() {
|
|||||||
if !args.noconfirm {
|
if !args.noconfirm {
|
||||||
loop {
|
loop {
|
||||||
if args.target.is_none() {
|
if args.target.is_none() {
|
||||||
eprintln!("[warn] You didn't set a `target` directory!\n[warn] Be careful not to update your index without actually applying the changes to the `target` filesystem!\nType 'Ok' and press enter to continue.");
|
eprintln!("[WARN] You didn't set a `target` directory!\n[WARN] Be careful not to update your index without actually applying the changes to the `target` filesystem!\nType 'Ok' and press enter to continue.");
|
||||||
} else {
|
} else {
|
||||||
eprintln!("Exclude unwanted directories/files using --ignore,\nor press enter to apply the changes.");
|
eprintln!("Exclude unwanted directories/files using --ignore,\nor press enter to apply the changes.");
|
||||||
}
|
}
|
||||||
@ -196,19 +195,18 @@ fn main() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let failure_count = apply_indexchanges(
|
match apply_indexchanges(
|
||||||
&args.source,
|
&args.source,
|
||||||
&args.index,
|
&args.index,
|
||||||
&args.target,
|
&args.target,
|
||||||
&changes,
|
&changes,
|
||||||
Some(add_file_total_size_gib),
|
Some(add_file_total_size_gib),
|
||||||
);
|
) {
|
||||||
eprintln!("[info] encountered {failure_count} failures");
|
Ok(()) => {}
|
||||||
if failure_count > 0 {
|
Err(e) => {
|
||||||
exit(
|
eprintln!("Failed to apply: {e}");
|
||||||
(EXIT_APPLY_FAILED_ONE as u64 + failure_count.ilog2() as u64)
|
exit(EXIT_APPLY_FAILED as _);
|
||||||
.min(EXIT_APPLY_FAILED_ALL as u64) as _,
|
}
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,15 +14,10 @@ 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
|
/// don't sort the changes that form the backup. disables sort options.
|
||||||
///
|
|
||||||
/// disables sort options.
|
|
||||||
/// symlinks will still be created last.
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub dont_sort: bool,
|
pub dont_sort: bool,
|
||||||
/// start with smaller directories rather than larger ones
|
/// start with smaller directories rather than larger ones
|
||||||
///
|
|
||||||
/// symlinks will still be created last.
|
|
||||||
#[arg(long)]
|
#[arg(long)]
|
||||||
pub smallest_first: bool,
|
pub smallest_first: bool,
|
||||||
/// show changes in the order in which they will be applied, not reversed
|
/// show changes in the order in which they will be applied, not reversed
|
||||||
|
Loading…
x
Reference in New Issue
Block a user