diff --git a/Cargo.lock b/Cargo.lock index db22cf4..5a45adc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1261,7 +1261,9 @@ dependencies = [ "test-log", "tokio", "trash", + "unix_permissions_ext", "url", + "users", "vergen", "xdg", "xdg-mime", @@ -1530,7 +1532,7 @@ checksum = "67e77553c4162a157adbf834ebae5b415acbecbeafc7a74b0e886657506a7611" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] @@ -6093,6 +6095,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[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" @@ -6111,6 +6119,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" @@ -7413,7 +7431,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.72", + "syn 2.0.75", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index e25b637..dcd7e3e 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..2302b34 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,60 @@ 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 readable = match owner { + PermissionOwner::Owner => metadata.permissions().readable_by_owner(), + PermissionOwner::Group => metadata.permissions().readable_by_group(), + PermissionOwner::Other => metadata.permissions().readable_by_other(), + }; + let writeable = match owner { + PermissionOwner::Owner => metadata.permissions().writable_by_owner(), + PermissionOwner::Group => metadata.permissions().writable_by_group(), + PermissionOwner::Other => metadata.permissions().writable_by_other(), + }; + let executable = match owner { + PermissionOwner::Owner => metadata.permissions().executable_by_owner(), + PermissionOwner::Group => metadata.permissions().executable_by_group(), + PermissionOwner::Other => metadata.permissions().executable_by_other(), + }; + format!( + "{} {} {}", + if readable { + fl!("read") + } else { + String::from("") + }, + if writeable { + fl!("write") + } else { + String::from("") + }, + if executable { + fl!("execute") + } else { + String::from("") + } + ) + .trim_end() + .to_string() +} #[cfg(not(target_os = "windows"))] fn hidden_attribute(_metadata: &Metadata) -> bool { @@ -828,6 +885,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