use indexmap::IndexMap;
#[derive(Debug, Clone, Default)]
pub struct PieDiagram {
pub title: Option<String>,
pub show_data: bool,
pub sections: IndexMap<String, f64>,
}
pub fn parse(input: &str) -> crate::error::ParseResult<PieDiagram> {
let mut diag = PieDiagram::default();
for line in input.lines() {
let trimmed = line.trim();
if trimmed.is_empty() || trimmed.starts_with("%%") {
continue;
}
if let Some(stripped) = trimmed.strip_prefix("pie") {
let rest = stripped.trim();
parse_header(rest, &mut diag);
continue;
}
if trimmed.starts_with('"') {
if let Some((label, value)) = parse_data_line(trimmed) {
if value >= 0.0 {
diag.sections.insert(label, value);
}
}
}
}
crate::error::ParseResult::ok(diag)
}
fn parse_header(rest: &str, diag: &mut PieDiagram) {
let mut s = rest;
if s.starts_with("showData") {
diag.show_data = true;
s = s["showData".len()..].trim_start();
}
if let Some(stripped) = s.strip_prefix("title") {
let title_text = stripped.trim();
if !title_text.is_empty() {
diag.title = Some(title_text.to_string());
}
}
}
fn parse_data_line(line: &str) -> Option<(String, f64)> {
let start = line.find('"')?;
let end = line[start + 1..].find('"')? + start + 1;
let label = line[start + 1..end].to_string();
let after_quote = &line[end + 1..];
let colon_pos = after_quote.find(':')?;
let value_str = after_quote[colon_pos + 1..].trim();
let value: f64 = value_str.parse().ok()?;
Some((label, value))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn basic_pie() {
let input = "pie\n \"Dogs\" : 386\n \"Cats\" : 85\n \"Rats\" : 15";
let d = parse(input).diagram;
assert_eq!(d.title, None);
assert!(!d.show_data);
assert_eq!(d.sections["Dogs"], 386.0);
assert_eq!(d.sections["Cats"], 85.0);
assert_eq!(d.sections["Rats"], 15.0);
}
#[test]
fn pie_with_title() {
let input = "pie title Pets adopted by volunteers\n \"Dogs\" : 386\n \"Cats\" : 85";
let d = parse(input).diagram;
assert_eq!(d.title.as_deref(), Some("Pets adopted by volunteers"));
assert_eq!(d.sections.len(), 2);
}
#[test]
fn pie_showdata() {
let input =
"pie showData title Key Elements\n \"Calcium\" : 42.96\n \"Potassium\" : 50.05";
let d = parse(input).diagram;
assert!(d.show_data);
assert_eq!(d.title.as_deref(), Some("Key Elements"));
assert!((d.sections["Calcium"] - 42.96).abs() < 1e-9);
}
#[test]
fn pie_many_slices() {
let input = "pie title Browser Market Share\n \"Chrome\" : 65.2\n \"Safari\" : 19.2\n \"Firefox\" : 4.0\n \"Edge\" : 3.1\n \"Samsung\" : 2.8\n \"Opera\" : 1.2\n \"Other\" : 4.5";
let d = parse(input).diagram;
assert_eq!(d.sections.len(), 7);
assert_eq!(d.title.as_deref(), Some("Browser Market Share"));
}
}