Skip to content

Commit

Permalink
v0.3.1
Browse files Browse the repository at this point in the history
- Now working on Windows (x64) and macOS (M3)
- Diff ouput is cleaner
- Packaging supports remote building of native Windows and macOS
  • Loading branch information
roylaurie committed May 24, 2024
1 parent 4a38133 commit 3ba7200
Show file tree
Hide file tree
Showing 19 changed files with 273 additions and 101 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
/target
/Cargo.lock
/pkg.cfg
/.vscode
*.snap
*.bak
Expand Down
27 changes: 20 additions & 7 deletions Building.md
Original file line number Diff line number Diff line change
@@ -1,18 +1,31 @@
# Building bak9

Most of these steps are for cross-compilation.
These steps are designed for cross-compilation from an Ubuntu 24 linux distro,
using a Windows 11 Pro VM and hardware running macOS Sonoma on an M3 CPU.

## Rust Targets

## Requirements
To install a target:
```bash
rustup target add $TARGET
rustup toolchain install stable-$TARGET
```

*Tested against Ubuntu Desktop 23*
- x86_64-unknown-linux-gnu
- armv7-unknown-linux-gnueabihf
- aarch64-unknown-linux-gnu
- aarch64-apple-darwin
- x86_64-pc-windows-msvc

## Requirements

Debian packaging requires:
```bash
sudo apt install \
pkg-config build-essential \
cross-build-essential-arm64 \
cross-build-essential-armhf
pkg-config \
build-essential \
crossbuild-essential-arm64 \
crossbuild-essential-armhf
```

Snap packaging requires:
Expand All @@ -27,7 +40,7 @@ cargo install cross

Debian packaging requires:
```bash
cargo install carg-deb
cargo install cargo-deb
```

RPM packaging requires:
Expand Down
6 changes: 1 addition & 5 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bak9"
version = "0.3.0"
version = "0.3.1"
edition = "2021"
description = "Creates a backup .bak copy of a file"
authors = ["Asmov LLC <[email protected]>"]
Expand Down Expand Up @@ -31,7 +31,3 @@ file_diff = "1"
assets = [
{ source = "target/release/bak", dest = "/usr/bin/bak", mode = "755" }
]

[package.metadata.packager]


4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ user's app data directory will be used instead.

If *multiple* backups of FILE exist, the rotating filename extension used will be: `.bak.N`.

The most recent rotating backup will be always `.bak.0`.
The most recent rotating backup will always be `.bak.0`.

Pruning of rotating backups occurs after `-n NUM` backups.

Expand All @@ -47,7 +47,7 @@ Force the operation without confirmation.
Lists all backups of FILE in DIR.

- `diff N`
Shows the differences of FILE and its `bak.N` copy in DIR. [default: 0]
Shows the differences between FILE and the specified `bak.N` backup in DIR. [default: 0]

- `rm`
Deletes all backups of FILE in DIR.
Expand Down
2 changes: 2 additions & 0 deletions pkg.cfg.template
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
WINDOWS_REMOTE=""
MACOS_REMOTE=""
30 changes: 21 additions & 9 deletions snapcraft.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,37 @@ name: bak9
version: git
summary: Creates a backup bak copy of a file
description: |
Creates a backup `.bak` copy of a file.
bak [OPTIONS] FILE [DIR] [COMMAND]
Usage: bak [OPTION]... FILE [DIR]
Creates a backup .bak copy of FILE.
If DIR is not specified, the copy is created in the same directory as FILE.
If multiple backups of FILE exist, the filename extension used will be `.bak.N`.
If DIR is specifed as -, or if the user lacks permissions to copy to DIR, the user's app data directory will be used instead.
Pruning occurs after `-n NUM` backups.
If multiple backups of FILE exist, the rotating filename extension used will be: .bak.N.
base: core22
The most recent rotating backup will always be .bak.0.
Pruning of rotating backups occurs after -n NUM backups.
If the current backup is no different than its predecessor, copying will be skipped.
Additional COMMANDs may be appended to list, compare, or delete backups.
base: core24
confinement: classic

architectures:
- build-on: amd64
platforms:
amd64:
build-on: amd64
build-for: amd64
- build-on: [amd64, arm64]
arm64:
build-on: [amd64, arm64]
build-for: [arm64]
- build-on: [amd64, armhf]
armhf:
build-on: [amd64, armhf]
build-for: [armhf]

parts:
Expand Down
8 changes: 4 additions & 4 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use clap::{Parser, Subcommand};

use crate::{PathExt, E_STR};

#[derive(Parser)]
#[derive(Parser, Debug)]
#[command(version, about)]
pub struct Cli {
#[command(subcommand)]
Expand All @@ -27,15 +27,15 @@ pub struct Cli {
}


#[derive(Subcommand)]
#[derive(Subcommand, Debug)]
pub enum Command {
#[command(name = "ls", about = "List all backups of FILE in DIR")]
List,
#[command(name = "rm", about = "Deletes all backups of FILE in DIR")]
Wipe,
#[command(name = "diff", about = "Shows the differences between FILE and BAK.N")]
#[command(name = "diff", about = "Shows the differences between FILE and bak.N in DIR")]
Diff {
#[arg(default_value_t = 0, help = "The BAK index to compare FILE with")]
#[arg(default_value_t = 0, help = "The .bak.N index to compare FILE with")]
index: u8,
}
}
Expand Down
64 changes: 56 additions & 8 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -194,7 +194,7 @@ fn confirm_wipe(source_file: &Path, dir: &Path, force: bool) -> bool {
print!("{} Delete all backups of {} in {}? {} ",
"confirm:".bright_yellow(),
source_file.filename_str().expect(E_FILENAME).cyan(),
dir.to_str().expect(E_STR).cyan(),
sanitize_path_str(dir.to_str().expect(E_STR)).cyan(),
"(y/N):".magenta());

std::io::stdout().flush()
Expand Down Expand Up @@ -283,7 +283,9 @@ fn print_list_backups(source_file: &Path, dir: &Path) -> Result<(), Error> {
return Ok(())
}

println!("Backups of {file} in {dir}:", file = source_file.to_str().expect(E_STR).cyan(), dir = dir.to_str().expect(E_STR).cyan());
println!("Backups of {file} in {dir}:",
file = sanitize_path_str(source_file.to_str().expect(E_STR)).cyan(),
dir = sanitize_path_str(dir.to_str().expect(E_STR)).cyan());

for bak_filepath in bak_filepaths {
println!(" {}", bak_filepath.filename_str().expect(E_STR).green());
Expand Down Expand Up @@ -344,7 +346,7 @@ fn wipe(source_file: &Path, dest_dir: &Path) -> Result<(), Error> {
}

/// Retrieves a list of all `.bak.N` files in the directory.
fn list_bak_n_files(file: &Path, dir: &Path, ) -> Result<Vec<PathBuf>, Error> {
fn list_bak_n_files(file: &Path, dir: &Path) -> Result<Vec<PathBuf>, Error> {
let bak_n_file_pattern = file
.append_extension(BAK_DOT)
.filename_string().expect(E_FILENAME);
Expand Down Expand Up @@ -411,7 +413,8 @@ fn run_backup(cli: &cli::Cli) -> Result<(), Error> {
.map_err(|_| Error::copy(&cli.file, &home_bak_filepath, e))?;

if !cli.quiet {
eprintln!("{} copied to {}", "notice:".yellow(), home_bak_filepath.to_str().expect("Expected string").cyan());
eprintln!("{} copied to {}", "notice:".yellow(),
sanitize_path_str(home_bak_filepath.to_str().expect(E_STR)).cyan());
}

Ok(())
Expand Down Expand Up @@ -466,21 +469,42 @@ fn determine_destination(source_file: &Path, dest_dir: &Path, max: u8) -> Result
Ok(Some(bak_filepath))
}

pub fn mirror_dir(base_dir: &Path, src_file: &Path, mkdir: bool) -> Result<PathBuf, Error> {
pub fn sanitize_path_str(path: &str) -> &str {
sanitize_windows_path_str(path)
}

pub fn sanitize_windows_path_str(path: &str) -> &str {
path.trim_start_matches("\\\\?\\")
}

fn determine_mirror_dir(base_dir: &Path, src_file: &Path) -> Result<PathBuf, Error> {
let src_dir = src_file.parent().expect("Expected parent directory");
let mut mirror_dir = base_dir.to_path_buf();

for component in src_dir.components() {
let dirname = component.as_os_str().to_str().expect(E_STR);
let dirname = component.as_os_str().to_str().expect(E_STR)
.trim_start_matches("\\\\?\\"); // remove any windows extended path prefix

match dirname {
"." | "/" => continue,
"." | "/" | "\\" => continue,
".." => unreachable!("Expected absolute path"),
_ => {}
}

mirror_dir.push(dirname);
// windows drives (C:, D:, etc)
if dirname.chars().count() == 2 && dirname.chars().nth(1).unwrap() == ':' {
mirror_dir.push(dirname.chars().nth(0).unwrap().to_string());
} else {
mirror_dir.push(dirname);
}
}

Ok(mirror_dir)
}

pub fn mirror_dir(base_dir: &Path, src_file: &Path, mkdir: bool) -> Result<PathBuf, Error> {
let mirror_dir = determine_mirror_dir(base_dir, src_file)?;

if !mirror_dir.is_dir() && mkdir {
fs::create_dir_all(&mirror_dir)
.map_err(|e| Error::io(IoOp::Create, &mirror_dir, e))?
Expand Down Expand Up @@ -551,3 +575,27 @@ fn find_last_bak(file: &Path, dir: &Path) -> Option<PathBuf> {
}
}
}

#[cfg(test)]
mod tests {
use std::path::Path;
use super::*;

#[test]
fn test_determine_mirror_dir() {
let base_dir = "/home/dev/.local/share/bak9";
let src_file = "/home/dev/tmp/source.txt";
let mirror_dir = determine_mirror_dir(Path::new(base_dir), Path::new(src_file)).unwrap();
assert_eq!(Path::new("/home/dev/.local/share/bak9/home/dev/tmp"), mirror_dir);
}

#[cfg(target_os = "windows")]
#[test]
fn test_determine_mirror_dir_windows() {
// test a windows path with path extensions
let base_dir = "\\\\?\\C:\\Users\\dev\\AppData\\Local\\bak9";
let src_file = "\\\\?\\C:\\Users\\dev\\tmp\\source.txt";
let mirror_dir = determine_mirror_dir(Path::new(base_dir), Path::new(src_file)).unwrap();
assert_eq!("\\\\?\\C:\\Users\\dev\\AppData\\Local\\bak9\\C\\Users\\dev\\tmp", mirror_dir.to_str().unwrap());
}
}
Loading

0 comments on commit 3ba7200

Please sign in to comment.