1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
|
#![forbid(unsafe_code)]
#![allow(clippy::match_bool)]
use anyhow::{anyhow, Result};
use dua::{ByteFormat, TraversalSorting};
use std::{fs, io, io::Write, path::PathBuf, process};
use structopt::StructOpt;
use wild;
#[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))]
mod interactive;
mod options;
fn main() -> Result<()> {
use options::Command::*;
let opt: options::Args = options::Args::from_iter(wild::args_os());
let walk_options = dua::WalkOptions {
threads: opt.threads.unwrap_or(0),
byte_format: opt.format.map(Into::into).unwrap_or(ByteFormat::Metric),
apparent_size: opt.apparent_size,
count_hard_links: opt.count_hard_links,
sorting: TraversalSorting::None,
cross_filesystems: !opt.stay_on_filesystem,
};
let res = match opt.command {
#[cfg(any(feature = "tui-unix", feature = "tui-crossplatform"))]
Some(Interactive { input }) => {
use crate::interactive::{Interaction, TerminalApp};
use anyhow::Context;
use crosstermion::terminal::{tui::new_terminal, AlternateRawScreen};
let no_tty_msg = "Interactive mode requires a connected terminal";
if atty::isnt(atty::Stream::Stdout) {
return Err(anyhow!(no_tty_msg));
}
let mut terminal = new_terminal(
AlternateRawScreen::try_from(io::stdout()).with_context(|| no_tty_msg)?,
)
.with_context(|| "Could not instantiate terminal")?;
let res = TerminalApp::initialize(
&mut terminal,
walk_options,
paths_from(input)?,
Interaction::Full,
)?
.map(|(keys_rx, mut app)| {
let res = app.process_events(&mut terminal, keys_rx.into_iter());
// Leak app memory to avoid having to wait for the hashmap to deallocate, which causes a noticable delay shortly before the the
// program exits anyway.
std::mem::forget(app);
res
});
drop(terminal);
io::stdout().flush().ok();
// Exit 'quickly' to avoid having to not have to deal with slightly different types in the other match branches
std::process::exit(res.transpose()?.map(|e| e.to_exit_code()).unwrap_or(0));
}
Some(Aggregate {
input,
no_total,
no_sort,
statistics,
}) => {
let stdout = io::stdout();
let stdout_locked = stdout.lock();
let (res, stats) = dua::aggregate(
stdout_locked,
walk_options,
!no_total,
!no_sort,
paths_from(input)?,
)?;
if statistics {
writeln!(io::stderr(), "{:?}", stats).ok();
}
res
}
None => {
let stdout = io::stdout();
let stdout_locked = stdout.lock();
dua::aggregate(
stdout_locked,
walk_options,
true,
true,
paths_from(opt.input)?,
)?
.0
}
};
process::exit(res.to_exit_code());
}
fn paths_from(paths: Vec<PathBuf>) -> Result<Vec<PathBuf>, io::Error> {
if paths.is_empty() {
cwd_dirlist()
} else {
Ok(paths)
}
}
fn cwd_dirlist() -> Result<Vec<PathBuf>, io::Error> {
let mut v: Vec<_> = fs::read_dir(".")?
.filter_map(|e| {
e.ok()
.and_then(|e| e.path().strip_prefix(".").ok().map(ToOwned::to_owned))
})
.filter(|p| {
if let Ok(meta) = p.symlink_metadata() {
if meta.file_type().is_symlink() {
return false;
}
};
true
})
.collect();
v.sort();
Ok(v)
}
|