use crate::slots::{Slots, slot_builders};
use crate::value::Value;
#[derive(Clone, Copy, Debug, Default, PartialEq)]
pub struct Enter {
pub dx: f32,
pub dy: f32,
pub slots: Slots,
}
slot_builders!(Enter);
impl Enter {
pub fn from(dx: f32, dy: f32) -> Self {
Self {
dx,
dy,
..Self::default()
}
}
pub fn offset(mut self, dx: f32, dy: f32) -> Self {
self.dx = dx;
self.dy = dy;
self
}
pub fn offsets(&self) -> bool {
self.dx != 0.0 || self.dy != 0.0
}
}
pub fn parse(v: &Value) -> Result<Enter, String> {
parse_with(v, None)
}
pub fn parse_with(
v: &Value,
mut refs: Option<&mut crate::tokens::NameRefs<'_>>,
) -> Result<Enter, String> {
let Value::Map(fields) = v else {
return Err("enter must be an object".into());
};
let mut e = Enter::default();
for (k, v) in fields {
let bad = |what: &str| format!("enter: {what}");
if e.slots
.parse_field(k, v, refs.as_deref_mut())
.map_err(|e| bad(&e))?
{
continue;
}
let num = |what: &str| {
v.as_float()
.map(|n| n as f32)
.ok_or_else(|| bad(&format!("{what} must be a number")))
};
match k.as_str() {
"dx" => e.dx = num("dx")?,
"dy" => e.dy = num("dy")?,
other => return Err(bad(&format!("unknown field {other:?}"))),
}
}
Ok(e)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::color::Color;
use crate::spec::Sizing;
#[test]
fn parses_prop_shaped_values() {
let e = parse(&Value::map([
("dx", Value::Int(-40)),
("dy", Value::Float(2.5)),
("width", Value::map([("grow", Value::Int(0))])),
("height", Value::str("50%")),
("bg", Value::str("#ff000000")),
("radius", Value::Int(3)),
("opacity", Value::Float(0.0)),
]))
.unwrap();
assert_eq!(
e,
Enter::from(-40.0, 2.5)
.width(Sizing::Grow(0.0))
.height(Sizing::Percent(0.5))
.bg(Color::hex(0xff000000))
.radius(3.0)
.opacity(0.0)
);
assert!(e.offsets());
assert!(!Enter::default().bg(Color::WHITE).offsets());
}
#[test]
fn rejects_bad_shapes() {
let bad = |v: Value| parse(&v).unwrap_err();
assert!(bad(Value::List(vec![])).contains("object"));
assert!(bad(Value::map([("dx", Value::str("far"))])).contains("dx must be a number"));
assert!(bad(Value::map([("colour", Value::str("#fff"))])).contains("unknown field"));
assert!(bad(Value::map([("width", Value::str("wide"))])).contains("bad sizing"));
}
}