use std::collections::{HashMap, HashSet};
use std::fmt::Write;
use crate::ir::{StyleId, StylePool, ToCss};
#[derive(Debug, Clone)]
pub struct CssArtifact {
pub stylesheet: String,
pub class_map: HashMap<StyleId, String>,
}
impl CssArtifact {
pub fn class_name(&self, id: StyleId) -> Option<&str> {
self.class_map.get(&id).map(|s| s.as_str())
}
pub fn is_empty(&self) -> bool {
self.stylesheet.is_empty()
}
}
pub fn generate_css(pool: &StylePool, used_styles: &[StyleId]) -> CssArtifact {
let mut stylesheet = String::new();
let mut class_map = HashMap::new();
let unique_styles: HashSet<StyleId> = used_styles.iter().copied().collect();
let mut sorted_styles: Vec<StyleId> = unique_styles.into_iter().collect();
sorted_styles.sort_by_key(|s| s.0);
for id in sorted_styles {
let Some(style) = pool.get(id) else {
continue;
};
if style.is_default() {
continue;
}
let class_name = format!("c{}", id.0);
write!(stylesheet, ".{} {{ ", class_name).unwrap();
style.to_css(&mut stylesheet);
stylesheet.push_str("}\n");
class_map.insert(id, class_name);
}
CssArtifact {
stylesheet,
class_map,
}
}
pub fn generate_css_all(pool: &StylePool) -> CssArtifact {
let all_ids: Vec<StyleId> = pool.iter().map(|(id, _)| id).collect();
generate_css(pool, &all_ids)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ir::{Color, ComputedStyle, FontWeight, TextAlign};
#[test]
fn test_generate_css_empty() {
let pool = StylePool::new();
let artifact = generate_css(&pool, &[StyleId::DEFAULT]);
assert!(artifact.stylesheet.is_empty());
assert!(artifact.class_map.is_empty());
}
#[test]
fn test_generate_css_with_styles() {
let mut pool = StylePool::new();
let bold_style = ComputedStyle {
font_weight: FontWeight::BOLD,
..Default::default()
};
let bold_id = pool.intern(bold_style);
let center_style = ComputedStyle {
text_align: TextAlign::Center,
..Default::default()
};
let center_id = pool.intern(center_style);
let artifact = generate_css(&pool, &[bold_id, center_id]);
assert!(artifact.stylesheet.contains(".c1"));
assert!(artifact.stylesheet.contains(".c2"));
assert!(artifact.stylesheet.contains("font-weight: bold"));
assert!(artifact.stylesheet.contains("text-align: center"));
assert_eq!(artifact.class_name(bold_id), Some("c1"));
assert_eq!(artifact.class_name(center_id), Some("c2"));
}
#[test]
fn test_generate_css_color() {
let mut pool = StylePool::new();
let style = ComputedStyle {
color: Some(Color::rgb(255, 0, 0)),
..Default::default()
};
let id = pool.intern(style);
let artifact = generate_css(&pool, &[id]);
assert!(artifact.stylesheet.contains("color: #ff0000"));
}
#[test]
fn test_generate_css_deduplicates() {
let mut pool = StylePool::new();
let style = ComputedStyle {
font_weight: FontWeight::BOLD,
..Default::default()
};
let id = pool.intern(style);
let artifact = generate_css(&pool, &[id, id, id]);
let rule_count = artifact.stylesheet.matches(".c").count();
assert_eq!(rule_count, 1);
}
#[test]
fn test_generate_css_all() {
let mut pool = StylePool::new();
let style = ComputedStyle {
font_weight: FontWeight::BOLD,
..Default::default()
};
pool.intern(style);
let artifact = generate_css_all(&pool);
assert!(artifact.stylesheet.contains("font-weight: bold"));
}
}