use markdownx::parser::Node;
use markdownx::{Component, Error};
use std::collections::HashMap;
pub struct RatingComponent;
impl RatingComponent {
pub fn new() -> Self {
Self
}
}
impl Component for RatingComponent {
fn name(&self) -> &str {
"rating"
}
fn render(
&self,
attributes: &HashMap<String, String>,
_children: &[Node],
) -> Result<String, Error> {
let value = attributes
.get("value")
.and_then(|v| v.parse::<f32>().ok())
.unwrap_or(0.0);
let max = attributes
.get("max")
.and_then(|v| v.parse::<u8>().ok())
.unwrap_or(5);
let size = attributes.get("size").unwrap_or(&"medium".to_string());
let value = value.max(0.0).min(max as f32);
let mut stars_html = String::new();
for i in 1..=max {
let fill = if i as f32 <= value {
"full"
} else if (i as f32 - 0.5) <= value {
"half"
} else {
"empty"
};
stars_html.push_str(&format!(
r#"<span class="markrust-star markrust-star-{}">{}</span>"#,
fill,
get_star_svg(fill)
));
}
let value_html = if attributes.get("show_value").is_some() {
format!(r#"<span class="markrust-rating-value">{}</span>"#, value)
} else {
String::new()
};
Ok(format!(
r#"<div class="markrust-rating markrust-rating-{size}">
{stars_html}
{value_html}
</div>"#,
size = size,
stars_html = stars_html,
value_html = value_html
))
}
fn css(&self) -> Option<String> {
Some(
r#"
/* Rating component styles */
.markrust-rating {
display: inline-flex;
align-items: center;
gap: 0.25rem;
}
.markrust-rating-small {
font-size: 1rem;
}
.markrust-rating-medium {
font-size: 1.5rem;
}
.markrust-rating-large {
font-size: 2rem;
}
.markrust-star {
color: #d1d5db;
line-height: 1;
}
.markrust-star-full {
color: #f59e0b;
}
.markrust-star-half {
color: #f59e0b;
}
.markrust-rating-value {
margin-left: 0.5rem;
font-weight: 600;
}
"#
.to_string(),
)
}
}
fn get_star_svg(fill: &str) -> &'static str {
match fill {
"full" => "★",
"half" => "✭",
_ => "☆",
}
}