mirror of
https://github.com/RGBCube/uutils-coreutils
synced 2025-07-28 11:37:44 +00:00
ls: add support for +FORMAT in timestyle
This commit is contained in:
parent
e523a56dab
commit
b8a5588b81
2 changed files with 70 additions and 30 deletions
|
@ -10,7 +10,10 @@
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
extern crate uucore;
|
extern crate uucore;
|
||||||
|
|
||||||
use clap::{builder::ValueParser, crate_version, Arg, Command};
|
use clap::{
|
||||||
|
builder::{NonEmptyStringValueParser, ValueParser},
|
||||||
|
crate_version, Arg, Command,
|
||||||
|
};
|
||||||
use glob::Pattern;
|
use glob::Pattern;
|
||||||
use lscolors::LsColors;
|
use lscolors::LsColors;
|
||||||
use number_prefix::NumberPrefix;
|
use number_prefix::NumberPrefix;
|
||||||
|
@ -156,6 +159,7 @@ enum LsError {
|
||||||
IOErrorContext(std::io::Error, PathBuf, bool),
|
IOErrorContext(std::io::Error, PathBuf, bool),
|
||||||
BlockSizeParseError(String),
|
BlockSizeParseError(String),
|
||||||
AlreadyListedError(PathBuf),
|
AlreadyListedError(PathBuf),
|
||||||
|
TimeStyleParseError(String, Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UError for LsError {
|
impl UError for LsError {
|
||||||
|
@ -167,6 +171,7 @@ impl UError for LsError {
|
||||||
Self::IOErrorContext(_, _, true) => 2,
|
Self::IOErrorContext(_, _, true) => 2,
|
||||||
Self::BlockSizeParseError(_) => 1,
|
Self::BlockSizeParseError(_) => 1,
|
||||||
Self::AlreadyListedError(_) => 2,
|
Self::AlreadyListedError(_) => 2,
|
||||||
|
Self::TimeStyleParseError(_, _) => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,6 +184,14 @@ impl Display for LsError {
|
||||||
Self::BlockSizeParseError(s) => {
|
Self::BlockSizeParseError(s) => {
|
||||||
write!(f, "invalid --block-size argument {}", s.quote())
|
write!(f, "invalid --block-size argument {}", s.quote())
|
||||||
}
|
}
|
||||||
|
Self::TimeStyleParseError(s, possible_time_styles) => {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"invalid --time-style argument {}\nPossible values are: {:?}\n\nFor more information try --help",
|
||||||
|
s.quote(),
|
||||||
|
possible_time_styles
|
||||||
|
)
|
||||||
|
}
|
||||||
Self::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()),
|
Self::InvalidLineWidth(s) => write!(f, "invalid line width: {}", s.quote()),
|
||||||
Self::IOError(e) => write!(f, "general io error: {}", e),
|
Self::IOError(e) => write!(f, "general io error: {}", e),
|
||||||
Self::IOErrorContext(e, p, _) => {
|
Self::IOErrorContext(e, p, _) => {
|
||||||
|
@ -300,8 +313,46 @@ enum TimeStyle {
|
||||||
LongIso,
|
LongIso,
|
||||||
Iso,
|
Iso,
|
||||||
Locale,
|
Locale,
|
||||||
|
Format(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_time_style(options: &clap::ArgMatches) -> Result<TimeStyle, LsError> {
|
||||||
|
let possible_time_styles = vec![
|
||||||
|
"full-iso".to_string(),
|
||||||
|
"long-iso".to_string(),
|
||||||
|
"iso".to_string(),
|
||||||
|
"locale".to_string(),
|
||||||
|
"+FORMAT (e.g., +%H:%M) for a 'date'-style format".to_string(),
|
||||||
|
];
|
||||||
|
if let Some(field) = options.get_one::<String>(options::TIME_STYLE) {
|
||||||
|
//If both FULL_TIME and TIME_STYLE are present
|
||||||
|
//The one added last is dominant
|
||||||
|
if options.contains_id(options::FULL_TIME)
|
||||||
|
&& options.indices_of(options::FULL_TIME).unwrap().last()
|
||||||
|
> options.indices_of(options::TIME_STYLE).unwrap().last()
|
||||||
|
{
|
||||||
|
Ok(TimeStyle::FullIso)
|
||||||
|
} else {
|
||||||
|
match field.as_str() {
|
||||||
|
"full-iso" => Ok(TimeStyle::FullIso),
|
||||||
|
"long-iso" => Ok(TimeStyle::LongIso),
|
||||||
|
"iso" => Ok(TimeStyle::Iso),
|
||||||
|
"locale" => Ok(TimeStyle::Locale),
|
||||||
|
_ => match field.chars().next().unwrap() {
|
||||||
|
'+' => Ok(TimeStyle::Format(String::from(&field[1..]))),
|
||||||
|
_ => Err(LsError::TimeStyleParseError(
|
||||||
|
String::from(field),
|
||||||
|
possible_time_styles,
|
||||||
|
)),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if options.contains_id(options::FULL_TIME) {
|
||||||
|
Ok(TimeStyle::FullIso)
|
||||||
|
} else {
|
||||||
|
Ok(TimeStyle::Locale)
|
||||||
|
}
|
||||||
|
}
|
||||||
enum Dereference {
|
enum Dereference {
|
||||||
None,
|
None,
|
||||||
DirArgs,
|
DirArgs,
|
||||||
|
@ -700,31 +751,7 @@ impl Config {
|
||||||
} else {
|
} else {
|
||||||
IndicatorStyle::None
|
IndicatorStyle::None
|
||||||
};
|
};
|
||||||
|
let time_style = parse_time_style(options)?;
|
||||||
let time_style = if let Some(field) = options.get_one::<String>(options::TIME_STYLE) {
|
|
||||||
//If both FULL_TIME and TIME_STYLE are present
|
|
||||||
//The one added last is dominant
|
|
||||||
if options.contains_id(options::FULL_TIME)
|
|
||||||
&& options.indices_of(options::FULL_TIME).unwrap().last()
|
|
||||||
> options.indices_of(options::TIME_STYLE).unwrap().last()
|
|
||||||
{
|
|
||||||
TimeStyle::FullIso
|
|
||||||
} else {
|
|
||||||
//Clap handles the env variable "TIME_STYLE"
|
|
||||||
match field.as_str() {
|
|
||||||
"full-iso" => TimeStyle::FullIso,
|
|
||||||
"long-iso" => TimeStyle::LongIso,
|
|
||||||
"iso" => TimeStyle::Iso,
|
|
||||||
"locale" => TimeStyle::Locale,
|
|
||||||
// below should never happen as clap already restricts the values.
|
|
||||||
_ => unreachable!("Invalid field for --time-style"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if options.contains_id(options::FULL_TIME) {
|
|
||||||
TimeStyle::FullIso
|
|
||||||
} else {
|
|
||||||
TimeStyle::Locale
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut ignore_patterns: Vec<Pattern> = Vec::new();
|
let mut ignore_patterns: Vec<Pattern> = Vec::new();
|
||||||
|
|
||||||
|
@ -1511,13 +1538,13 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
//This still needs support for posix-*, +FORMAT
|
//This still needs support for posix-*
|
||||||
Arg::new(options::TIME_STYLE)
|
Arg::new(options::TIME_STYLE)
|
||||||
.long(options::TIME_STYLE)
|
.long(options::TIME_STYLE)
|
||||||
.help("time/date format with -l; see TIME_STYLE below")
|
.help("time/date format with -l; see TIME_STYLE below")
|
||||||
.value_name("TIME_STYLE")
|
.value_name("TIME_STYLE")
|
||||||
.env("TIME_STYLE")
|
.env("TIME_STYLE")
|
||||||
.value_parser(["full-iso", "long-iso", "iso", "locale"])
|
.value_parser(NonEmptyStringValueParser::new())
|
||||||
.overrides_with_all(&[options::TIME_STYLE]),
|
.overrides_with_all(&[options::TIME_STYLE]),
|
||||||
)
|
)
|
||||||
.arg(
|
.arg(
|
||||||
|
@ -1549,7 +1576,7 @@ pub fn uu_app<'a>() -> Command<'a> {
|
||||||
.value_parser(ValueParser::os_string()),
|
.value_parser(ValueParser::os_string()),
|
||||||
)
|
)
|
||||||
.after_help(
|
.after_help(
|
||||||
"The TIME_STYLE argument can be full-iso, long-iso, iso. \
|
"The TIME_STYLE argument can be full-iso, long-iso, iso, locale or +FORMAT. FORMAT is interpreted like in date. \
|
||||||
Also the TIME_STYLE environment variable sets the default style to use.",
|
Also the TIME_STYLE environment variable sets the default style to use.",
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -2519,7 +2546,7 @@ fn display_date(metadata: &Metadata, config: &Config) -> String {
|
||||||
//According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
//According to GNU a Gregorian year has 365.2425 * 24 * 60 * 60 == 31556952 seconds on the average.
|
||||||
let recent = time + chrono::Duration::seconds(31_556_952 / 2) > chrono::Local::now();
|
let recent = time + chrono::Duration::seconds(31_556_952 / 2) > chrono::Local::now();
|
||||||
|
|
||||||
match config.time_style {
|
match &config.time_style {
|
||||||
TimeStyle::FullIso => time.format("%Y-%m-%d %H:%M:%S.%f %z"),
|
TimeStyle::FullIso => time.format("%Y-%m-%d %H:%M:%S.%f %z"),
|
||||||
TimeStyle::LongIso => time.format("%Y-%m-%d %H:%M"),
|
TimeStyle::LongIso => time.format("%Y-%m-%d %H:%M"),
|
||||||
TimeStyle::Iso => time.format(if recent { "%m-%d %H:%M" } else { "%Y-%m-%d " }),
|
TimeStyle::Iso => time.format(if recent { "%m-%d %H:%M" } else { "%Y-%m-%d " }),
|
||||||
|
@ -2534,6 +2561,7 @@ fn display_date(metadata: &Metadata, config: &Config) -> String {
|
||||||
|
|
||||||
time.format(fmt)
|
time.format(fmt)
|
||||||
}
|
}
|
||||||
|
TimeStyle::Format(e) => time.format(e),
|
||||||
}
|
}
|
||||||
.to_string()
|
.to_string()
|
||||||
}
|
}
|
||||||
|
|
|
@ -1653,6 +1653,7 @@ fn test_ls_styles() {
|
||||||
let re_iso = Regex::new(r"[a-z-]* \d* \w* \w* \d* \d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
|
let re_iso = Regex::new(r"[a-z-]* \d* \w* \w* \d* \d{2}-\d{2} \d{2}:\d{2} test\n").unwrap();
|
||||||
let re_locale =
|
let re_locale =
|
||||||
Regex::new(r"[a-z-]* \d* \w* \w* \d* [A-Z][a-z]{2} ( |\d)\d \d{2}:\d{2} test\n").unwrap();
|
Regex::new(r"[a-z-]* \d* \w* \w* \d* [A-Z][a-z]{2} ( |\d)\d \d{2}:\d{2} test\n").unwrap();
|
||||||
|
let re_custom_format = Regex::new(r"[a-z-]* \d* \w* \w* \d* \d{4}__\d{2} test\n").unwrap();
|
||||||
|
|
||||||
//full-iso
|
//full-iso
|
||||||
let result = scene
|
let result = scene
|
||||||
|
@ -1675,6 +1676,17 @@ fn test_ls_styles() {
|
||||||
let result = scene.ucmd().arg("-l").arg("--time-style=locale").succeeds();
|
let result = scene.ucmd().arg("-l").arg("--time-style=locale").succeeds();
|
||||||
assert!(re_locale.is_match(result.stdout_str()));
|
assert!(re_locale.is_match(result.stdout_str()));
|
||||||
|
|
||||||
|
//+FORMAT
|
||||||
|
let result = scene
|
||||||
|
.ucmd()
|
||||||
|
.arg("-l")
|
||||||
|
.arg("--time-style=+%Y__%M")
|
||||||
|
.succeeds();
|
||||||
|
assert!(re_custom_format.is_match(result.stdout_str()));
|
||||||
|
|
||||||
|
// Also fails due to not having full clap support for time_styles
|
||||||
|
scene.ucmd().arg("-l").arg("-time-style=invalid").fails();
|
||||||
|
|
||||||
//Overwrite options tests
|
//Overwrite options tests
|
||||||
let result = scene
|
let result = scene
|
||||||
.ucmd()
|
.ucmd()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue