mirror of
https://github.com/tealdeer-rs/tealdeer.git
synced 2026-08-09 09:49:10 +02:00
Support XDG directory spec on macOS by switching app_dirs2 to etcetera (#467)
Closes #311 Similar to how `bat` does it, we now have a global struct which holds all the queried system directories. We prefer the XDG directories, but use the "native" directories as a fallback. This should ensure that when MacOs users upgrade from an older version, their existing config and cache are still used. After deleting the old directories, the new ones should be used by tealdeer automatically.
This commit is contained in:
parent
28ed785001
commit
37b0dee39f
5 changed files with 83 additions and 75 deletions
2
.github/workflows/ci.yml
vendored
2
.github/workflows/ci.yml
vendored
|
|
@ -15,7 +15,7 @@ jobs:
|
|||
strategy:
|
||||
matrix:
|
||||
platform: [ubuntu-latest, macos-latest, windows-latest]
|
||||
toolchain: [stable, 1.85.0]
|
||||
toolchain: [stable, 1.87.0] # MSRV
|
||||
include:
|
||||
- platform: windows-latest
|
||||
exe_suffix: .exe
|
||||
|
|
|
|||
36
Cargo.lock
generated
36
Cargo.lock
generated
|
|
@ -73,18 +73,6 @@ version = "1.0.100"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "app_dirs2"
|
||||
version = "2.5.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a7e7b35733e3a8c1ccb90385088dd5b6eaa61325cb4d1ad56e683b5224ff352e"
|
||||
dependencies = [
|
||||
"jni",
|
||||
"ndk-context",
|
||||
"winapi",
|
||||
"xdg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
|
|
@ -380,6 +368,16 @@ dependencies = [
|
|||
"serde_json",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "etcetera"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"windows-sys 0.61.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "2.3.0"
|
||||
|
|
@ -635,12 +633,6 @@ dependencies = [
|
|||
"tempfile",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ndk-context"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b"
|
||||
|
||||
[[package]]
|
||||
name = "normalize-line-endings"
|
||||
version = "0.3.0"
|
||||
|
|
@ -1117,11 +1109,11 @@ name = "tealdeer"
|
|||
version = "1.8.1"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"app_dirs2",
|
||||
"assert_cmd",
|
||||
"clap",
|
||||
"env_logger",
|
||||
"escargot",
|
||||
"etcetera",
|
||||
"filetime",
|
||||
"log",
|
||||
"pager",
|
||||
|
|
@ -1634,12 +1626,6 @@ version = "0.46.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "xdg"
|
||||
version = "2.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "213b7324336b53d2414b2db8537e56544d981803139155afa84f76eeebb7a546"
|
||||
|
||||
[[package]]
|
||||
name = "yansi"
|
||||
version = "1.0.1"
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ repository = "https://github.com/tealdeer-rs/tealdeer/"
|
|||
documentation = "https://tealdeer-rs.github.io/tealdeer/"
|
||||
version = "1.8.1"
|
||||
include = ["/src/**/*", "/tests/**/*", "/Cargo.toml", "/README.md", "/LICENSE-*", "/screenshot.png", "completion/*"]
|
||||
rust-version = "1.85"
|
||||
rust-version = "1.87" # MSRV
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
|
|
@ -21,9 +21,9 @@ path = "src/main.rs"
|
|||
|
||||
[dependencies]
|
||||
anyhow = "1"
|
||||
app_dirs = { version = "2", package = "app_dirs2" }
|
||||
clap = { version = "4", features = ["std", "derive", "help", "usage", "cargo", "error-context", "color", "wrap_help"], default-features = false }
|
||||
env_logger = { version = "0.11", optional = true }
|
||||
etcetera = "0.11.0"
|
||||
log = "0.4"
|
||||
serde = "1.0.21"
|
||||
serde_derive = "1.0.21"
|
||||
|
|
|
|||
|
|
@ -8,8 +8,7 @@ use std::{
|
|||
time::Duration,
|
||||
};
|
||||
|
||||
use anyhow::{anyhow, bail, ensure, Context, Result};
|
||||
use app_dirs::{get_app_root, AppDataType};
|
||||
use anyhow::{anyhow, ensure, Context, Result};
|
||||
use clap::ValueEnum;
|
||||
use log::info;
|
||||
use serde::Serialize as _;
|
||||
|
|
@ -33,6 +32,50 @@ const SUPPORTED_TLS_BACKENDS: &[RawTlsBackend] = &[
|
|||
RawTlsBackend::RustlsWithNativeRoots,
|
||||
];
|
||||
|
||||
struct SystemDirectories {
|
||||
config: PathBuf,
|
||||
cache: PathBuf,
|
||||
data: PathBuf,
|
||||
}
|
||||
|
||||
impl SystemDirectories {
|
||||
fn discover() -> Result<Self> {
|
||||
use etcetera::{
|
||||
app_strategy::choose_native_strategy, choose_app_strategy, AppStrategy, AppStrategyArgs,
|
||||
};
|
||||
|
||||
let args = AppStrategyArgs {
|
||||
top_level_domain: String::new(),
|
||||
author: String::new(),
|
||||
app_name: crate::NAME.to_string(),
|
||||
};
|
||||
|
||||
// The app strategy prefers XDG on MacOs, whereas the native strategy returns paths which
|
||||
// are used by installed applications. On Linux and Windows, the strategies are the same.
|
||||
let app_dirs = choose_app_strategy(args.clone())?;
|
||||
let native_dirs = choose_native_strategy(args)?;
|
||||
|
||||
// We prefer the XDG paths, but before tealdeer 1.9, we used only the native paths on MacOs.
|
||||
// So if we find files in these locations, we keep using them.
|
||||
let fallback = |app_dir: PathBuf, native_dir: PathBuf| {
|
||||
if !app_dir.exists() && native_dir.exists() {
|
||||
native_dir
|
||||
} else {
|
||||
app_dir
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
config: fallback(app_dirs.config_dir(), native_dirs.config_dir()),
|
||||
cache: fallback(app_dirs.cache_dir(), native_dirs.cache_dir()),
|
||||
data: fallback(app_dirs.data_dir(), native_dirs.data_dir()),
|
||||
})
|
||||
}
|
||||
}
|
||||
static SYSTEM_DIRECTORIES: LazyLock<SystemDirectories> = LazyLock::new(|| {
|
||||
SystemDirectories::discover().expect("Failed to initialize system directories.")
|
||||
});
|
||||
|
||||
pub(crate) fn supported_tls_backends_string() -> String {
|
||||
SUPPORTED_TLS_BACKENDS
|
||||
.iter()
|
||||
|
|
@ -619,15 +662,11 @@ impl<'a> Config<'a> {
|
|||
path: resolved_path,
|
||||
source: PathSource::ConfigFile,
|
||||
}
|
||||
} else if let Ok(default_dir) = get_app_root(AppDataType::UserCache, &crate::APP_INFO) {
|
||||
// Otherwise, fall back to the default user cache directory.
|
||||
} else {
|
||||
PathWithSource {
|
||||
path: default_dir,
|
||||
path: SYSTEM_DIRECTORIES.cache.clone(),
|
||||
source: PathSource::OsConvention,
|
||||
}
|
||||
} else {
|
||||
// If everything fails, give up
|
||||
bail!("Could not determine user cache directory");
|
||||
};
|
||||
let custom_pages_dir = raw_config
|
||||
.directories
|
||||
|
|
@ -646,15 +685,11 @@ impl<'a> Config<'a> {
|
|||
})
|
||||
.transpose()?
|
||||
.or_else(|| {
|
||||
get_app_root(AppDataType::UserData, &crate::APP_INFO)
|
||||
.map(|path| {
|
||||
// Note: The `join("")` call ensures that there's a trailing slash
|
||||
PathWithSource {
|
||||
path: path.join("pages").join(""),
|
||||
source: PathSource::OsConvention,
|
||||
}
|
||||
})
|
||||
.ok()
|
||||
// Note: The `join("")` call ensures that there's a trailing slash
|
||||
Some(PathWithSource {
|
||||
path: SYSTEM_DIRECTORIES.data.join("pages").join(""),
|
||||
source: PathSource::OsConvention,
|
||||
})
|
||||
});
|
||||
let directories = DirectoriesConfig {
|
||||
cache_dir,
|
||||
|
|
@ -743,7 +778,7 @@ impl ConfigLoader {
|
|||
/// Create a loader that uses the default config file location. If no file is present at the default location, the
|
||||
/// default configuration is used.
|
||||
pub fn read_default_path() -> Result<Self> {
|
||||
let path = get_default_config_path().context("Could not determine default config path.")?;
|
||||
let path = get_default_config_path();
|
||||
Self::read_internal(path, true)
|
||||
}
|
||||
|
||||
|
|
@ -761,30 +796,24 @@ impl ConfigLoader {
|
|||
///
|
||||
/// Note that this function does not verify whether the directory at that
|
||||
/// location exists, or is a directory.
|
||||
pub fn get_config_dir() -> Result<(PathBuf, PathSource)> {
|
||||
pub fn get_config_dir() -> (PathBuf, PathSource) {
|
||||
// Allow overriding the config directory by setting the
|
||||
// $TEALDEER_CONFIG_DIR env variable.
|
||||
if let Ok(value) = env::var("TEALDEER_CONFIG_DIR") {
|
||||
return Ok((PathBuf::from(value), PathSource::EnvVar));
|
||||
return (PathBuf::from(value), PathSource::EnvVar);
|
||||
}
|
||||
|
||||
// Otherwise, fall back to the user config directory.
|
||||
let dirs = get_app_root(AppDataType::UserConfig, &crate::APP_INFO)
|
||||
.context("Failed to determine the user config directory")?;
|
||||
Ok((dirs, PathSource::OsConvention))
|
||||
(SYSTEM_DIRECTORIES.config.clone(), PathSource::OsConvention)
|
||||
}
|
||||
|
||||
/// Return the path to the config file.
|
||||
///
|
||||
/// Note that this function does not verify whether the file at that location
|
||||
/// exists, or is a file.
|
||||
pub fn get_default_config_path() -> Result<PathWithSource> {
|
||||
let (config_dir, source) = get_config_dir()?;
|
||||
let config_file_path = config_dir.join(CONFIG_FILE_NAME);
|
||||
Ok(PathWithSource {
|
||||
path: config_file_path,
|
||||
source,
|
||||
})
|
||||
pub fn get_default_config_path() -> PathWithSource {
|
||||
let (mut path, source) = get_config_dir();
|
||||
path.push(CONFIG_FILE_NAME);
|
||||
PathWithSource { path, source }
|
||||
}
|
||||
|
||||
/// Create default config file.
|
||||
|
|
@ -794,7 +823,7 @@ pub fn make_default_config(path: Option<&Path>) -> Result<PathBuf> {
|
|||
let config_file_path = if let Some(p) = path {
|
||||
p.into()
|
||||
} else {
|
||||
let (config_dir, _) = get_config_dir()?;
|
||||
let (config_dir, _) = get_config_dir();
|
||||
|
||||
// Ensure that config directory exists
|
||||
if config_dir.exists() {
|
||||
|
|
|
|||
23
src/main.rs
23
src/main.rs
|
|
@ -36,7 +36,6 @@ use std::{
|
|||
};
|
||||
|
||||
use anyhow::{anyhow, Context, Result};
|
||||
use app_dirs::AppInfo;
|
||||
use cache::{CacheConfig, TLDR_OLD_PAGES_DIR};
|
||||
use clap::Parser;
|
||||
use config::{ConfigLoader, Language, StyleConfig, TlsBackend};
|
||||
|
|
@ -65,10 +64,6 @@ use crate::{
|
|||
};
|
||||
|
||||
const NAME: &str = "tealdeer";
|
||||
const APP_INFO: AppInfo = AppInfo {
|
||||
name: NAME,
|
||||
author: NAME,
|
||||
};
|
||||
static TEALDEER_PAGE: &str =
|
||||
include_str!(concat!(env!("CARGO_MANIFEST_DIR"), "/pages/tealdeer.md"));
|
||||
|
||||
|
|
@ -110,16 +105,14 @@ fn update_cache(
|
|||
|
||||
/// Show file paths
|
||||
fn show_paths(config: &Config) {
|
||||
let config_dir = get_config_dir().map_or_else(
|
||||
|e| format!("[Error: {e}]"),
|
||||
|(mut path, source)| {
|
||||
path.push(""); // Trailing path separator
|
||||
match path.to_str() {
|
||||
Some(path) => format!("{path} ({source})"),
|
||||
None => "[Invalid]".to_string(),
|
||||
}
|
||||
},
|
||||
);
|
||||
let config_dir = {
|
||||
let (mut path, source) = get_config_dir();
|
||||
path.push(""); // Trailing path separator
|
||||
match path.to_str() {
|
||||
Some(path) => format!("{path} ({source})"),
|
||||
None => "[Invalid]".to_string(),
|
||||
}
|
||||
};
|
||||
let config_path = config.file_path.to_string();
|
||||
let cache_dir = config.directories.cache_dir.to_string();
|
||||
let pages_dir = {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue