Compare commits

..

29 commits

Author SHA1 Message Date
1351a6bd9b (chore): update dependencies 2025-09-08 19:12:53 -05:00
a2de007c87 (chore): fix systemd units
- Best wording
- Don't allow to enable `.service` units, that wouldn't make sense because they're managed by timers
2025-09-08 11:54:31 -05:00
2f47feccd7 (chore): bump version and update deps 2025-08-10 01:21:03 -05:00
daca0998fa (chore): change default for database file
It's more common that people uses @snapshots (/.snapshopts/) subvolume to store snapshots, so let's default to that dir
2025-08-10 01:20:50 -05:00
2c8c16ad4e Bump dependencies 2025-08-02 14:21:21 -05:00
0285857b91 Exclude windows builds 2025-08-02 14:09:02 -05:00
c2d1d12abf Update builder.sh 2025-08-02 13:55:36 -05:00
d57c34e9d1 (chore): update builder.sh
- Disable docker checks, we now use podman
- Use cross for all builds, otherwise the build ends up in glibc versions mismatch
2025-08-02 13:00:58 -05:00
bfca23d1c7 (chore): bump version. 2025-05-04 02:01:54 -05:00
ae403db784 (chore): bump version. 2025-05-04 02:00:36 -05:00
f23e74c610 (feat): improve database handling. 2025-05-04 01:59:44 -05:00
9c7f10c186 Merge branch 'master' of https://github.com/Edu4rdSHL/rusnapshot 2025-05-04 01:41:50 -05:00
b47ebd7616 (feat): update examples. 2025-05-04 01:41:49 -05:00
97fa393825
Create LICENSE 2025-05-04 00:33:15 -05:00
8f67b4d044 (chore): update edition 2025-05-04 00:19:51 -05:00
7b363e0207 (chore): bump version 2025-05-04 00:12:50 -05:00
2a2058f781 (build): disable LTO by default 2025-05-03 23:37:48 -05:00
bd0887f416 (chore): update deps. 2025-05-03 23:34:47 -05:00
5e1a3e7115 (chore): clippy fix. 2025-05-03 23:34:36 -05:00
0b117ac07c (docs): point out the main feature and update description. 2025-05-03 23:34:16 -05:00
b8b2c6f0f8 (chore): more sensible defaults. 2025-05-03 23:33:54 -05:00
844b6543b4 Update dependencies 2025-04-20 14:50:57 -05:00
e421db4013 Clippy fixes 2025-04-20 14:50:52 -05:00
573c50d356
Create FUNDING.yml 2024-02-07 01:05:46 -05:00
41951edb8b Rename file. 2024-02-07 01:01:05 -05:00
235945c8e8 Update SETUP documentation 2024-02-07 00:58:39 -05:00
01e518d3d3 Update documentation. 2024-02-07 00:04:37 -05:00
e24d29c048 Update config templates. 2024-02-07 00:03:29 -05:00
230b2babbe Add support for using a hostname identifier 2024-02-07 00:01:10 -05:00
24 changed files with 552 additions and 360 deletions

1
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1 @@
github: [edu4rdshl]

625
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -1,26 +1,25 @@
[package]
name = "rusnapshot"
version = "0.4.1"
version = "0.5.3"
authors = ["edu4rdshl"]
edition = "2018"
edition = "2024"
description = "Simple and handy btrfs snapshoting tool."
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
clap = { version = "4.4.18", features = ["derive", "env"] }
serde = { version = "1.0.188", features = ["derive"] }
sqlite = "0.33.0"
md5 = "0.7.0"
chrono = "0.4.33"
clap = { version = "4.5.47", features = ["derive", "env"] }
serde = { version = "1.0.219", features = ["derive"] }
sqlite = "0.37.0"
md5 = "0.8.0"
chrono = "0.4.42"
prettytable-rs = "0.10.0"
anyhow = "1.0.79"
toml = "0.8.9"
anyhow = "1.0.99"
toml = "0.9.5"
hostname = "0.4.1"
[profile.release]
lto = true
codegen-units = 1
panic = 'abort'
incremental = false
opt-level = "s"
strip = true

21
LICENSE Normal file
View file

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 Eduard Tolosa
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View file

@ -1,5 +1,5 @@
# Description
Simple and handy btrfs snapshoting tool. Supports unattended snapshots, tracking, restoring, automatic cleanup and more. Backed with SQLite.
Simple and handy definitions-based snapshoting tool for BTRFS. Supports unattended snapshots, tracking, restoring, automatic cleanup and more. Backed with SQLite.
# Documentation
@ -7,6 +7,10 @@ See the [docs folder](https://github.com/Edu4rdSHL/rusnapshot/blob/master/docs/)
# Features
**The main feature of Rustnapshot is the ability to create snapshots of BTRFS subvolumes using simple TOML definitions. It is designed to be simple and easy to use, while still providing powerful features for managing snapshots. See [the examples folder](https://github.com/Edu4rdSHL/rusnapshot/tree/master/examples) for more information.**
Among the features of Rustnapshot are:
- Allows you to specify the origin and destination of snapshots at will of the user.
- Track snapshots using SQLite as backend database.
- Easy setup using small templates instead of confusing long files.
@ -15,8 +19,9 @@ See the [docs folder](https://github.com/Edu4rdSHL/rusnapshot/blob/master/docs/)
- Ability to use the same SQLite database for everything.
- Ability to specify the prefix of the name for the snapshots for better identification.
- Ability to specify a `kind` identifier to differentiate them in the database. Useful if you plan to have hourly, weekly, monthly or more "kind" of snapshots of the same subvolume(s).
- Ability to specify the maximum number of snapshots to keep for automatic cleanup.
- Supports restoration of snapshots in the original directory or a specific one.
- Automatic snapshots cleanup.
- Supports machine name identification for better tracking when using the same database in multiple machines.
- Nice CLI output to see the status and details of snapshots.
# Known limitations

View file

@ -3,23 +3,22 @@ NAME="rusnapshot"
LINUX_TARGET="x86_64-unknown-linux-musl"
LINUX_X86_TARGET="i686-unknown-linux-musl"
ARMV7_TARGET="armv7-unknown-linux-gnueabihf"
AARCH_TARGET="aarch64-unknown-linux-gnu"
MANPAGE_DIR="./$NAME.1"
BIN_OUTPUT_DIR="./ghbinaries"
if ! systemctl is-active docker >/dev/null 2>&1; then
echo "Docker is not running. Starting docker."
if ! sudo systemctl start docker; then
echo "Failed to start docker."
exit 1
fi
fi
rm -rf "$BIN_OUTPUT_DIR"
mkdir -p "$BIN_OUTPUT_DIR"
# Linux build
echo "Building Linux artifact."
if cargo build -q --release --target="$LINUX_TARGET"; then
if cross build -q --release --target="$LINUX_TARGET"; then
echo "Linux artifact build: SUCCESS"
cp "target/$LINUX_TARGET/release/$NAME" "target/$LINUX_TARGET/release/$NAME-linux"
strip "target/$LINUX_TARGET/release/$NAME-linux"
sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"target/$LINUX_TARGET/release/$NAME-linux.sha512"
sha512sum "target/$LINUX_TARGET/release/$NAME-linux" >"$BIN_OUTPUT_DIR/$NAME-linux.sha512"
zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-x64.zip" "target/$LINUX_TARGET/release/$NAME-linux"
else
echo "Linux artifact build: FAILED"
fi
@ -30,11 +29,36 @@ if cross build -q --release --target="$LINUX_X86_TARGET"; then
echo "Linux x86 artifact build: SUCCESS"
cp "target/$LINUX_X86_TARGET/release/$NAME" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
strip "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"target/$LINUX_X86_TARGET/release/$NAME-linux-i386.sha512"
sha512sum "target/$LINUX_X86_TARGET/release/$NAME-linux-i386" >"$BIN_OUTPUT_DIR/$NAME-linux-i386.sha512"
zip -q -j "$BIN_OUTPUT_DIR/$NAME-linux-i386.zip" "target/$LINUX_X86_TARGET/release/$NAME-linux-i386"
else
echo "Linux x86 artifact build: FAILED"
fi
# ARMV7 build
echo "Building ARMv7 artifact."
if cross build -q --release --target="$ARMV7_TARGET"; then
echo "ARMv7 artifact build: SUCCESS"
cp "target/$ARMV7_TARGET/release/$NAME" "target/$ARMV7_TARGET/release/$NAME-armv7"
strip "target/$ARMV7_TARGET/release/$NAME-armv7"
sha512sum "target/$ARMV7_TARGET/release/$NAME-armv7" >"$BIN_OUTPUT_DIR/$NAME-armv7.sha512"
zip -q -j "$BIN_OUTPUT_DIR/$NAME-armv7.zip" "target/$ARMV7_TARGET/release/$NAME-armv7"
else
echo "ARMv7 artifact build: FAILED"
fi
# Aarch64 build
echo "Building Aarch64 artifact."
if cross build -q --release --target="$AARCH_TARGET"; then
echo "Aarch64 artifact build: SUCCESS"
cp "target/$AARCH_TARGET/release/$NAME" "target/$AARCH_TARGET/release/$NAME-aarch64"
strip "target/$AARCH_TARGET/release/$NAME-aarch64"
sha512sum "target/$AARCH_TARGET/release/$NAME-aarch64" >"$BIN_OUTPUT_DIR/$NAME-aarch64.sha512"
zip -q -j "$BIN_OUTPUT_DIR/$NAME-aarch64.zip" "target/$AARCH_TARGET/release/$NAME-aarch64"
else
echo "Aarch64 artifact build: FAILED"
fi
echo "Creating manpage..."
if command -v help2man >/dev/null; then
if help2man -o "$MANPAGE_DIR" "target/$LINUX_TARGET/release/$NAME"; then
@ -45,10 +69,3 @@ if command -v help2man >/dev/null; then
else
echo "Please install the help2man package."
fi
# Stop docker
echo "Stopping docker."
if ! sudo systemctl stop docker; then
echo "Failed to stop docker."
exit 1
fi

View file

@ -14,7 +14,7 @@
- Create a snapshot using a [config file](https://github.com/Edu4rdSHL/rusnapshot/tree/master/examples/config-templates):
`sudo rusnapshot --config {{path/to/config.toml}} --cr`
`sudo rusnapshot --config {{path/to/config.toml}} --create`
- List created snapshots:
@ -30,11 +30,11 @@
- Create a read-write snapshot:
`sudo rusnapshot -c {{path/to/config.toml}} --cr --rw`
`sudo rusnapshot -c {{path/to/config.toml}} --create --rw`
- Restore a snapshot:
`sudo rusnapshot -c {{path/to/config.toml}} --id {{snapshot_id}} --restore`
`sudo rusnapshot --id {{snapshot_id}} --restore`
# Notes

View file

@ -2,14 +2,16 @@
# These are all the current available options
# Snapshots directory
dest_dir = "/.rusnapshot"
dest_dir = "/.snapshots"
# Directory to be snapshot-ed
source_dir = "/"
# SQLite database file PATH where snapshots metadata will be stored. Created if not exists
database_file = "/.rusnapshot/rusnapshot.db"
database_file = "/.snapshots/rusnapshot.db"
# Prefix for the snapshots, they will be saved in the format prefix-$current_date
snapshot_prefix = "root"
# Snapshots identifier
snapshot_kind = "weekly"
# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix will be deleted
keep_only = 2
keep_only = 3
# Machine name to be used in the snapshots metadata
machine = "Oribos"

View file

@ -1,10 +1,12 @@
# Snapshots directory
dest_dir = "/.rusnapshot"
dest_dir = "/.snapshots"
# Directory to be snapshot-ed
source_dir = "/home"
# SQLite database file PATH where snapshots metadata will be stored. Created if not exists
database_file = "/.rusnapshot/rusnapshot.db"
database_file = "/.snapshots/rusnapshot.db"
# Prefix for the snapshots, they will be saved in the format prefix-$current_date
snapshot_prefix = "home"
# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix will be deleted
keep_only = 2
keep_only = 3
# Machine name to be used in the snapshots metadata
machine = "Oribos"

View file

@ -1,10 +1,12 @@
# Snapshots directory
dest_dir = "/.rusnapshot"
dest_dir = "/.snapshots"
# Directory to be snapshot-ed
source_dir = "/"
# SQLite database file PATH where snapshots metadata will be stored. Created if not exists
database_file = "/.rusnapshot/rusnapshot.db"
database_file = "/.snapshots/rusnapshot.db"
# Prefix for the snapshots, they will be saved in the format prefix-$current_date
snapshot_prefix = "root"
# Last snapshots to keep. If prefix is specified then only the snapshots with that prefix will be deleted
keep_only = 2
keep_only = 3
# Machine name to be used in the snapshots metadata
machine = "Oribos"

View file

@ -1,12 +1,9 @@
[Unit]
Description=Snapshot root directory.
Description=Daily snapshots for home and root subvolumes
[Service]
Type=oneshot
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind three-hours
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind three-hours --rw
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind three-hours
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind three-hours --rw
[Install]
WantedBy=default.target
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --create --kind daily
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --clean --kind daily
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --create --kind daily
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --clean --kind daily

View file

@ -0,0 +1,9 @@
[Unit]
Description=Take daily snapshots with rusnapshot
[Timer]
OnCalendar=daily
Persistent=true
[Install]
WantedBy=timers.target

View file

@ -1,12 +1,9 @@
[Unit]
Description=Take system snapshots monthly.
Description=Monthly snapshots for home and root subvolumes
[Service]
Type=oneshot
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind monthly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind monthly --rw
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind monthly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind monthly --rw
[Install]
WantedBy=default.target
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --create --kind monthly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --clean --kind monthly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --create --kind monthly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --clean --kind monthly

View file

@ -1,5 +1,5 @@
[Unit]
Description=Take system snapshots monthly.
Description=Take monthly snapshots with rusnapshot
[Timer]
OnCalendar=monthly

View file

@ -1,9 +0,0 @@
[Unit]
Description=Take snapshots every 3 hours.
[Timer]
OnBootSec=1h
OnUnitActiveSec=3h
[Install]
WantedBy=timers.target

View file

@ -1,12 +1,9 @@
[Unit]
Description=Take system snapshots weekly.
Description=Weekly snapshots for home and root subvolumes
[Service]
Type=oneshot
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind weekly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --cr --clean --kind weekly --rw
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind weekly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --cr --clean --kind weekly --rw
[Install]
WantedBy=default.target
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --create --kind weekly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-root.toml --clean --kind weekly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --create --kind weekly
ExecStart=/usr/bin/rusnapshot -c /etc/rusnapshot/config-home.toml --clean --kind weekly

View file

@ -1,5 +1,5 @@
[Unit]
Description=Take system snapshots weekly.
Description=Take weekly snapshots with rusnapshot
[Timer]
OnCalendar=weekly

View file

@ -23,12 +23,12 @@ pub struct Args {
/// Snapshot id or name to work with.
#[clap(long = "id", default_value = "")]
pub snapshot_id: String,
/// Path to the SQLite database file.
/// Path to the `SQLite` database file.
#[clap(
short = 'd',
long = "dfile",
env = "RUSNAPSHOT_DB_FILE",
default_value = "/.rusnapshot/rusnapshot.db"
default_value = "/.snapshots/rustnapshot.sqlite"
)]
pub database_file: String,
/// Prefix for the snapshot name.
@ -38,10 +38,10 @@ pub struct Args {
#[clap(long = "kind", default_value = "rusnapshot")]
pub snapshot_kind: String,
/// Keep only the last X items.
#[clap(short = 'k', long = "keep", default_value = "10")]
#[clap(short = 'k', long = "keep", default_value = "3")]
pub keep_only: usize,
/// Time in milliseconds until SQLite can return a timeout. Do not touch if you don't know what you are doing.
#[clap(long = "timeout", default_value = "5000")]
/// Time in milliseconds until `SQLite` can return a timeout. Do not touch if you don't know what you are doing.
#[clap(long = "timeout", default_value = "10000")]
pub timeout: usize,
/// Create a read-only/ro snapshot.
#[clap(long = "create", conflicts_with_all = &["restore_snapshot", "delete_snapshot", "list_snapshots", "clean_snapshots"])]
@ -62,6 +62,9 @@ pub struct Args {
/// Create read-write/rw snapshots.
#[clap(short = 'w', long = "rw")]
pub read_write: bool,
/// Machine name to be used in the metadata.
#[clap(short, long, default_value = "")]
pub machine: String,
}
impl Args {
@ -148,6 +151,12 @@ impl Args {
.parse()
.expect("Failed to parse timeout, make sure it's a number");
}
if let Some(machine) = config.get("machine") {
self.machine = machine
.to_string()
.parse()
.expect("Failed to parse machine");
}
Ok(())
}

View file

@ -2,7 +2,7 @@ use {
crate::{args::Args, database, operations, structs::ExtraArgs},
anyhow::Result,
md5,
prettytable::{row, Table},
prettytable::{Table, row},
sqlite::Connection,
};
@ -79,7 +79,8 @@ pub fn manage_listing(database_connection: &Connection) -> Result<()> {
"KIND",
"SOURCE DIR",
"DESTINATION DIR",
"RO/RW",
"MACHINE",
"RW",
"DATE"
]);
@ -90,6 +91,7 @@ pub fn manage_listing(database_connection: &Connection) -> Result<()> {
data.kind,
data.source,
data.destination,
data.machine,
data.ro_rw,
data.date,
]);
@ -104,7 +106,7 @@ pub fn keep_only_x(args: &mut Args, extra_args: &mut ExtraArgs) -> Result<()> {
for data in &snaps_data {
extra_args.snapshot_name = data.destination.clone() + &data.name;
args.snapshot_id = data.snap_id.clone();
args.snapshot_id.clone_from(&data.snap_id);
manage_deletion(args, extra_args)?;
}

View file

@ -8,7 +8,7 @@ use {
};
pub fn setup_initial_database(connection: &Connection) -> Result<()> {
connection.execute("CREATE TABLE IF NOT EXISTS snapshots (name TEXT NOT NULL, snap_id TEXT NOT NULL, kind TEXT NOT NULL, source TEXT NOT NULL, destination TEXT NOT NULL, ro_rw TEXT NOT NULL, date TEXT DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(name, snap_id))")?;
connection.execute("CREATE TABLE IF NOT EXISTS snapshots (name TEXT NOT NULL, snap_id TEXT NOT NULL, kind TEXT NOT NULL, source TEXT NOT NULL, destination TEXT NOT NULL, machine TEXT NOT NULL, ro_rw TEXT NOT NULL, date TEXT DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY(name, snap_id))")?;
connection.execute("PRAGMA journal_mode=WAL")?;
Ok(())
@ -16,8 +16,14 @@ pub fn setup_initial_database(connection: &Connection) -> Result<()> {
pub fn commit_to_database(args: &Args, extra_args: &ExtraArgs) -> Result<()> {
let statement = format!(
"INSERT INTO snapshots (name, snap_id, kind, source, destination, ro_rw) VALUES ('{}', '{}', '{}', '{}', '{}', '{}')",
&extra_args.snapshot_name, args.snapshot_id, args.snapshot_kind, args.source_dir, args.dest_dir, args.read_write
"INSERT INTO snapshots (name, snap_id, kind, source, destination, machine, ro_rw) VALUES ('{}', '{}', '{}', '{}', '{}', '{}', '{}')",
&extra_args.snapshot_name,
args.snapshot_id,
args.snapshot_kind,
args.source_dir,
args.dest_dir,
args.machine,
args.read_write
);
extra_args.database_connection.execute(statement)?;
@ -42,7 +48,7 @@ pub fn return_snapshot_data(connection: &Connection, args: &Args) -> Result<Data
let mut snap_data = Database::default();
while statement.next()? == State::Row {
snap_data = populate_db_struct(&statement, snap_data)?;
snap_data = populate_db_struct(&statement)?;
}
Ok(snap_data)
@ -52,11 +58,10 @@ pub fn return_all_data(connection: &Connection) -> Result<Vec<Database>> {
let mut snapshots_data: Vec<Database> = Vec::new();
let mut statement = connection
.prepare("SELECT name,snap_id,kind,source,destination,ro_rw,datetime(date, 'localtime') FROM snapshots ORDER BY date DESC")?;
.prepare("SELECT name,snap_id,kind,source,destination,machine,ro_rw,datetime(date, 'localtime') FROM snapshots ORDER BY date DESC")?;
while statement.next()? == State::Row {
let db_struct = Database::default();
snapshots_data.push(populate_db_struct(&statement, db_struct)?);
snapshots_data.push(populate_db_struct(&statement)?);
}
Ok(snapshots_data)
@ -65,26 +70,24 @@ pub fn return_all_data(connection: &Connection) -> Result<Vec<Database>> {
pub fn return_only_x_items(connection: &Connection, args: &Args) -> Result<Vec<Database>> {
let mut snapshots_data: Vec<Database> = Vec::new();
let mut statement = connection.prepare(&format!("SELECT name,snap_id,kind,source,destination,ro_rw,date FROM (SELECT row_number() over(ORDER BY date DESC) n,* from snapshots WHERE name like '{}%' AND kind = '{}' AND ro_rw = '{}') WHERE n > {}", args.snapshot_prefix, args.snapshot_kind, args.read_write, args.keep_only))?;
let mut statement = connection.prepare(format!("SELECT name,snap_id,kind,source,destination,machine,ro_rw,date FROM (SELECT row_number() over(ORDER BY date DESC) n,* from snapshots WHERE name like '{}%' AND kind = '{}' AND ro_rw = '{}') WHERE n > {}", args.snapshot_prefix, args.snapshot_kind, args.read_write, args.keep_only))?;
while statement.next()? == State::Row {
let db_struct = Database::default();
snapshots_data.push(populate_db_struct(&statement, db_struct)?);
snapshots_data.push(populate_db_struct(&statement)?);
}
Ok(snapshots_data)
}
fn populate_db_struct(row: &Statement, mut db_struct: Database) -> Result<Database> {
// I'll improve this later.
db_struct.name = row.read(0)?;
db_struct.snap_id = row.read(1)?;
db_struct.kind = row.read(2)?;
db_struct.source = row.read(3)?;
db_struct.destination = row.read(4)?;
db_struct.ro_rw = row.read(5)?;
db_struct.date = row.read(6)?;
Ok(db_struct)
fn populate_db_struct(stmt: &Statement) -> Result<Database> {
Ok(Database {
name: stmt.read(0)?,
snap_id: stmt.read(1)?,
kind: stmt.read(2)?,
source: stmt.read(3)?,
destination: stmt.read(4)?,
machine: stmt.read(5)?,
ro_rw: stmt.read(6)?,
date: stmt.read(7)?,
})
}

View file

@ -1,7 +1,9 @@
use anyhow::Result;
use chrono::Utc;
use clap::Parser;
use rusnapshot::{args, controller, structs::ExtraArgs, utils};
use {
anyhow::Result,
chrono::Utc,
clap::Parser,
rusnapshot::{args, controller, structs::ExtraArgs, utils},
};
fn try_run() -> Result<()> {
let mut arguments = args::Args::parse();

View file

@ -41,7 +41,7 @@ pub fn del_snapshot(snapshot_name: &str) -> bool {
pub fn restore_snapshot(args: &Args, snapshot_name: &str) -> bool {
!Path::new(&args.dest_dir).exists()
&& Command::new("btrfs")
.args(["subvolume", "snapshot", &snapshot_name, &args.dest_dir])
.args(["subvolume", "snapshot", snapshot_name, &args.dest_dir])
.status()
.expect("Error restoring the snapshot.")
.success()

View file

@ -8,6 +8,7 @@ pub struct Database {
pub source: String,
pub destination: String,
pub ro_rw: String,
pub machine: String,
pub date: String,
}

View file

@ -42,5 +42,9 @@ pub fn check_creation_requirements(arguments: &mut Args, extra_args: &ExtraArgs)
.to_string();
}
if arguments.machine.is_empty() {
arguments.machine = hostname::get()?.to_string_lossy().to_string();
}
Ok(())
}