diff --git a/packages/pagetop-hljs/src/lang.rs b/packages/pagetop-hljs/src/lang.rs index 0a3d4579..eb28dedb 100644 --- a/packages/pagetop-hljs/src/lang.rs +++ b/packages/pagetop-hljs/src/lang.rs @@ -122,7 +122,7 @@ pub enum HljsLang { static HLJS_LANGS: LazyLock> = LazyLock::new(|| { use HljsLang::*; - kv![ + hm![ // Common languages. Bash => "bash", C => "c", diff --git a/packages/pagetop-hljs/src/theme.rs b/packages/pagetop-hljs/src/theme.rs index 7c3b7881..d8a65e2f 100644 --- a/packages/pagetop-hljs/src/theme.rs +++ b/packages/pagetop-hljs/src/theme.rs @@ -121,7 +121,7 @@ pub enum HljsTheme { static HLJS_THEMES: LazyLock> = LazyLock::new(|| { use HljsTheme::*; - kv![ + hm![ A11yDark => "a11y-dark", A11yLight => "a11y-light", Agate => "agate", diff --git a/pagetop/src/locale.rs b/pagetop/src/locale.rs index 6a935e68..7c8a9e3c 100644 --- a/pagetop/src/locale.rs +++ b/pagetop/src/locale.rs @@ -85,7 +85,7 @@ //! ``` use crate::html::{Markup, PreEscaped}; -use crate::{global, kv, AutoDefault}; +use crate::{global, hm, AutoDefault}; pub use fluent_templates; pub use unic_langid::{CharacterDirection, LanguageIdentifier}; @@ -103,7 +103,7 @@ use std::fmt; /// A mapping between language codes (e.g., "en-US") and their corresponding [`LanguageIdentifier`] /// and locale key names. static LANGUAGES: LazyLock> = LazyLock::new(|| { - kv![ + hm![ "en" => ( langid!("en-US"), "english" ), "en-GB" => ( langid!("en-GB"), "english_british" ), "en-US" => ( langid!("en-US"), "english_united_states" ), diff --git a/pagetop/src/prelude.rs b/pagetop/src/prelude.rs index 5418fe02..8f4de49f 100644 --- a/pagetop/src/prelude.rs +++ b/pagetop/src/prelude.rs @@ -9,7 +9,7 @@ pub use crate::{AutoDefault, ComponentClasses, StaticResources, UniqueId, Weight // MACROS. // crate::util -pub use crate::{join_string, kv}; +pub use crate::{hm, join_string, option_string, trio_string}; // crate::config pub use crate::include_config; // crate::locale diff --git a/pagetop/src/util.rs b/pagetop/src/util.rs index 2dc56a98..c113214a 100644 --- a/pagetop/src/util.rs +++ b/pagetop/src/util.rs @@ -146,30 +146,17 @@ pub use paste::paste; #[doc(hidden)] pub use concat_string::concat_string; -/// Concatena varios fragmentos de cadenas en una cadena *String*. -/// -/// Exporta la macro [`concat_string!`](https://docs.rs/concat-string), que permite concatenar de -/// forma eficiente fragmentos de cadenas (*string slices*) en una cadena *String*. Acepta cualquier -/// número de argumentos que implementen `AsRef` y crea una cadena `String` con el tamaño -/// adecuado, sin requerir cadenas de formato que puedan sobrecargar el rendimiento. #[macro_export] -macro_rules! join_string { - ($($arg:tt)*) => { - $crate::util::concat_string!($($arg)*) - }; -} - -#[macro_export] -/// Macro para construir grupos de pares clave-valor. +/// Macro para construir una colección de pares clave-valor. /// /// ```rust#ignore -/// let args = kv![ +/// let args = hm![ /// "userName" => "Roberto", /// "photoCount" => 3, /// "userGender" => "male", /// ]; /// ``` -macro_rules! kv { +macro_rules! hm { ( $($key:expr => $value:expr),* $(,)? ) => {{ let mut a = std::collections::HashMap::new(); $( @@ -178,3 +165,115 @@ macro_rules! kv { a }}; } + +/// Concatena varios fragmentos de cadenas (*string slices*) en una cadena *String*. +/// +/// Exporta la macro [`concat_string!`](https://docs.rs/concat-string), que permite concatenar de +/// forma eficiente fragmentos de cadenas en una cadena *String*. Acepta cualquier número de +/// argumentos que implementen `AsRef` y crea una cadena `String` con el tamaño adecuado, sin +/// requerir cadenas de formato que puedan sobrecargar el rendimiento. +/// +/// # Ejemplo +/// +/// ``` +/// // Concatena todos los fragmentos directamente. +/// let result = join_string!("Hello", " ", "World"); +/// assert_eq!(result, "Hello World".to_string()); +/// +/// // También funciona con valores vacíos. +/// let result_with_empty = join_string!("Hello", "", "World"); +/// assert_eq!(result_with_empty, "HelloWorld".to_string()); +/// +/// // Un único fragmento devuelve el mismo valor. +/// let single_result = join_string!("Hello"); +/// assert_eq!(single_result, "Hello".to_string()); +/// ``` +#[macro_export] +macro_rules! join_string { + ($($arg:tt)*) => { + $crate::util::concat_string!($($arg)*) + }; +} + +/// Concatena varios fragmentos de cadenas (*string slices*) en una cadena *String* utilizando +/// opcionalmente un separador. +/// +/// Crea una cadena que contiene los fragmentos no vacíos concatenados. La macro puede utilizar un +/// separador explícito o concatenar directamente los fragmentos sin un separador. Acepta cualquier +/// número de argumentos que implementen `AsRef`. +/// +/// Si todos los fragmentos son cadenas vacías, devuelve `None`. +/// +/// # Ejemplo +/// +/// ``` +/// // Concatena los fragmentos no vacíos con un espacio como separador. +/// let result_with_separator = option_string!(["Hello", "", "World"]; " "); +/// assert_eq!(result_with_separator, Some("Hello World".to_string())); +/// +/// // Concatena los fragmentos no vacíos sin un separador. +/// let result_without_separator = option_string!(["Hello", "", "World"]); +/// assert_eq!(result_without_separator, Some("HelloWorld".to_string())); +/// +/// // Devuelve `None` si todos los fragmentos están vacíos. +/// let result_empty = option_string!(["", "", ""]); +/// assert_eq!(result_empty, None); +/// ``` +#[macro_export] +macro_rules! option_string { + ([$($arg:expr),* $(,)?]) => {{ + let s = $crate::util::concat_string!($($arg),*); + (!s.is_empty()).then_some(s) + }}; + ([$($arg:expr),* $(,)?]; $separator:expr) => {{ + let s = [$($arg),*] + .iter() + .filter(|&item| !item.is_empty()) + .cloned() + .collect::>() + .join($separator); + (!s.is_empty()).then_some(s) + }}; +} + +/// Concatena dos fragmentos de cadenas (*string slices*) en una cadena *String* con un separador. +/// +/// Concatena los dos fragmentos que implementen `AsRef` usando el separador proporcionado, +/// pero devuelve directamente el primer fragmento si el segundo está vacío, o el segundo fragmento +/// si el primero está vacío. +/// +/// # Ejemplo +/// +/// ``` +/// let first = "Hello"; +/// let separator = "-"; +/// let second = "World"; +/// +/// // Concatena los dos fragmentos cuando ambos no están vacíos. +/// let result = trio_string!(first, separator, second); +/// assert_eq!(result, "Hello-World".to_string()); +/// +/// // Si el primer fragmento está vacío, devuelve el segundo. +/// let result_empty_first = trio_string!("", separator, second); +/// assert_eq!(result_empty_first, "World".to_string()); +/// +/// // Si el segundo fragmento está vacío, devuelve el primero. +/// let result_empty_second = trio_string!(first, separator, ""); +/// assert_eq!(result_empty_second, "Hello".to_string()); +/// +/// // Si ambos fragmentos están vacíos, devuelve una cadena vacía. +/// let result_both_empty = trio_string!("", separator, ""); +/// assert_eq!(result_both_empty, "".to_string()); +/// ``` +#[macro_export] +macro_rules! trio_string { + ($first:expr, $separator:expr, $second:expr) => {{ + if $first.is_empty() { + $second.to_string() + } else if $second.is_empty() { + $first.to_string() + } else { + $crate::util::concat_string!($first, $separator, $second) + } + }}; +}