use crate::entry::Entry;
use crate::output::DisplayOptions;
use crate::output::format::{format_size_width, write_date, write_name, write_size};
use std::borrow::Borrow;
use std::io::{self, Write};
pub fn write<E: Borrow<Entry>>(
w: &mut impl Write,
entries: &[E],
opts: &DisplayOptions,
) -> io::Result<()> {
if entries.is_empty() {
return Ok(());
}
let max_size_width = entries
.iter()
.map(|e| {
let entry: &Entry = e.borrow();
format_size_width(entry.size, entry.is_dir)
})
.max()
.unwrap_or(0);
let now_secs = time::OffsetDateTime::now_utc().unix_timestamp();
for e in entries {
let entry: &Entry = e.borrow();
let padding = max_size_width - format_size_width(entry.size, entry.is_dir);
if padding > 0 {
write!(w, "{:padding$}", "")?;
}
write_size(w, entry.size, entry.is_dir)?;
w.write_all(b" ")?;
write_date(w, entry.modified, now_secs)?;
w.write_all(b" ")?;
write_name(w, entry, opts)?;
writeln!(w)?;
}
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
fn make_opts() -> DisplayOptions {
DisplayOptions {
short: false,
classify: true,
color_enabled: false,
terminal_width: 80,
ls_colors: lscolors::LsColors::empty(),
}
}
fn file(name: &str, size: u64, modified: i64) -> Entry {
Entry {
relative_path: name.to_owned(),
depth: 0,
size,
is_dir: false,
is_symlink: false,
is_hidden: false,
modified,
}
}
fn dir(name: &str, modified: i64) -> Entry {
Entry {
relative_path: name.to_owned(),
depth: 0,
size: 0,
is_dir: true,
is_symlink: false,
is_hidden: false,
modified,
}
}
#[test]
fn alignment() {
let entries = vec![
file("small.txt", 42, 0),
file("big.bin", 1024 * 1024 * 5, 0),
dir("src", 0),
];
let refs: Vec<&Entry> = entries.iter().collect();
let mut buf = Vec::new();
write(&mut buf, &refs, &make_opts()).unwrap();
let output = String::from_utf8(buf).unwrap();
let lines: Vec<&str> = output.lines().collect();
assert!(lines[0].starts_with(" 42"), "got: {:?}", lines[0]);
assert!(lines[1].starts_with("5.0M"), "got: {:?}", lines[1]);
assert!(lines[2].starts_with(" -"), "got: {:?}", lines[2]);
assert!(lines[2].ends_with("src/"), "got: {:?}", lines[2]);
}
#[test]
fn zero_byte_file_shows_zero() {
let entries = vec![file("empty.lock", 0, 0)];
let refs: Vec<&Entry> = entries.iter().collect();
let mut buf = Vec::new();
write(&mut buf, &refs, &make_opts()).unwrap();
let output = String::from_utf8(buf).unwrap();
assert!(
output.starts_with("0"),
"expected '0' for empty file, got: {output}"
);
}
#[test]
fn empty() {
let mut buf = Vec::new();
write::<&Entry>(&mut buf, &[], &make_opts()).unwrap();
assert!(buf.is_empty());
}
}