mirror of
https://github.com/rustic-rs/rustic.git
synced 2025-10-26 11:18:51 +00:00
Add tag command
This commit is contained in:
parent
9c65a0b432
commit
d91045910f
@ -46,8 +46,10 @@ pub(super) async fn execute(
|
||||
opts: Opts,
|
||||
command: String,
|
||||
) -> Result<()> {
|
||||
let mut snap = SnapshotFile::default();
|
||||
snap.command = Some(command);
|
||||
let mut snap = SnapshotFile {
|
||||
command: Some(command),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
let config: ConfigFile = be.get_file(&Id::default()).await?;
|
||||
let be = DryRunBackend::new(be.clone(), opts.dry_run);
|
||||
@ -67,9 +69,7 @@ pub(super) async fn execute(
|
||||
.ok_or_else(|| anyhow!("non-unicode hostname {:?}", hostname))?
|
||||
.to_string();
|
||||
|
||||
for tags in opts.tag {
|
||||
snap.tags.add_all(tags);
|
||||
}
|
||||
snap.set_tags(opts.tag);
|
||||
|
||||
let parent = match (opts.force, opts.parent) {
|
||||
(true, _) => None,
|
||||
|
||||
@ -17,6 +17,7 @@ mod prune;
|
||||
mod repoinfo;
|
||||
mod restore;
|
||||
mod snapshots;
|
||||
mod tag;
|
||||
|
||||
use helpers::*;
|
||||
use vlog::*;
|
||||
@ -76,6 +77,9 @@ enum Command {
|
||||
|
||||
/// show general information about repository
|
||||
Repoinfo(repoinfo::Opts),
|
||||
|
||||
/// change tags of snapshots
|
||||
Tag(tag::Opts),
|
||||
}
|
||||
|
||||
pub async fn execute() -> Result<()> {
|
||||
@ -107,5 +111,6 @@ pub async fn execute() -> Result<()> {
|
||||
Command::Prune(opts) => prune::execute(&dbe, opts).await,
|
||||
Command::Restore(opts) => restore::execute(&dbe, opts).await,
|
||||
Command::Repoinfo(opts) => repoinfo::execute(&dbe, opts).await,
|
||||
Command::Tag(opts) => tag::execute(&dbe, opts).await,
|
||||
}
|
||||
}
|
||||
|
||||
63
src/commands/tag.rs
Normal file
63
src/commands/tag.rs
Normal file
@ -0,0 +1,63 @@
|
||||
use anyhow::Result;
|
||||
use clap::Parser;
|
||||
|
||||
use crate::backend::{DecryptFullBackend, FileType};
|
||||
use crate::repo::{SnapshotFile, SnapshotFilter, StringList};
|
||||
|
||||
#[derive(Parser)]
|
||||
pub(super) struct Opts {
|
||||
#[clap(flatten)]
|
||||
filter: SnapshotFilter,
|
||||
|
||||
/// Tags to add add (can be specified multiple times)
|
||||
#[clap(long, value_name = "TAG[,TAG,..]", conflicts_with = "remove")]
|
||||
add: Vec<StringList>,
|
||||
|
||||
/// Tags to remove (can be specified multiple times)
|
||||
#[clap(long, value_name = "TAG[,TAG,..]")]
|
||||
remove: Vec<StringList>,
|
||||
|
||||
/// Tag list to set (can be specified multiple times)
|
||||
#[clap(long, value_name = "TAG[,TAG,..]", conflicts_with = "remove")]
|
||||
set: Vec<StringList>,
|
||||
// TODO: allow giving specific snapshots
|
||||
}
|
||||
|
||||
pub(super) async fn execute(be: &impl DecryptFullBackend, opts: Opts) -> Result<()> {
|
||||
let snapshots = SnapshotFile::all_from_backend(be).await?;
|
||||
|
||||
let mut count = 0;
|
||||
for sn in snapshots.into_iter().filter(|sn| sn.matches(&opts.filter)) {
|
||||
if modify_sn(sn, be, &opts).await? {
|
||||
count += 1;
|
||||
}
|
||||
}
|
||||
|
||||
println!("changed {} snapshot(s)", count);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn modify_sn(
|
||||
mut sn: SnapshotFile,
|
||||
be: &impl DecryptFullBackend,
|
||||
opts: &Opts,
|
||||
) -> Result<bool> {
|
||||
let mut changed = false;
|
||||
|
||||
if !opts.set.is_empty() {
|
||||
changed |= sn.set_tags(opts.set.clone());
|
||||
}
|
||||
changed |= sn.add_tags(opts.add.clone());
|
||||
changed |= sn.remove_tags(opts.remove.clone());
|
||||
|
||||
// FIXME: For some reason, changed is always true...?!?
|
||||
if changed {
|
||||
// TODO: Save original snapshot ID
|
||||
// TODO: Save and delete in parallel
|
||||
be.save_file(&sn).await?;
|
||||
be.remove(FileType::Snapshot, &sn.id).await?;
|
||||
}
|
||||
|
||||
Ok(changed)
|
||||
}
|
||||
@ -184,6 +184,32 @@ impl SnapshotFile {
|
||||
&& self.tags.matches(&filter.tags)
|
||||
&& (filter.hostnames.is_empty() || filter.hostnames.contains(&self.hostname))
|
||||
}
|
||||
|
||||
/// Add tag lists to snapshot. return wheter snapshot was changed
|
||||
pub fn add_tags(&mut self, tag_lists: Vec<StringList>) -> bool {
|
||||
let old_tags = self.tags.clone();
|
||||
self.tags.add_all(tag_lists);
|
||||
self.tags.sort();
|
||||
|
||||
old_tags == self.tags
|
||||
}
|
||||
|
||||
/// Set tag lists to snapshot. return wheter snapshot was changed
|
||||
pub fn set_tags(&mut self, tag_lists: Vec<StringList>) -> bool {
|
||||
let old_tags = std::mem::take(&mut self.tags);
|
||||
self.tags.add_all(tag_lists);
|
||||
self.tags.sort();
|
||||
|
||||
old_tags == self.tags
|
||||
}
|
||||
|
||||
/// Remove tag lists from snapshot. return wheter snapshot was changed
|
||||
pub fn remove_tags(&mut self, tag_lists: Vec<StringList>) -> bool {
|
||||
let old_tags = self.tags.clone();
|
||||
self.tags.remove_all(tag_lists);
|
||||
|
||||
old_tags == self.tags
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialOrd for SnapshotFile {
|
||||
@ -197,7 +223,7 @@ impl Ord for SnapshotFile {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Serialize, Deserialize)]
|
||||
#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Clone, Serialize, Deserialize)]
|
||||
pub struct StringList(Vec<String>);
|
||||
|
||||
impl FromStr for StringList {
|
||||
@ -226,12 +252,27 @@ impl StringList {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_all(&mut self, sl: StringList) {
|
||||
pub fn add_list(&mut self, sl: StringList) {
|
||||
for s in sl.0 {
|
||||
self.add(s)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn add_all(&mut self, string_lists: Vec<StringList>) {
|
||||
for sl in string_lists {
|
||||
self.add_list(sl)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn remove_all(&mut self, string_lists: Vec<StringList>) {
|
||||
self.0
|
||||
.retain(|s| !string_lists.iter().any(|sl| sl.contains(s)));
|
||||
}
|
||||
|
||||
pub fn sort(&mut self) {
|
||||
self.0.sort_unstable();
|
||||
}
|
||||
|
||||
pub fn formatln(&self) -> String {
|
||||
self.0
|
||||
.iter()
|
||||
|
||||
Loading…
Reference in New Issue
Block a user