feat: add option --smart-group

This commit is contained in:
Rami Chasygov 2023-10-11 01:23:35 +03:00 committed by Rami Chasygov
parent a7718c84d7
commit 61284c80af
5 changed files with 125 additions and 35 deletions

View file

@ -40,23 +40,24 @@ const SORTS: Values = &[ "name", "Name", "size", "extension",
"created", "inode", "type", "none" ];
// display options
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 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 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 LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden };
pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden };
pub static CHANGED: Arg = Arg { short: None, long: "changed", takes_value: TakesValue::Forbidden };
pub static BLOCKSIZE: Arg = Arg { short: Some(b'S'), long: "blocksize", takes_value: TakesValue::Forbidden };
pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) };
pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden };
pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden };
pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
pub static HYPERLINK: Arg = Arg { short: None, long: "hyperlink", takes_value: TakesValue::Forbidden };
pub static MOUNTS: Arg = Arg { short: Some(b'M'), long: "mounts", 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 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 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 LINKS: Arg = Arg { short: Some(b'H'), long: "links", takes_value: TakesValue::Forbidden };
pub static MODIFIED: Arg = Arg { short: Some(b'm'), long: "modified", takes_value: TakesValue::Forbidden };
pub static CHANGED: Arg = Arg { short: None, long: "changed", takes_value: TakesValue::Forbidden };
pub static BLOCKSIZE: Arg = Arg { short: Some(b'S'), long: "blocksize", takes_value: TakesValue::Forbidden };
pub static TIME: Arg = Arg { short: Some(b't'), long: "time", takes_value: TakesValue::Necessary(Some(TIMES)) };
pub static ACCESSED: Arg = Arg { short: Some(b'u'), long: "accessed", takes_value: TakesValue::Forbidden };
pub static CREATED: Arg = Arg { short: Some(b'U'), long: "created", takes_value: TakesValue::Forbidden };
pub static TIME_STYLE: Arg = Arg { short: None, long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
pub static HYPERLINK: Arg = Arg { short: None, long: "hyperlink", takes_value: TakesValue::Forbidden };
pub static MOUNTS: Arg = Arg { short: Some(b'M'), long: "mounts", takes_value: TakesValue::Forbidden };
pub static SMART_GROUP: Arg = Arg { short: None, long: "smart-group", takes_value: TakesValue::Forbidden };
const TIMES: Values = &["modified", "changed", "accessed", "created"];
const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso", "relative"];
@ -87,7 +88,7 @@ pub static ALL_ARGS: Args = Args(&[
&BINARY, &BYTES, &GROUP, &NUMERIC, &HEADER, &ICONS, &INODE, &LINKS, &MODIFIED, &CHANGED,
&BLOCKSIZE, &TIME, &ACCESSED, &CREATED, &TIME_STYLE, &HYPERLINK, &MOUNTS,
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS,
&NO_PERMISSIONS, &NO_FILESIZE, &NO_USER, &NO_TIME, &NO_ICONS, &SMART_GROUP,
&GIT, &NO_GIT, &GIT_REPOS, &GIT_REPOS_NO_STAT,
&EXTENDED, &OCTAL, &SECURITY_CONTEXT

View file

@ -27,6 +27,7 @@ DISPLAY OPTIONS
--no-quotes don't quote file names with spaces
--hyperlink display entries as hyperlinks
-w, --width COLS set screen width in columns
--smart-group only show group if it has a different name from owner
FILTERING AND SORTING OPTIONS

View file

@ -3,7 +3,9 @@ use crate::options::parser::MatchedFlags;
use crate::options::{flags, NumberSource, OptionsError, Vars};
use crate::output::file_name::Options as FileStyle;
use crate::output::grid_details::{self, RowThreshold};
use crate::output::table::{Columns, Options as TableOptions, SizeFormat, TimeTypes, UserFormat};
use crate::output::table::{
Columns, GroupFormat, Options as TableOptions, SizeFormat, TimeTypes, UserFormat,
};
use crate::output::time::TimeFormat;
use crate::output::{details, grid, Mode, TerminalWidth, View};
@ -231,11 +233,13 @@ impl TableOptions {
let time_format = TimeFormat::deduce(matches, vars)?;
let size_format = SizeFormat::deduce(matches)?;
let user_format = UserFormat::deduce(matches)?;
let group_format = GroupFormat::deduce(matches)?;
let columns = Columns::deduce(matches, vars)?;
Ok(Self {
size_format,
time_format,
user_format,
group_format,
columns,
})
}
@ -341,6 +345,13 @@ impl UserFormat {
}
}
impl GroupFormat {
fn deduce(matches: &MatchedFlags<'_>) -> Result<Self, OptionsError> {
let flag = matches.has(&flags::SMART_GROUP)?;
Ok(if flag { Self::Smart } else { Self::Regular })
}
}
impl TimeTypes {
/// Determine which of a files time fields should be displayed for it
/// based on the users options.

View file

@ -3,14 +3,15 @@ use uzers::{Groups, Users};
use crate::fs::fields as f;
use crate::output::cell::TextCell;
use crate::output::table::UserFormat;
use crate::output::table::{GroupFormat, UserFormat};
pub trait Render {
fn render<C: Colours, U: Users + Groups>(
self,
colours: &C,
users: &U,
format: UserFormat,
user_format: UserFormat,
group_format: GroupFormat,
) -> TextCell;
}
@ -19,7 +20,8 @@ impl Render for Option<f::Group> {
self,
colours: &C,
users: &U,
format: UserFormat,
user_format: UserFormat,
group_format: GroupFormat,
) -> TextCell {
use uzers::os::unix::GroupExt;
@ -46,11 +48,26 @@ impl Render for Option<f::Group> {
style = colours.root_group();
}
let group_name = match format {
let mut group_name = match user_format {
UserFormat::Name => group.name().to_string_lossy().into(),
UserFormat::Numeric => group.gid().to_string(),
};
group_name = match group_format {
GroupFormat::Smart => {
if let Some(current_user) = users.get_user_by_uid(current_uid) {
if current_user.name() == group.name() {
":".to_string()
} else {
group_name
}
} else {
group_name
}
}
GroupFormat::Regular => group_name,
};
TextCell::paint(style, group_name)
}
}
@ -68,7 +85,7 @@ pub mod test {
use super::{Colours, Render};
use crate::fs::fields as f;
use crate::output::cell::TextCell;
use crate::output::table::UserFormat;
use crate::output::table::{GroupFormat, UserFormat};
use ansiterm::Colour::*;
use ansiterm::Style;
@ -95,13 +112,18 @@ pub mod test {
let expected = TextCell::paint_str(Fixed(81).normal(), "folk");
assert_eq!(
expected,
group.render(&TestColours, &users, UserFormat::Name)
group.render(&TestColours, &users, UserFormat::Name, GroupFormat::Regular)
);
let expected = TextCell::paint_str(Fixed(81).normal(), "100");
assert_eq!(
expected,
group.render(&TestColours, &users, UserFormat::Numeric)
group.render(
&TestColours,
&users,
UserFormat::Numeric,
GroupFormat::Regular
)
);
}
@ -113,11 +135,16 @@ pub mod test {
let expected = TextCell::paint_str(Fixed(81).normal(), "100");
assert_eq!(
expected,
group.render(&TestColours, &users, UserFormat::Name)
group.render(&TestColours, &users, UserFormat::Name, GroupFormat::Regular)
);
assert_eq!(
expected,
group.render(&TestColours, &users, UserFormat::Numeric)
group.render(
&TestColours,
&users,
UserFormat::Numeric,
GroupFormat::Regular
)
);
}
@ -131,7 +158,7 @@ pub mod test {
let expected = TextCell::paint_str(Fixed(80).normal(), "folk");
assert_eq!(
expected,
group.render(&TestColours, &users, UserFormat::Name)
group.render(&TestColours, &users, UserFormat::Name, GroupFormat::Regular)
)
}
@ -147,7 +174,7 @@ pub mod test {
let expected = TextCell::paint_str(Fixed(80).normal(), "folk");
assert_eq!(
expected,
group.render(&TestColours, &users, UserFormat::Name)
group.render(&TestColours, &users, UserFormat::Name, GroupFormat::Regular)
)
}
@ -160,8 +187,42 @@ pub mod test {
group.render(
&TestColours,
&MockUsers::with_current_uid(0),
UserFormat::Numeric
UserFormat::Numeric,
GroupFormat::Regular
)
);
}
#[test]
fn smart() {
let mut users = MockUsers::with_current_uid(1000);
users.add_user(User::new(1000, "user", 110));
users.add_group(Group::new(100, "user"));
users.add_group(Group::new(101, "http"));
let same_group = Some(f::Group(100));
let expected = TextCell::paint_str(Fixed(81).normal(), ":");
assert_eq!(
expected,
same_group.render(&TestColours, &users, UserFormat::Name, GroupFormat::Smart)
);
let expected = TextCell::paint_str(Fixed(81).normal(), ":");
assert_eq!(
expected,
same_group.render(
&TestColours,
&users,
UserFormat::Numeric,
GroupFormat::Smart
)
);
let http_group = Some(f::Group(101));
let expected = TextCell::paint_str(Fixed(81).normal(), "http");
assert_eq!(
expected,
http_group.render(&TestColours, &users, UserFormat::Name, GroupFormat::Smart)
);
}
}

View file

@ -25,6 +25,7 @@ pub struct Options {
pub size_format: SizeFormat,
pub time_format: TimeFormat,
pub user_format: UserFormat,
pub group_format: GroupFormat,
pub columns: Columns,
}
@ -239,6 +240,15 @@ pub enum UserFormat {
Name,
}
/// Formatting options for group only.
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
pub enum GroupFormat {
/// Numeric or text value
Regular,
/// Show ":" if user-group value is the same
Smart,
}
impl Default for SizeFormat {
fn default() -> Self {
Self::DecimalBytes
@ -355,6 +365,8 @@ pub struct Table<'a> {
size_format: SizeFormat,
#[cfg(unix)]
user_format: UserFormat,
#[cfg(unix)]
group_format: GroupFormat,
git: Option<&'a GitCache>,
}
@ -379,6 +391,8 @@ impl<'a> Table<'a> {
size_format: options.size_format,
#[cfg(unix)]
user_format: options.user_format,
#[cfg(unix)]
group_format: options.group_format,
}
}
@ -457,10 +471,12 @@ impl<'a> Table<'a> {
.render(self.theme, &*self.env.lock_users(), self.user_format)
}
#[cfg(unix)]
Column::Group => {
file.group()
.render(self.theme, &*self.env.lock_users(), self.user_format)
}
Column::Group => file.group().render(
self.theme,
&*self.env.lock_users(),
self.user_format,
self.group_format,
),
#[cfg(unix)]
Column::SecurityContext => file.security_context().render(self.theme),
Column::GitStatus => self.git_status(file).render(self.theme),