use serde_json::{json, Map, Value};
use super::model::{Component, Diagram, Edge, LayoutNode, Region};
fn pair((a, b): (i64, i64)) -> Value {
json!([a, b])
}
fn quad((a, b, c, d): (i64, i64, i64, i64)) -> Value {
json!([a, b, c, d])
}
fn opt_pair(p: Option<(i64, i64)>) -> Value {
p.map_or(Value::Null, pair)
}
fn opt_str(s: &Option<String>) -> Value {
s.as_ref().map_or(Value::Null, |v| json!(v))
}
fn points(p: &Option<Vec<(i64, i64)>>) -> Value {
match p {
None => Value::Null,
Some(pts) => Value::Array(pts.iter().map(|&xy| pair(xy)).collect()),
}
}
fn component_json(c: &Component) -> Value {
let mut m = Map::new();
m.insert("id".into(), json!(c.id));
m.insert("name".into(), json!(c.name));
m.insert("subtitle".into(), json!(c.subtitle));
m.insert("icon".into(), json!(c.icon));
m.insert("shape".into(), json!(c.shape));
m.insert("accent".into(), json!(c.accent));
m.insert("pos".into(), pair(c.pos));
m.insert("size".into(), opt_pair(c.size));
m.insert("parent".into(), opt_str(&c.parent));
m.insert("align".into(), opt_str(&c.align));
m.insert("align_gap".into(), json!(c.align_gap));
m.insert("align_offset".into(), pair(c.align_offset));
m.insert("label_box".into(), c.label_box.map_or(Value::Null, quad));
Value::Object(m)
}
fn region_json(r: &Region) -> Value {
let mut m = Map::new();
m.insert("id".into(), json!(r.id));
m.insert("label".into(), json!(r.label));
m.insert("bounds".into(), quad(r.bounds));
m.insert("contains".into(), json!(r.contains));
m.insert("padding".into(), pair(r.padding));
m.insert(
"padding_bottom".into(),
r.padding_bottom.map_or(Value::Null, |v| json!(v)),
);
m.insert("style".into(), json!(r.style));
m.insert("icon".into(), opt_str(&r.icon));
m.insert("layout".into(), opt_str(&r.layout));
m.insert("pos".into(), opt_pair(r.pos));
m.insert("gap".into(), json!(r.gap));
m.insert("align".into(), json!(r.align));
m.insert("visible".into(), json!(r.visible));
m.insert("border_dash".into(), opt_pair(r.border_dash));
m.insert("border_stroke".into(), opt_str(&r.border_stroke));
m.insert("label_anchor".into(), json!(r.label_anchor));
m.insert("label_position".into(), opt_str(&r.label_position));
Value::Object(m)
}
fn edge_json(e: &Edge) -> Value {
let mut m = Map::new();
m.insert("src".into(), json!(e.src));
m.insert("dst".into(), json!(e.dst));
m.insert("label".into(), json!(e.label));
m.insert("style".into(), json!(e.style));
m.insert("src_anchor".into(), opt_str(&e.src_anchor));
m.insert("dst_anchor".into(), opt_str(&e.dst_anchor));
m.insert("route".into(), json!(e.route));
m.insert(
"via".into(),
Value::Array(e.via.iter().map(|&xy| pair(xy)).collect()),
);
m.insert("src_offset".into(), pair(e.src_offset));
m.insert("dst_offset".into(), pair(e.dst_offset));
m.insert("label_offset".into(), pair(e.label_offset));
m.insert("label_anchor".into(), json!(e.label_anchor));
m.insert("label_small".into(), json!(e.label_small));
m.insert("label_pos".into(), opt_pair(e.label_pos));
m.insert("dashed".into(), json!(e.dashed));
m.insert("no_arrow".into(), json!(e.no_arrow));
m.insert("trunk_offset".into(), json!(e.trunk_offset));
m.insert("shared_port".into(), json!(e.shared_port));
m.insert("points".into(), points(&e.points));
m.insert("bpmn_flow".into(), opt_str(&e.bpmn_flow));
Value::Object(m)
}
fn layout_node_json(n: &LayoutNode) -> Value {
match n {
LayoutNode::Id(id) => json!({ "t": "id", "id": id }),
LayoutNode::Group { dir, children } => json!({
"t": "group",
"dir": dir,
"children": children.iter().map(layout_node_json).collect::<Vec<_>>(),
}),
}
}
pub fn model_json(d: &Diagram) -> Value {
json!({
"width": d.width,
"height": d.height,
"title": d.title,
"subtitle": d.subtitle,
"components": d.components.iter().map(component_json).collect::<Vec<_>>(),
"regions": d.regions.iter().map(region_json).collect::<Vec<_>>(),
"edges": d.edges.iter().map(edge_json).collect::<Vec<_>>(),
"layout_trees": d.layout_trees.iter().map(layout_node_json).collect::<Vec<_>>(),
})
}