Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions config/keymap.toml
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ keymap = [

{ keys = ["delete"], commands = ["delete_files"] },
{ keys = ["d", "D"], commands = ["delete_files"] },
{ keys = ["d", "c"], commands = ["calculate_cumulative_size"] },

{ keys = ["p", "p"], commands = ["paste_files"] },
{ keys = ["p", "o"], commands = ["paste_files --overwrite=true"] },
Expand Down
93 changes: 93 additions & 0 deletions src/commands/cumulative_size.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::fs;
use std::path;

use crate::error::AppResult;
use crate::types::state::AppState;
use crate::utils::format::file_size_to_string;

/// Recursively compute the total size in bytes of every regular file rooted
/// at `path`. Symlinks are not followed; their own size is included instead.
/// I/O errors on individual entries are silently skipped, matching `du`.
fn compute_size(path: &path::Path) -> u64 {
let symlink_meta = match fs::symlink_metadata(path) {
Ok(m) => m,
Err(_) => return 0,
};

let file_type = symlink_meta.file_type();
if file_type.is_symlink() {
return symlink_meta.len();
}
if !file_type.is_dir() {
return symlink_meta.len();
}

let read_dir = match fs::read_dir(path) {
Ok(rd) => rd,
Err(_) => return symlink_meta.len(),
};

let mut total: u64 = 0;
for entry in read_dir.flatten() {
total = total.saturating_add(compute_size(&entry.path()));
}
total
}

pub fn calculate_cumulative_size(app_state: &mut AppState) -> AppResult {
let targets: Vec<(path::PathBuf, String)> = match app_state
.state
.tab_state_ref()
.curr_tab_ref()
.curr_list_ref()
{
Some(list) => list
.selected_or_current()
.into_iter()
.map(|e| (e.file_path().to_path_buf(), e.file_name().to_string()))
.collect(),
None => Vec::new(),
};

if targets.is_empty() {
return Ok(());
}

let mut total: u64 = 0;
let mut sizes: Vec<(String, u64)> = Vec::with_capacity(targets.len());
for (path, name) in &targets {
let size = compute_size(path);
total = total.saturating_add(size);
sizes.push((name.clone(), size));
}

if let Some(list) = app_state
.state
.tab_state_mut()
.curr_tab_mut()
.curr_list_mut()
{
for (name, size) in &sizes {
if let Some(entry) = list.iter_mut().find(|e| e.file_name() == name.as_str()) {
entry.metadata.update_cumulative_size(*size);
}
}
}

let msg = if sizes.len() == 1 {
format!(
"Size of {}: {}",
sizes[0].0.trim(),
file_size_to_string(total).trim()
)
} else {
format!(
"Cumulative size of {} items: {}",
sizes.len(),
file_size_to_string(total).trim()
)
};
app_state.state.message_queue_mut().push_info(msg);

Ok(())
}
1 change: 1 addition & 0 deletions src/commands/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ pub mod bulk_rename;
pub mod case_sensitivity;
pub mod change_directory;
pub mod command_line;
pub mod cumulative_size;
pub mod cursor_move;
pub mod custom_search;
pub mod delete_files;
Expand Down
1 change: 1 addition & 0 deletions src/constants/command_name.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,4 +101,5 @@ cmd_constants![
(CMD_CUSTOM_SEARCH, "custom_search"),
(CMD_CUSTOM_SEARCH_INTERACTIVE, "custom_search_interactive"),
(CMD_SIGNAL_SUSPEND, "suspend"),
(CMD_CALCULATE_CUMULATIVE_SIZE, "calculate_cumulative_size"),
];
11 changes: 11 additions & 0 deletions src/fs/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ pub enum LinkType {
pub struct JoshutoMetadata {
pub len: u64,
pub directory_size: Option<usize>,
pub cumulative_size: Option<u64>,
pub modified: time::SystemTime,
pub accessed: time::SystemTime,
pub mode: Mode,
Expand Down Expand Up @@ -75,6 +76,7 @@ impl JoshutoMetadata {
};

let directory_size = None;
let cumulative_size = None;
let (file_type, mode) = match metadata.as_ref() {
Ok(metadata) => {
let metadata_mode = metadata.mode();
Expand Down Expand Up @@ -121,6 +123,7 @@ impl JoshutoMetadata {
Ok(Self {
len,
directory_size,
cumulative_size,
modified,
accessed,
mode,
Expand All @@ -145,6 +148,14 @@ impl JoshutoMetadata {
self.directory_size = Some(size);
}

pub fn cumulative_size(&self) -> Option<u64> {
self.cumulative_size
}

pub fn update_cumulative_size(&mut self, size: u64) {
self.cumulative_size = Some(size);
}

pub fn modified(&self) -> time::SystemTime {
self.modified
}
Expand Down
3 changes: 3 additions & 0 deletions src/history.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ pub fn create_dirlist_with_history(
if let Some(former_entry) = former_entries_by_file_name.get(entry.file_name()) {
entry.set_permanent_selected(former_entry.is_permanent_selected());
entry.set_visual_mode_selected(former_entry.is_visual_mode_selected());
if let Some(size) = former_entry.metadata.cumulative_size() {
entry.metadata.update_cumulative_size(size);
}
}
}
}
Expand Down
2 changes: 2 additions & 0 deletions src/types/command/impl_appcommand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ impl AppCommand for Command {

Self::BookmarkAdd => CMD_BOOKMARK_ADD,
Self::BookmarkChangeDirectory => CMD_BOOKMARK_CHANGE_DIRECTORY,

Self::CalculateCumulativeSize => CMD_CALCULATE_CUMULATIVE_SIZE,
}
}
}
2 changes: 2 additions & 0 deletions src/types/command/impl_appexecute.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,8 @@ impl AppExecute for Command {
bookmark::change_directory_bookmark(app_state, backend)
}

Self::CalculateCumulativeSize => cumulative_size::calculate_cumulative_size(app_state),

Self::CustomSearch(words) => {
custom_search::custom_search(app_state, backend, words.as_slice(), false)
}
Expand Down
1 change: 1 addition & 0 deletions src/types/command/impl_comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ impl CommandComment for Command {

Self::BookmarkAdd => "Add a bookmark",
Self::BookmarkChangeDirectory => "Navigate to a bookmark",
Self::CalculateCumulativeSize => "Calculate cumulative size of selected files",
Self::CustomSearch(_) => "Find file based on the custom command",
Self::CustomSearchInteractive(_) => {
"Interactively find file based on the custom command"
Expand Down
6 changes: 6 additions & 0 deletions src/types/command/impl_from_str.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,12 @@ impl std::str::FromStr for Command {

simple_command_conversion_case!(command, CMD_SIGNAL_SUSPEND, Self::SignalSuspend);

simple_command_conversion_case!(
command,
CMD_CALCULATE_CUMULATIVE_SIZE,
Self::CalculateCumulativeSize
);

if command == CMD_QUIT {
match arg {
"--force" => Ok(Self::Quit(QuitAction::Force)),
Expand Down
2 changes: 2 additions & 0 deletions src/types/command/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,4 +213,6 @@ pub enum Command {

BookmarkAdd,
BookmarkChangeDirectory,

CalculateCumulativeSize,
}
3 changes: 2 additions & 1 deletion src/types/option/sort/sort_option.rs
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,8 @@ fn mtime_sort(file1: &JoshutoDirEntry, file2: &JoshutoDirEntry) -> cmp::Ordering
}

fn size_sort(file1: &JoshutoDirEntry, file2: &JoshutoDirEntry) -> cmp::Ordering {
file1.metadata.len().cmp(&file2.metadata.len())
let size = |e: &JoshutoDirEntry| e.metadata.cumulative_size().unwrap_or(e.metadata.len());
size(file1).cmp(&size(file2))
}

fn ext_sort(file1: &JoshutoDirEntry, file2: &JoshutoDirEntry) -> cmp::Ordering {
Expand Down
3 changes: 3 additions & 0 deletions src/ui/widgets/tui_dirlist_detailed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ impl Widget for TuiDirListDetailed<'_> {
}

fn get_entry_size_string(entry: &JoshutoDirEntry) -> String {
if let Some(size) = entry.metadata.cumulative_size() {
return format::file_size_to_string(size);
}
match entry.metadata.file_type() {
FileType::Directory => entry
.metadata
Expand Down