Add tag command

This commit is contained in:
Alexander Weiss 2022-04-22 00:13:32 +02:00
parent 9c65a0b432
commit d91045910f
4 changed files with 116 additions and 7 deletions

View File

@ -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,

View File

@ -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
View 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)
}

View File

@ -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()