diff --git a/Cargo.lock b/Cargo.lock index 068c484..80d10c8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1267,7 +1267,9 @@ dependencies = [ "test-log", "tokio", "trash", + "unix_permissions_ext", "url", + "users", "vergen", "xdg", "xdg-mime", @@ -6107,6 +6109,12 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "229730647fbc343e3a80e463c1db7f78f3855d3f3739bee0dda773c9a037c90a" +[[package]] +name = "unix_permissions_ext" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7497808a85e03f612f13e9c5061e4c81cdee86e6c00adfa1096690990ccd08e9" + [[package]] name = "url" version = "2.5.2" @@ -6125,6 +6133,16 @@ version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" +[[package]] +name = "users" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24cc0f6d6f267b73e5a2cadf007ba8f9bc39c6a6f9666f8cf25ea809a153b032" +dependencies = [ + "libc", + "log", +] + [[package]] name = "usvg" version = "0.37.0" diff --git a/Cargo.toml b/Cargo.toml index 5c0a197..8de3e3b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -48,6 +48,8 @@ i18n-embed-fl = "0.7" rust-embed = "8" slotmap = "1.0.7" zip = "2.1.6" +unix_permissions_ext = "0.1.2" +users = "0.11.0" [dependencies.libcosmic] git = "https://github.com/pop-os/libcosmic.git" diff --git a/i18n/en/cosmic_files.ftl b/i18n/en/cosmic_files.ftl index a157052..b150ffc 100644 --- a/i18n/en/cosmic_files.ftl +++ b/i18n/en/cosmic_files.ftl @@ -58,6 +58,15 @@ apply-to-all = Apply to all keep-both = Keep both skip = Skip + +## Metadata Dialog +owner = Owner +group = Group +other = Other +read = Read +write = Write +execute = Execute + # Context Pages ## About diff --git a/src/tab.rs b/src/tab.rs index 78ae8bb..cfc90dd 100644 --- a/src/tab.rs +++ b/src/tab.rs @@ -43,6 +43,7 @@ use std::{ fmt, fs::{self, Metadata}, num::NonZeroU16, + os::unix::fs::MetadataExt, path::PathBuf, sync::{Arc, Mutex}, time::{Duration, Instant}, @@ -60,6 +61,8 @@ use crate::{ mime_icon::{mime_for_path, mime_icon}, mouse_area, }; +use unix_permissions_ext::UNIXPermissionsExt; +use users::{get_group_by_gid, get_user_by_uid}; pub const DOUBLE_CLICK_DURATION: Duration = Duration::from_millis(500); pub const HOVER_DURATION: Duration = Duration::from_millis(1600); @@ -199,6 +202,49 @@ fn format_size(size: u64) -> String { format!("{} B", size) } } +enum PermissionOwner { + Owner, + Group, + Other, +} + +fn format_permissions_owner(metadata: &Metadata, owner: PermissionOwner) -> String { + return match owner { + PermissionOwner::Owner => get_user_by_uid(metadata.uid()) + .and_then(|user| user.name().to_str().map(ToOwned::to_owned)) + .unwrap_or_default(), + PermissionOwner::Group => get_group_by_gid(metadata.gid()) + .and_then(|group| group.name().to_str().map(ToOwned::to_owned)) + .unwrap_or_default(), + PermissionOwner::Other => String::from(""), + }; +} +fn format_permissions(metadata: &Metadata, owner: PermissionOwner) -> String { + let mut perms: Vec = Vec::new(); + if match owner { + PermissionOwner::Owner => metadata.permissions().readable_by_owner(), + PermissionOwner::Group => metadata.permissions().readable_by_group(), + PermissionOwner::Other => metadata.permissions().readable_by_other(), + } { + perms.push(fl!("read")); + } + if match owner { + PermissionOwner::Owner => metadata.permissions().writable_by_owner(), + PermissionOwner::Group => metadata.permissions().writable_by_group(), + PermissionOwner::Other => metadata.permissions().writable_by_other(), + } { + perms.push(fl!("write")); + } + if match owner { + PermissionOwner::Owner => metadata.permissions().executable_by_owner(), + PermissionOwner::Group => metadata.permissions().executable_by_group(), + PermissionOwner::Other => metadata.permissions().executable_by_other(), + } { + perms.push(fl!("execute")); + } + + perms.join(" ") +} #[cfg(not(target_os = "windows"))] fn hidden_attribute(_metadata: &Metadata) -> bool { @@ -828,6 +874,46 @@ impl Item { .format_localized(TIME_FORMAT, *LANGUAGE_CHRONO) ))); } + #[cfg(not(target_os = "windows"))] + { + column = column.push( + widget::Row::new() + .push(widget::text(format!("{}:", fl!("owner")))) + .push(widget::text(format_permissions_owner( + metadata, + PermissionOwner::Owner, + ))) + .push(widget::text(format!( + "({})", + format_permissions(metadata, PermissionOwner::Owner,) + ))) + .spacing(10), + ); + + column = column.push( + widget::Row::new() + .push(widget::text(format!("{}:", fl!("group")))) + .push(widget::text(format_permissions_owner( + metadata, + PermissionOwner::Group, + ))) + .push(widget::text(format!( + "({})", + format_permissions(metadata, PermissionOwner::Group,) + ))) + .spacing(10), + ); + + column = column.push( + widget::Row::new() + .push(widget::text(format!("{}", fl!("other")))) + .push(widget::text(format!( + "({})", + format_permissions(metadata, PermissionOwner::Other,) + ))) + .spacing(10), + ); + } } ItemMetadata::Trash { .. } => { //TODO: trash metadata