use crate::Error;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiagramKind {
Flowchart,
Sequence,
State,
Pie,
Er,
Class,
Journey,
Gantt,
Timeline,
GitGraph,
Mindmap,
QuadrantChart,
RequirementDiagram,
}
pub fn detect(input: &str) -> Result<DiagramKind, Error> {
let first = input
.lines()
.map(str::trim)
.find(|l| !l.is_empty() && !l.starts_with("%%"))
.ok_or(Error::EmptyInput)?;
let keyword = first.split_whitespace().next().unwrap_or(first);
match keyword.to_lowercase().as_str() {
"graph" | "flowchart" => Ok(DiagramKind::Flowchart),
"sequencediagram" => Ok(DiagramKind::Sequence),
"statediagram" | "statediagram-v2" => Ok(DiagramKind::State),
"pie" => Ok(DiagramKind::Pie),
"erdiagram" => Ok(DiagramKind::Er),
"classdiagram" => Ok(DiagramKind::Class),
"journey" => Ok(DiagramKind::Journey),
"gantt" => Ok(DiagramKind::Gantt),
"timeline" => Ok(DiagramKind::Timeline),
"gitgraph" => Ok(DiagramKind::GitGraph),
"mindmap" => Ok(DiagramKind::Mindmap),
"quadrantchart" => Ok(DiagramKind::QuadrantChart),
"requirementdiagram" => Ok(DiagramKind::RequirementDiagram),
other => Err(Error::UnsupportedDiagram(other.to_string())),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn detects_graph_keyword() {
assert_eq!(detect("graph LR\nA-->B").unwrap(), DiagramKind::Flowchart);
}
#[test]
fn detects_flowchart_keyword() {
assert_eq!(
detect("flowchart TD\nA-->B").unwrap(),
DiagramKind::Flowchart
);
}
#[test]
fn empty_returns_error() {
assert!(matches!(detect(""), Err(Error::EmptyInput)));
assert!(matches!(detect(" \n "), Err(Error::EmptyInput)));
}
#[test]
fn unknown_type_returns_error() {
assert!(matches!(
detect("xychart title Roadmap"),
Err(Error::UnsupportedDiagram(_))
));
}
#[test]
fn detects_gantt_keyword() {
assert_eq!(detect("gantt\ntitle A Plan").unwrap(), DiagramKind::Gantt);
assert_eq!(detect("Gantt").unwrap(), DiagramKind::Gantt);
}
#[test]
fn detects_pie_keyword() {
assert_eq!(
detect("pie title Pets\n\"Dogs\" : 5").unwrap(),
DiagramKind::Pie
);
assert_eq!(detect("PIE\n\"X\" : 1").unwrap(), DiagramKind::Pie);
}
#[test]
fn skips_comment_lines() {
assert_eq!(
detect("%% This is a comment\ngraph LR\nA-->B").unwrap(),
DiagramKind::Flowchart
);
}
#[test]
fn detects_state_diagram_keyword() {
assert_eq!(
detect("stateDiagram\n[*] --> A").unwrap(),
DiagramKind::State
);
}
#[test]
fn detects_state_diagram_v2_keyword() {
assert_eq!(
detect("stateDiagram-v2\n[*] --> A").unwrap(),
DiagramKind::State
);
}
#[test]
fn state_diagram_keyword_is_case_insensitive() {
assert_eq!(
detect("StateDiagram-V2\n[*] --> A").unwrap(),
DiagramKind::State
);
}
#[test]
fn detects_class_diagram_keyword() {
assert_eq!(
detect("classDiagram\nclass Animal").unwrap(),
DiagramKind::Class
);
assert_eq!(detect("ClassDiagram").unwrap(), DiagramKind::Class);
}
#[test]
fn detects_journey_keyword() {
assert_eq!(
detect("journey\ntitle My Day").unwrap(),
DiagramKind::Journey
);
assert_eq!(detect("Journey").unwrap(), DiagramKind::Journey);
}
#[test]
fn detects_timeline_keyword() {
assert_eq!(
detect("timeline\n2002 : LinkedIn").unwrap(),
DiagramKind::Timeline
);
assert_eq!(detect("Timeline").unwrap(), DiagramKind::Timeline);
}
#[test]
fn detects_git_graph_keyword() {
assert_eq!(detect("gitGraph\ncommit").unwrap(), DiagramKind::GitGraph);
assert_eq!(detect("GitGraph").unwrap(), DiagramKind::GitGraph);
}
#[test]
fn detects_mindmap_keyword() {
assert_eq!(
detect("mindmap\n root").unwrap(),
DiagramKind::Mindmap
);
assert_eq!(detect("Mindmap").unwrap(), DiagramKind::Mindmap);
}
#[test]
fn detects_quadrant_chart_keyword() {
assert_eq!(
detect("quadrantChart\nA: [0.5, 0.5]").unwrap(),
DiagramKind::QuadrantChart
);
assert_eq!(detect("QuadrantChart").unwrap(), DiagramKind::QuadrantChart);
}
#[test]
fn detects_requirement_diagram_keyword() {
assert_eq!(
detect("requirementDiagram\n").unwrap(),
DiagramKind::RequirementDiagram
);
assert_eq!(
detect("RequirementDiagram").unwrap(),
DiagramKind::RequirementDiagram
);
}
}