use ansiterm::Style; use uzers::{Groups, Users}; use crate::fs::fields as f; use crate::fs::fields::User; use crate::output::cell::TextCell; use crate::output::table::{GroupFormat, UserFormat}; pub trait Render { fn render( self, colours: &C, users: &U, user_format: UserFormat, group_format: GroupFormat, file_user: Option, ) -> TextCell; } impl Render for Option { fn render( self, colours: &C, users: &U, user_format: UserFormat, group_format: GroupFormat, file_user: Option, ) -> TextCell { use uzers::os::unix::GroupExt; let mut style = colours.not_yours(); let group = match self { Some(g) => match users.get_group_by_gid(g.0) { Some(g) => (*g).clone(), None => return TextCell::paint(style, g.0.to_string()), }, None => return TextCell::blank(colours.no_group()), }; let current_uid = users.get_current_uid(); if let Some(current_user) = users.get_user_by_uid(current_uid) { if current_user.primary_group_id() == group.gid() || group.members().iter().any(|u| u == current_user.name()) { style = colours.yours(); } } if group.gid() == 0 && style != colours.yours() { style = colours.root_group(); } let mut group_name = match user_format { UserFormat::Name => group.name().to_string_lossy().into(), UserFormat::Numeric => group.gid().to_string(), }; if let GroupFormat::Smart = group_format { if let Some(file_uid) = file_user { if let Some(file_user) = users.get_user_by_uid(file_uid.0) { if file_user.name().to_string_lossy() == group.name().to_string_lossy() { group_name = ":".to_string(); } } } } TextCell::paint(style, group_name) } } pub trait Colours { fn yours(&self) -> Style; fn not_yours(&self) -> Style; fn no_group(&self) -> Style; fn root_group(&self) -> Style; } #[cfg(test)] #[allow(unused_results)] pub mod test { use super::{Colours, Render}; use crate::fs::fields as f; use crate::output::cell::TextCell; use crate::output::table::{GroupFormat, UserFormat}; use ansiterm::Colour::*; use ansiterm::Style; use uzers::mock::MockUsers; use uzers::os::unix::GroupExt; use uzers::{Group, User}; struct TestColours; #[rustfmt::skip] impl Colours for TestColours { fn yours(&self) -> Style { Fixed(80).normal() } fn not_yours(&self) -> Style { Fixed(81).normal() } fn no_group(&self) -> Style { Black.italic() } fn root_group(&self) -> Style { Fixed(82).normal() } } #[test] fn named() { let mut users = MockUsers::with_current_uid(1000); users.add_group(Group::new(100, "folk")); let group = Some(f::Group(100)); let file_user = Some(f::User(1000)); let expected = TextCell::paint_str(TestColours.not_yours(), "folk"); assert_eq!( expected, group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Regular, file_user ) ); let expected = TextCell::paint_str(TestColours.not_yours(), "100"); assert_eq!( expected, group.render( &TestColours, &users, UserFormat::Numeric, GroupFormat::Regular, file_user ) ); } #[test] fn unnamed() { let users = MockUsers::with_current_uid(1000); let group = Some(f::Group(100)); let file_user = Some(f::User(1000)); let expected = TextCell::paint_str(TestColours.not_yours(), "100"); assert_eq!( expected, group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Regular, file_user ) ); assert_eq!( expected, group.render( &TestColours, &users, UserFormat::Numeric, GroupFormat::Regular, file_user ) ); } #[test] fn primary() { let mut users = MockUsers::with_current_uid(2); users.add_user(User::new(2, "eve", 100)); users.add_group(Group::new(100, "folk")); let group = Some(f::Group(100)); let file_user = Some(f::User(2)); let expected = TextCell::paint_str(TestColours.yours(), "folk"); assert_eq!( expected, group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Regular, file_user ) ) } #[test] fn secondary() { let mut users = MockUsers::with_current_uid(2); users.add_user(User::new(2, "eve", 666)); let test_group = Group::new(100, "folk").add_member("eve"); users.add_group(test_group); let group = Some(f::Group(100)); let file_user = Some(f::User(2)); let expected = TextCell::paint_str(TestColours.yours(), "folk"); assert_eq!( expected, group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Regular, file_user ) ) } #[test] fn overflow() { let group = Some(f::Group(2_147_483_648)); let file_user = Some(f::User(1000)); let expected = TextCell::paint_str(TestColours.not_yours(), "2147483648"); assert_eq!( expected, group.render( &TestColours, &MockUsers::with_current_uid(0), UserFormat::Numeric, GroupFormat::Regular, file_user ) ); } #[test] fn smart() { let mut users = MockUsers::with_current_uid(1000); users.add_user(User::new(1000, "user", 100)); users.add_user(User::new(1001, "http", 101)); users.add_group(Group::new(100, "user")); users.add_group(Group::new(101, "http")); let user_group = Some(f::Group(100)); let user_file = Some(f::User(1000)); let expected = TextCell::paint_str(TestColours.yours(), ":"); assert_eq!( expected, user_group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Smart, user_file ) ); let expected = TextCell::paint_str(TestColours.yours(), ":"); assert_eq!( expected, user_group.render( &TestColours, &users, UserFormat::Numeric, GroupFormat::Smart, user_file ) ); let http_group = Some(f::Group(101)); let expected = TextCell::paint_str(TestColours.not_yours(), "http"); assert_eq!( expected, http_group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Smart, user_file ) ); let http_file = Some(f::User(1001)); let expected = TextCell::paint_str(TestColours.not_yours(), ":"); assert_eq!( expected, http_group.render( &TestColours, &users, UserFormat::Name, GroupFormat::Smart, http_file ) ); } }