Add the directory-order flag

This commit is contained in:
Peltoche 2019-01-12 13:50:11 +01:00 committed by Pierre Peltier
parent 5f50be78e9
commit 92d018255a
4 changed files with 238 additions and 87 deletions

View file

@ -98,4 +98,14 @@ pub fn build() -> App<'static, 'static> {
.multiple(true)
.help("Reverse the order of the sort"),
)
.arg(
Arg::with_name("group-dirs")
.long("group-dirs")
.possible_value("none")
.possible_value("first")
.possible_value("last")
.default_value("none")
.multiple(true)
.help("Sort the directories then the files"),
)
}

View file

@ -1,9 +1,8 @@
use ansi_term::{ANSIString, ANSIStrings};
use color::Colors;
use flags::{Flags, SortFlag, SortOrder};
use flags::{DirOrderFlag, Flags, SortFlag, SortOrder};
use icon::Icons;
use meta::FileType;
use meta::Meta;
use meta::{FileType, Meta};
use std::cmp::Ordering;
use std::iter::IntoIterator;
use std::vec::IntoIter;
@ -139,98 +138,230 @@ impl Batch {
}
fn sort_by_meta(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
let ord = match flags.sort_by {
SortFlag::Name => {
match flags.sort_by {
SortFlag::Name => match flags.directory_order {
DirOrderFlag::First => sort_by_name_with_dirs_first(a, b, flags),
DirOrderFlag::None => sort_by_name_with_dirs_unordored(a, b, flags),
DirOrderFlag::Last => sort_by_name_with_files_first(a, b, flags),
},
SortFlag::Time => match flags.directory_order {
DirOrderFlag::First => sort_by_date_with_dirs_first(a, b, flags),
DirOrderFlag::None => sort_by_date_with_dirs_unordored(a, b, flags),
DirOrderFlag::Last => sort_by_date_with_files_first(a, b, flags),
},
}
}
fn sort_by_name_with_dirs_unordored(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
if flags.sort_order == SortOrder::Default {
a.name.cmp(&b.name)
} else {
b.name.cmp(&a.name)
}
}
fn sort_by_name_with_dirs_first(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
if a.file_type == FileType::Directory && b.file_type != FileType::Directory {
Ordering::Less
} else if b.file_type == FileType::Directory && a.file_type != FileType::Directory {
Ordering::Greater
} else {
if flags.sort_order == SortOrder::Default {
a.name.cmp(&b.name)
} else {
b.name.cmp(&a.name)
}
}
// most recently modified first
SortFlag::Time => b.date.cmp(&a.date).then(a.name.cmp(&b.name)),
};
match flags.sort_order {
SortOrder::Default => ord,
SortOrder::Reverse => ord.reverse(),
}
fn sort_by_name_with_files_first(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
if a.file_type == FileType::Directory && b.file_type != FileType::Directory {
Ordering::Greater
} else if b.file_type == FileType::Directory && a.file_type != FileType::Directory {
Ordering::Less
} else {
if flags.sort_order == SortOrder::Default {
a.name.cmp(&b.name)
} else {
b.name.cmp(&a.name)
}
}
}
fn sort_by_date_with_dirs_unordored(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
if flags.sort_order == SortOrder::Default {
b.date.cmp(&a.date).then(a.name.cmp(&b.name))
} else {
a.date.cmp(&b.date).then(b.name.cmp(&a.name))
}
}
fn sort_by_date_with_dirs_first(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
if a.file_type == FileType::Directory && b.file_type != FileType::Directory {
Ordering::Less
} else if b.file_type == FileType::Directory && a.file_type != FileType::Directory {
Ordering::Greater
} else {
if flags.sort_order == SortOrder::Default {
b.date.cmp(&a.date).then(a.name.cmp(&b.name))
} else {
a.date.cmp(&b.date).then(b.name.cmp(&a.name))
}
}
}
fn sort_by_date_with_files_first(a: &Meta, b: &Meta, flags: Flags) -> Ordering {
if a.file_type == FileType::Directory && b.file_type != FileType::Directory {
Ordering::Greater
} else if b.file_type == FileType::Directory && a.file_type != FileType::Directory {
Ordering::Less
} else {
if flags.sort_order == SortOrder::Default {
b.date.cmp(&a.date).then(a.name.cmp(&b.name))
} else {
a.date.cmp(&b.date).then(b.name.cmp(&a.name))
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use flags::Flags;
use std::fs::{create_dir, File};
use std::process::Command;
use tempdir::TempDir;
#[test]
fn test_sort_by_meta() {
fn test_sort_by_meta_by_name_with_dirs_first() {
let tmp_dir = TempDir::new("test_dir").expect("failed to create temp dir");
// Create a file and make sure that its mod time is before now.
let path_a = tmp_dir.path().join("a.txt");
// Create the file;
let path_a = tmp_dir.path().join("zzz");
File::create(&path_a).expect("failed to create file");
let success = Command::new("touch")
.arg("-t")
.arg("198511160000")
.arg(&path_a)
.status()
.unwrap()
.success();
assert!(success, "failed to exec touch");
let meta_a = Meta::from_path(&path_a).expect("failed to get meta");
// Create a dir;
let path_b = tmp_dir.path().join("b");
create_dir(&path_b).expect("failed to create dir");
let meta_b = Meta::from_path(&path_b).expect("failed to get meta");
let path_z = tmp_dir.path().join("aaa");
create_dir(&path_z).expect("failed to create dir");
let meta_z = Meta::from_path(&path_z).expect("failed to get meta");
// Sort by name
assert_eq!(
sort_by_meta(&meta_a, &meta_b, Flags::default()),
Ordering::Greater
);
let mut flags = Flags::default();
flags.directory_order = DirOrderFlag::First;
// Sort by name reversed
assert_eq!(
sort_by_meta(
&meta_a,
&meta_b,
Flags {
sort_order: SortOrder::Reverse,
..Flags::default()
// Sort with the dirs first
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Greater);
// Sort with the dirs first (the dirs stay first)
flags.sort_order = SortOrder::Reverse;
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Greater);
}
),
Ordering::Less
);
#[test]
fn test_sort_by_meta_by_name_with_files_first() {
let tmp_dir = TempDir::new("test_dir").expect("failed to create temp dir");
// Create the file;
let path_a = tmp_dir.path().join("zzz");
File::create(&path_a).expect("failed to create file");
let meta_a = Meta::from_path(&path_a).expect("failed to get meta");
// Create a dir;
let path_z = tmp_dir.path().join("aaa");
create_dir(&path_z).expect("failed to create dir");
let meta_z = Meta::from_path(&path_z).expect("failed to get meta");
let mut flags = Flags::default();
flags.directory_order = DirOrderFlag::Last;
// Sort with file first
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Less);
// Sort with file first reversed (thie files stay first)
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Less);
}
#[test]
fn test_sort_by_meta_by_name_unordered() {
let tmp_dir = TempDir::new("test_dir").expect("failed to create temp dir");
// Create the file;
let path_a = tmp_dir.path().join("aaa");
File::create(&path_a).expect("failed to create file");
let meta_a = Meta::from_path(&path_a).expect("failed to get meta");
// Create a dir;
let path_z = tmp_dir.path().join("zzz");
create_dir(&path_z).expect("failed to create dir");
let meta_z = Meta::from_path(&path_z).expect("failed to get meta");
let mut flags = Flags::default();
flags.directory_order = DirOrderFlag::None;
// Sort by name unordered
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Less);
// Sort by name unordered
flags.sort_order = SortOrder::Reverse;
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Greater);
}
#[test]
fn test_sort_by_meta_by_name_unordered_2() {
let tmp_dir = TempDir::new("test_dir").expect("failed to create temp dir");
// Create the file;
let path_a = tmp_dir.path().join("zzz");
File::create(&path_a).expect("failed to create file");
let meta_a = Meta::from_path(&path_a).expect("failed to get meta");
// Create a dir;
let path_z = tmp_dir.path().join("aaa");
create_dir(&path_z).expect("failed to create dir");
let meta_z = Meta::from_path(&path_z).expect("failed to get meta");
let mut flags = Flags::default();
flags.directory_order = DirOrderFlag::None;
// Sort by name unordered
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Greater);
// Sort by name unordered reversed
flags.sort_order = SortOrder::Reverse;
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Less);
}
#[test]
fn test_sort_by_meta_by_time() {
let tmp_dir = TempDir::new("test_dir").expect("failed to create temp dir");
// Create the file;
let path_a = tmp_dir.path().join("aaa");
File::create(&path_a).expect("failed to create file");
let meta_a = Meta::from_path(&path_a).expect("failed to get meta");
// Create the file;
let path_z = tmp_dir.path().join("zzz");
File::create(&path_z).expect("failed to create file");
let success = Command::new("touch")
.arg("-t")
.arg("198511160000")
.arg(&path_z)
.status()
.unwrap()
.success();
assert_eq!(true, success, "failed to exec mkfifo");
let meta_z = Meta::from_path(&path_z).expect("failed to get meta");
let mut flags = Flags::default();
flags.sort_by = SortFlag::Time;
// Sort by time
assert_eq!(
sort_by_meta(
&meta_a,
&meta_b,
Flags {
sort_by: SortFlag::Time,
..Flags::default()
}
),
Ordering::Greater
);
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Less);
// Sort by time reversed
assert_eq!(
sort_by_meta(
&meta_a,
&meta_b,
Flags {
sort_by: SortFlag::Time,
sort_order: SortOrder::Reverse,
..Flags::default()
}
),
Ordering::Less
);
flags.sort_order = SortOrder::Reverse;
assert_eq!(sort_by_meta(&meta_a, &meta_z, flags), Ordering::Greater);
}
}

View file

@ -10,6 +10,7 @@ pub struct Flags {
pub recursive: bool,
pub sort_by: SortFlag,
pub sort_order: SortOrder,
pub directory_order: DirOrderFlag,
pub date: DateFlag,
pub color: WhenFlag,
pub icon: WhenFlag,
@ -21,6 +22,7 @@ impl Flags {
let color_inputs: Vec<&str> = matches.values_of("color").unwrap().collect();
let icon_inputs: Vec<&str> = matches.values_of("icon").unwrap().collect();
let date_inputs: Vec<&str> = matches.values_of("date").unwrap().collect();
let dir_order_inputs: Vec<&str> = matches.values_of("group-dirs").unwrap().collect();
let sort_by = if matches.is_present("timesort") {
SortFlag::Time
@ -68,6 +70,7 @@ impl Flags {
date: DateFlag::from(date_inputs[date_inputs.len() - 1]),
color: WhenFlag::from(color_inputs[color_inputs.len() - 1]),
icon: WhenFlag::from(icon_inputs[icon_inputs.len() - 1]),
directory_order: DirOrderFlag::from(dir_order_inputs[dir_order_inputs.len() - 1]),
})
}
}
@ -84,6 +87,7 @@ impl Default for Flags {
recursion_depth: usize::max_value(),
sort_by: SortFlag::Name,
sort_order: SortOrder::Default,
directory_order: DirOrderFlag::None,
date: DateFlag::Date,
color: WhenFlag::Auto,
icon: WhenFlag::Auto,
@ -137,6 +141,24 @@ pub enum SortOrder {
Reverse,
}
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
pub enum DirOrderFlag {
None,
First,
Last,
}
impl<'a> From<&'a str> for DirOrderFlag {
fn from(when: &'a str) -> Self {
match when {
"none" => DirOrderFlag::None,
"first" => DirOrderFlag::First,
"last" => DirOrderFlag::Last,
_ => panic!("invalid \"when\" flag: {}", when),
}
}
}
#[cfg(test)]
mod test {
use super::Flags;

View file

@ -34,7 +34,7 @@ impl Indicator {
#[cfg(test)]
mod test {
use super::Indicator;
use flags::{DateFlag, Flags, SortFlag, SortOrder, WhenFlag};
use flags::Flags;
use meta::FileType;
#[test]
@ -69,20 +69,8 @@ mod test {
#[test]
fn test_symlink_indicator() {
let flags = Flags {
display_all: true,
display_long: true,
display_online: true,
display_tree: true,
display_indicators: true,
recursive: true,
recursion_depth: usize::max_value(),
sort_by: SortFlag::Name,
sort_order: SortOrder::Default,
date: DateFlag::Relative,
color: WhenFlag::Always,
icon: WhenFlag::Always,
};
let mut flags = Flags::default();
flags.display_indicators = true;
let file_type = Indicator::from(FileType::SymLink);