Add search.platforms config option and search all platforms by default (#435)

This commit is contained in:
Niklas Mohrin 2025-10-03 20:15:34 +02:00 committed by GitHub
commit 911508ce33
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 122 additions and 28 deletions

View file

@ -12,3 +12,18 @@ Either way, the language used can be overwritten using the `--language` command
[search]
# Show pages in German if available, otherwise show in English
languages = ["de", "en"]
## `platforms`
The list of platforms that should be considered when searching.
In addition to the platforms listed in the help text of the `--platform` flag, there are two special platforms available:
- `"current"`: equals the platform that tealdeer was compiled for
- `"all"`: adds all remaining platforms to the list
Tealdeer searches the platforms in order of appearance in this list.
The default list of platforms is `["current", "common", "all"]`.
The list of platforms can be overwritten using the `--platform` command line flag.
[search]
# Search for linux and common, and then search windows before trying the remaining platforms
platforms = ["linux", "common", "windows", "all"]

View file

@ -9,11 +9,15 @@ use std::{
use anyhow::{anyhow, bail, ensure, Context, Result};
use app_dirs::{get_app_root, AppDataType};
use clap::ValueEnum;
use serde::Serialize as _;
use serde_derive::{Deserialize, Serialize};
use yansi::{Color, Style};
use crate::{extensions::Dedup as _, types::PathSource};
use crate::{
extensions::Dedup as _,
types::{PathSource, PlatformType},
};
pub const CONFIG_FILE_NAME: &str = "config.toml";
pub const MAX_CACHE_AGE: Duration = Duration::from_secs(2_592_000); // 30 days
@ -217,9 +221,61 @@ struct RawDirectoriesConfig {
pub custom_pages_dir: Option<PathBuf>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
enum RawPlatformType {
Current,
All,
MacOs, // alias for Platform(PlatformType::OsX)
#[serde(untagged)]
Platform(PlatformType),
}
impl RawPlatformType {
pub fn flatten(raw_platforms: impl IntoIterator<Item = Self>) -> Vec<PlatformType> {
let mut flattened = Vec::new();
for raw_platform in raw_platforms {
match raw_platform {
RawPlatformType::Current => flattened.push(PlatformType::current()),
RawPlatformType::Platform(platform) => flattened.push(platform),
RawPlatformType::MacOs => flattened.push(PlatformType::OsX),
RawPlatformType::All => flattened.extend(PlatformType::value_variants()),
}
}
flattened.clear_duplicates();
flattened
}
}
#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq)]
struct RawSearchConfig {
pub languages: Option<Vec<String>>,
pub platforms: Option<Vec<RawPlatformType>>,
}
impl<'a> From<&'a RawSearchConfig> for SearchConfig<'a> {
fn from(raw_search_config: &'a RawSearchConfig) -> Self {
let languages = raw_search_config
.languages
.as_ref()
.map_or_else(get_languages_from_env, |langs| {
langs.iter().map(|lang| Language(lang)).collect()
});
let platforms = if let Some(raw_platforms) = raw_search_config.platforms.as_ref() {
RawPlatformType::flatten(raw_platforms.iter().copied())
} else {
RawPlatformType::flatten([
RawPlatformType::Current,
RawPlatformType::Platform(PlatformType::Common),
RawPlatformType::All,
])
};
Self {
languages,
platforms,
}
}
}
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
@ -305,6 +361,7 @@ pub struct DirectoriesConfig {
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct SearchConfig<'a> {
pub languages: Vec<Language<'a>>,
pub platforms: Vec<PlatformType>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
@ -427,16 +484,7 @@ impl<'a> Config<'a> {
fn from_raw(raw_config: &'a RawConfig, config_file_path: PathWithSource) -> Result<Self> {
let style = (&raw_config.style).into();
let display = (&raw_config.display).into();
let search = SearchConfig {
languages: raw_config
.search
.languages
.as_ref()
.map_or_else(get_languages_from_env, |langs| {
langs.iter().map(|lang| Language(lang)).collect()
}),
};
let search: SearchConfig<'a> = (&raw_config.search).into();
let updates = UpdatesConfig {
auto_update: raw_config.updates.auto_update,

View file

@ -39,6 +39,7 @@ use cache::{CacheConfig, TLDR_OLD_PAGES_DIR};
use clap::Parser;
use config::{ConfigLoader, Language, StyleConfig, TlsBackend};
use log::debug;
use types::PlatformType;
mod cache;
mod cli;
@ -55,7 +56,7 @@ use crate::{
cli::Cli,
config::{get_config_dir, make_default_config, Config, PathWithSource},
output::print_page,
types::{ColorOptions, PlatformType},
types::ColorOptions,
utils::{print_error, print_warning},
};
@ -259,7 +260,13 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
return Ok(ExitCode::SUCCESS);
}
let platforms = compute_platforms(args.platforms.as_ref());
if let Some(platforms) = args.platforms {
config.search.platforms = platforms;
if !config.search.platforms.contains(&PlatformType::Common) {
config.search.platforms.push(PlatformType::Common);
}
}
let (search_languages, download_languages): (&[_], &[_]) = match args.language.as_deref() {
Some(lang) => (&[Language(lang)], &[Language(lang)]),
None => (&config.search.languages, &config.updates.download_languages),
@ -272,7 +279,7 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
.custom_pages_dir
.as_ref()
.map(PathWithSource::path),
platforms: &platforms,
platforms: &config.search.platforms,
search_languages,
download_languages,
};
@ -394,17 +401,3 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
Ok(ExitCode::SUCCESS)
}
/// Returns the passed or default platform types and appends `PlatformType::Common` as fallback.
fn compute_platforms(platforms: Option<&Vec<PlatformType>>) -> Vec<PlatformType> {
match platforms {
Some(p) => {
let mut result = p.clone();
if !result.contains(&PlatformType::Common) {
result.push(PlatformType::Common);
}
result
}
None => vec![PlatformType::current(), PlatformType::Common],
}
}

View file

@ -715,6 +715,36 @@ fn test_os_specific_page() {
.success();
}
#[test]
fn test_config_platforms() {
let testenv = TestEnv::new();
testenv.add_os_entry("sunos", "sunos-command", "");
let set_config_platforms = |platforms| {
testenv.delete_config();
testenv.init_config();
testenv.append_to_config(format!("search.platforms = {platforms}"));
};
// By default all platforms are searched
testenv.command().arg("sunos-command").assert().success();
set_config_platforms("[]");
testenv.command().arg("sunos-command").assert().failure();
set_config_platforms("['linux']");
testenv.command().arg("sunos-command").assert().failure();
set_config_platforms("['sunos']");
testenv.command().arg("sunos-command").assert().success();
set_config_platforms("['linux', 'all']");
testenv.command().arg("sunos-command").assert().success();
set_config_platforms("['current', 'all']");
testenv.command().arg("sunos-command").assert().success();
}
#[test]
fn test_markdown_rendering() {
let testenv = TestEnv::new().install_default_cache();
@ -956,6 +986,14 @@ fn test_macos_is_alias_for_osx() {
.args(["--platform", "osx", "--list"])
.assert()
.stdout("maconly\n");
testenv.append_to_config("search.platforms = ['osx']\n");
testenv.command().arg("--list").assert().stdout("maconly\n");
testenv.delete_config();
testenv.init_config();
testenv.append_to_config("search.platforms = ['macos']\n");
testenv.command().arg("--list").assert().stdout("maconly\n");
}
#[test]