use std::collections::HashMap;
use std::sync::{OnceLock, RwLock};
use crate::Style;
use crate::style::{StyleAttrs, write_styled};
use crate::terminal;
fn store() -> &'static RwLock<HashMap<String, StyleAttrs>> {
static STORE: OnceLock<RwLock<HashMap<String, StyleAttrs>>> = OnceLock::new();
STORE.get_or_init(|| RwLock::new(HashMap::new()))
}
pub fn define_tag<S: Into<String>>(name: S, style: Style) {
let attrs = style.attrs();
if let Ok(mut map) = store().write() {
let _ = map.insert(name.into(), attrs);
}
}
#[must_use]
pub fn tag(name: &str) -> Tag {
let attrs = store().read().ok().and_then(|map| map.get(name).copied());
Tag { attrs }
}
#[derive(Clone, Copy, Debug)]
pub struct Tag {
attrs: Option<StyleAttrs>,
}
impl Tag {
#[must_use]
pub fn render_with(&self, text: &str) -> String {
let mut buf = String::with_capacity(text.len() + 24);
let _ = write_styled(
&mut buf,
self.attrs_or_empty(),
text,
terminal::color_level(),
);
buf
}
#[cfg(test)]
pub(crate) fn attrs_or_empty(&self) -> StyleAttrs {
self.attrs.unwrap_or(StyleAttrs::EMPTY)
}
#[cfg(not(test))]
fn attrs_or_empty(&self) -> StyleAttrs {
self.attrs.unwrap_or(StyleAttrs::EMPTY)
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::unwrap_used)]
use super::*;
use crate::style::style;
use crate::terminal::ColorLevel;
fn render_resolved(resolved: &Tag, text: &str, level: ColorLevel) -> String {
let attrs = resolved.attrs.unwrap_or(StyleAttrs::EMPTY);
let mut s = String::new();
write_styled(&mut s, attrs, text, level).unwrap();
s
}
#[test]
fn test_define_and_recall_applies_attributes() {
define_tag("reg-error", style("").red().bold());
let resolved = tag("reg-error");
assert_eq!(
render_resolved(&resolved, "failed", ColorLevel::Ansi16),
"\x1b[1;31mfailed\x1b[0m"
);
}
#[test]
fn test_redefining_replaces() {
define_tag("reg-x", style("").red());
define_tag("reg-x", style("").green());
let resolved = tag("reg-x");
assert_eq!(
render_resolved(&resolved, "v", ColorLevel::Ansi16),
"\x1b[32mv\x1b[0m"
);
}
#[test]
fn test_unknown_tag_is_plain() {
assert_eq!(tag("reg-undefined").render_with("text"), "text");
let resolved = tag("reg-undefined");
assert_eq!(
render_resolved(&resolved, "text", ColorLevel::TrueColor),
"text"
);
}
}