use super::model::Shape;
pub(super) const SHAPE_NAMES: &[(&str, Shape)] = &[
("rect", Shape::Rect),
("proc", Shape::Rect),
("process", Shape::Rect),
("rectangle", Shape::Rect),
("rounded", Shape::RoundedRect),
("event", Shape::RoundedRect),
("stadium", Shape::Stadium),
("terminal", Shape::Stadium),
("pill", Shape::Stadium),
("fr-rect", Shape::Subroutine),
("subprocess", Shape::Subroutine),
("subproc", Shape::Subroutine),
("framed-rectangle", Shape::Subroutine),
("subroutine", Shape::Subroutine),
("cyl", Shape::Cylinder),
("db", Shape::Cylinder),
("database", Shape::Cylinder),
("cylinder", Shape::Cylinder),
("datastore", Shape::Cylinder),
("data-store", Shape::Cylinder),
("bucket", Shape::Cylinder),
("h-cyl", Shape::Cylinder),
("das", Shape::Cylinder),
("horizontal-cylinder", Shape::Cylinder),
("lin-cyl", Shape::Cylinder),
("disk", Shape::Cylinder),
("lined-cylinder", Shape::Cylinder),
("folder", Shape::Rect),
("directory", Shape::Rect),
("console", Shape::Rect),
("browser", Shape::Rect),
("person", Shape::Rect),
("bang", Shape::Rect),
("notch-rect", Shape::Rect),
("card", Shape::Rect),
("notched-rectangle", Shape::Rect),
("lin-rect", Shape::Rect),
("lined-rectangle", Shape::Rect),
("lined-process", Shape::Rect),
("lin-proc", Shape::Rect),
("shaded-process", Shape::Rect),
("fork", Shape::Rect),
("join", Shape::Rect),
("hourglass", Shape::Rect),
("collate", Shape::Rect),
("brace", Shape::Rect),
("comment", Shape::Rect),
("brace-l", Shape::Rect),
("brace-r", Shape::Rect),
("braces", Shape::Rect),
("bolt", Shape::Rect),
("com-link", Shape::Rect),
("lightning-bolt", Shape::Rect),
("doc", Shape::Rect),
("document", Shape::Rect),
("div-rect", Shape::Rect),
("div-proc", Shape::Rect),
("divided-rectangle", Shape::Rect),
("divided-process", Shape::Rect),
("tri", Shape::Rect),
("extract", Shape::Rect),
("triangle", Shape::Rect),
("win-pane", Shape::Rect),
("internal-storage", Shape::Rect),
("window-pane", Shape::Rect),
("flip-tri", Shape::Rect),
("manual-file", Shape::Rect),
("flipped-triangle", Shape::Rect),
("sl-rect", Shape::Rect),
("manual-input", Shape::Rect),
("sloped-rectangle", Shape::Rect),
("docs", Shape::Rect),
("documents", Shape::Rect),
("st-doc", Shape::Rect),
("stacked-document", Shape::Rect),
("st-rect", Shape::Rect),
("procs", Shape::Rect),
("processes", Shape::Rect),
("stacked-rectangle", Shape::Rect),
("bow-rect", Shape::Rect),
("stored-data", Shape::Rect),
("bow-tie-rectangle", Shape::Rect),
("tag-doc", Shape::Rect),
("tagged-document", Shape::Rect),
("tag-rect", Shape::Rect),
("tagged-rectangle", Shape::Rect),
("tag-proc", Shape::Rect),
("tagged-process", Shape::Rect),
("flag", Shape::Rect),
("paper-tape", Shape::Rect),
("lin-doc", Shape::Rect),
("lined-document", Shape::Rect),
("cloud", Shape::RoundedRect),
("delay", Shape::RoundedRect),
("half-rounded-rectangle", Shape::RoundedRect),
("circle", Shape::Circle),
("circ", Shape::Circle),
("sm-circ", Shape::Circle),
("start", Shape::Circle),
("small-circle", Shape::Circle),
("f-circ", Shape::Circle),
("junction", Shape::Circle),
("filled-circle", Shape::Circle),
("cross-circ", Shape::Circle),
("summary", Shape::Circle),
("crossed-circle", Shape::Circle),
("dbl-circ", Shape::DoubleCircle),
("double-circle", Shape::DoubleCircle),
("doublecircle", Shape::DoubleCircle),
("fr-circ", Shape::DoubleCircle),
("stop", Shape::DoubleCircle),
("framed-circle", Shape::DoubleCircle),
("diam", Shape::Diamond),
("decision", Shape::Diamond),
("diamond", Shape::Diamond),
("question", Shape::Diamond),
("hex", Shape::Hexagon),
("hexagon", Shape::Hexagon),
("prepare", Shape::Hexagon),
("notch-pent", Shape::Hexagon),
("loop-limit", Shape::Hexagon),
("notched-pentagon", Shape::Hexagon),
("lean-r", Shape::LeanRight),
("lean-right", Shape::LeanRight),
("in-out", Shape::LeanRight),
("lean-l", Shape::LeanLeft),
("lean-left", Shape::LeanLeft),
("out-in", Shape::LeanLeft),
("trap-b", Shape::Trapezoid),
("priority", Shape::Trapezoid),
("trapezoid-bottom", Shape::Trapezoid),
("trapezoid", Shape::Trapezoid),
("curv-trap", Shape::Trapezoid),
("curved-trapezoid", Shape::Trapezoid),
("display", Shape::Trapezoid),
("trap-t", Shape::InvTrapezoid),
("manual", Shape::InvTrapezoid),
("trapezoid-top", Shape::InvTrapezoid),
("inv-trapezoid", Shape::InvTrapezoid),
("text", Shape::Text),
("odd", Shape::Odd),
("state", Shape::Rect),
("note", Shape::Rect),
("composite", Shape::Rect),
("anchor", Shape::Rect),
("icon", Shape::Rect),
("choice", Shape::Diamond),
];
pub fn resolve_shape(name: &str) -> Option<Shape> {
if name.contains('_') || name.chars().any(|c| c.is_uppercase()) {
return None;
}
SHAPE_NAMES
.iter()
.find(|(n, _)| *n == name)
.map(|(_, s)| *s)
}
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct ShapeData {
pub shape: Option<String>,
pub label: Option<String>,
}
pub fn parse_shape_data(body: &str) -> ShapeData {
let body = fold_string_newlines(body);
let mut data = ShapeData::default();
let entries: Vec<String> = if body.contains('\n') {
body.lines().map(|l| l.to_string()).collect()
} else {
split_flow(&body)
};
for entry in entries {
let entry = entry.trim();
let Some((key, value)) = entry.split_once(':') else {
continue;
};
let key = key.trim().trim_matches('"').trim_matches('\'');
let value = unquote(value.trim());
match key {
"shape" => data.shape = Some(value),
"label" => data.label = Some(value),
_ => {}
}
}
data
}
fn fold_string_newlines(body: &str) -> String {
let chars: Vec<char> = body.chars().collect();
let mut out = String::with_capacity(body.len());
let mut i = 0;
let mut quote: Option<char> = None;
while i < chars.len() {
let c = chars[i];
match quote {
Some(q) => {
if c == q {
quote = None;
out.push(c);
i += 1;
} else if c == '\n' {
out.push_str("<br/>");
i += 1;
while i < chars.len() && (chars[i] == ' ' || chars[i] == '\t') {
i += 1;
}
} else {
out.push(c);
i += 1;
}
}
None => {
if c == '"' || c == '\'' {
quote = Some(c);
}
out.push(c);
i += 1;
}
}
}
out
}
fn split_flow(body: &str) -> Vec<String> {
let mut out = Vec::new();
let mut cur = String::new();
let mut quote: Option<char> = None;
for c in body.chars() {
match quote {
Some(q) if c == q => {
quote = None;
cur.push(c);
}
Some(_) => cur.push(c),
None if c == '"' || c == '\'' => {
quote = Some(c);
cur.push(c);
}
None if c == ',' => {
out.push(std::mem::take(&mut cur));
}
None => cur.push(c),
}
}
out.push(cur);
out
}
fn unquote(v: &str) -> String {
for q in ['"', '\''] {
if v.len() >= 2 && v.starts_with(q) && v.ends_with(q) {
return v[1..v.len() - 1].to_string();
}
}
v.to_string()
}