Skip to content

Commit b2b061e

Browse files
committed
✨ -O option for list mode
1 parent 4f77b2a commit b2b061e

2 files changed

Lines changed: 56 additions & 72 deletions

File tree

cli/src/command/list.rs

Lines changed: 55 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -285,6 +285,7 @@ fn list_archive(args: ListCommand) -> anyhow::Result<()> {
285285
hide_control_chars: args.hide_control_chars,
286286
classify: args.classify,
287287
format: args.format,
288+
out_to_stderr: false,
288289
};
289290
let archive = args.file.archive();
290291
let files = args.file.files();
@@ -363,6 +364,7 @@ pub(crate) struct ListOptions {
363364
pub(crate) hide_control_chars: bool,
364365
pub(crate) classify: bool,
365366
pub(crate) format: Option<Format>,
367+
pub(crate) out_to_stderr: bool,
366368
}
367369

368370
pub(crate) fn run_list_archive<'a>(
@@ -441,19 +443,31 @@ fn print_entries<'a>(
441443
})
442444
.collect::<Vec<_>>();
443445
globs.ensure_all_matched()?;
446+
let mut stdout = io::stdout().lock();
447+
let mut stderr = io::stderr().lock();
448+
let out: &mut dyn Write = if options.out_to_stderr {
449+
&mut stderr
450+
} else {
451+
&mut stdout
452+
};
453+
444454
match options.format {
445-
Some(Format::Line) => simple_list_entries(entries, options),
446-
Some(Format::JsonL) => json_line_entries(entries),
447-
Some(Format::Table) => detail_list_entries(entries, options),
448-
Some(Format::Tree) => tree_entries(entries, options),
449-
Some(Format::BsdTar) => bsd_tar_list_entries(entries, options),
450-
None if options.long => detail_list_entries(entries, options),
451-
None => simple_list_entries(entries, options),
452-
}
455+
Some(Format::Line) => simple_list_entries_to(entries, &options, out)?,
456+
Some(Format::JsonL) => json_line_entries_to(entries, out)?,
457+
Some(Format::Table) => detail_list_entries_to(entries, &options, out)?,
458+
Some(Format::Tree) => tree_entries_to(entries, &options, out)?,
459+
Some(Format::BsdTar) => bsd_tar_list_entries_to(entries, &options, out)?,
460+
None if options.long => detail_list_entries_to(entries, &options, out)?,
461+
None => simple_list_entries_to(entries, &options, out)?,
462+
};
453463
Ok(())
454464
}
455465

456-
fn bsd_tar_list_entries(entries: Vec<TableRow>, options: ListOptions) {
466+
fn bsd_tar_list_entries_to(
467+
entries: Vec<TableRow>,
468+
options: &ListOptions,
469+
out: &mut dyn Write,
470+
) -> io::Result<()> {
457471
let now = SystemTime::now();
458472
let mut uname_width = 6;
459473
let mut gname_width = 6;
@@ -486,10 +500,12 @@ fn bsd_tar_list_entries(entries: Vec<TableRow>, options: ListOptions) {
486500

487501
// permission nlink uname gname size mtime name link
488502
// ex: -rw-r--r-- 0 1000 1000 0 Jan 1 1980 f
489-
println!(
503+
writeln!(
504+
out,
490505
"{perm} {nlink} {uname:<uname_width$} {gname:<gname_width$} {size:8} {mtime} {name}"
491-
);
506+
)?;
492507
}
508+
Ok(())
493509
}
494510

495511
fn bsd_tar_time(now: SystemTime, time: SystemTime) -> DelayedFormat<StrftimeItems<'static>> {
@@ -527,17 +543,23 @@ impl<'a> Display for SimpleListDisplay<'a> {
527543
}
528544
}
529545

530-
fn simple_list_entries(entries: Vec<TableRow>, options: ListOptions) {
531-
print!(
532-
"{}",
533-
SimpleListDisplay {
534-
entries: &entries,
535-
options: &options,
536-
}
537-
);
546+
fn simple_list_entries_to(
547+
entries: Vec<TableRow>,
548+
options: &ListOptions,
549+
out: &mut dyn Write,
550+
) -> io::Result<()> {
551+
let display = SimpleListDisplay {
552+
entries: &entries,
553+
options,
554+
};
555+
write!(out, "{display}")
538556
}
539557

540-
fn detail_list_entries(entries: impl IntoIterator<Item = TableRow>, options: ListOptions) {
558+
fn detail_list_entries_to(
559+
entries: impl IntoIterator<Item = TableRow>,
560+
options: &ListOptions,
561+
out: &mut dyn Write,
562+
) -> io::Result<()> {
541563
let underline = Color::new("\x1B[4m", "\x1B[0m");
542564
let reset = Color::new("\x1B[8m", "\x1B[0m");
543565
let header = [
@@ -663,7 +685,7 @@ fn detail_list_entries(entries: impl IntoIterator<Item = TableRow>, options: Lis
663685
Color::empty(),
664686
Color::empty(),
665687
));
666-
println!("{table}");
688+
writeln!(out, "{table}")
667689
}
668690

669691
const DURATION_SIX_MONTH: Duration = Duration::from_secs(60 * 60 * 24 * 30 * 6);
@@ -843,7 +865,7 @@ struct XAttr<'a> {
843865
value: String,
844866
}
845867

846-
fn json_line_entries(entries: Vec<TableRow>) {
868+
fn json_line_entries_to(entries: Vec<TableRow>, out: &mut dyn Write) -> anyhow::Result<()> {
847869
let entries = entries
848870
.par_iter()
849871
.map(|it| {
@@ -899,54 +921,11 @@ fn json_line_entries(entries: Vec<TableRow>) {
899921
})
900922
.collect::<Vec<_>>();
901923

902-
print!("{}", JsonLDisplay(entries));
903-
}
904-
905-
struct JsonLDisplay<T>(Vec<T>);
906-
907-
impl<T: serde::Serialize> Display for JsonLDisplay<T> {
908-
#[inline]
909-
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
910-
struct FormatterAdapter<'f, 'a>(&'f mut Formatter<'a>);
911-
impl Write for FormatterAdapter<'_, '_> {
912-
#[inline]
913-
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
914-
self.0
915-
.write_str(unsafe { std::str::from_utf8_unchecked(buf) })
916-
.map_err(io::Error::other)?;
917-
Ok(buf.len())
918-
}
919-
920-
#[inline]
921-
fn flush(&mut self) -> io::Result<()> {
922-
Ok(())
923-
}
924-
}
925-
926-
impl fmt::Write for FormatterAdapter<'_, '_> {
927-
#[inline]
928-
fn write_str(&mut self, s: &str) -> fmt::Result {
929-
self.0.write_str(s)
930-
}
931-
932-
#[inline]
933-
fn write_char(&mut self, c: char) -> fmt::Result {
934-
self.0.write_char(c)
935-
}
936-
937-
#[inline]
938-
fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result {
939-
self.0.write_fmt(args)
940-
}
941-
}
942-
let mut writer = FormatterAdapter(f);
943-
for line in &self.0 {
944-
use core::fmt::Write;
945-
serde_json::to_writer(&mut writer, &line).map_err(|_| fmt::Error)?;
946-
writer.write_char('\n')?;
947-
}
948-
Ok(())
924+
for line in entries {
925+
serde_json::to_writer(&mut *out, &line)?;
926+
out.write_all(b"\n")?;
949927
}
928+
Ok(())
950929
}
951930

952931
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug)]
@@ -962,16 +941,20 @@ impl<'s> TreeEntry<'s> {
962941
}
963942
}
964943

965-
fn tree_entries(entries: Vec<TableRow>, options: ListOptions) {
944+
fn tree_entries_to(
945+
entries: Vec<TableRow>,
946+
options: &ListOptions,
947+
out: &mut dyn Write,
948+
) -> io::Result<()> {
966949
let entries = entries.iter().map(|it| match &it.entry_type {
967950
EntryType::File(name) => (name.as_str(), DataKind::File),
968951
EntryType::Directory(name) => (name.as_str(), DataKind::Directory),
969952
EntryType::SymbolicLink(name, _) => (name.as_str(), DataKind::SymbolicLink),
970953
EntryType::HardLink(name, _) => (name.as_str(), DataKind::HardLink),
971954
});
972955
let map = build_tree_map(entries);
973-
let tree = build_term_tree(&map, Cow::Borrowed(""), None, DataKind::Directory, &options);
974-
println!("{tree}");
956+
let tree = build_term_tree(&map, Cow::Borrowed(""), None, DataKind::Directory, options);
957+
writeln!(out, "{tree}")
975958
}
976959

977960
fn build_tree_map<'s>(

cli/src/command/stdio.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ fn run_list_archive(args: StdioCommand) -> anyhow::Result<()> {
671671
} else {
672672
Format::Line
673673
}),
674+
out_to_stderr: args.to_stdout,
674675
};
675676
let files_globs = GlobPatterns::new(args.files.iter().map(|it| it.as_str()))?;
676677

0 commit comments

Comments
 (0)