From 978debe6ae862a083c7c7ff2d70a6ea5a08547ac Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Sat, 30 Jan 2021 21:31:10 +0100 Subject: [PATCH 1/3] Implement new --show-dirs command The command prints out the different directories (config, cache, etc) used by tealdeer. --- bash_tealdeer | 2 +- docs/src/config.md | 2 +- fish_tealdeer | 2 +- src/cache.rs | 2 +- src/main.rs | 42 +++++++++++++++++++++++++++++++++++++++--- src/usage.docopt | 3 ++- tests/lib.rs | 18 +++++++++++++++--- zsh_tealdeer | 2 +- 8 files changed, 61 insertions(+), 12 deletions(-) diff --git a/bash_tealdeer b/bash_tealdeer index 874356e..aadcc05 100644 --- a/bash_tealdeer +++ b/bash_tealdeer @@ -6,7 +6,7 @@ _tealdeer() _init_completion || return case $prev in - -h|--help|-v|--version|-l|--list|-u|--update|-c|--clear-cache|-p|--pager|-m|--markdown|--config-path|--seed-config|-q|--quiet) + -h|--help|-v|--version|-l|--list|-u|--update|-c|--clear-cache|-p|--pager|-m|--markdown|--show-paths|--seed-config|-q|--quiet) return ;; -f|--render) diff --git a/docs/src/config.md b/docs/src/config.md index 38e367b..79afcdf 100644 --- a/docs/src/config.md +++ b/docs/src/config.md @@ -8,7 +8,7 @@ the config file can be done manually or with the help of `tldr`: The configuration file path follows OS conventions. It can be queried with the following command: - $ tldr --config-path + $ tldr --show-paths On Linux, this will usually be `~/.config/tealdeer/config.toml`. diff --git a/fish_tealdeer b/fish_tealdeer index d571739..1aa95c5 100644 --- a/fish_tealdeer +++ b/fish_tealdeer @@ -13,7 +13,7 @@ complete -c tldr -s c -l clear-cache -d 'Clear the local cache.' -f complete -c tldr -s p -l pager -d 'Use a pager to page output.' -f complete -c tldr -s m -l markdown -d 'Display the raw markdown instead of rendering it.' -f complete -c tldr -s q -l quiet -d 'Suppress informational messages.' -f -complete -c tldr -l config-path -d 'Show config file path.' -f +complete -c tldr -l show-paths -d 'Show file and directory paths used by tealdeer.' -f complete -c tldr -l seed-config -d 'Create a basic config.' -f complete -c tldr -l color -d 'Controls when to use color.' -xa 'always auto never' diff --git a/src/cache.rs b/src/cache.rs index 0faddf4..802c06e 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -33,7 +33,7 @@ impl Cache { } /// Return the path to the cache directory. - fn get_cache_dir() -> Result { + pub fn get_cache_dir() -> Result { // Allow overriding the cache directory by setting the // $TEALDEER_CACHE_DIR env variable. if let Ok(value) = env::var("TEALDEER_CACHE_DIR") { diff --git a/src/main.rs b/src/main.rs index ef3e2aa..94d6506 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ mod tokenizer; mod types; use crate::cache::Cache; -use crate::config::{get_config_path, make_default_config, Config, MAX_CACHE_AGE}; +use crate::config::{get_config_dir, get_config_path, make_default_config, Config, MAX_CACHE_AGE}; use crate::dedup::Dedup; use crate::error::TealdeerError::{CacheError, ConfigError, UpdateError}; use crate::formatter::print_lines; @@ -73,6 +73,7 @@ struct Args { flag_clear_cache: bool, flag_pager: bool, flag_quiet: bool, + flag_show_paths: bool, flag_config_path: bool, flag_seed_config: bool, flag_markdown: bool, @@ -179,7 +180,7 @@ fn update_cache(cache: &Cache, quietly: bool) { } } -/// Show the config path +/// Show the config path (DEPRECATED) fn show_config_path() { match get_config_path() { Ok(config_file_path) => { @@ -196,6 +197,36 @@ fn show_config_path() { } } +/// Show file paths +fn show_paths() { + let config_dir = get_config_dir() + .map(|mut path| { + path.push(""); // Trailing path separator + path.to_str().unwrap_or("[Invalid]").to_string() + }) + .unwrap_or_else(|e| format!("[Error: {}]", e)); + let config_path = get_config_path() + .map(|path| path.to_str().unwrap_or("[Invalid]").to_string()) + .unwrap_or_else(|e| format!("[Error: {}]", e)); + let cache_dir = Cache::get_cache_dir() + .map(|mut path| { + path.push(""); // Trailing path separator + path.to_str().unwrap_or("[Invalid]").to_string() + }) + .unwrap_or_else(|e| format!("[Error: {}]", e)); + let pages_dir = Cache::get_cache_dir() + .map(|path| path.join("tldr-master")) + .map(|mut path| { + path.push(""); // Trailing path separator + path.to_str().unwrap_or("[Invalid]").to_string() + }) + .unwrap_or_else(|e| format!("[Error: {}]", e)); + println!("Config dir: {}", config_dir); + println!("Config path: {}", config_path); + println!("Cache dir: {}", cache_dir); + println!("Pages dir: {}", pages_dir); +} + /// Create seed config file and exit fn create_config_and_exit() { match make_default_config() { @@ -315,8 +346,12 @@ fn main() { // Show config file and path, pass through if args.flag_config_path { + eprintln!("Warning: The --config-path flag is deprecated, use --show-paths instead"); show_config_path(); } + if args.flag_show_paths { + show_paths(); + } // Create a basic config and exit if args.flag_seed_config { @@ -450,7 +485,8 @@ fn main() { } // Some flags can be run without a command. - if !(args.flag_update || args.flag_clear_cache || args.flag_config_path) { + if !(args.flag_update || args.flag_clear_cache || args.flag_config_path || args.flag_show_paths) + { eprintln!("{}", USAGE); process::exit(1); } diff --git a/src/usage.docopt b/src/usage.docopt index 4a70c86..9ba0968 100644 --- a/src/usage.docopt +++ b/src/usage.docopt @@ -16,7 +16,8 @@ Options: -p --pager Use a pager to page output -m --markdown Display the raw markdown instead of rendering it -q --quiet Suppress informational messages - --config-path Show config file path + --show-paths Show file and directory paths used by tealdeer + --config-path Show config file path (deprecated) --seed-config Create a basic config --color Control when to use color [always, auto, never] [default: auto] diff --git a/tests/lib.rs b/tests/lib.rs index f45e314..d880584 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -202,22 +202,34 @@ fn test_setup_seed_config() { } #[test] -fn test_show_config_path() { +fn test_show_paths() { let testenv = TestEnv::new(); testenv .command() - .args(&["--config-path"]) + .args(&["--show-paths"]) .assert() .success() .stdout(contains(format!( - "Config path is: {}", + "Config dir: {}", + testenv.config_dir.path().to_str().unwrap(), + ))) + .stdout(contains(format!( + "Config path: {}", testenv .config_dir .path() .join("config.toml") .to_str() .unwrap(), + ))) + .stdout(contains(format!( + "Cache dir: {}", + testenv.cache_dir.path().to_str().unwrap(), + ))) + .stdout(contains(format!( + "Pages dir: {}", + testenv.cache_dir.path().join("tldr-master").to_str().unwrap(), ))); } diff --git a/zsh_tealdeer b/zsh_tealdeer index 2c8e284..2f55a38 100644 --- a/zsh_tealdeer +++ b/zsh_tealdeer @@ -23,7 +23,7 @@ _tealdeer() { "($I -p --pager)"{-p,--pager}"[Use a pager to page output]" "($I -m --markdown)"{-m,--markdown}"[Display the raw markdown instead of rendering it]" "($I -q --quiet)"{-q,--quiet}"[Suppress informational messages]" - "($I)--config-path[Show config file path]" + "($I)--show-paths[Show file and directory paths used by tealdeer]" "($I)--seed-config[Create a basic config]" "($I)--color[Controls when to use color]:when:(( always From ca1527938658cd2abbd4b75a58509fc7f9a8541f Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Sat, 30 Jan 2021 21:55:28 +0100 Subject: [PATCH 2/3] Show reason for choice of a certain cache/config dir --- src/cache.rs | 19 ++++++++++--------- src/config.rs | 17 +++++++++-------- src/main.rs | 39 ++++++++++++++++++++++++--------------- src/types.rs | 27 ++++++++++++++++++++++++++- tests/lib.rs | 7 ++++++- 5 files changed, 75 insertions(+), 34 deletions(-) diff --git a/src/cache.rs b/src/cache.rs index 802c06e..961fd0c 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -13,7 +13,7 @@ use tar::Archive; use walkdir::{DirEntry, WalkDir}; use crate::error::TealdeerError::{self, CacheError, UpdateError}; -use crate::types::OsType; +use crate::types::{OsType, PathSource}; #[derive(Debug)] pub struct Cache { @@ -33,14 +33,14 @@ impl Cache { } /// Return the path to the cache directory. - pub fn get_cache_dir() -> Result { + pub fn get_cache_dir() -> Result<(PathBuf, PathSource), TealdeerError> { // Allow overriding the cache directory by setting the // $TEALDEER_CACHE_DIR env variable. if let Ok(value) = env::var("TEALDEER_CACHE_DIR") { let path = PathBuf::from(value); if path.exists() && path.is_dir() { - return Ok(path); + return Ok((path, PathSource::EnvVar)); } else { return Err(CacheError( "Path specified by $TEALDEER_CACHE_DIR \ @@ -52,7 +52,7 @@ impl Cache { // Otherwise, fall back to user cache directory. match get_app_root(AppDataType::UserCache, &crate::APP_INFO) { - Ok(dirs) => Ok(dirs), + Ok(dirs) => Ok((dirs, PathSource::OsConvention)), Err(_) => Err(CacheError( "Could not determine user cache directory.".into(), )), @@ -94,7 +94,7 @@ impl Cache { let mut archive = Self::decompress(&bytes[..]); // Determine paths - let cache_dir = Self::get_cache_dir()?; + let (cache_dir, _) = Self::get_cache_dir()?; // Make sure that cache directory exists debug!("Ensure cache directory {:?} exists", &cache_dir); @@ -119,7 +119,7 @@ impl Cache { /// Return the duration since the cache directory was last modified. pub fn last_update() -> Option { - if let Ok(cache_dir) = Self::get_cache_dir() { + if let Ok((cache_dir, _)) = Self::get_cache_dir() { if let Ok(metadata) = fs::metadata(cache_dir.join("tldr-master")) { if let Ok(mtime) = metadata.modified() { let now = SystemTime::now(); @@ -161,7 +161,7 @@ impl Cache { // Get cache dir let cache_dir = match Self::get_cache_dir() { - Ok(cache_dir) => cache_dir.join("tldr-master"), + Ok((cache_dir, _)) => cache_dir.join("tldr-master"), Err(e) => { log::error!("Could not get cache directory: {}", e); return None; @@ -194,7 +194,7 @@ impl Cache { /// Return the available pages. pub fn list_pages(&self) -> Result, TealdeerError> { // Determine platforms directory and platform - let cache_dir = Self::get_cache_dir()?; + let (cache_dir, _) = Self::get_cache_dir()?; let platforms_dir = cache_dir.join("tldr-master").join("pages"); let platform_dir = self.get_platform_dir(); @@ -242,8 +242,9 @@ impl Cache { } /// Delete the cache directory. + #[allow(clippy::map_err_ignore)] pub fn clear() -> Result<(), TealdeerError> { - let path = Self::get_cache_dir()?; + let (path, _) = Self::get_cache_dir()?; if path.exists() && path.is_dir() { fs::remove_dir_all(&path).map_err(|_| { CacheError(format!( diff --git a/src/config.rs b/src/config.rs index 302846f..447725b 100644 --- a/src/config.rs +++ b/src/config.rs @@ -10,6 +10,7 @@ use log::debug; use serde_derive::{Deserialize, Serialize}; use crate::error::TealdeerError::{self, ConfigError}; +use crate::types::PathSource; pub const CONFIG_FILE_NAME: &str = "config.toml"; pub const MAX_CACHE_AGE: Duration = Duration::from_secs(2_592_000); // 30 days @@ -234,7 +235,7 @@ impl Config { debug!("Loading config"); // Determine path - let config_file_path = get_config_path() + let (config_file_path, _) = get_config_path() .map_err(|e| ConfigError(format!("Could not determine config path: {}", e)))?; // Load raw config @@ -276,16 +277,16 @@ impl Config { /// /// Note that this function does not verify whether the directory at that /// location exists, or is a directory. -pub fn get_config_dir() -> Result { +pub fn get_config_dir() -> Result<(PathBuf, PathSource), TealdeerError> { // 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)); + return Ok((PathBuf::from(value), PathSource::EnvVar)); }; // Otherwise, fall back to the user config directory. match get_app_root(AppDataType::UserConfig, &crate::APP_INFO) { - Ok(dirs) => Ok(dirs), + Ok(dirs) => Ok((dirs, PathSource::OsConvention)), Err(_) => Err(ConfigError( "Could not determine the user config directory.".into(), )), @@ -296,15 +297,15 @@ pub fn get_config_dir() -> Result { /// /// Note that this function does not verify whether the file at that location /// exists, or is a file. -pub fn get_config_path() -> Result { - let config_dir = get_config_dir()?; +pub fn get_config_path() -> Result<(PathBuf, PathSource), TealdeerError> { + let (config_dir, source) = get_config_dir()?; let config_file_path = config_dir.join(CONFIG_FILE_NAME); - Ok(config_file_path) + Ok((config_file_path, source)) } /// Create default config file. pub fn make_default_config() -> Result { - let config_dir = get_config_dir()?; + let (config_dir, _) = get_config_dir()?; // Ensure that config directory exists if !config_dir.exists() { diff --git a/src/main.rs b/src/main.rs index 94d6506..b32acd7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -183,7 +183,7 @@ fn update_cache(cache: &Cache, quietly: bool) { /// Show the config path (DEPRECATED) fn show_config_path() { match get_config_path() { - Ok(config_file_path) => { + Ok((config_file_path, _)) => { println!("Config path is: {}", config_file_path.to_str().unwrap()); } Err(ConfigError(msg)) => { @@ -199,23 +199,32 @@ fn show_config_path() { /// Show file paths fn show_paths() { - let config_dir = get_config_dir() - .map(|mut path| { + let config_dir = get_config_dir().map_or_else( + |e| format!("[Error: {}]", e), + |(mut path, source)| { path.push(""); // Trailing path separator - path.to_str().unwrap_or("[Invalid]").to_string() - }) - .unwrap_or_else(|e| format!("[Error: {}]", e)); - let config_path = get_config_path() - .map(|path| path.to_str().unwrap_or("[Invalid]").to_string()) - .unwrap_or_else(|e| format!("[Error: {}]", e)); - let cache_dir = Cache::get_cache_dir() - .map(|mut path| { + match path.to_str() { + Some(path) => format!("{} ({})", path, source), + None => "[Invalid]".to_string(), + } + }, + ); + let config_path = get_config_path().map_or_else( + |e| format!("[Error: {}]", e), + |(path, _)| path.to_str().unwrap_or("[Invalid]").to_string(), + ); + let cache_dir = Cache::get_cache_dir().map_or_else( + |e| format!("[Error: {}]", e), + |(mut path, source)| { path.push(""); // Trailing path separator - path.to_str().unwrap_or("[Invalid]").to_string() - }) - .unwrap_or_else(|e| format!("[Error: {}]", e)); + match path.to_str() { + Some(path) => format!("{} ({})", path, source), + None => "[Invalid]".to_string(), + } + }, + ); let pages_dir = Cache::get_cache_dir() - .map(|path| path.join("tldr-master")) + .map(|(path, _)| path.join("tldr-master")) .map(|mut path| { path.push(""); // Trailing path separator path.to_str().unwrap_or("[Invalid]").to_string() diff --git a/src/types.rs b/src/types.rs index c0dfb27..c68dba8 100644 --- a/src/types.rs +++ b/src/types.rs @@ -1,4 +1,4 @@ -//! Types used in the client. +//! Shared types used in tealdeer. use std::fmt; @@ -101,6 +101,31 @@ impl LineType { } } +/// The reason why a certain path (e.g. config path or cache dir) was chosen. +#[derive(Debug, PartialEq)] +pub enum PathSource { + /// OS convention (e.g. XDG on Linux) + OsConvention, + /// Env variable (TEALDEER_*) + EnvVar, + /// Config file variable + ConfigVar, +} + +impl fmt::Display for PathSource { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + Self::OsConvention => "OS convention", + Self::EnvVar => "env variable", + Self::ConfigVar => "config file variable", + } + ) + } +} + #[cfg(test)] mod test { use super::LineType; diff --git a/tests/lib.rs b/tests/lib.rs index d880584..7f7ad4c 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -229,7 +229,12 @@ fn test_show_paths() { ))) .stdout(contains(format!( "Pages dir: {}", - testenv.cache_dir.path().join("tldr-master").to_str().unwrap(), + testenv + .cache_dir + .path() + .join("tldr-master") + .to_str() + .unwrap(), ))); } From cb57ac1d5d1df1b2fe77bcd884ff3d2255085eaa Mon Sep 17 00:00:00 2001 From: Danilo Bargen Date: Fri, 5 Feb 2021 00:01:33 +0100 Subject: [PATCH 3/3] Require Rust 1.41 (was 1.40) This is required for Result::map_or_else. Additionally, run clippy with 1.41 as well. Otherwise it reports suggestions that are not compatible with 1.41. --- .github/workflows/ci.yml | 4 ++-- src/cache.rs | 1 - src/main.rs | 1 - 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 184a33d..f1438a1 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,7 +15,7 @@ jobs: strategy: matrix: platform: [ubuntu-latest, macos-latest, windows-latest] - rust: [1.40.0, stable] + rust: [1.41.1, stable] runs-on: ${{ matrix.platform }} steps: - uses: actions/checkout@v2 @@ -45,7 +45,7 @@ jobs: - uses: actions/checkout@v2 - uses: actions-rs/toolchain@v1 with: - toolchain: 1.46.0 + toolchain: 1.41.1 components: clippy override: true - uses: actions-rs/clippy-check@v1 diff --git a/src/cache.rs b/src/cache.rs index 961fd0c..409e228 100644 --- a/src/cache.rs +++ b/src/cache.rs @@ -242,7 +242,6 @@ impl Cache { } /// Delete the cache directory. - #[allow(clippy::map_err_ignore)] pub fn clear() -> Result<(), TealdeerError> { let (path, _) = Self::get_cache_dir()?; if path.exists() && path.is_dir() { diff --git a/src/main.rs b/src/main.rs index b32acd7..4b43090 100644 --- a/src/main.rs +++ b/src/main.rs @@ -61,7 +61,6 @@ const ARCHIVE_URL: &str = "https://github.com/tldr-pages/tldr/archive/master.tar const PAGER_COMMAND: &str = "less -R"; #[derive(Debug, Deserialize)] -#[allow(clippy::struct_excessive_bools)] struct Args { arg_command: Option>, flag_help: bool,