소스 검색

feat: add option --smart-group

Rami Chasygov 2 년 전
부모
커밋
61284c80af
5개의 변경된 파일125개의 추가작업 그리고 35개의 파일을 삭제
  1. 19 18
      src/options/flags.rs
  2. 1 0
      src/options/help.rs
  3. 12 1
      src/options/view.rs
  4. 73 12
      src/output/render/groups.rs
  5. 20 4
      src/output/table.rs

+ 19 - 18
src/options/flags.rs

@@ -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

+ 1 - 0
src/options/help.rs

@@ -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

+ 12 - 1
src/options/view.rs

@@ -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 file’s time fields should be displayed for it
     /// based on the user’s options.

+ 73 - 12
src/output/render/groups.rs

@@ -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)
+        );
+    }
 }

+ 20 - 4
src/output/table.rs

@@ -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),