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,
}
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;
fn get_offset_from_previous_line(dired_positions: &[BytePosition]) -> usize {
if let Some(last_position) = dired_positions.last() {
last_position.end + 1
} else {
0
}
}
pub fn calculate_dired(
dired_positions: &[BytePosition],
output_display_len: usize,
dfn_len: usize,
) -> (usize, usize) {
let offset_from_previous_line = get_offset_from_previous_line(dired_positions);
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 = get_offset_from_previous_line(&dired.dired_positions);
let additional_offset = if dired.subdired_positions.is_empty() {
0
} else {
2
};
let start = offset_from_previous_line + DIRED_TRAILING_OFFSET + additional_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 config.recursive {
print_positions("//SUBDIRED//", &dired.subdired_positions);
}
println!("//DIRED-OPTIONS// --quoting-style={}", config.quoting_style);
Ok(())
}
fn print_positions(prefix: &str, positions: &Vec<BytePosition>) {
print!("{prefix}");
for c in positions {
print!(" {c}");
}
println!();
}
pub fn add_total(dired: &mut DiredOutput, total_len: usize) {
if dired.padding == 0 {
let offset_from_previous_line = get_offset_from_previous_line(&dired.dired_positions);
dired.padding = total_len + offset_from_previous_line + DIRED_TRAILING_OFFSET;
} else {
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 + 1;
}
pub fn calculate_and_update_positions(
dired: &mut DiredOutput,
output_display_len: usize,
dfn_len: usize,
) {
let offset = dired
.dired_positions
.last()
.map_or(DIRED_TRAILING_OFFSET, |last_position| {
last_position.start + DIRED_TRAILING_OFFSET
});
let start = output_display_len + offset + DIRED_TRAILING_OFFSET;
let end = start + dfn_len;
update_positions(dired, start, end);
}
pub fn update_positions(dired: &mut DiredOutput, start: usize, end: usize) {
dired.dired_positions.push(BytePosition {
start: start + dired.padding,
end: end + dired.padding,
});
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_positions = vec![BytePosition { start: 5, end: 10 }];
let (start, end) = calculate_dired(&dired_positions, output_display.len(), dfn.len());
assert_eq!(start, 24);
assert_eq!(end, 35);
}
#[test]
fn test_get_offset_from_previous_line() {
let positions = vec![
BytePosition { start: 0, end: 3 },
BytePosition { start: 4, end: 7 },
BytePosition { start: 8, end: 11 },
];
assert_eq!(get_offset_from_previous_line(&positions), 12);
}
#[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,
};
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,
};
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: 8
}
);
}
#[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,
};
let total_len = 8;
add_total(&mut dired, total_len);
assert_eq!(dired.padding, 22);
}
#[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,
};
let dir_len = 5;
add_dir_name(&mut dired, dir_len);
assert_eq!(dired.padding, 8);
let total_len = 8;
add_total(&mut dired, total_len);
assert_eq!(dired.padding, 18);
}
#[test]
fn test_dired_update_positions() {
let mut dired = DiredOutput {
dired_positions: vec![BytePosition { start: 5, end: 10 }],
subdired_positions: vec![],
padding: 10,
};
update_positions(&mut dired, 15, 20);
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);
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,
};
let output_display_len = 15;
let dfn_len = 5;
calculate_and_update_positions(&mut dired, output_display_len, dfn_len);
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);
}
}