use std::collections::HashMap;
use std::sync::{OnceLock, RwLock};
pub static STYLE_REGISTRY: OnceLock<RwLock<StyleRegistry>> = OnceLock::new();
#[inline]
fn get_registry() -> &'static RwLock<StyleRegistry> {
STYLE_REGISTRY.get_or_init(|| RwLock::new(StyleRegistry::new()))
}
#[derive(Debug, Default)]
pub struct StyleRegistry {
styles: HashMap<&'static str, &'static str>,
order: Vec<&'static str>,
cached_output: Option<String>,
}
impl StyleRegistry {
#[inline]
#[must_use]
pub fn new() -> Self {
Self {
styles: HashMap::with_capacity(32), order: Vec::with_capacity(32),
cached_output: None,
}
}
#[inline]
pub fn register(&mut self, hash: &'static str, css: &'static str) {
use std::collections::hash_map::Entry;
match self.styles.entry(hash) {
Entry::Occupied(mut entry) => {
entry.insert(css);
self.cached_output = None;
}
Entry::Vacant(entry) => {
entry.insert(css);
self.order.push(hash);
self.cached_output = None;
}
}
}
#[inline]
#[must_use]
pub fn get_all_styles(&mut self) -> &str {
if let Some(ref cached) = self.cached_output {
return cached.as_str();
}
if self.order.is_empty() {
return "";
}
let total_size: usize = self
.styles
.values()
.map(|s| s.len() + 1) .sum();
let mut result = String::with_capacity(total_size);
for hash in &self.order {
if let Some(css) = self.styles.get(hash) {
result.push_str(css);
result.push('\n');
}
}
self.cached_output = Some(result);
self.cached_output.as_ref().unwrap()
}
#[inline]
#[must_use]
pub fn contains(&self, hash: &str) -> bool {
self.styles.contains_key(hash)
}
#[inline]
pub fn clear(&mut self) {
self.styles.clear();
self.order.clear();
self.cached_output = None;
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.styles.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.styles.is_empty()
}
}
#[inline]
#[must_use]
pub fn inject_styles() -> String {
match get_registry().write() {
Ok(mut guard) => guard.get_all_styles().to_string(),
Err(_) => String::new(), }
}
#[derive(Debug, Clone, Copy)]
pub struct ScopedStyle {
pub scope: &'static str,
}
impl ScopedStyle {
#[inline]
pub fn new(scope: &'static str, css: &'static str) -> Self {
if let Ok(mut guard) = get_registry().write() {
guard.register(scope, css);
}
Self { scope }
}
#[inline]
pub fn scope(&self) -> &'static str {
self.scope
}
}
impl std::fmt::Display for ScopedStyle {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.scope)
}
}