mirror of
https://github.com/tealdeer-rs/tealdeer.git
synced 2026-08-09 09:49:10 +02:00
Introduce Language struct (#425)
* Remove unneeded Clone bound on Dedup * Introduce Language struct to represent language strings * Move language directory name logic into own method on Language type
This commit is contained in:
parent
43ab2cb920
commit
d1be7d6bb9
3 changed files with 65 additions and 35 deletions
26
src/cache.rs
26
src/cache.rs
|
|
@ -18,6 +18,19 @@ use crate::{config::TlsBackend, types::PlatformType, utils::print_warning};
|
||||||
pub static TLDR_PAGES_DIR: &str = "tldr-pages";
|
pub static TLDR_PAGES_DIR: &str = "tldr-pages";
|
||||||
static TLDR_OLD_PAGES_DIR: &str = "tldr-master";
|
static TLDR_OLD_PAGES_DIR: &str = "tldr-master";
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq, Hash)]
|
||||||
|
pub struct Language<'a>(pub &'a str);
|
||||||
|
|
||||||
|
impl Language<'_> {
|
||||||
|
fn directory_name(&self) -> String {
|
||||||
|
if *self == Language("en") {
|
||||||
|
String::from("pages")
|
||||||
|
} else {
|
||||||
|
format!("pages.{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Cache {
|
pub struct Cache {
|
||||||
cache_dir: PathBuf,
|
cache_dir: PathBuf,
|
||||||
|
|
@ -227,7 +240,7 @@ impl Cache {
|
||||||
pub fn find_page(
|
pub fn find_page(
|
||||||
&self,
|
&self,
|
||||||
name: &str,
|
name: &str,
|
||||||
languages: &[String],
|
languages: &[Language<'_>],
|
||||||
custom_pages_dir: Option<&Path>,
|
custom_pages_dir: Option<&Path>,
|
||||||
platforms: &[PlatformType],
|
platforms: &[PlatformType],
|
||||||
) -> Option<PageLookupResult> {
|
) -> Option<PageLookupResult> {
|
||||||
|
|
@ -237,16 +250,7 @@ impl Cache {
|
||||||
|
|
||||||
// Determine directory paths
|
// Determine directory paths
|
||||||
let pages_dir = self.pages_dir();
|
let pages_dir = self.pages_dir();
|
||||||
let lang_dirs: Vec<String> = languages
|
let lang_dirs: Vec<String> = languages.iter().map(Language::directory_name).collect();
|
||||||
.iter()
|
|
||||||
.map(|lang| {
|
|
||||||
if lang == "en" {
|
|
||||||
String::from("pages")
|
|
||||||
} else {
|
|
||||||
format!("pages.{lang}")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
// Look up custom page (<name>.page.md). If it exists, return it directly
|
// Look up custom page (<name>.page.md). If it exists, return it directly
|
||||||
if let Some(config_dir) = custom_pages_dir {
|
if let Some(config_dir) = custom_pages_dir {
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,14 @@
|
||||||
use std::mem;
|
use std::mem;
|
||||||
|
|
||||||
/// An extension trait to clear duplicates from a collection.
|
/// An extension trait to clear duplicates from a collection.
|
||||||
pub(crate) trait Dedup<T: PartialEq + Clone> {
|
pub(crate) trait Dedup<T: PartialEq> {
|
||||||
fn clear_duplicates(&mut self);
|
fn clear_duplicates(&mut self);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Clear duplicates from a collection, keep the first one seen.
|
/// Clear duplicates from a collection, keep the first one seen.
|
||||||
///
|
///
|
||||||
/// For small vectors, this will be faster than a `HashSet`.
|
/// For small vectors, this will be faster than a `HashSet`.
|
||||||
impl<T: PartialEq + Clone> Dedup<T> for Vec<T> {
|
impl<T: PartialEq> Dedup<T> for Vec<T> {
|
||||||
fn clear_duplicates(&mut self) {
|
fn clear_duplicates(&mut self) {
|
||||||
let orig = mem::replace(self, Vec::with_capacity(self.len()));
|
let orig = mem::replace(self, Vec::with_capacity(self.len()));
|
||||||
for item in orig {
|
for item in orig {
|
||||||
|
|
|
||||||
70
src/main.rs
70
src/main.rs
|
|
@ -31,10 +31,12 @@ use std::{
|
||||||
io::{self, IsTerminal},
|
io::{self, IsTerminal},
|
||||||
path::Path,
|
path::Path,
|
||||||
process::{Command, ExitCode},
|
process::{Command, ExitCode},
|
||||||
|
sync::LazyLock,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{anyhow, Context, Result};
|
||||||
use app_dirs::AppInfo;
|
use app_dirs::AppInfo;
|
||||||
|
use cache::Language;
|
||||||
use clap::Parser;
|
use clap::Parser;
|
||||||
use config::StyleConfig;
|
use config::StyleConfig;
|
||||||
use log::debug;
|
use log::debug;
|
||||||
|
|
@ -191,14 +193,16 @@ fn init_log() {
|
||||||
#[cfg(not(feature = "logging"))]
|
#[cfg(not(feature = "logging"))]
|
||||||
fn init_log() {}
|
fn init_log() {}
|
||||||
|
|
||||||
fn get_languages(env_lang: Option<&str>, env_language: Option<&str>) -> Vec<String> {
|
fn get_languages<'a>(
|
||||||
|
env_lang: Option<&'a str>,
|
||||||
|
env_language: Option<&'a str>,
|
||||||
|
) -> Vec<Language<'a>> {
|
||||||
// Language list according to
|
// Language list according to
|
||||||
// https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#language
|
// https://github.com/tldr-pages/tldr/blob/main/CLIENT-SPECIFICATION.md#language
|
||||||
|
|
||||||
if env_lang.is_none() {
|
let Some(env_lang) = env_lang else {
|
||||||
return vec!["en".to_string()];
|
return vec![Language("en")];
|
||||||
}
|
};
|
||||||
let env_lang = env_lang.unwrap();
|
|
||||||
|
|
||||||
// Create an iterator that contains $LANGUAGE (':' separated list) followed by $LANG (single language)
|
// Create an iterator that contains $LANGUAGE (':' separated list) followed by $LANG (single language)
|
||||||
let locales = env_language.unwrap_or("").split(':').chain([env_lang]);
|
let locales = env_language.unwrap_or("").split(':').chain([env_lang]);
|
||||||
|
|
@ -207,23 +211,25 @@ fn get_languages(env_lang: Option<&str>, env_language: Option<&str>) -> Vec<Stri
|
||||||
for locale in locales {
|
for locale in locales {
|
||||||
// Language plus country code (e.g. `en_US`)
|
// Language plus country code (e.g. `en_US`)
|
||||||
if locale.len() >= 5 && locale.chars().nth(2) == Some('_') {
|
if locale.len() >= 5 && locale.chars().nth(2) == Some('_') {
|
||||||
lang_list.push(&locale[..5]);
|
lang_list.push(Language(&locale[..5]));
|
||||||
}
|
}
|
||||||
// Language code only (e.g. `en`)
|
// Language code only (e.g. `en`)
|
||||||
if locale.len() >= 2 && locale != "POSIX" {
|
if locale.len() >= 2 && locale != "POSIX" {
|
||||||
lang_list.push(&locale[..2]);
|
lang_list.push(Language(&locale[..2]));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
lang_list.push("en");
|
lang_list.push(Language("en"));
|
||||||
lang_list.clear_duplicates();
|
lang_list.clear_duplicates();
|
||||||
lang_list.into_iter().map(str::to_string).collect()
|
lang_list
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_languages_from_env() -> Vec<String> {
|
fn get_languages_from_env<'a>() -> Vec<Language<'a>> {
|
||||||
|
static LANG: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("LANG").ok());
|
||||||
|
static LANGUAGE: LazyLock<Option<String>> = LazyLock::new(|| std::env::var("LANGUAGE").ok());
|
||||||
get_languages(
|
get_languages(
|
||||||
std::env::var("LANG").ok().as_deref(),
|
LANG.as_ref().map(String::as_str),
|
||||||
std::env::var("LANGUAGE").ok().as_deref(),
|
LANGUAGE.as_ref().map(String::as_str),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -372,7 +378,8 @@ fn try_main(args: Cli, enable_styles: bool) -> Result<ExitCode> {
|
||||||
// Collect languages
|
// Collect languages
|
||||||
let languages = args
|
let languages = args
|
||||||
.language
|
.language
|
||||||
.map_or_else(get_languages_from_env, |lang| vec![lang]);
|
.as_deref()
|
||||||
|
.map_or_else(get_languages_from_env, |lang| vec![Language(lang)]);
|
||||||
|
|
||||||
// Search for command in cache
|
// Search for command in cache
|
||||||
let Some(lookup_result) = cache.find_page(
|
let Some(lookup_result) = cache.find_page(
|
||||||
|
|
@ -422,7 +429,7 @@ fn compute_platforms(platforms: Option<&Vec<PlatformType>>) -> Vec<PlatformType>
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod test {
|
mod test {
|
||||||
use crate::get_languages;
|
use super::*;
|
||||||
|
|
||||||
mod language {
|
mod language {
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
@ -430,41 +437,60 @@ mod test {
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_lang_env() {
|
fn missing_lang_env() {
|
||||||
let lang_list = get_languages(None, Some("de:fr"));
|
let lang_list = get_languages(None, Some("de:fr"));
|
||||||
assert_eq!(lang_list, ["en"]);
|
assert_eq!(lang_list, [Language("en")]);
|
||||||
let lang_list = get_languages(None, None);
|
let lang_list = get_languages(None, None);
|
||||||
assert_eq!(lang_list, ["en"]);
|
assert_eq!(lang_list, [Language("en")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn missing_language_env() {
|
fn missing_language_env() {
|
||||||
let lang_list = get_languages(Some("de"), None);
|
let lang_list = get_languages(Some("de"), None);
|
||||||
assert_eq!(lang_list, ["de", "en"]);
|
assert_eq!(lang_list, [Language("de"), Language("en")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn preference_order() {
|
fn preference_order() {
|
||||||
let lang_list = get_languages(Some("de"), Some("fr:cn"));
|
let lang_list = get_languages(Some("de"), Some("fr:cn"));
|
||||||
assert_eq!(lang_list, ["fr", "cn", "de", "en"]);
|
assert_eq!(
|
||||||
|
lang_list,
|
||||||
|
[
|
||||||
|
Language("fr"),
|
||||||
|
Language("cn"),
|
||||||
|
Language("de"),
|
||||||
|
Language("en")
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn country_code_expansion() {
|
fn country_code_expansion() {
|
||||||
let lang_list = get_languages(Some("pt_BR"), None);
|
let lang_list = get_languages(Some("pt_BR"), None);
|
||||||
assert_eq!(lang_list, ["pt_BR", "pt", "en"]);
|
assert_eq!(
|
||||||
|
lang_list,
|
||||||
|
[Language("pt_BR"), Language("pt"), Language("en")]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn ignore_posix_and_c() {
|
fn ignore_posix_and_c() {
|
||||||
let lang_list = get_languages(Some("POSIX"), None);
|
let lang_list = get_languages(Some("POSIX"), None);
|
||||||
assert_eq!(lang_list, ["en"]);
|
assert_eq!(lang_list, [Language("en")]);
|
||||||
let lang_list = get_languages(Some("C"), None);
|
let lang_list = get_languages(Some("C"), None);
|
||||||
assert_eq!(lang_list, ["en"]);
|
assert_eq!(lang_list, [Language("en")]);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn no_duplicates() {
|
fn no_duplicates() {
|
||||||
let lang_list = get_languages(Some("de"), Some("fr:de:cn:de"));
|
let lang_list = get_languages(Some("de"), Some("fr:de:cn:de"));
|
||||||
assert_eq!(lang_list, ["fr", "de", "cn", "en"]);
|
assert_eq!(
|
||||||
|
lang_list,
|
||||||
|
[
|
||||||
|
Language("fr"),
|
||||||
|
Language("de"),
|
||||||
|
Language("cn"),
|
||||||
|
Language("en")
|
||||||
|
]
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue