diff --git a/Cargo.toml b/Cargo.toml index 05f1cb7..965caab 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -31,9 +31,11 @@ scoped_threadpool = "0.1" term_grid = "0.2.0" terminal_size = "0.1.16" unicode-width = "0.1" -users = "0.11" zoneinfo_compiled = "0.5.1" +[target.'cfg(unix)'.dependencies] +users = "0.11" + [dependencies.datetime] version = "0.5.2" default-features = false diff --git a/src/fs/dir.rs b/src/fs/dir.rs index 1a6ce01..fcb393f 100644 --- a/src/fs/dir.rs +++ b/src/fs/dir.rs @@ -111,6 +111,13 @@ impl<'dir, 'ig> Files<'dir, 'ig> { continue; } + // Also hide _prefix files on Windows because it's used by old applications + // as an alternative to dot-prefix files. + #[cfg(windows)] + if ! self.dotfiles && filename.starts_with('_') { + continue; + } + if self.git_ignoring { let git_status = self.git.map(|g| g.get(path, false)).unwrap_or_default(); if git_status.unstaged == GitStatus::Ignored { diff --git a/src/fs/feature/git.rs b/src/fs/feature/git.rs index 1a0c60d..c700730 100644 --- a/src/fs/feature/git.rs +++ b/src/fs/feature/git.rs @@ -296,6 +296,7 @@ impl Git { /// Paths need to be absolute for them to be compared properly, otherwise /// you’d ask a repo about “./README.md” but it only knows about /// “/vagrant/README.md”, prefixed by the workdir. +#[cfg(unix)] fn reorient(path: &Path) -> PathBuf { use std::env::current_dir; @@ -308,6 +309,14 @@ fn reorient(path: &Path) -> PathBuf { path.canonicalize().unwrap_or(path) } +#[cfg(windows)] +fn reorient(path: &Path) -> PathBuf { + let unc_path = path.canonicalize().unwrap(); + // On Windows UNC path is returned. We need to strip the prefix for it to work. + let normal_path = unc_path.as_os_str().to_str().unwrap().trim_left_matches("\\\\?\\"); + return PathBuf::from(normal_path); +} + /// The character to display if the file has been modified, but not staged. fn working_tree_status(status: git2::Status) -> f::GitStatus { match status { diff --git a/src/fs/fields.rs b/src/fs/fields.rs index d768ebf..6ad41e3 100644 --- a/src/fs/fields.rs +++ b/src/fs/fields.rs @@ -82,13 +82,27 @@ pub struct Permissions { pub setuid: bool, } +/// The file's FileAttributes field, available only on Windows. +#[derive(Copy, Clone)] +pub struct Attributes { + pub archive: bool, + pub directory: bool, + pub readonly: bool, + pub hidden: bool, + pub system: bool, + pub reparse_point: 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. #[derive(Copy, Clone)] pub struct PermissionsPlus { pub file_type: Type, + #[cfg(unix)] pub permissions: Permissions, + #[cfg(windows)] + pub attributes: Attributes, pub xattrs: bool, } diff --git a/src/fs/file.rs b/src/fs/file.rs index ea83f08..7112109 100644 --- a/src/fs/file.rs +++ b/src/fs/file.rs @@ -1,7 +1,10 @@ //! Files, and methods and fields to access their metadata. use std::io; +#[cfg(unix)] use std::os::unix::fs::{FileTypeExt, MetadataExt, PermissionsExt}; +#[cfg(windows)] +use std::os::windows::fs::MetadataExt; use std::path::{Path, PathBuf}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; @@ -174,6 +177,7 @@ impl<'dir> File<'dir> { /// Whether this file is both a regular file *and* executable for the /// current user. An executable file has a different purpose from an /// executable directory, so they should be highlighted differently. + #[cfg(unix)] pub fn is_executable_file(&self) -> bool { let bit = modes::USER_EXECUTE; self.is_file() && (self.metadata.permissions().mode() & bit) == bit @@ -185,21 +189,25 @@ impl<'dir> File<'dir> { } /// Whether this file is a named pipe on the filesystem. + #[cfg(unix)] pub fn is_pipe(&self) -> bool { self.metadata.file_type().is_fifo() } /// Whether this file is a char device on the filesystem. + #[cfg(unix)] pub fn is_char_device(&self) -> bool { self.metadata.file_type().is_char_device() } /// Whether this file is a block device on the filesystem. + #[cfg(unix)] pub fn is_block_device(&self) -> bool { self.metadata.file_type().is_block_device() } /// Whether this file is a socket on the filesystem. + #[cfg(unix)] pub fn is_socket(&self) -> bool { self.metadata.file_type().is_socket() } @@ -270,6 +278,7 @@ impl<'dir> File<'dir> { /// is uncommon, while you come across directories and other types /// with multiple links much more often. Thus, it should get highlighted /// more attentively. + #[cfg(unix)] pub fn links(&self) -> f::Links { let count = self.metadata.nlink(); @@ -280,6 +289,7 @@ impl<'dir> File<'dir> { } /// This file’s inode. + #[cfg(unix)] pub fn inode(&self) -> f::Inode { f::Inode(self.metadata.ino()) } @@ -287,6 +297,7 @@ impl<'dir> File<'dir> { /// This file’s number of filesystem blocks. /// /// (Not the size of each block, which we don’t actually report on) + #[cfg(unix)] pub fn blocks(&self) -> f::Blocks { if self.is_file() || self.is_link() { f::Blocks::Some(self.metadata.blocks()) @@ -297,11 +308,13 @@ impl<'dir> File<'dir> { } /// The ID of the user that own this file. + #[cfg(unix)] pub fn user(&self) -> f::User { f::User(self.metadata.uid()) } /// The ID of the group that owns this file. + #[cfg(unix)] pub fn group(&self) -> f::Group { f::Group(self.metadata.gid()) } @@ -314,6 +327,7 @@ impl<'dir> File<'dir> { /// /// Block and character devices return their device IDs, because they /// usually just have a file size of zero. + #[cfg(unix)] pub fn size(&self) -> f::Size { if self.is_directory() { f::Size::None @@ -335,12 +349,23 @@ impl<'dir> File<'dir> { } } + #[cfg(windows)] + pub fn size(&self) -> f::Size { + if self.is_directory() { + f::Size::None + } + else { + f::Size::Some(self.metadata.len()) + } + } + /// This file’s last modified timestamp, if available on this platform. pub fn modified_time(&self) -> Option { self.metadata.modified().ok() } /// This file’s last changed timestamp, if available on this platform. + #[cfg(unix)] pub fn changed_time(&self) -> Option { let (mut sec, mut nanosec) = (self.metadata.ctime(), self.metadata.ctime_nsec()); @@ -359,6 +384,11 @@ impl<'dir> File<'dir> { } } + #[cfg(windows)] + pub fn changed_time(&self) -> Option { + return self.modified_time() + } + /// This file’s last accessed timestamp, if available on this platform. pub fn accessed_time(&self) -> Option { self.metadata.accessed().ok() @@ -374,6 +404,7 @@ impl<'dir> File<'dir> { /// This is used a the leftmost character of the permissions column. /// The file type can usually be guessed from the colour of the file, but /// ls puts this character there. + #[cfg(unix)] pub fn type_char(&self) -> f::Type { if self.is_file() { f::Type::File @@ -401,7 +432,21 @@ impl<'dir> File<'dir> { } } + #[cfg(windows)] + pub fn type_char(&self) -> f::Type { + if self.is_file() { + f::Type::File + } + else if self.is_directory() { + f::Type::Directory + } + else { + f::Type::Special + } + } + /// This file’s permissions, with flags for each bit. + #[cfg(unix)] pub fn permissions(&self) -> f::Permissions { let bits = self.metadata.mode(); let has_bit = |bit| bits & bit == bit; @@ -425,6 +470,22 @@ impl<'dir> File<'dir> { } } + #[cfg(windows)] + pub fn attributes(&self) -> f::Attributes { + let bits = self.metadata.file_attributes(); + let has_bit = |bit| bits & bit == bit; + + // https://docs.microsoft.com/en-us/windows/win32/fileio/file-attribute-constants + f::Attributes { + directory: has_bit(0x10), + archive: has_bit(0x20), + readonly: has_bit(0x1), + hidden: has_bit(0x2), + system: has_bit(0x4), + reparse_point: has_bit(0x400), + } + } + /// Whether this file’s extension is any of the strings that get passed in. /// /// This will always return `false` if the file has no extension. @@ -482,6 +543,7 @@ impl<'dir> FileTarget<'dir> { /// More readable aliases for the permission bits exposed by libc. #[allow(trivial_numeric_casts)] +#[cfg(unix)] mod modes { // The `libc::mode_t` type’s actual type varies, but the value returned @@ -559,6 +621,7 @@ mod filename_test { } #[test] + #[cfg(unix)] fn topmost() { assert_eq!("/", File::filename(Path::new("/"))) } diff --git a/src/fs/filter.rs b/src/fs/filter.rs index 5fb3f55..9662931 100644 --- a/src/fs/filter.rs +++ b/src/fs/filter.rs @@ -2,6 +2,7 @@ use std::cmp::Ordering; use std::iter::FromIterator; +#[cfg(unix)] use std::os::unix::fs::MetadataExt; use crate::fs::DotFilter; @@ -130,6 +131,7 @@ pub enum SortField { /// The file’s inode, which usually corresponds to the order in which /// files were created on the filesystem, more or less. + #[cfg(unix)] FileInode, /// The time the file was modified (the “mtime”). @@ -223,6 +225,7 @@ impl SortField { Self::Name(AaBbCc) => natord::compare_ignore_case(&a.name, &b.name), Self::Size => a.metadata.len().cmp(&b.metadata.len()), + #[cfg(unix)] Self::FileInode => a.metadata.ino().cmp(&b.metadata.ino()), Self::ModifiedDate => a.modified_time().cmp(&b.modified_time()), Self::AccessedDate => a.accessed_time().cmp(&b.accessed_time()), diff --git a/src/main.rs b/src/main.rs index bbdf0d6..7dcff10 100644 --- a/src/main.rs +++ b/src/main.rs @@ -49,12 +49,18 @@ mod theme; fn main() { use std::process::exit; + #[cfg(unix)] unsafe { libc::signal(libc::SIGPIPE, libc::SIG_DFL); } logger::configure(env::var_os(vars::EXA_DEBUG)); + #[cfg(windows)] + if let Err(e) = ansi_term::enable_ansi_support() { + warn!("Failed to enable ANSI support: {}", e); + } + let args: Vec<_> = env::args_os().skip(1).collect(); match Options::parse(args.iter().map(|e| e.as_ref()), &LiveVars) { OptionsResult::Ok(options, mut input_paths) => { diff --git a/src/options/filter.rs b/src/options/filter.rs index 5524b92..ad88ff7 100644 --- a/src/options/filter.rs +++ b/src/options/filter.rs @@ -88,6 +88,7 @@ impl SortField { "cr" | "created" => { Self::CreatedDate } + #[cfg(unix)] "inode" => { Self::FileInode } diff --git a/src/options/parser.rs b/src/options/parser.rs index f2e1d0a..63a1606 100644 --- a/src/options/parser.rs +++ b/src/options/parser.rs @@ -146,8 +146,6 @@ impl Args { pub fn parse<'args, I>(&self, inputs: I, strictness: Strictness) -> Result, ParseError> where I: IntoIterator { - use std::os::unix::ffi::OsStrExt; - let mut parsing = true; // The results that get built up. @@ -159,7 +157,7 @@ impl Args { // doesn’t have one in its string so it needs the next one. let mut inputs = inputs.into_iter(); while let Some(arg) = inputs.next() { - let bytes = arg.as_bytes(); + let bytes = os_str_to_bytes(arg); // Stop parsing if one of the arguments is the literal string “--”. // This allows a file named “--arg” to be specified by passing in @@ -174,7 +172,7 @@ impl Args { // If the string starts with *two* dashes then it’s a long argument. else if bytes.starts_with(b"--") { - let long_arg_name = OsStr::from_bytes(&bytes[2..]); + let long_arg_name = bytes_to_os_str(&bytes[2..]); // If there’s an equals in it, then the string before the // equals will be the flag’s name, and the string after it @@ -221,7 +219,7 @@ impl Args { // If the string starts with *one* dash then it’s one or more // short arguments. else if bytes.starts_with(b"-") && arg != "-" { - let short_arg = OsStr::from_bytes(&bytes[1..]); + let short_arg = bytes_to_os_str(&bytes[1..]); // If there’s an equals in it, then the argument immediately // before the equals was the one that has the value, with the @@ -236,7 +234,7 @@ impl Args { // it’s an error if any of the first set of arguments actually // takes a value. if let Some((before, after)) = split_on_equals(short_arg) { - let (arg_with_value, other_args) = before.as_bytes().split_last().unwrap(); + let (arg_with_value, other_args) = os_str_to_bytes(before).split_last().unwrap(); // Process the characters immediately following the dash... for byte in other_args { @@ -291,7 +289,7 @@ impl Args { TakesValue::Optional(values) => { if index < bytes.len() - 1 { let remnants = &bytes[index+1 ..]; - result_flags.push((flag, Some(OsStr::from_bytes(remnants)))); + result_flags.push((flag, Some(bytes_to_os_str(remnants)))); break; } else if let Some(next_arg) = inputs.next() { @@ -495,19 +493,42 @@ impl fmt::Display for ParseError { } } +#[cfg(unix)] +fn os_str_to_bytes<'b>(s: &'b OsStr) -> &'b [u8]{ + use std::os::unix::ffi::OsStrExt; + + return s.as_bytes() +} + +#[cfg(unix)] +fn bytes_to_os_str<'b>(b: &'b [u8]) -> &'b OsStr{ + use std::os::unix::ffi::OsStrExt; + + return OsStr::from_bytes(b); +} + +#[cfg(windows)] +fn os_str_to_bytes<'b>(s: &'b OsStr) -> &'b [u8]{ + return s.to_str().unwrap().as_bytes() +} + +#[cfg(windows)] +fn bytes_to_os_str<'b>(b: &'b [u8]) -> &'b OsStr{ + use std::str; + + return OsStr::new(str::from_utf8(b).unwrap()); +} /// Splits a string on its `=` character, returning the two substrings on /// either side. Returns `None` if there’s no equals or a string is missing. fn split_on_equals(input: &OsStr) -> Option<(&OsStr, &OsStr)> { - use std::os::unix::ffi::OsStrExt; - - if let Some(index) = input.as_bytes().iter().position(|elem| *elem == b'=') { - let (before, after) = input.as_bytes().split_at(index); + if let Some(index) = os_str_to_bytes(input).iter().position(|elem| *elem == b'=') { + let (before, after) = os_str_to_bytes(input).split_at(index); // The after string contains the = that we need to remove. if ! before.is_empty() && after.len() >= 2 { - return Some((OsStr::from_bytes(before), - OsStr::from_bytes(&after[1..]))) + return Some((bytes_to_os_str(before), + bytes_to_os_str(&after[1..]))) } } diff --git a/src/output/file_name.rs b/src/output/file_name.rs index b6a38c0..eeb359b 100644 --- a/src/output/file_name.rs +++ b/src/output/file_name.rs @@ -226,7 +226,7 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> { let coconut = parent.components().count(); if coconut == 1 && parent.has_root() { - bits.push(self.colours.symlink_path().paint("/")); + bits.push(self.colours.symlink_path().paint(std::path::MAIN_SEPARATOR.to_string())); } else if coconut >= 1 { escape( @@ -235,12 +235,13 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> { self.colours.symlink_path(), self.colours.control_char(), ); - bits.push(self.colours.symlink_path().paint("/")); + bits.push(self.colours.symlink_path().paint(std::path::MAIN_SEPARATOR.to_string())); } } /// The character to be displayed after a file when classifying is on, if /// the file’s type has one associated with it. + #[cfg(unix)] fn classify_char(&self, file: &File<'_>) -> Option<&'static str> { if file.is_executable_file() { Some("*") @@ -262,6 +263,19 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> { } } + #[cfg(windows)] + fn classify_char(&self, file: &File<'_>) -> Option<&'static str> { + if file.is_directory() { + Some("/") + } + else if file.is_link() { + Some("@") + } + else { + None + } + } + /// Returns at least one ANSI-highlighted string representing this file’s /// name using the given set of colours. /// @@ -301,11 +315,16 @@ impl<'a, 'dir, C: Colours> FileName<'a, 'dir, C> { match self.file { f if f.is_directory() => self.colours.directory(), + #[cfg(unix)] f if f.is_executable_file() => self.colours.executable_file(), f if f.is_link() => self.colours.symlink(), + #[cfg(unix)] f if f.is_pipe() => self.colours.pipe(), + #[cfg(unix)] f if f.is_block_device() => self.colours.block_device(), + #[cfg(unix)] f if f.is_char_device() => self.colours.char_device(), + #[cfg(unix)] f if f.is_socket() => self.colours.socket(), f if ! f.is_file() => self.colours.special(), _ => self.colours.colour_file(self.file), diff --git a/src/output/render/mod.rs b/src/output/render/mod.rs index 7bb1189..cc3e128 100644 --- a/src/output/render/mod.rs +++ b/src/output/render/mod.rs @@ -7,7 +7,9 @@ pub use self::filetype::Colours as FiletypeColours; mod git; pub use self::git::Colours as GitColours; +#[cfg(unix)] mod groups; +#[cfg(unix)] pub use self::groups::Colours as GroupColours; mod inode; @@ -26,7 +28,9 @@ mod times; pub use self::times::Render as TimeRender; // times does too +#[cfg(unix)] mod users; +#[cfg(unix)] pub use self::users::Colours as UserColours; mod octal; diff --git a/src/output/render/permissions.rs b/src/output/render/permissions.rs index 2e23776..a2f83a5 100644 --- a/src/output/render/permissions.rs +++ b/src/output/render/permissions.rs @@ -6,6 +6,7 @@ use crate::output::render::FiletypeColours; impl f::PermissionsPlus { + #[cfg(unix)] pub fn render(&self, colours: &C) -> TextCell { let mut chars = vec![ self.file_type.render(colours) ]; chars.extend(self.permissions.render(colours, self.file_type.is_regular_file())); @@ -22,6 +23,17 @@ impl f::PermissionsPlus { contents: chars.into(), } } + + #[cfg(windows)] + pub fn render(&self, colours: &C) -> TextCell { + let mut chars = vec![ self.attributes.render_type(colours) ]; + chars.extend(self.attributes.render(colours)); + + TextCell { + width: DisplayWidth::from(chars.len()), + contents: chars.into(), + } + } } @@ -76,6 +88,33 @@ impl f::Permissions { } } +impl f::Attributes { + pub fn render(&self, colours: &C) -> Vec> { + let bit = |bit, chr: &'static str, style: Style| { + if bit { style.paint(chr) } + else { colours.dash().paint("-") } + }; + + vec![ + bit(self.archive, "a", colours.normal()), + bit(self.readonly, "r", colours.user_read()), + bit(self.hidden, "h", colours.special_user_file()), + bit(self.system, "s", colours.special_other()), + ] + } + + pub fn render_type(&self, colours: &C) -> ANSIString<'static> { + if self.reparse_point { + return colours.pipe().paint("l") + } + else if self.directory { + return colours.directory().paint("d") + } + else { + return colours.dash().paint("-") + } + } +} pub trait Colours { fn dash(&self) -> Style; diff --git a/src/output/table.rs b/src/output/table.rs index cf8b6cf..6bd1f8e 100644 --- a/src/output/table.rs +++ b/src/output/table.rs @@ -1,6 +1,7 @@ use std::cmp::max; use std::env; use std::ops::Deref; +#[cfg(unix)] use std::sync::{Mutex, MutexGuard}; use datetime::TimeZone; @@ -8,6 +9,7 @@ use zoneinfo_compiled::{CompiledData, Result as TZResult}; use lazy_static::lazy_static; use log::*; +#[cfg(unix)] use users::UsersCache; use crate::fs::{File, fields as f}; @@ -54,10 +56,12 @@ impl Columns { let mut columns = Vec::with_capacity(4); if self.inode { + #[cfg(unix)] columns.push(Column::Inode); } if self.octal { + #[cfg(unix)] columns.push(Column::Octal); } @@ -66,6 +70,7 @@ impl Columns { } if self.links { + #[cfg(unix)] columns.push(Column::HardLinks); } @@ -74,14 +79,17 @@ impl Columns { } if self.blocks { + #[cfg(unix)] columns.push(Column::Blocks); } if self.user { + #[cfg(unix)] columns.push(Column::User); } if self.group { + #[cfg(unix)] columns.push(Column::Group); } @@ -116,12 +124,18 @@ pub enum Column { Permissions, FileSize, Timestamp(TimeType), + #[cfg(unix)] Blocks, + #[cfg(unix)] User, + #[cfg(unix)] Group, + #[cfg(unix)] HardLinks, + #[cfg(unix)] Inode, GitStatus, + #[cfg(unix)] Octal, } @@ -136,6 +150,7 @@ pub enum Alignment { impl Column { /// Get the alignment this column should use. + #[cfg(unix)] pub fn alignment(self) -> Alignment { match self { Self::FileSize | @@ -147,19 +162,37 @@ impl Column { } } + #[cfg(windows)] + pub fn alignment(&self) -> Alignment { + match self { + Self::FileSize | + Self::GitStatus => Alignment::Right, + _ => Alignment::Left, + } + } + /// Get the text that should be printed at the top, when the user elects /// to have a header row printed. pub fn header(self) -> &'static str { match self { + #[cfg(unix)] Self::Permissions => "Permissions", + #[cfg(windows)] + Self::Permissions => "Mode", Self::FileSize => "Size", Self::Timestamp(t) => t.header(), + #[cfg(unix)] Self::Blocks => "Blocks", + #[cfg(unix)] Self::User => "User", + #[cfg(unix)] Self::Group => "Group", + #[cfg(unix)] Self::HardLinks => "Links", + #[cfg(unix)] Self::Inode => "inode", Self::GitStatus => "Git", + #[cfg(unix)] Self::Octal => "Octal", } } @@ -274,10 +307,12 @@ pub struct Environment { tz: Option, /// Mapping cache of user IDs to usernames. + #[cfg(unix)] users: Mutex, } impl Environment { + #[cfg(unix)] pub fn lock_users(&self) -> MutexGuard<'_, UsersCache> { self.users.lock().unwrap() } @@ -296,12 +331,14 @@ impl Environment { let numeric = locale::Numeric::load_user_locale() .unwrap_or_else(|_| locale::Numeric::english()); + #[cfg(unix)] let users = Mutex::new(UsersCache::new()); - Self { numeric, tz, users } + Self { numeric, tz, #[cfg(unix)] users } } } +#[cfg(unix)] fn determine_time_zone() -> TZResult { if let Ok(file) = env::var("TZ") { TimeZone::from_file({ @@ -322,6 +359,31 @@ fn determine_time_zone() -> TZResult { } } +#[cfg(windows)] +fn determine_time_zone() -> TZResult { + use datetime::zone::{FixedTimespan, FixedTimespanSet, StaticTimeZone, TimeZoneSource}; + use std::borrow::Cow; + + Ok(TimeZone(TimeZoneSource::Static(&StaticTimeZone { + name: "Unsupported", + fixed_timespans: FixedTimespanSet { + first: FixedTimespan { + offset: 0, + is_dst: false, + name: Cow::Borrowed("ZONE_A"), + }, + rest: &[( + 1206838800, + FixedTimespan { + offset: 3600, + is_dst: false, + name: Cow::Borrowed("ZONE_B"), + }, + )], + }, + }))) +} + lazy_static! { static ref ENVIRONMENT: Environment = Environment::load_all(); } @@ -388,11 +450,15 @@ impl<'a, 'f> Table<'a> { fn permissions_plus(&self, file: &File<'_>, xattrs: bool) -> f::PermissionsPlus { f::PermissionsPlus { file_type: file.type_char(), + #[cfg(unix)] permissions: file.permissions(), + #[cfg(windows)] + attributes: file.attributes(), xattrs, } } + #[cfg(unix)] fn octal_permissions(&self, file: &File<'_>) -> f::OctalPermissions { f::OctalPermissions { permissions: file.permissions(), @@ -407,24 +473,30 @@ impl<'a, 'f> Table<'a> { Column::FileSize => { file.size().render(self.theme, self.size_format, &self.env.numeric) } + #[cfg(unix)] Column::HardLinks => { file.links().render(self.theme, &self.env.numeric) } + #[cfg(unix)] Column::Inode => { file.inode().render(self.theme.ui.inode) } + #[cfg(unix)] Column::Blocks => { file.blocks().render(self.theme) } + #[cfg(unix)] Column::User => { file.user().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::GitStatus => { self.git_status(file).render(self.theme) } + #[cfg(unix)] Column::Octal => { self.octal_permissions(file).render(self.theme.ui.octal) } diff --git a/src/theme/mod.rs b/src/theme/mod.rs index 255f054..6f282d9 100644 --- a/src/theme/mod.rs +++ b/src/theme/mod.rs @@ -229,6 +229,7 @@ impl render::GitColours for Theme { fn conflicted(&self) -> Style { self.ui.git.conflicted } } +#[cfg(unix)] impl render::GroupColours for Theme { fn yours(&self) -> Style { self.ui.users.group_yours } fn not_yours(&self) -> Style { self.ui.users.group_not_yours } @@ -287,6 +288,7 @@ impl render::SizeColours for Theme { fn minor(&self) -> Style { self.ui.size.minor } } +#[cfg(unix)] impl render::UserColours for Theme { fn you(&self) -> Style { self.ui.users.user_you } fn someone_else(&self) -> Style { self.ui.users.user_someone_else }