use anyhow::{bail, Context, Result};
use clap::Clap;
use dialoguer::{theme::ColorfulTheme, Confirm};
use log::debug;
use std::path::Path;
use crate::cfg::Config;
use crate::cli::color;
use crate::cmd::Command;
use crate::file_listing::FileListing;
use crate::ssh::SshSession;
#[derive(Clap, Debug)]
pub struct Clean {
#[clap(long)]
all: bool,
#[clap(long, short)]
details: bool,
#[clap(short, long = "file")]
files: Vec<String>,
#[clap(long, short = 'F', value_name = "regex")]
filter: Option<String>,
#[clap(short = 'n', long)]
last: Option<usize>,
#[clap()]
indices: Vec<i64>,
#[clap(long = "no-confirm")]
no_confirm: bool,
#[clap(long = "newer")]
select_newer: Option<String>,
#[clap(long = "older")]
select_older: Option<String>,
#[clap(long, short = 'S')]
sort_size: bool,
#[clap(long, short = 'T')]
sort_time: bool,
#[clap(long, short)]
reverse: bool,
}
impl Command for Clean {
fn run(&self, session: &SshSession, _config: &Config) -> Result<()> {
debug!("Cleaning remote files..");
let files: Vec<&str> = self.files.iter().map(|s| s.as_str()).collect();
let files_to_delete = session
.list_files()?
.with_all(self.all)
.by_indices(&self.indices[..])?
.by_filter(self.filter.as_ref().map(|s| s.as_str()))?
.with_all_if_none(self.select_newer.is_some() || self.select_older.is_some())
.select_newer(self.select_newer.as_deref())?
.select_older(self.select_older.as_deref())?
.sort_by_size(self.sort_size)?
.sort_by_time(self.sort_time)?
.revert(self.reverse)
.last(self.last)
.by_name(&files[..], session.host.prefix_length)?
.with_stats(self.details && !self.no_confirm)?;
let do_delete = self.no_confirm || self.user_confirm_deletion(&files_to_delete)?;
let remove_file =
|file_to_delete: &Path| -> Result<()> {
if file_to_delete.components().count() != 2 {
bail!("Invalid filename: {}", file_to_delete.display());
}
session.remove_folder(file_to_delete.parent().with_context(|| {
format!("File had not parent: {}", file_to_delete.display())
})?)?;
Ok(())
};
if do_delete {
for (_, file, _) in files_to_delete.iter()? {
remove_file(&file)?
}
}
Ok(())
}
}
impl Clean {
fn user_confirm_deletion(&self, files: &FileListing) -> Result<bool> {
let with_stats = files.has_stats();
let formatted_files = files.format_files(None, with_stats, with_stats, with_stats)?;
crate::cli::draw_boxed(
&format!(
"Will {delete} the following files:",
delete = console::Style::new()
.bold()
.red()
.bright()
.apply_to("delete")
)
.as_str(),
formatted_files.iter().map(|s| s.as_str()),
&color::frame,
)?;
Ok(Confirm::with_theme(&ColorfulTheme::default())
.with_prompt("Delete files?")
.default(false)
.interact()?)
}
}