use crate::Config;
use std::fmt;
use std::io::{BufWriter, Stdout, Write};
use uucore::error::UResult;
#[derive(Debug, Clone, PartialEq)]
pub struct BytePosition {
pub start: usize,
pub end: usize,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct DiredOutput {
pub dired_positions: Vec<BytePosition>,
pub subdired_positions: Vec<BytePosition>,
pub padding: usize,
pub line_offset: usize,
}
impl fmt::Display for BytePosition {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{} {}", self.start, self.end)
}
}
static DIRED_TRAILING_OFFSET: usize = 2;
pub fn calculate_dired(
dired: &DiredOutput,
output_display_len: usize,
dfn_len: usize,
) -> (usize, usize) {
let offset_from_previous_line = dired.line_offset;
let start = output_display_len + offset_from_previous_line;
let end = start + dfn_len;
(start, end)
}
pub fn indent(out: &mut BufWriter<Stdout>) -> UResult<()> {
write!(out, " ")?;
Ok(())
}
pub fn calculate_subdired(dired: &mut DiredOutput, path_len: usize) {
let offset_from_previous_line = dired.line_offset + dired.padding;
let start = offset_from_previous_line + DIRED_TRAILING_OFFSET;
let end = start + path_len;
dired.subdired_positions.push(BytePosition { start, end });
}
pub fn print_dired_output(
config: &Config,
dired: &DiredOutput,
out: &mut BufWriter<Stdout>,
) -> UResult<()> {
out.flush()?;
if !dired.dired_positions.is_empty() {
print_positions("//DIRED//", &dired.dired_positions);
}
if !dired.subdired_positions.is_empty() {
print_positions("//SUBDIRED//", &dired.subdired_positions);
}
println!("//DIRED-OPTIONS// --quoting-style={}", config.quoting_style);
Ok(())
}
fn print_positions(prefix: &str, positions: &[BytePosition]) {
print!("{prefix}");
for c in positions {
print!(" {c}");
}
println!();
}
pub fn add_total(dired: &mut DiredOutput, total_len: usize) {
dired.padding += total_len + DIRED_TRAILING_OFFSET;
}
pub fn add_dir_name(dired: &mut DiredOutput, dir_len: usize) {
dired.padding += dir_len + DIRED_TRAILING_OFFSET + 2;
}
pub fn calculate_and_update_positions(
dired: &mut DiredOutput,
output_display_len: usize,
dfn_len: usize,
line_len: usize,
) {
let (start, end) = calculate_dired(dired, output_display_len, dfn_len);
update_positions(dired, start, end, line_len);
}
pub fn update_positions(dired: &mut DiredOutput, start: usize, end: usize, line_len: usize) {
let padding = dired.padding;
dired.dired_positions.push(BytePosition {
start: start + padding,
end: end + padding,
});
dired.line_offset += padding + line_len;
dired.padding = 0;
}
pub fn is_dired_arg_present() -> bool {
std::env::args_os().any(|x| x == "--dired" || x == "-D")
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_calculate_dired() {
let output_display = "sample_output".to_string();
let dfn = "sample_file".to_string();
let dired = DiredOutput {
dired_positions: vec![BytePosition { start: 5, end: 10 }],
subdired_positions: vec![],
padding: 0,
line_offset: 11,
};
let (start, end) = calculate_dired(&dired, output_display.len(), dfn.len());
assert_eq!(start, 24);
assert_eq!(end, 35);
}
#[test]
fn test_calculate_subdired() {
let mut dired = DiredOutput {
dired_positions: vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
],
subdired_positions: vec![],
padding: 0,
line_offset: 12,
};
let path_len = 5;
calculate_subdired(&mut dired, path_len);
assert_eq!(
dired.subdired_positions,
vec![BytePosition { start: 14, end: 19 }],
);
}
#[test]
fn test_add_dir_name() {
let mut dired = DiredOutput {
dired_positions: vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
],
subdired_positions: vec![],
padding: 0,
line_offset: 0,
};
let dir_len = 5;
add_dir_name(&mut dired, dir_len);
assert_eq!(
dired,
DiredOutput {
dired_positions: vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
],
subdired_positions: vec![],
padding: 9,
line_offset: 0,
}
);
}
#[test]
fn test_add_total() {
let mut dired = DiredOutput {
dired_positions: vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
],
subdired_positions: vec![],
padding: 0,
line_offset: 12,
};
let total_len = 8;
add_total(&mut dired, total_len);
assert_eq!(dired.padding, 10);
}
#[test]
fn test_add_dir_name_and_total() {
let mut dired = DiredOutput {
dired_positions: vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
],
subdired_positions: vec![],
padding: 0,
line_offset: 12,
};
let dir_len = 5;
add_dir_name(&mut dired, dir_len);
assert_eq!(dired.padding, 9);
let total_len = 8;
add_total(&mut dired, total_len);
assert_eq!(dired.padding, 19);
}
#[test]
fn test_dired_update_positions() {
let mut dired = DiredOutput {
dired_positions: vec![BytePosition { start: 5, end: 10 }],
subdired_positions: vec![],
padding: 10,
line_offset: 0,
};
update_positions(&mut dired, 15, 20, 1);
let last_position = dired.dired_positions.last().unwrap();
assert_eq!(last_position.start, 25); assert_eq!(last_position.end, 30);
update_positions(&mut dired, 30, 35, 1);
let last_position = dired.dired_positions.last().unwrap();
assert_eq!(last_position.start, 30);
assert_eq!(last_position.end, 35);
}
#[test]
fn test_calculate_and_update_positions() {
let mut dired = DiredOutput {
dired_positions: vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
],
subdired_positions: vec![],
padding: 5,
line_offset: 12,
};
let output_display_len = 15;
let dfn_len = 5;
calculate_and_update_positions(&mut dired, output_display_len, dfn_len, 20);
assert_eq!(
dired.dired_positions,
vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
BytePosition { start: 32, end: 37 },
]
);
assert_eq!(dired.padding, 0);
}
}