Merge pull request #813 from b05902132/numeric_gid_uid

Add options for -n or --numeric-gid-uid.
This commit is contained in:
Benjamin Sago 2021-04-01 09:18:59 +01:00 committed by GitHub
commit c1435411c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 87 additions and 37 deletions

View File

@ -66,6 +66,7 @@ complete -c exa -s 't' -l 'time' -x -d "Which timestamp field to list" -a "
created\t'Display created time' created\t'Display created time'
" "
complete -c exa -s 'm' -l 'modified' -d "Use the modified timestamp field" complete -c exa -s 'm' -l 'modified' -d "Use the modified timestamp field"
complete -c exa -s 'n' -l 'numeric' -d "List numeric user and group IDs."
complete -c exa -l 'changed' -d "Use the changed timestamp field" complete -c exa -l 'changed' -d "Use the changed timestamp field"
complete -c exa -s 'u' -l 'accessed' -d "Use the accessed timestamp field" complete -c exa -s 'u' -l 'accessed' -d "Use the accessed timestamp field"
complete -c exa -s 'U' -l 'created' -d "Use the created timestamp field" complete -c exa -s 'U' -l 'created' -d "Use the created timestamp field"

View File

@ -40,6 +40,7 @@ __exa() {
{-H,--links}"[List each file's number of hard links]" \ {-H,--links}"[List each file's number of hard links]" \
{-i,--inode}"[List each file's inode number]" \ {-i,--inode}"[List each file's inode number]" \
{-m,--modified}"[Use the modified timestamp field]" \ {-m,--modified}"[Use the modified timestamp field]" \
{-n, --numeric}"[List numeric user and group IDs.]" \
{-S,--blocks}"[List each file's number of filesystem blocks]" \ {-S,--blocks}"[List each file's number of filesystem blocks]" \
{-t,--time}="[Which time field to show]:(time field):(accessed changed created modified)" \ {-t,--time}="[Which time field to show]:(time field):(accessed changed created modified)" \
--time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso)" \ --time-style="[How to format timestamps]:(time style):(default iso long-iso full-iso)" \

View File

@ -143,6 +143,9 @@ These options are available when running with `--long` (`-l`):
`-m`, `--modified` `-m`, `--modified`
: Use the modified timestamp field. : Use the modified timestamp field.
`-n`, `--numeric`
: List numeric user and group IDs.
`-S`, `--blocks` `-S`, `--blocks`
: List each files number of file system blocks. : List each files number of file system blocks.

View File

@ -39,6 +39,7 @@ const SORTS: Values = &[ "name", "Name", "size", "extension",
pub static BINARY: Arg = Arg { short: Some(b'b'), long: "binary", takes_value: TakesValue::Forbidden }; pub static BINARY: Arg = Arg { short: Some(b'b'), long: "binary", takes_value: TakesValue::Forbidden };
pub static BYTES: Arg = Arg { short: Some(b'B'), long: "bytes", takes_value: TakesValue::Forbidden }; pub static BYTES: Arg = Arg { short: Some(b'B'), long: "bytes", takes_value: TakesValue::Forbidden };
pub static GROUP: Arg = Arg { short: Some(b'g'), long: "group", takes_value: TakesValue::Forbidden }; pub static GROUP: Arg = Arg { short: Some(b'g'), long: "group", takes_value: TakesValue::Forbidden };
pub static NUMERIC: Arg = Arg { short: Some(b'n'), long: "numeric", takes_value: TakesValue::Forbidden };
pub static HEADER: Arg = Arg { short: Some(b'h'), long: "header", takes_value: TakesValue::Forbidden }; pub static HEADER: Arg = Arg { short: Some(b'h'), long: "header", takes_value: TakesValue::Forbidden };
pub static ICONS: Arg = Arg { short: None, long: "icons", takes_value: TakesValue::Forbidden }; pub static ICONS: Arg = Arg { short: None, long: "icons", takes_value: TakesValue::Forbidden };
pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden }; pub static INODE: Arg = Arg { short: Some(b'i'), long: "inode", takes_value: TakesValue::Forbidden };
@ -75,7 +76,7 @@ pub static ALL_ARGS: Args = Args(&[
&ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST, &ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST,
&IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS, &IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,
&BINARY, &BYTES, &GROUP, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED, &BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED,
&BLOCKS, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &BLOCKS, &TIME, &ACCESSED, &CREATED, &TIME_STYLE,
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS, &NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS,

View File

@ -48,6 +48,7 @@ LONG VIEW OPTIONS
-H, --links list each file's number of hard links -H, --links list each file's number of hard links
-i, --inode list each file's inode number -i, --inode list each file's inode number
-m, --modified use the modified timestamp field -m, --modified use the modified timestamp field
-n, --numeric list numeric user and group IDs
-S, --blocks show number of file system blocks -S, --blocks show number of file system blocks
-t, --time FIELD which timestamp field to list (modified, accessed, created) -t, --time FIELD which timestamp field to list (modified, accessed, created)
-u, --accessed use the accessed timestamp field -u, --accessed use the accessed timestamp field

View File

@ -4,7 +4,7 @@ use crate::options::parser::MatchedFlags;
use crate::output::{View, Mode, TerminalWidth, grid, details}; use crate::output::{View, Mode, TerminalWidth, grid, details};
use crate::output::grid_details::{self, RowThreshold}; use crate::output::grid_details::{self, RowThreshold};
use crate::output::file_name::Options as FileStyle; use crate::output::file_name::Options as FileStyle;
use crate::output::table::{TimeTypes, SizeFormat, Columns, Options as TableOptions}; use crate::output::table::{TimeTypes, SizeFormat, UserFormat, Columns, Options as TableOptions};
use crate::output::time::TimeFormat; use crate::output::time::TimeFormat;
@ -85,7 +85,7 @@ impl Mode {
// user about flags that wont have any effect. // user about flags that wont have any effect.
if matches.is_strict() { if matches.is_strict() {
for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS, for option in &[ &flags::BINARY, &flags::BYTES, &flags::INODE, &flags::LINKS,
&flags::HEADER, &flags::BLOCKS, &flags::TIME, &flags::GROUP ] { &flags::HEADER, &flags::BLOCKS, &flags::TIME, &flags::GROUP, &flags::NUMERIC ] {
if matches.has(option)? { if matches.has(option)? {
return Err(OptionsError::Useless(*option, false, &flags::LONG)); return Err(OptionsError::Useless(*option, false, &flags::LONG));
} }
@ -183,8 +183,9 @@ impl TableOptions {
fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> { fn deduce<V: Vars>(matches: &MatchedFlags<'_>, vars: &V) -> Result<Self, OptionsError> {
let time_format = TimeFormat::deduce(matches, vars)?; let time_format = TimeFormat::deduce(matches, vars)?;
let size_format = SizeFormat::deduce(matches)?; let size_format = SizeFormat::deduce(matches)?;
let user_format = UserFormat::deduce(matches)?;
let columns = Columns::deduce(matches)?; let columns = Columns::deduce(matches)?;
Ok(Self { time_format, size_format, columns }) Ok(Self { time_format, size_format, columns , user_format})
} }
} }
@ -266,6 +267,14 @@ impl TimeFormat {
} }
impl UserFormat {
fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
let flag = matches.has(&flags::NUMERIC)?;
Ok(if flag { Self::Numeric } else { Self::Name })
}
}
impl TimeTypes { impl TimeTypes {
/// Determine which of a files time fields should be displayed for it /// Determine which of a files time fields should be displayed for it
@ -345,7 +354,8 @@ mod test {
&flags::CREATED, &flags::ACCESSED, &flags::CREATED, &flags::ACCESSED,
&flags::HEADER, &flags::GROUP, &flags::INODE, &flags::GIT, &flags::HEADER, &flags::GROUP, &flags::INODE, &flags::GIT,
&flags::LINKS, &flags::BLOCKS, &flags::LONG, &flags::LEVEL, &flags::LINKS, &flags::BLOCKS, &flags::LONG, &flags::LEVEL,
&flags::GRID, &flags::ACROSS, &flags::ONE_LINE, &flags::TREE ]; &flags::GRID, &flags::ACROSS, &flags::ONE_LINE, &flags::TREE,
&flags::NUMERIC ];
macro_rules! test { macro_rules! test {
@ -554,6 +564,7 @@ mod test {
test!(just_blocks: Mode <- ["--blocks"], None; Last => like Ok(Mode::Grid(_))); test!(just_blocks: Mode <- ["--blocks"], None; Last => like Ok(Mode::Grid(_)));
test!(just_binary: Mode <- ["--binary"], None; Last => like Ok(Mode::Grid(_))); test!(just_binary: Mode <- ["--binary"], None; Last => like Ok(Mode::Grid(_)));
test!(just_bytes: Mode <- ["--bytes"], None; Last => like Ok(Mode::Grid(_))); test!(just_bytes: Mode <- ["--bytes"], None; Last => like Ok(Mode::Grid(_)));
test!(just_numeric: Mode <- ["--numeric"], None; Last => like Ok(Mode::Grid(_)));
#[cfg(feature = "git")] #[cfg(feature = "git")]
test!(just_git: Mode <- ["--git"], None; Last => like Ok(Mode::Grid(_))); test!(just_git: Mode <- ["--git"], None; Last => like Ok(Mode::Grid(_)));
@ -565,6 +576,7 @@ mod test {
test!(just_blocks_2: Mode <- ["--blocks"], None; Complain => err OptionsError::Useless(&flags::BLOCKS, false, &flags::LONG)); test!(just_blocks_2: Mode <- ["--blocks"], None; Complain => err OptionsError::Useless(&flags::BLOCKS, false, &flags::LONG));
test!(just_binary_2: Mode <- ["--binary"], None; Complain => err OptionsError::Useless(&flags::BINARY, false, &flags::LONG)); test!(just_binary_2: Mode <- ["--binary"], None; Complain => err OptionsError::Useless(&flags::BINARY, false, &flags::LONG));
test!(just_bytes_2: Mode <- ["--bytes"], None; Complain => err OptionsError::Useless(&flags::BYTES, false, &flags::LONG)); test!(just_bytes_2: Mode <- ["--bytes"], None; Complain => err OptionsError::Useless(&flags::BYTES, false, &flags::LONG));
test!(just_numeric2: Mode <- ["--numeric"], None; Complain => err OptionsError::Useless(&flags::NUMERIC, false, &flags::LONG));
#[cfg(feature = "git")] #[cfg(feature = "git")]
test!(just_git_2: Mode <- ["--git"], None; Complain => err OptionsError::Useless(&flags::GIT, false, &flags::LONG)); test!(just_git_2: Mode <- ["--git"], None; Complain => err OptionsError::Useless(&flags::GIT, false, &flags::LONG));

View File

@ -3,10 +3,11 @@ use users::{Users, Groups};
use crate::fs::fields as f; use crate::fs::fields as f;
use crate::output::cell::TextCell; use crate::output::cell::TextCell;
use crate::output::table::UserFormat;
impl f::Group { impl f::Group {
pub fn render<C: Colours, U: Users+Groups>(self, colours: &C, users: &U) -> TextCell { pub fn render<C: Colours, U: Users+Groups>(self, colours: &C, users: &U, format: UserFormat) -> TextCell {
use users::os::unix::GroupExt; use users::os::unix::GroupExt;
let mut style = colours.not_yours(); let mut style = colours.not_yours();
@ -26,7 +27,12 @@ impl f::Group {
} }
} }
TextCell::paint(style, group.name().to_string_lossy().into()) let group_name = match format {
UserFormat::Name => group.name().to_string_lossy().into(),
UserFormat::Numeric => group.gid().to_string(),
};
TextCell::paint(style, group_name)
} }
} }
@ -43,6 +49,7 @@ pub mod test {
use super::Colours; use super::Colours;
use crate::fs::fields as f; use crate::fs::fields as f;
use crate::output::cell::TextCell; use crate::output::cell::TextCell;
use crate::output::table::UserFormat;
use users::{User, Group}; use users::{User, Group};
use users::mock::MockUsers; use users::mock::MockUsers;
@ -66,16 +73,21 @@ pub mod test {
let group = f::Group(100); let group = f::Group(100);
let expected = TextCell::paint_str(Fixed(81).normal(), "folk"); let expected = TextCell::paint_str(Fixed(81).normal(), "folk");
assert_eq!(expected, group.render(&TestColours, &users)) assert_eq!(expected, group.render(&TestColours, &users, UserFormat::Name));
let expected = TextCell::paint_str(Fixed(81).normal(), "100");
assert_eq!(expected, group.render(&TestColours, &users, UserFormat::Numeric));
} }
#[test] #[test]
fn unnamed() { fn unnamed() {
let users = MockUsers::with_current_uid(1000); let users = MockUsers::with_current_uid(1000);
let group = f::Group(100); let group = f::Group(100);
let expected = TextCell::paint_str(Fixed(81).normal(), "100"); let expected = TextCell::paint_str(Fixed(81).normal(), "100");
assert_eq!(expected, group.render(&TestColours, &users)); assert_eq!(expected, group.render(&TestColours, &users, UserFormat::Name));
assert_eq!(expected, group.render(&TestColours, &users, UserFormat::Numeric));
} }
#[test] #[test]
@ -86,7 +98,7 @@ pub mod test {
let group = f::Group(100); let group = f::Group(100);
let expected = TextCell::paint_str(Fixed(80).normal(), "folk"); let expected = TextCell::paint_str(Fixed(80).normal(), "folk");
assert_eq!(expected, group.render(&TestColours, &users)) assert_eq!(expected, group.render(&TestColours, &users, UserFormat::Name))
} }
#[test] #[test]
@ -99,13 +111,13 @@ pub mod test {
let group = f::Group(100); let group = f::Group(100);
let expected = TextCell::paint_str(Fixed(80).normal(), "folk"); let expected = TextCell::paint_str(Fixed(80).normal(), "folk");
assert_eq!(expected, group.render(&TestColours, &users)) assert_eq!(expected, group.render(&TestColours, &users, UserFormat::Name))
} }
#[test] #[test]
fn overflow() { fn overflow() {
let group = f::Group(2_147_483_648); let group = f::Group(2_147_483_648);
let expected = TextCell::paint_str(Fixed(81).normal(), "2147483648"); let expected = TextCell::paint_str(Fixed(81).normal(), "2147483648");
assert_eq!(expected, group.render(&TestColours, &MockUsers::with_current_uid(0))); assert_eq!(expected, group.render(&TestColours, &MockUsers::with_current_uid(0), UserFormat::Numeric));
} }
} }

View File

@ -3,13 +3,15 @@ use users::Users;
use crate::fs::fields as f; use crate::fs::fields as f;
use crate::output::cell::TextCell; use crate::output::cell::TextCell;
use crate::output::table::UserFormat;
impl f::User { impl f::User {
pub fn render<C: Colours, U: Users>(self, colours: &C, users: &U) -> TextCell { pub fn render<C: Colours, U: Users>(self, colours: &C, users: &U, format: UserFormat) -> TextCell {
let user_name = match users.get_user_by_uid(self.0) { let user_name = match (format, users.get_user_by_uid(self.0)) {
Some(user) => user.name().to_string_lossy().into(), (_, None) => self.0.to_string(),
None => self.0.to_string(), (UserFormat::Numeric, _) => self.0.to_string(),
(UserFormat::Name, Some(user)) => user.name().to_string_lossy().into(),
}; };
let style = if users.get_current_uid() == self.0 { colours.you() } let style = if users.get_current_uid() == self.0 { colours.you() }
@ -31,6 +33,7 @@ pub mod test {
use super::Colours; use super::Colours;
use crate::fs::fields as f; use crate::fs::fields as f;
use crate::output::cell::TextCell; use crate::output::cell::TextCell;
use crate::output::table::UserFormat;
use users::User; use users::User;
use users::mock::MockUsers; use users::mock::MockUsers;
@ -53,7 +56,10 @@ pub mod test {
let user = f::User(1000); let user = f::User(1000);
let expected = TextCell::paint_str(Red.bold(), "enoch"); let expected = TextCell::paint_str(Red.bold(), "enoch");
assert_eq!(expected, user.render(&TestColours, &users)) assert_eq!(expected, user.render(&TestColours, &users, UserFormat::Name));
let expected = TextCell::paint_str(Red.bold(), "1000");
assert_eq!(expected, user.render(&TestColours, &users, UserFormat::Numeric));
} }
#[test] #[test]
@ -62,7 +68,8 @@ pub mod test {
let user = f::User(1000); let user = f::User(1000);
let expected = TextCell::paint_str(Red.bold(), "1000"); let expected = TextCell::paint_str(Red.bold(), "1000");
assert_eq!(expected, user.render(&TestColours, &users)); assert_eq!(expected, user.render(&TestColours, &users, UserFormat::Name));
assert_eq!(expected, user.render(&TestColours, &users, UserFormat::Numeric));
} }
#[test] #[test]
@ -72,20 +79,20 @@ pub mod test {
let user = f::User(1000); let user = f::User(1000);
let expected = TextCell::paint_str(Blue.underline(), "enoch"); let expected = TextCell::paint_str(Blue.underline(), "enoch");
assert_eq!(expected, user.render(&TestColours, &users)); assert_eq!(expected, user.render(&TestColours, &users, UserFormat::Name));
} }
#[test] #[test]
fn different_unnamed() { fn different_unnamed() {
let user = f::User(1000); let user = f::User(1000);
let expected = TextCell::paint_str(Blue.underline(), "1000"); let expected = TextCell::paint_str(Blue.underline(), "1000");
assert_eq!(expected, user.render(&TestColours, &MockUsers::with_current_uid(0))); assert_eq!(expected, user.render(&TestColours, &MockUsers::with_current_uid(0), UserFormat::Numeric));
} }
#[test] #[test]
fn overflow() { fn overflow() {
let user = f::User(2_147_483_648); let user = f::User(2_147_483_648);
let expected = TextCell::paint_str(Blue.underline(), "2147483648"); let expected = TextCell::paint_str(Blue.underline(), "2147483648");
assert_eq!(expected, user.render(&TestColours, &MockUsers::with_current_uid(0))); assert_eq!(expected, user.render(&TestColours, &MockUsers::with_current_uid(0), UserFormat::Numeric));
} }
} }

View File

@ -23,6 +23,7 @@ use crate::theme::Theme;
pub struct Options { pub struct Options {
pub size_format: SizeFormat, pub size_format: SizeFormat,
pub time_format: TimeFormat, pub time_format: TimeFormat,
pub user_format: UserFormat,
pub columns: Columns, pub columns: Columns,
} }
@ -180,6 +181,15 @@ pub enum SizeFormat {
JustBytes, JustBytes,
} }
/// Formatting options for user and group.
#[derive(PartialEq, Debug, Copy, Clone)]
pub enum UserFormat {
/// The UID / GID
Numeric,
/// Show the name
Name,
}
impl Default for SizeFormat { impl Default for SizeFormat {
fn default() -> Self { fn default() -> Self {
Self::DecimalBytes Self::DecimalBytes
@ -322,6 +332,7 @@ pub struct Table<'a> {
widths: TableWidths, widths: TableWidths,
time_format: TimeFormat, time_format: TimeFormat,
size_format: SizeFormat, size_format: SizeFormat,
user_format: UserFormat,
git: Option<&'a GitCache>, git: Option<&'a GitCache>,
} }
@ -344,6 +355,7 @@ impl<'a, 'f> Table<'a> {
env, env,
time_format: options.time_format, time_format: options.time_format,
size_format: options.size_format, size_format: options.size_format,
user_format: options.user_format,
} }
} }
@ -403,10 +415,10 @@ impl<'a, 'f> Table<'a> {
file.blocks().render(self.theme) file.blocks().render(self.theme)
} }
Column::User => { Column::User => {
file.user().render(self.theme, &*self.env.lock_users()) file.user().render(self.theme, &*self.env.lock_users(), self.user_format)
} }
Column::Group => { Column::Group => {
file.group().render(self.theme, &*self.env.lock_users()) file.group().render(self.theme, &*self.env.lock_users(), self.user_format)
} }
Column::GitStatus => { Column::GitStatus => {
self.git_status(file).render(self.theme) self.git_status(file).render(self.theme)