mirror of
https://github.com/lsd-rs/lsd
synced 2025-03-04 23:17:15 +00:00
config: 🔨 using target enum and vec string for config options
Signed-off-by: zwPapEr <zw.paper@gmail.com>
This commit is contained in:
parent
3ef4ec6e81
commit
cf9030cdf7
12 changed files with 167 additions and 187 deletions
|
@ -1,11 +1,16 @@
|
|||
///! This module provides methods to handle the program's config files and operations related to
|
||||
///! this.
|
||||
use crate::flags::color::ColorOption;
|
||||
use crate::flags::display::Display;
|
||||
use crate::flags::icons::IconOption;
|
||||
use crate::flags::layout::Layout;
|
||||
use crate::flags::size::SizeFlag;
|
||||
use crate::flags::sorting::{DirGrouping, SortColumn};
|
||||
use crate::print_error;
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
use serde_yaml::Sequence;
|
||||
|
||||
use std::fs;
|
||||
|
||||
|
@ -15,48 +20,51 @@ const YAML_LONG_EXT: &str = "yaml";
|
|||
|
||||
/// A struct to hold an optional file path [String] and an optional [Yaml], and provides methods
|
||||
/// around error handling in a config file.
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Config {
|
||||
pub classic: Option<bool>,
|
||||
pub blocks: Option<Sequence>,
|
||||
pub blocks: Option<Vec<String>>,
|
||||
pub color: Option<Color>,
|
||||
pub date: Option<String>, // enum?
|
||||
pub date: Option<String>,
|
||||
pub dereference: Option<bool>,
|
||||
pub display: Option<String>, // enum?
|
||||
pub display: Option<Display>,
|
||||
pub icons: Option<Icons>,
|
||||
pub ignore_globs: Option<Sequence>,
|
||||
pub ignore_globs: Option<Vec<String>>,
|
||||
pub indicators: Option<bool>,
|
||||
pub layout: Option<String>, // enum?
|
||||
pub layout: Option<Layout>,
|
||||
pub recursion: Option<Recursion>,
|
||||
pub size: Option<String>, // enum?
|
||||
pub size: Option<SizeFlag>,
|
||||
pub sorting: Option<Sorting>,
|
||||
pub no_symlink: Option<bool>,
|
||||
pub total_size: Option<bool>,
|
||||
pub symlink_arrow: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
pub struct Color {
|
||||
pub when: String, // enum?
|
||||
pub when: ColorOption,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
pub struct Icons {
|
||||
pub when: Option<String>, // enum?
|
||||
pub when: Option<IconOption>,
|
||||
pub theme: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
pub struct Recursion {
|
||||
pub enabled: Option<bool>,
|
||||
pub depth: Option<usize>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Deserialize)]
|
||||
#[derive(Eq, PartialEq, Debug, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub struct Sorting {
|
||||
pub column: Option<String>, // enum?
|
||||
pub column: Option<SortColumn>,
|
||||
pub reverse: Option<bool>,
|
||||
pub dir_grouping: Option<String>, // enum?
|
||||
pub dir_grouping: Option<DirGrouping>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
|
@ -98,7 +106,7 @@ impl Config {
|
|||
|
||||
/// This constructs a Config struct with a passed [Yaml] str.
|
||||
fn with_yaml(yaml: &str) -> Option<Self> {
|
||||
match serde_yaml::from_str(yaml) {
|
||||
match serde_yaml::from_str::<Self>(yaml) {
|
||||
Ok(c) => Some(c),
|
||||
Err(e) => {
|
||||
print_error!("configuration file format error, {}\n\n", e);
|
||||
|
@ -145,8 +153,11 @@ impl Default for Config {
|
|||
return c;
|
||||
}
|
||||
}
|
||||
Self::with_yaml(
|
||||
r#"---
|
||||
Self::with_yaml(DEFAULT_CONFIG).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG: &str = r#"---
|
||||
# == Classic ==
|
||||
# This is a shorthand to override some of the options to be backwards compatible
|
||||
# with `ls`. It affects the "color"->"when", "sorting"->"dir-grouping", "date"
|
||||
|
@ -256,26 +267,76 @@ total-size: false
|
|||
|
||||
# == Symlink arrow ==
|
||||
# Specifies how the symlink arrow display, chars in both ascii and utf8
|
||||
symlink-arrow: ⇒"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
symlink-arrow: ⇒
|
||||
"#;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Config;
|
||||
use crate::config_file;
|
||||
use crate::flags::color::ColorOption;
|
||||
use crate::flags::icons::IconOption;
|
||||
use crate::flags::layout::Layout;
|
||||
use crate::flags::size::SizeFlag;
|
||||
use crate::flags::sorting::{DirGrouping, SortColumn};
|
||||
|
||||
#[test]
|
||||
fn test_read_default() {
|
||||
let c = Config::with_yaml(config_file::DEFAULT_CONFIG).unwrap();
|
||||
assert_eq!(
|
||||
Config {
|
||||
classic: Some(false),
|
||||
blocks: Some(
|
||||
vec![
|
||||
"permission".into(),
|
||||
"user".into(),
|
||||
"group".into(),
|
||||
"size".into(),
|
||||
"date".into(),
|
||||
"name".into(),
|
||||
]
|
||||
.into()
|
||||
),
|
||||
color: Some(config_file::Color {
|
||||
when: ColorOption::Auto,
|
||||
}),
|
||||
date: Some("date".into()),
|
||||
dereference: Some(false),
|
||||
display: None,
|
||||
icons: Some(config_file::Icons {
|
||||
when: Some(IconOption::Auto),
|
||||
theme: Some("fancy".into()),
|
||||
}),
|
||||
ignore_globs: None,
|
||||
indicators: Some(false),
|
||||
layout: Some(Layout::Grid),
|
||||
recursion: Some(config_file::Recursion {
|
||||
enabled: Some(false),
|
||||
depth: None,
|
||||
}),
|
||||
size: Some(SizeFlag::Default),
|
||||
sorting: Some(config_file::Sorting {
|
||||
column: Some(SortColumn::Name),
|
||||
reverse: Some(false),
|
||||
dir_grouping: Some(DirGrouping::None),
|
||||
}),
|
||||
no_symlink: Some(false),
|
||||
total_size: Some(false),
|
||||
symlink_arrow: Some("⇒".into()),
|
||||
},
|
||||
c
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_config_ok() {
|
||||
let c = Config::with_yaml("classic: true").unwrap();
|
||||
println!("{:?}", c);
|
||||
assert!(c.classic.unwrap())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_config_bad_bool() {
|
||||
let c = Config::with_yaml("classic: notbool");
|
||||
println!("{:?}", c);
|
||||
assert!(c.is_none())
|
||||
}
|
||||
|
||||
|
@ -284,4 +345,9 @@ mod tests {
|
|||
let c = Config::with_file("not-existed".to_string());
|
||||
assert!(c.is_none())
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_read_bad_display() {
|
||||
assert!(Config::with_yaml("display: bad").is_none())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -95,7 +95,7 @@ impl Core {
|
|||
};
|
||||
|
||||
let recurse =
|
||||
self.flags.layout == Layout::Tree || self.flags.display != Display::DirectoryItself;
|
||||
self.flags.layout == Layout::Tree || self.flags.display != Display::DirectoryOnly;
|
||||
if recurse {
|
||||
match meta.recurse_into(depth, &self.flags) {
|
||||
Ok(content) => {
|
||||
|
|
|
@ -61,7 +61,7 @@ fn inner_display_grid(
|
|||
// The first iteration (depth == 0) corresponds to the inputs given by the
|
||||
// user. We defer displaying directories given by the user unless we've been
|
||||
// asked to display the directory itself (rather than its contents).
|
||||
let skip_dirs = (depth == 0) && (flags.display != Display::DirectoryItself);
|
||||
let skip_dirs = (depth == 0) && (flags.display != Display::DirectoryOnly);
|
||||
|
||||
// print the files first.
|
||||
for meta in metas {
|
||||
|
|
|
@ -97,16 +97,16 @@ impl Blocks {
|
|||
|
||||
/// Get a potential `Blocks` struct from a [Config].
|
||||
///
|
||||
/// of its [String](Yaml::String) values is returned in a `Blocks` in a [Some]. Otherwise it
|
||||
/// of its [String]values is returned in a `Blocks` in a [Some]. Otherwise it
|
||||
/// returns [None].
|
||||
/// Config make sure blocks are Strings, we can unwrap here without panic
|
||||
fn from_config(config: &Config) -> Option<Self> {
|
||||
if let Some(c) = &config.blocks {
|
||||
let mut blocks: Vec<Block> = vec![];
|
||||
for b in c.iter() {
|
||||
match Block::try_from(b.as_str().unwrap()) {
|
||||
match Block::try_from(b.as_str()) {
|
||||
Ok(block) => blocks.push(block),
|
||||
Err(err) => print_error!("bad blocks: {}", err),
|
||||
Err(err) => print_error!("{}.", err),
|
||||
}
|
||||
}
|
||||
if blocks.is_empty() {
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::config_file::Config;
|
|||
use crate::print_error;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// A collection of flags on how to use colors.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Default)]
|
||||
|
@ -26,7 +27,8 @@ impl Color {
|
|||
}
|
||||
|
||||
/// The flag showing when to use colors in the output.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum ColorOption {
|
||||
Always,
|
||||
Auto,
|
||||
|
@ -84,7 +86,7 @@ impl Configurable<Self> for ColorOption {
|
|||
}
|
||||
|
||||
if let Some(color) = &config.color {
|
||||
Self::from_str(&color.when)
|
||||
Some(color.when)
|
||||
} else {
|
||||
// TODO: maybe return default value?
|
||||
None
|
||||
|
@ -163,7 +165,7 @@ mod test_color_option {
|
|||
fn test_from_config_always() {
|
||||
let mut c = Config::with_none();
|
||||
c.color = Some(config_file::Color {
|
||||
when: "always".into(),
|
||||
when: ColorOption::Always,
|
||||
});
|
||||
|
||||
assert_eq!(Some(ColorOption::Always), ColorOption::from_config(&c));
|
||||
|
@ -173,7 +175,7 @@ mod test_color_option {
|
|||
fn test_from_config_auto() {
|
||||
let mut c = Config::with_none();
|
||||
c.color = Some(config_file::Color {
|
||||
when: "auto".into(),
|
||||
when: ColorOption::Auto,
|
||||
});
|
||||
assert_eq!(Some(ColorOption::Auto), ColorOption::from_config(&c));
|
||||
}
|
||||
|
@ -182,7 +184,7 @@ mod test_color_option {
|
|||
fn test_from_config_never() {
|
||||
let mut c = Config::with_none();
|
||||
c.color = Some(config_file::Color {
|
||||
when: "never".into(),
|
||||
when: ColorOption::Never,
|
||||
});
|
||||
assert_eq!(Some(ColorOption::Never), ColorOption::from_config(&c));
|
||||
}
|
||||
|
@ -191,7 +193,7 @@ mod test_color_option {
|
|||
fn test_from_config_classic_mode() {
|
||||
let mut c = Config::with_none();
|
||||
c.color = Some(config_file::Color {
|
||||
when: "always".into(),
|
||||
when: ColorOption::Never,
|
||||
});
|
||||
c.classic = Some(true);
|
||||
assert_eq!(Some(ColorOption::Never), ColorOption::from_config(&c));
|
||||
|
|
|
@ -4,35 +4,18 @@
|
|||
use super::Configurable;
|
||||
|
||||
use crate::config_file::Config;
|
||||
use crate::print_error;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// The flag showing which file system nodes to display.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum Display {
|
||||
All,
|
||||
AlmostAll,
|
||||
DirectoryItself,
|
||||
DisplayOnlyVisible,
|
||||
}
|
||||
|
||||
impl Display {
|
||||
/// Get a value from a str
|
||||
fn from_str(value: &str) -> Option<Self> {
|
||||
match value {
|
||||
"all" => Some(Self::All),
|
||||
"almost-all" => Some(Self::AlmostAll),
|
||||
"directory-only" => Some(Self::DirectoryItself),
|
||||
_ => {
|
||||
print_error!(
|
||||
"display can only be one of all, almost-all or directory-only, but got {}",
|
||||
&value
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
DirectoryOnly,
|
||||
VisibleOnly,
|
||||
}
|
||||
|
||||
impl Configurable<Self> for Display {
|
||||
|
@ -47,7 +30,7 @@ impl Configurable<Self> for Display {
|
|||
} else if matches.is_present("almost-all") {
|
||||
Some(Self::AlmostAll)
|
||||
} else if matches.is_present("directory-only") {
|
||||
Some(Self::DirectoryItself)
|
||||
Some(Self::DirectoryOnly)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -59,18 +42,14 @@ impl Configurable<Self> for Display {
|
|||
/// this returns the corresponding `Display` variant in a [Some].
|
||||
/// Otherwise this returns [None].
|
||||
fn from_config(config: &Config) -> Option<Self> {
|
||||
if let Some(disp) = &config.display {
|
||||
Self::from_str(&disp)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
config.display
|
||||
}
|
||||
}
|
||||
|
||||
/// The default value for `Display` is [Display::DisplayOnlyVisible].
|
||||
impl Default for Display {
|
||||
fn default() -> Self {
|
||||
Self::DisplayOnlyVisible
|
||||
Display::VisibleOnly
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -111,7 +90,7 @@ mod test {
|
|||
let argv = vec!["lsd", "--directory-only"];
|
||||
let matches = app::build().get_matches_from_safe(argv).unwrap();
|
||||
assert_eq!(
|
||||
Some(Display::DirectoryItself),
|
||||
Some(Display::DirectoryOnly),
|
||||
Display::from_arg_matches(&matches)
|
||||
);
|
||||
}
|
||||
|
@ -124,21 +103,21 @@ mod test {
|
|||
#[test]
|
||||
fn test_from_config_all() {
|
||||
let mut c = Config::with_none();
|
||||
c.display = Some("all".into());
|
||||
c.display = Some(Display::All);
|
||||
assert_eq!(Some(Display::All), Display::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_almost_all() {
|
||||
let mut c = Config::with_none();
|
||||
c.display = Some("almost-all".into());
|
||||
c.display = Some(Display::AlmostAll);
|
||||
assert_eq!(Some(Display::AlmostAll), Display::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_directory_only() {
|
||||
let mut c = Config::with_none();
|
||||
c.display = Some("directory-only".into());
|
||||
assert_eq!(Some(Display::DirectoryItself), Display::from_config(&c));
|
||||
c.display = Some(Display::DirectoryOnly);
|
||||
assert_eq!(Some(Display::DirectoryOnly), Display::from_config(&c));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,6 +7,7 @@ use crate::config_file::Config;
|
|||
use crate::print_error;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// A collection of flags on how to use icons.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Default)]
|
||||
|
@ -30,31 +31,14 @@ impl Icons {
|
|||
}
|
||||
|
||||
/// The flag showing when to use icons in the output.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum IconOption {
|
||||
Always,
|
||||
Auto,
|
||||
Never,
|
||||
}
|
||||
|
||||
impl IconOption {
|
||||
/// Get a value from a str.
|
||||
fn from_str(value: &str) -> Option<Self> {
|
||||
match value {
|
||||
"always" => Some(Self::Always),
|
||||
"auto" => Some(Self::Auto),
|
||||
"never" => Some(Self::Never),
|
||||
_ => {
|
||||
print_error!(
|
||||
"icons/when can only be one of auto, always or never, but got {}",
|
||||
&value
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Configurable<Self> for IconOption {
|
||||
/// Get a potential `IconOption` variant from [ArgMatches].
|
||||
///
|
||||
|
@ -88,11 +72,7 @@ impl Configurable<Self> for IconOption {
|
|||
}
|
||||
|
||||
if let Some(icon) = &config.icons {
|
||||
if let Some(when) = &icon.when {
|
||||
Self::from_str(&when)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
icon.when
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -234,7 +214,7 @@ mod test_icon_option {
|
|||
fn test_from_config_always() {
|
||||
let mut c = Config::with_none();
|
||||
c.icons = Some(Icons {
|
||||
when: Some("always".into()),
|
||||
when: Some(IconOption::Always),
|
||||
theme: None,
|
||||
});
|
||||
assert_eq!(Some(IconOption::Always), IconOption::from_config(&c));
|
||||
|
@ -244,7 +224,7 @@ mod test_icon_option {
|
|||
fn test_from_config_auto() {
|
||||
let mut c = Config::with_none();
|
||||
c.icons = Some(Icons {
|
||||
when: Some("auto".into()),
|
||||
when: Some(IconOption::Auto),
|
||||
theme: None,
|
||||
});
|
||||
assert_eq!(Some(IconOption::Auto), IconOption::from_config(&c));
|
||||
|
@ -254,7 +234,7 @@ mod test_icon_option {
|
|||
fn test_from_config_never() {
|
||||
let mut c = Config::with_none();
|
||||
c.icons = Some(Icons {
|
||||
when: Some("never".into()),
|
||||
when: Some(IconOption::Never),
|
||||
theme: None,
|
||||
});
|
||||
assert_eq!(Some(IconOption::Never), IconOption::from_config(&c));
|
||||
|
@ -265,7 +245,7 @@ mod test_icon_option {
|
|||
let mut c = Config::with_none();
|
||||
c.classic = Some(true);
|
||||
c.icons = Some(Icons {
|
||||
when: Some("always".into()),
|
||||
when: Some(IconOption::Always),
|
||||
theme: None,
|
||||
});
|
||||
assert_eq!(Some(IconOption::Never), IconOption::from_config(&c));
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
//! [Default] value, use the [configure_from](IgnoreGlobs::configure_from) method.
|
||||
|
||||
use crate::config_file::Config;
|
||||
use crate::print_error;
|
||||
|
||||
use clap::{ArgMatches, Error, ErrorKind};
|
||||
use globset::{Glob, GlobSet, GlobSetBuilder};
|
||||
|
@ -79,23 +78,17 @@ impl IgnoreGlobs {
|
|||
/// If the `Config::ignore-globs` contains an Array of Strings,
|
||||
/// each of its values is used to build the [GlobSet]. If the building
|
||||
/// succeeds, the [GlobSet] is returned in the [Result] in a [Some]. If any error is
|
||||
/// encountered while building, an [Error] is returned in the Result instead. If the Yaml does
|
||||
/// encountered while building, an [Error] is returned in the Result instead. If the Config does
|
||||
/// not contain such a key, this returns [None].
|
||||
fn from_config(config: &Config) -> Option<Result<GlobSet, Error>> {
|
||||
if let Some(globs) = &config.ignore_globs {
|
||||
let mut glob_set_builder = GlobSetBuilder::new();
|
||||
for glob in globs.iter() {
|
||||
match glob.as_str() {
|
||||
Some(glob) => match Self::create_glob(glob) {
|
||||
Ok(glob) => {
|
||||
glob_set_builder.add(glob);
|
||||
}
|
||||
Err(err) => return Some(Err(err)),
|
||||
},
|
||||
None => {
|
||||
print_error!("ignore-globs must be a string");
|
||||
continue;
|
||||
match Self::create_glob(glob) {
|
||||
Ok(glob) => {
|
||||
glob_set_builder.add(glob);
|
||||
}
|
||||
Err(err) => return Some(Err(err)),
|
||||
}
|
||||
}
|
||||
Some(Self::create_glob_set(&glob_set_builder))
|
||||
|
|
|
@ -2,14 +2,15 @@
|
|||
//! [Default] value, use its [configure_from](Configurable::configure_from) method.
|
||||
|
||||
use crate::config_file::Config;
|
||||
use crate::print_error;
|
||||
|
||||
use super::Configurable;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// The flag showing which output layout to print.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
pub enum Layout {
|
||||
Grid,
|
||||
Tree,
|
||||
|
@ -44,22 +45,7 @@ impl Configurable<Layout> for Layout {
|
|||
/// this returns the corresponding `Layout` variant in a [Some].
|
||||
/// Otherwise this returns [None].
|
||||
fn from_config(config: &Config) -> Option<Self> {
|
||||
if let Some(layout) = &config.layout {
|
||||
match layout.as_ref() {
|
||||
"tree" => Some(Self::Tree),
|
||||
"oneline" => Some(Self::OneLine),
|
||||
"grid" => Some(Self::Grid),
|
||||
_ => {
|
||||
print_error!(
|
||||
"layout can only be one of tree, oneline or grid, but got {}",
|
||||
&layout
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
config.layout
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -121,21 +107,21 @@ mod test {
|
|||
#[test]
|
||||
fn test_from_config_tree() {
|
||||
let mut c = Config::with_none();
|
||||
c.layout = Some("tree".into());
|
||||
c.layout = Some(Layout::Tree);
|
||||
assert_eq!(Some(Layout::Tree), Layout::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_oneline() {
|
||||
let mut c = Config::with_none();
|
||||
c.layout = Some("oneline".into());
|
||||
c.layout = Some(Layout::OneLine);
|
||||
assert_eq!(Some(Layout::OneLine), Layout::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_grid() {
|
||||
let mut c = Config::with_none();
|
||||
c.layout = Some("grid".into());
|
||||
c.layout = Some(Layout::Grid);
|
||||
assert_eq!(Some(Layout::Grid), Layout::from_config(&c));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,9 +7,11 @@ use crate::config_file::Config;
|
|||
use crate::print_error;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// The flag showing which file size units to use.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SizeFlag {
|
||||
/// The variant to show file size with SI unit prefix and a B for bytes.
|
||||
Default,
|
||||
|
@ -57,10 +59,7 @@ impl Configurable<Self> for SizeFlag {
|
|||
/// this returns the corresponding `SizeFlag` variant in a [Some].
|
||||
/// Otherwise this returns [None].
|
||||
fn from_config(config: &Config) -> Option<Self> {
|
||||
if let Some(size) = &config.size {
|
||||
return Self::from_str(size);
|
||||
}
|
||||
None
|
||||
config.size
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -118,21 +117,21 @@ mod test {
|
|||
#[test]
|
||||
fn test_from_config_default() {
|
||||
let mut c = Config::with_none();
|
||||
c.size = Some("default".into());
|
||||
c.size = Some(SizeFlag::Default);
|
||||
assert_eq!(Some(SizeFlag::Default), SizeFlag::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_short() {
|
||||
let mut c = Config::with_none();
|
||||
c.size = Some("short".into());
|
||||
c.size = Some(SizeFlag::Short);
|
||||
assert_eq!(Some(SizeFlag::Short), SizeFlag::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_bytes() {
|
||||
let mut c = Config::with_none();
|
||||
c.size = Some("bytes".into());
|
||||
c.size = Some(SizeFlag::Bytes);
|
||||
assert_eq!(Some(SizeFlag::Bytes), SizeFlag::from_config(&c));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,9 +4,9 @@
|
|||
use super::Configurable;
|
||||
|
||||
use crate::config_file::Config;
|
||||
use crate::print_error;
|
||||
|
||||
use clap::ArgMatches;
|
||||
use serde::Deserialize;
|
||||
|
||||
/// A collection of flags on how to sort the output.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Default)]
|
||||
|
@ -34,7 +34,8 @@ impl Sorting {
|
|||
}
|
||||
|
||||
/// The flag showing which column to use for sorting.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum SortColumn {
|
||||
Extension,
|
||||
Name,
|
||||
|
@ -70,25 +71,7 @@ impl Configurable<Self> for SortColumn {
|
|||
/// Otherwise this returns [None].
|
||||
fn from_config(config: &Config) -> Option<Self> {
|
||||
if let Some(sort) = &config.sorting {
|
||||
if let Some(column) = &sort.column {
|
||||
match column.as_ref() {
|
||||
"extension" => Some(Self::Extension),
|
||||
"name" => Some(Self::Name),
|
||||
"size" => Some(Self::Size),
|
||||
"time" => Some(Self::Time),
|
||||
"version" => Some(Self::Version),
|
||||
_ => {
|
||||
print_error!(
|
||||
"sorting/column can only be one of \
|
||||
extension, name, size, time or version, but got {}",
|
||||
&column
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
sort.column
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -153,7 +136,8 @@ impl Default for SortOrder {
|
|||
}
|
||||
|
||||
/// The flag showing where to place directories.
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq)]
|
||||
#[derive(Clone, Debug, Copy, PartialEq, Eq, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum DirGrouping {
|
||||
None,
|
||||
First,
|
||||
|
@ -201,9 +185,7 @@ impl Configurable<Self> for DirGrouping {
|
|||
return Some(Self::None);
|
||||
}
|
||||
if let Some(sort) = &config.sorting {
|
||||
if let Some(group) = &sort.dir_grouping {
|
||||
return Self::from_str(group);
|
||||
}
|
||||
return sort.dir_grouping;
|
||||
}
|
||||
None
|
||||
}
|
||||
|
@ -329,23 +311,11 @@ mod test_sort_column {
|
|||
assert_eq!(None, SortColumn::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_invalid() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: Some("foo".into()),
|
||||
reverse: None,
|
||||
dir_grouping: None,
|
||||
});
|
||||
|
||||
assert_eq!(None, SortColumn::from_config(&c));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_from_config_extension() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: Some("extension".into()),
|
||||
column: Some(SortColumn::Extension),
|
||||
reverse: None,
|
||||
dir_grouping: None,
|
||||
});
|
||||
|
@ -356,7 +326,7 @@ mod test_sort_column {
|
|||
fn test_from_config_name() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: Some("name".into()),
|
||||
column: Some(SortColumn::Name),
|
||||
reverse: None,
|
||||
dir_grouping: None,
|
||||
});
|
||||
|
@ -367,7 +337,7 @@ mod test_sort_column {
|
|||
fn test_from_config_time() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: Some("time".into()),
|
||||
column: Some(SortColumn::Time),
|
||||
reverse: None,
|
||||
dir_grouping: None,
|
||||
});
|
||||
|
@ -378,7 +348,7 @@ mod test_sort_column {
|
|||
fn test_from_config_size() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: Some("size".into()),
|
||||
column: Some(SortColumn::Size),
|
||||
reverse: None,
|
||||
dir_grouping: None,
|
||||
});
|
||||
|
@ -389,7 +359,7 @@ mod test_sort_column {
|
|||
fn test_from_config_version() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: Some("version".into()),
|
||||
column: Some(SortColumn::Version),
|
||||
reverse: None,
|
||||
dir_grouping: None,
|
||||
});
|
||||
|
@ -529,7 +499,7 @@ mod test_dir_grouping {
|
|||
c.sorting = Some(Sorting {
|
||||
column: None,
|
||||
reverse: None,
|
||||
dir_grouping: Some("first".into()),
|
||||
dir_grouping: Some(DirGrouping::First),
|
||||
});
|
||||
assert_eq!(Some(DirGrouping::First), DirGrouping::from_config(&c));
|
||||
}
|
||||
|
@ -540,7 +510,7 @@ mod test_dir_grouping {
|
|||
c.sorting = Some(Sorting {
|
||||
column: None,
|
||||
reverse: None,
|
||||
dir_grouping: Some("last".into()),
|
||||
dir_grouping: Some(DirGrouping::Last),
|
||||
});
|
||||
assert_eq!(Some(DirGrouping::Last), DirGrouping::from_config(&c));
|
||||
}
|
||||
|
@ -559,6 +529,11 @@ mod test_dir_grouping {
|
|||
#[test]
|
||||
fn test_from_config_classic_mode() {
|
||||
let mut c = Config::with_none();
|
||||
c.sorting = Some(Sorting {
|
||||
column: None,
|
||||
reverse: None,
|
||||
dir_grouping: Some(DirGrouping::Last),
|
||||
});
|
||||
c.classic = Some(true);
|
||||
assert_eq!(Some(DirGrouping::None), DirGrouping::from_config(&c));
|
||||
}
|
||||
|
|
|
@ -54,7 +54,7 @@ impl Meta {
|
|||
return Ok(None);
|
||||
}
|
||||
|
||||
if flags.display == Display::DirectoryItself && flags.layout != Layout::Tree {
|
||||
if flags.display == Display::DirectoryOnly && flags.layout != Layout::Tree {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
|
@ -103,7 +103,7 @@ impl Meta {
|
|||
continue;
|
||||
}
|
||||
|
||||
if let Display::DisplayOnlyVisible = flags.display {
|
||||
if let Display::VisibleOnly = flags.display {
|
||||
if name.to_string_lossy().starts_with('.') {
|
||||
continue;
|
||||
}
|
||||
|
@ -119,7 +119,7 @@ impl Meta {
|
|||
|
||||
// skip files for --tree -d
|
||||
if flags.layout == Layout::Tree {
|
||||
if let Display::DirectoryItself = flags.display {
|
||||
if let Display::DirectoryOnly = flags.display {
|
||||
if !entry.file_type()?.is_dir() {
|
||||
continue;
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue