Skip to content

Commit

Permalink
ls: Add options --summary and --long
Browse files Browse the repository at this point in the history
  • Loading branch information
aawsome committed Jul 17, 2023
1 parent 1232a06 commit d47cd20
Show file tree
Hide file tree
Showing 2 changed files with 117 additions and 4 deletions.
3 changes: 2 additions & 1 deletion changelog/new.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,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.
118 changes: 115 additions & 3 deletions src/commands/ls.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
//! `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};

use abscissa_core::{Command, Runnable, Shutdown};
use anyhow::Result;

use rustic_core::TreeStreamerOptions;
use rustic_core::{Node, NodeType, TreeStreamerOptions};

/// `ls` subcommand
#[derive(clap::Parser, Command, Debug)]
Expand All @@ -20,6 +22,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,
}
Expand All @@ -33,6 +43,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();
Expand All @@ -42,13 +71,96 @@ 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()
}
);
}

// consts from man page inode(7)
const S_IRUSR: u32 = 0o400; // owner has read permission
const S_IWUSR: u32 = 0o200; // owner has write permission
const S_IXUSR: u32 = 0o100; // owner has execute permission

const S_IRGRP: u32 = 0o040; // group has read permission
const S_IWGRP: u32 = 0o020; // group has write permission
const S_IXGRP: u32 = 0o010; // group has execute permission

const S_IROTH: u32 = 0o004; // others have read permission
const S_IWOTH: u32 = 0o002; // others have write permission
const S_IXOTH: u32 = 0o001; // others have execute permission

// 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()
}

0 comments on commit d47cd20

Please sign in to comment.