mirror of
https://github.com/Llewellynvdm/exa.git
synced 2025-01-30 17:18:37 +00:00
PermissionsPlus holds the leftmost column values
The three pieces of information for the leftmost details view column (file type, permissions, and whether xattrs are present) used to be gathered from separate sources and passed around separately before being displayed at the end. Now, file type and permissions are put into a struct, along with the xattrs boolean that’s still getting passed around all over the place but not quite as much. This was all done because I wanted to be able to test permissions rendering, without having file type and xattrs dragged into the same function.
This commit is contained in:
parent
a2eb724483
commit
957c1925b1
@ -69,6 +69,15 @@ pub struct Permissions {
|
|||||||
pub other_execute: bool,
|
pub other_execute: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The three pieces of information that are displayed as a single column in
|
||||||
|
/// the details view. These values are fused together to make the output a
|
||||||
|
/// little more compressed.
|
||||||
|
pub struct PermissionsPlus {
|
||||||
|
pub file_type: Type,
|
||||||
|
pub permissions: Permissions,
|
||||||
|
pub xattrs: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/// A file’s number of hard links on the filesystem.
|
/// A file’s number of hard links on the filesystem.
|
||||||
///
|
///
|
||||||
|
@ -10,26 +10,6 @@ use fs::dir::Dir;
|
|||||||
use fs::fields as f;
|
use fs::fields as f;
|
||||||
|
|
||||||
|
|
||||||
#[allow(trivial_numeric_casts)]
|
|
||||||
mod modes {
|
|
||||||
use libc;
|
|
||||||
|
|
||||||
pub type Mode = u32;
|
|
||||||
// The `libc::mode_t` type’s actual type varies, but the value returned
|
|
||||||
// from `metadata.permissions().mode()` is always `u32`.
|
|
||||||
|
|
||||||
pub const USER_READ: Mode = libc::S_IRUSR as Mode;
|
|
||||||
pub const USER_WRITE: Mode = libc::S_IWUSR as Mode;
|
|
||||||
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
|
|
||||||
pub const GROUP_READ: Mode = libc::S_IRGRP as Mode;
|
|
||||||
pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode;
|
|
||||||
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
|
|
||||||
pub const OTHER_READ: Mode = libc::S_IROTH as Mode;
|
|
||||||
pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode;
|
|
||||||
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// A **File** is a wrapper around one of Rust's Path objects, along with
|
/// A **File** is a wrapper around one of Rust's Path objects, along with
|
||||||
/// associated data about the file.
|
/// associated data about the file.
|
||||||
///
|
///
|
||||||
@ -327,11 +307,7 @@ impl<'dir> File<'dir> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This file's permissions, with flags for each bit.
|
/// This file’s permissions, with flags for each bit.
|
||||||
///
|
|
||||||
/// The extended-attribute '@' character that you see in here is in fact
|
|
||||||
/// added in later, to avoid querying the extended attributes more than
|
|
||||||
/// once. (Yes, it's a little hacky.)
|
|
||||||
pub fn permissions(&self) -> f::Permissions {
|
pub fn permissions(&self) -> f::Permissions {
|
||||||
let bits = self.metadata.permissions().mode();
|
let bits = self.metadata.permissions().mode();
|
||||||
let has_bit = |bit| { bits & bit == bit };
|
let has_bit = |bit| { bits & bit == bit };
|
||||||
@ -449,6 +425,27 @@ impl<'dir> FileTarget<'dir> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// More readable aliases for the permission bits exposed by libc.
|
||||||
|
#[allow(trivial_numeric_casts)]
|
||||||
|
mod modes {
|
||||||
|
use libc;
|
||||||
|
|
||||||
|
pub type Mode = u32;
|
||||||
|
// The `libc::mode_t` type’s actual type varies, but the value returned
|
||||||
|
// from `metadata.permissions().mode()` is always `u32`.
|
||||||
|
|
||||||
|
pub const USER_READ: Mode = libc::S_IRUSR as Mode;
|
||||||
|
pub const USER_WRITE: Mode = libc::S_IWUSR as Mode;
|
||||||
|
pub const USER_EXECUTE: Mode = libc::S_IXUSR as Mode;
|
||||||
|
pub const GROUP_READ: Mode = libc::S_IRGRP as Mode;
|
||||||
|
pub const GROUP_WRITE: Mode = libc::S_IWGRP as Mode;
|
||||||
|
pub const GROUP_EXECUTE: Mode = libc::S_IXGRP as Mode;
|
||||||
|
pub const OTHER_READ: Mode = libc::S_IROTH as Mode;
|
||||||
|
pub const OTHER_WRITE: Mode = libc::S_IWOTH as Mode;
|
||||||
|
pub const OTHER_EXECUTE: Mode = libc::S_IXOTH as Mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use super::ext;
|
use super::ext;
|
||||||
|
@ -492,11 +492,19 @@ impl<'a, U: Users+Groups+'a> Table<'a, U> {
|
|||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn permissions_plus(&self, file: &File, xattrs: bool) -> f::PermissionsPlus {
|
||||||
|
f::PermissionsPlus {
|
||||||
|
file_type: file.type_char(),
|
||||||
|
permissions: file.permissions(),
|
||||||
|
xattrs: xattrs,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
|
fn display(&self, file: &File, column: &Column, xattrs: bool) -> TextCell {
|
||||||
use output::column::TimeType::*;
|
use output::column::TimeType::*;
|
||||||
|
|
||||||
match *column {
|
match *column {
|
||||||
Column::Permissions => file.permissions().render(&self.opts.colours, file.type_char(), xattrs),
|
Column::Permissions => self.permissions_plus(file, xattrs).render(&self.opts.colours),
|
||||||
Column::FileSize(fmt) => file.size().render(&self.opts.colours, fmt, &self.env.numeric),
|
Column::FileSize(fmt) => file.size().render(&self.opts.colours, fmt, &self.env.numeric),
|
||||||
Column::Timestamp(Modified) => self.render_time(file.modified_time()),
|
Column::Timestamp(Modified) => self.render_time(file.modified_time()),
|
||||||
Column::Timestamp(Created) => self.render_time(file.created_time()),
|
Column::Timestamp(Created) => self.render_time(file.created_time()),
|
||||||
|
@ -4,29 +4,15 @@ use output::cell::{TextCell, DisplayWidth};
|
|||||||
use ansi_term::{ANSIString, Style};
|
use ansi_term::{ANSIString, Style};
|
||||||
|
|
||||||
|
|
||||||
impl f::Permissions {
|
impl f::PermissionsPlus {
|
||||||
pub fn render(&self, colours: &Colours, file_type: f::Type, xattrs: bool) -> TextCell {
|
pub fn render(&self, colours: &Colours) -> TextCell {
|
||||||
let bit = |bit, chr: &'static str, style: Style| {
|
let x_colour = if self.file_type.is_regular_file() { colours.perms.user_execute_file }
|
||||||
if bit { style.paint(chr) } else { colours.punctuation.paint("-") }
|
|
||||||
};
|
|
||||||
|
|
||||||
let x_colour = if file_type.is_regular_file() { colours.perms.user_execute_file }
|
|
||||||
else { colours.perms.user_execute_other };
|
else { colours.perms.user_execute_other };
|
||||||
|
|
||||||
let mut chars = vec![
|
let mut chars = vec![ self.file_type.render(colours) ];
|
||||||
file_type.render(colours),
|
chars.extend(self.permissions.render(colours, x_colour));
|
||||||
bit(self.user_read, "r", colours.perms.user_read),
|
|
||||||
bit(self.user_write, "w", colours.perms.user_write),
|
|
||||||
bit(self.user_execute, "x", x_colour),
|
|
||||||
bit(self.group_read, "r", colours.perms.group_read),
|
|
||||||
bit(self.group_write, "w", colours.perms.group_write),
|
|
||||||
bit(self.group_execute, "x", colours.perms.group_execute),
|
|
||||||
bit(self.other_read, "r", colours.perms.other_read),
|
|
||||||
bit(self.other_write, "w", colours.perms.other_write),
|
|
||||||
bit(self.other_execute, "x", colours.perms.other_execute),
|
|
||||||
];
|
|
||||||
|
|
||||||
if xattrs {
|
if self.xattrs {
|
||||||
chars.push(colours.perms.attribute.paint("@"));
|
chars.push(colours.perms.attribute.paint("@"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -42,6 +28,26 @@ impl f::Permissions {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl f::Permissions {
|
||||||
|
pub fn render(&self, colours: &Colours, x_colour: Style) -> Vec<ANSIString<'static>> {
|
||||||
|
let bit = |bit, chr: &'static str, style: Style| {
|
||||||
|
if bit { style.paint(chr) } else { colours.punctuation.paint("-") }
|
||||||
|
};
|
||||||
|
|
||||||
|
vec![
|
||||||
|
bit(self.user_read, "r", colours.perms.user_read),
|
||||||
|
bit(self.user_write, "w", colours.perms.user_write),
|
||||||
|
bit(self.user_execute, "x", x_colour),
|
||||||
|
bit(self.group_read, "r", colours.perms.group_read),
|
||||||
|
bit(self.group_write, "w", colours.perms.group_write),
|
||||||
|
bit(self.group_execute, "x", colours.perms.group_execute),
|
||||||
|
bit(self.other_read, "r", colours.perms.other_read),
|
||||||
|
bit(self.other_write, "w", colours.perms.other_write),
|
||||||
|
bit(self.other_execute, "x", colours.perms.other_execute),
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl f::Type {
|
impl f::Type {
|
||||||
pub fn render(&self, colours: &Colours) -> ANSIString<'static> {
|
pub fn render(&self, colours: &Colours) -> ANSIString<'static> {
|
||||||
match *self {
|
match *self {
|
||||||
@ -63,78 +69,59 @@ impl f::Type {
|
|||||||
#[allow(unused_results)]
|
#[allow(unused_results)]
|
||||||
pub mod test {
|
pub mod test {
|
||||||
use output::details::Details;
|
use output::details::Details;
|
||||||
|
use output::cell::TextCellContents;
|
||||||
use fs::fields as f;
|
use fs::fields as f;
|
||||||
use output::cell::TextCell;
|
|
||||||
|
|
||||||
use users::{User, Group};
|
|
||||||
use users::mock::MockUsers;
|
|
||||||
use users::os::unix::GroupExt;
|
|
||||||
use ansi_term::Colour::*;
|
use ansi_term::Colour::*;
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn named() {
|
fn negate() {
|
||||||
let mut details = Details::default();
|
let mut details = Details::default();
|
||||||
details.colours.users.group_not_yours = Fixed(101).normal();
|
details.colours.punctuation = Fixed(44).normal();
|
||||||
|
|
||||||
let mut users = MockUsers::with_current_uid(1000);
|
let bits = f::Permissions {
|
||||||
users.add_group(Group::new(100, "folk"));
|
user_read: false, user_write: false, user_execute: false,
|
||||||
|
group_read: false, group_write: false, group_execute: false,
|
||||||
|
other_read: false, other_write: false, other_execute: false,
|
||||||
|
};
|
||||||
|
|
||||||
let group = f::Group(100);
|
let expected = TextCellContents::from(vec![
|
||||||
let expected = TextCell::paint_str(Fixed(101).normal(), "folk");
|
Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(44).paint("-"),
|
||||||
assert_eq!(expected, group.render(&details.colours, &users))
|
Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(44).paint("-"),
|
||||||
|
Fixed(44).paint("-"), Fixed(44).paint("-"), Fixed(44).paint("-"),
|
||||||
|
]);
|
||||||
|
|
||||||
|
assert_eq!(expected, bits.render(&details.colours, Fixed(66).normal()).into())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn unnamed() {
|
fn affirm() {
|
||||||
let mut details = Details::default();
|
let mut details = Details::default();
|
||||||
details.colours.users.group_not_yours = Fixed(87).normal();
|
details.colours.perms.user_read = Fixed(101).normal();
|
||||||
|
details.colours.perms.user_write = Fixed(102).normal();
|
||||||
|
|
||||||
let users = MockUsers::with_current_uid(1000);
|
details.colours.perms.group_read = Fixed(104).normal();
|
||||||
|
details.colours.perms.group_write = Fixed(105).normal();
|
||||||
|
details.colours.perms.group_execute = Fixed(106).normal();
|
||||||
|
|
||||||
let group = f::Group(100);
|
details.colours.perms.other_read = Fixed(107).normal();
|
||||||
let expected = TextCell::paint_str(Fixed(87).normal(), "100");
|
details.colours.perms.other_write = Fixed(108).normal();
|
||||||
assert_eq!(expected, group.render(&details.colours, &users));
|
details.colours.perms.other_execute = Fixed(109).normal();
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
let bits = f::Permissions {
|
||||||
fn primary() {
|
user_read: true, user_write: true, user_execute: true,
|
||||||
let mut details = Details::default();
|
group_read: true, group_write: true, group_execute: true,
|
||||||
details.colours.users.group_yours = Fixed(64).normal();
|
other_read: true, other_write: true, other_execute: true,
|
||||||
|
};
|
||||||
|
|
||||||
let mut users = MockUsers::with_current_uid(2);
|
let expected = TextCellContents::from(vec![
|
||||||
users.add_user(User::new(2, "eve", 100));
|
Fixed(101).paint("r"), Fixed(102).paint("w"), Fixed(103).paint("x"),
|
||||||
users.add_group(Group::new(100, "folk"));
|
Fixed(104).paint("r"), Fixed(105).paint("w"), Fixed(106).paint("x"),
|
||||||
|
Fixed(107).paint("r"), Fixed(108).paint("w"), Fixed(109).paint("x"),
|
||||||
|
]);
|
||||||
|
|
||||||
let group = f::Group(100);
|
assert_eq!(expected, bits.render(&details.colours, Fixed(103).normal()).into())
|
||||||
let expected = TextCell::paint_str(Fixed(64).normal(), "folk");
|
|
||||||
assert_eq!(expected, group.render(&details.colours, &users))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn secondary() {
|
|
||||||
let mut details = Details::default();
|
|
||||||
details.colours.users.group_yours = Fixed(31).normal();
|
|
||||||
|
|
||||||
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 = f::Group(100);
|
|
||||||
let expected = TextCell::paint_str(Fixed(31).normal(), "folk");
|
|
||||||
assert_eq!(expected, group.render(&details.colours, &users))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn overflow() {
|
|
||||||
let mut details = Details::default();
|
|
||||||
details.colours.users.group_not_yours = Blue.underline();
|
|
||||||
|
|
||||||
let group = f::Group(2_147_483_648);
|
|
||||||
let expected = TextCell::paint_str(Blue.underline(), "2147483648");
|
|
||||||
assert_eq!(expected, group.render(&details.colours, &MockUsers::with_current_uid(0)));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user