mirror of
https://github.com/rustic-rs/rustic.git
synced 2025-10-26 11:18:51 +00:00
Merge pull request #763 from rustic-rs/extend-ls
ls: Add options --summary and --long
This commit is contained in:
commit
282eba028f
@ -16,4 +16,5 @@ New features:
|
||||
- fix: wait for password-command to successfully exit, allowing to input something into the command, and read password from stdout.
|
||||
- repoinfo: Added new options --json, --only-files, --only-index
|
||||
- Creation of new keys now enforces confirmation of entered key. This helps to prevent mistype of passwords during the initial entry
|
||||
- Check: Add check if time is set for packs-to-delete
|
||||
- Check: Add check if time is set for packs-to-delete
|
||||
- ls: Options --long (-l) and --summary (-s) have been added.
|
||||
@ -1,5 +1,7 @@
|
||||
//! `ls` subcommand
|
||||
|
||||
use std::path::Path;
|
||||
|
||||
/// App-local prelude includes `app_reader()`/`app_writer()`/`app_config()`
|
||||
/// accessors along with logging macros. Customize as you see fit.
|
||||
use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
|
||||
@ -7,7 +9,23 @@ use crate::{commands::open_repository, status_err, Application, RUSTIC_APP};
|
||||
use abscissa_core::{Command, Runnable, Shutdown};
|
||||
use anyhow::Result;
|
||||
|
||||
use rustic_core::TreeStreamerOptions;
|
||||
use rustic_core::{Node, NodeType, TreeStreamerOptions};
|
||||
|
||||
mod constants {
|
||||
// constants from man page inode(7)
|
||||
pub(super) const S_IRUSR: u32 = 0o400; // owner has read permission
|
||||
pub(super) const S_IWUSR: u32 = 0o200; // owner has write permission
|
||||
pub(super) const S_IXUSR: u32 = 0o100; // owner has execute permission
|
||||
|
||||
pub(super) const S_IRGRP: u32 = 0o040; // group has read permission
|
||||
pub(super) const S_IWGRP: u32 = 0o020; // group has write permission
|
||||
pub(super) const S_IXGRP: u32 = 0o010; // group has execute permission
|
||||
|
||||
pub(super) const S_IROTH: u32 = 0o004; // others have read permission
|
||||
pub(super) const S_IWOTH: u32 = 0o002; // others have write permission
|
||||
pub(super) const S_IXOTH: u32 = 0o001; // others have execute permission
|
||||
}
|
||||
use constants::{S_IRGRP, S_IROTH, S_IRUSR, S_IWGRP, S_IWOTH, S_IWUSR, S_IXGRP, S_IXOTH, S_IXUSR};
|
||||
|
||||
/// `ls` subcommand
|
||||
#[derive(clap::Parser, Command, Debug)]
|
||||
@ -20,6 +38,14 @@ pub(crate) struct LsCmd {
|
||||
#[clap(long)]
|
||||
recursive: bool,
|
||||
|
||||
/// show summary
|
||||
#[clap(long, short = 's')]
|
||||
summary: bool,
|
||||
|
||||
/// show long listing
|
||||
#[clap(long, short = 'l')]
|
||||
long: bool,
|
||||
|
||||
#[clap(flatten)]
|
||||
streamer_opts: TreeStreamerOptions,
|
||||
}
|
||||
@ -33,6 +59,25 @@ impl Runnable for LsCmd {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
struct Summary {
|
||||
files: usize,
|
||||
size: u64,
|
||||
dirs: usize,
|
||||
}
|
||||
|
||||
impl Summary {
|
||||
fn update(&mut self, node: &Node) {
|
||||
if node.is_dir() {
|
||||
self.dirs += 1;
|
||||
}
|
||||
if node.is_file() {
|
||||
self.files += 1;
|
||||
self.size += node.meta.size;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl LsCmd {
|
||||
fn inner_run(&self) -> Result<()> {
|
||||
let config = RUSTIC_APP.config();
|
||||
@ -42,13 +87,83 @@ impl LsCmd {
|
||||
let node =
|
||||
repo.node_from_snapshot_path(&self.snap, |sn| config.snapshot_filter.matches(sn))?;
|
||||
|
||||
// recursive if standard if we specify a snapshot without dirs. In other cases, use the parameter `recursive`
|
||||
let recursive = !self.snap.contains(':') || self.recursive;
|
||||
|
||||
let mut summary = Summary::default();
|
||||
|
||||
for item in repo.ls(&node, &self.streamer_opts, recursive)? {
|
||||
let (path, _) = item?;
|
||||
println!("{path:?} ");
|
||||
let (path, node) = item?;
|
||||
summary.update(&node);
|
||||
if self.long {
|
||||
print_node(&node, &path);
|
||||
} else {
|
||||
println!("{path:?} ");
|
||||
}
|
||||
}
|
||||
|
||||
if self.summary {
|
||||
println!(
|
||||
"total: {} dirs, {} files, {} bytes",
|
||||
summary.dirs, summary.files, summary.size
|
||||
);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// print node in format similar to unix `ls`
|
||||
fn print_node(node: &Node, path: &Path) {
|
||||
println!(
|
||||
"{:>1}{:>9} {:>8} {:>8} {:>9} {:>12} {path:?} {}",
|
||||
match node.node_type {
|
||||
NodeType::Dir => 'd',
|
||||
NodeType::Symlink { .. } => 'l',
|
||||
NodeType::Chardev { .. } => 'c',
|
||||
NodeType::Dev { .. } => 'b',
|
||||
NodeType::Fifo { .. } => 'p',
|
||||
NodeType::Socket => 's',
|
||||
_ => '-',
|
||||
},
|
||||
node.meta
|
||||
.mode
|
||||
.map(parse_permissions)
|
||||
.unwrap_or_else(|| "?????????".to_string()),
|
||||
node.meta.user.clone().unwrap_or_else(|| "?".to_string()),
|
||||
node.meta.group.clone().unwrap_or_else(|| "?".to_string()),
|
||||
node.meta.size,
|
||||
node.meta
|
||||
.mtime
|
||||
.map(|t| t.format("%_d %b %H:%M").to_string())
|
||||
.unwrap_or_else(|| "?".to_string()),
|
||||
if let NodeType::Symlink { linktarget } = &node.node_type {
|
||||
["->", linktarget].join(" ")
|
||||
} else {
|
||||
String::new()
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// helper fn to put permissions in readable format
|
||||
fn parse_permissions(mode: u32) -> String {
|
||||
let user = triplet(mode, S_IRUSR, S_IWUSR, S_IXUSR);
|
||||
let group = triplet(mode, S_IRGRP, S_IWGRP, S_IXGRP);
|
||||
let other = triplet(mode, S_IROTH, S_IWOTH, S_IXOTH);
|
||||
[user, group, other].join("")
|
||||
}
|
||||
|
||||
// helper fn to put permissions in readable format
|
||||
fn triplet(mode: u32, read: u32, write: u32, execute: u32) -> String {
|
||||
match (mode & read, mode & write, mode & execute) {
|
||||
(0, 0, 0) => "---",
|
||||
(_, 0, 0) => "r--",
|
||||
(0, _, 0) => "-w-",
|
||||
(0, 0, _) => "--x",
|
||||
(_, 0, _) => "r-x",
|
||||
(_, _, 0) => "rw-",
|
||||
(0, _, _) => "-wx",
|
||||
(_, _, _) => "rwx",
|
||||
}
|
||||
.to_string()
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user