use crate::resolve::{AttrMap, ResolvedValue};
use std::collections::HashMap;
const SIDE_GAP: f64 = 12.0;
pub(super) enum Placement {
Over(Vec<String>),
Left(String),
Right(String),
}
pub(super) fn placement(
attrs: &AttrMap,
span: crate::span::Span,
) -> Result<Option<Placement>, crate::error::Error> {
let Some(v) = attrs.get("place") else {
return Ok(None);
};
let bad = || {
crate::error::Error::at(
span,
"'place' is a mode then its lifelines — 'place: over api db', 'place: left api'",
)
};
let parts = idents(v);
let (mode, ids) = parts.split_first().ok_or_else(bad)?;
match (mode.as_str(), ids) {
("over", [_, ..]) => Ok(Some(Placement::Over(ids.to_vec()))),
("left", [id]) => Ok(Some(Placement::Left(id.clone()))),
("right", [id]) => Ok(Some(Placement::Right(id.clone()))),
_ => Err(bad()),
}
}
pub(super) fn centre_x(
placement: &Placement,
box_w: f64,
lifeline_x: &HashMap<String, f64>,
) -> Option<f64> {
match placement {
Placement::Over(ids) => {
let (mut lo, mut hi) = (f64::INFINITY, f64::NEG_INFINITY);
for id in ids {
let x = *lifeline_x.get(id)?;
lo = lo.min(x);
hi = hi.max(x);
}
(lo <= hi).then_some((lo + hi) / 2.0)
}
Placement::Left(id) => lifeline_x.get(id).map(|x| x - box_w / 2.0 - SIDE_GAP),
Placement::Right(id) => lifeline_x.get(id).map(|x| x + box_w / 2.0 + SIDE_GAP),
}
}
fn idents(v: &ResolvedValue) -> Vec<String> {
match v {
ResolvedValue::Ident(s) => vec![s.clone()],
ResolvedValue::Tuple(xs) | ResolvedValue::List(xs) => {
xs.iter().filter_map(one_ident).collect()
}
_ => Vec::new(),
}
}
fn one_ident(v: &ResolvedValue) -> Option<String> {
match v {
ResolvedValue::Ident(s) => Some(s.clone()),
_ => None,
}
}