Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows tilde expansion and multiple config / theme paths #975

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
configuration fields) to truncate user and group names if they exceed a certain number
of characters (disabled by default).
- Add support for `--literal` from [PanGan21](https://github.com/PanGan21)
- Add support for tilde (`~`) expansion on Windows from [Ofer Sadan](https://github.com/ofersadan85)
- Add support to search multiple paths for config file and theme files from [Ofer Sadan](https://github.com/ofersadan85):
- `$XDG_CONFIG_HOME/lsd` or `$HOME/.config/lsd` (in that order) on non-Windows platforms (these are usually the same)
- `%APPDATA%\lsd` or `%USERPROFILE%\.config\lsd` (in that order) on Windows
- Add support for both `config.yaml` and `config.yml` for the config file name from [Ofer Sadan](https://github.com/ofersadan85)

## [v1.0.0] - 2023-08-25

Expand Down
24 changes: 12 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ version_check = "0.9.*"

[dependencies]
crossterm = { version = "0.27.0", features = ["serde"] }
dirs = "4"
dirs = "5.0.1"
libc = "0.2.*"
human-sort = "0.2.2"
term_grid = "0.1.*"
Expand All @@ -37,7 +37,6 @@ unicode-width = "0.1.*"
lscolors = "0.15.0"
wild = "2.0"
globset = "0.4.*"
xdg = "2.1"
yaml-rust = "0.4.*"
serde = { version = "1.0", features = ["derive"] }
serde_yaml = "0.8"
Expand Down
19 changes: 12 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,16 +102,21 @@ Check [Config file content](#config-file-content) for details.

On non-Windows systems `lsd` follows the
[XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html)
convention for the location of the configuration file. The configuration dir
`lsd` uses is itself named `lsd`. In that directory it looks first for a file
called `config.yaml`.
For most people it should be enough to put their config file at
`~/.config/lsd/config.yaml`.
convention for the location of the configuration file. A `config.yaml` or `config.yml` file will be searched for in these locations, in order:

- `$XDG_CONFIG_HOME/lsd`
- `$HOME/.config/lsd`

On most systems these are mapped to the same location, which is `~/.config/lsd/config.yaml`.

### Windows

On Windows systems `lsd` only looks for the `config.yaml` files in one location:
`%APPDATA%\lsd\`
On Windows systems `lsd` searches for `config.yaml` or `config.yml` in the following locations, in order:

- `%APPDATA%\lsd`
- `%USERPROFILE%\.config\lsd`

These are usually something like `C:\Users\username\AppData\Roaming\lsd\config.yaml` and `C:\Users\username\.config\lsd\config.yaml` respectively.

### Custom

Expand Down
64 changes: 34 additions & 30 deletions src/config_file.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,6 @@
use std::fs;
use std::io;

const CONF_DIR: &str = "lsd";
const CONF_FILE_NAME: &str = "config.yaml";

/// A struct to hold an optional configuration items, and provides methods
/// around error handling in a config file.
#[derive(Eq, PartialEq, Debug, Deserialize)]
Expand Down Expand Up @@ -144,27 +141,6 @@
serde_yaml::from_str::<Self>(yaml)
}

/// This provides the path for a configuration file, according to the XDG_BASE_DIRS specification.
/// return None if error like PermissionDenied
#[cfg(not(windows))]
pub fn config_file_path() -> Option<PathBuf> {
use xdg::BaseDirectories;
match BaseDirectories::with_prefix(CONF_DIR) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this change, can we handle the cases where people change the XDG_CONFIG_HOME env?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

with this change, can we handle the cases where people change the XDG_CONFIG_HOME env?

The xdg::BseDirectories was never available on Windows, since xdg doesn't support Windows at all. See here

It is possible to just add more possible paths ourselves, now that this can support multiple paths

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And internally, dirs already uses this variable anyway, so we are using it too, see here

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is used in not windows, according to https://docs.rs/dirs/5.0.1/dirs/fn.config_dir.html, linux will remain the XDG env, but not macOS, it seems like a breaking change for macOS users?

Copy link
Contributor Author

@ofersadan85 ofersadan85 Jan 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@zwpaper hmm interesting, didn't think of mac :(
Looks like internally dirs also uses this, but I can't test or verify that on my own because I don't have a mac. I'll review the code better to see if that can be corrected

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it breaks macos: https://github.com/dirs-dev/dirs-rs/blob/main/src/mac.rs#L10C54-L10C54

xdg crate would check the env in macOS

I tested locally

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Understood, I'll find a solution to solve this problem and revert back to xdg on any OS that isn't Windows

Ok(p) => Some(p.get_config_home()),
Err(e) => {
print_error!("Can not open config file: {}.", e);
None
}
}
}

/// This provides the path for a configuration file, inside the %APPDATA% directory.
/// return None if error like PermissionDenied
#[cfg(windows)]
pub fn config_file_path() -> Option<PathBuf> {
dirs::config_dir().map(|x| x.join(CONF_DIR))
}

/// This expand the `~` in path to HOME dir
/// returns the origin one if no `~` found;
/// returns None if error happened when getting home dir
Expand All @@ -189,16 +165,44 @@
}
})
}

/// Config paths for non-Windows platforms will be read from
/// `$XDG_CONFIG_HOME/lsd` or `$HOME/.config/lsd`
/// (usually, those are the same) in that order.
/// The default paths for Windows will be read from
/// `%APPDATA%\lsd` or `%USERPROFILE%\.config\lsd` in that order.
/// This will apply both to the config file and the theme file.
pub fn config_paths() -> impl Iterator<Item = PathBuf> {
[
dirs::config_dir(),
dirs::home_dir().map(|h| h.join(".config")),
]
.iter()
.filter_map(|p| p.as_ref().map(|p| p.join("lsd")))
.collect::<Vec<_>>()
.into_iter()
}
}

impl Default for Config {
/// Try to find either config.yaml or config.yml in the config directories
/// and use the first one that is found. If none are found, or the parsing fails,
/// use the default from DEFAULT_CONFIG.
fn default() -> Self {
if let Some(p) = Self::config_file_path() {
if let Some(c) = Self::from_file(p.join(CONF_FILE_NAME)) {
return c;
}
}
Self::from_yaml(DEFAULT_CONFIG).unwrap()
Config::config_paths()
.find_map(|p| {
let yaml = p.join("config.yaml");
let yml = p.join("config.yml");
if yaml.is_file() {
Config::from_file(yaml)
} else if yml.is_file() {
Config::from_file(yml)

Check warning on line 199 in src/config_file.rs

View check run for this annotation

Codecov / codecov/patch

src/config_file.rs#L192-L199

Added lines #L192 - L199 were not covered by tests
} else {
None

Check warning on line 201 in src/config_file.rs

View check run for this annotation

Codecov / codecov/patch

src/config_file.rs#L201

Added line #L201 was not covered by tests
}
})
.or(Self::from_yaml(DEFAULT_CONFIG).ok())

Check warning on line 204 in src/config_file.rs

View check run for this annotation

Codecov / codecov/patch

src/config_file.rs#L204

Added line #L204 was not covered by tests
.expect("Failed to read both config file and default config")
}
}

Expand Down
5 changes: 5 additions & 0 deletions src/core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
use crate::flags::blocks::Block;
use crate::git_theme::GitTheme;
#[cfg(target_os = "windows")]
use crate::meta::windows_utils;
#[cfg(target_os = "windows")]
use terminal_size::terminal_size;

pub struct Core {
Expand Down Expand Up @@ -105,6 +107,9 @@
_ => 1,
};

#[cfg(target_os = "windows")]
let paths: Vec<PathBuf> = paths.into_iter().map(windows_utils::expand_home).collect();

Check warning on line 111 in src/core.rs

View check run for this annotation

Codecov / codecov/patch

src/core.rs#L111

Added line #L111 was not covered by tests

for path in paths {
let mut meta = match Meta::from_path(
&path,
Expand Down
1 change: 0 additions & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ extern crate terminal_size;
extern crate unicode_width;
extern crate url;
extern crate wild;
extern crate xdg;
extern crate yaml_rust;

#[cfg(unix)]
Expand Down
2 changes: 1 addition & 1 deletion src/meta/filetype.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ pub enum FileType {

impl FileType {
#[cfg(windows)]
const EXECUTABLE_EXTENSIONS: &[&'static str] = &["exe", "msi", "bat", "ps1"];
const EXECUTABLE_EXTENSIONS: &'static[&'static str] = &["exe", "msi", "bat", "ps1"];

#[cfg(unix)]
pub fn new(
Expand Down
2 changes: 1 addition & 1 deletion src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
mod symlink;

#[cfg(windows)]
mod windows_utils;
pub mod windows_utils;

pub use self::access_control::AccessControl;
pub use self::date::Date;
Expand All @@ -27,7 +27,7 @@
pub use self::permissions::Permissions;
pub use self::size::Size;
pub use self::symlink::SymLink;
pub use crate::icon::Icons;

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / MinSRV

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (macos-latest, aarch64-apple-darwin)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, i686-pc-windows-gnu)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (macos-latest, x86_64-apple-darwin)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (macos-latest, x86_64-apple-darwin)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, i686-unknown-linux-gnu, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, i686-unknown-linux-gnu, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-unknown-linux-gnu, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-unknown-linux-gnu, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-unknown-linux-musl, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, x86_64-unknown-linux-musl, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, arm-unknown-linux-gnueabihf, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, arm-unknown-linux-gnueabihf, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, i686-unknown-linux-musl, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, i686-unknown-linux-musl, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, aarch64-unknown-linux-musl, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, aarch64-unknown-linux-musl, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, aarch64-unknown-linux-gnu, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, aarch64-unknown-linux-gnu, use-cross)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-gnu)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, x86_64-pc-windows-msvc)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, i686-pc-windows-msvc)

unused import: `crate::icon::Icons`

Check warning on line 30 in src/meta/mod.rs

View workflow job for this annotation

GitHub Actions / Build (windows-latest, i686-pc-windows-msvc)

unused import: `crate::icon::Icons`

use crate::flags::{Display, Flags, Layout, PermissionFlag};
use crate::{print_error, ExitCode};
Expand Down
14 changes: 13 additions & 1 deletion src/meta/windows_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
use std::io;
use std::mem::MaybeUninit;
use std::os::windows::ffi::{OsStrExt, OsStringExt};
use std::path::Path;
use std::path::{Path, PathBuf};

use windows::Win32::Foundation::PSID;
use windows::Win32::Security::{self, Authorization::TRUSTEE_W, ACL};
Expand Down Expand Up @@ -343,6 +343,18 @@
)
}

/// Expands the `~` in a path to the current user's home directory
pub fn expand_home(path: PathBuf) -> PathBuf {
if path.starts_with("~") {
if let Some(home) = dirs::home_dir() {
let mut expanded = home.to_path_buf();

Check failure on line 350 in src/meta/windows_utils.rs

View workflow job for this annotation

GitHub Actions / Style (windows-latest)

redundant clone
expanded.push(path.strip_prefix("~").unwrap());
return expanded;
}
}
path
}

#[cfg(test)]
mod test {
use super::*;
Expand Down
19 changes: 13 additions & 6 deletions src/theme.rs
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,9 @@
}

impl Theme {
/// This read theme from file,
/// use the file path if it is absolute
/// prefix the config_file dir to it if it is not
/// Read theme from a file path
/// use the file path as-is if it is absolute
/// search the config paths folders for it if not
pub fn from_path<D>(file: &str) -> Result<D, Error>
where
D: DeserializeOwned + Default,
Expand All @@ -54,9 +54,16 @@
let path = if Path::new(&real).is_absolute() {
real
} else {
match config_file::Config::config_file_path() {
Some(p) => p.join(real),
None => return Err(Error::InvalidPath("config home not existed".into())),
let path = config_file::Config::config_paths()
.map(|p| p.join(real.clone()))
.find(|p| p.is_file());
match path {
Some(p) => p,
None => {

Check warning on line 62 in src/theme.rs

View check run for this annotation

Codecov / codecov/patch

src/theme.rs#L61-L62

Added lines #L61 - L62 were not covered by tests
return Err(Error::InvalidPath(
"Did not find theme file in config folders".into(),
))
}
}
};

Expand Down
Loading