use crate::compat::{String, ToString, Vec};
use crate::core::{Font, Rect};
use crate::widget::capability::coercion::expect_i64;
use crate::widget::capability::coercion::expect_string;
use crate::widget::capability::types::{CapabilityAccessError, CapabilityValue};
pub(crate) fn row_height_for(font: &Font) -> u32 {
(font.size().max(1.0) + 8.0) as u32
}
pub(crate) fn rect_value(rect: Rect) -> CapabilityValue {
CapabilityValue::Tuple(vec![
CapabilityValue::Int(i64::from(rect.x)),
CapabilityValue::Int(i64::from(rect.y)),
CapabilityValue::Int(i64::from(rect.width)),
CapabilityValue::Int(i64::from(rect.height)),
])
}
pub(crate) fn expect_rect(value: CapabilityValue) -> Result<Rect, CapabilityAccessError> {
let CapabilityValue::Tuple(items) = value else {
return Err(CapabilityAccessError::TypeMismatch);
};
if items.len() != 4 {
return Err(CapabilityAccessError::TypeMismatch);
}
let mut items = items.into_iter();
let mut next = || -> Result<i64, CapabilityAccessError> {
expect_i64(items.next().ok_or(CapabilityAccessError::TypeMismatch)?)
};
let x = next()?;
let y = next()?;
let width = next()?;
let height = next()?;
if width < 0 || height < 0 {
return Err(CapabilityAccessError::OutOfRange);
}
Ok(Rect::new(
x.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32,
y.clamp(i64::from(i32::MIN), i64::from(i32::MAX)) as i32,
width.min(i64::from(u32::MAX)) as u32,
height.min(i64::from(u32::MAX)) as u32,
))
}
pub(crate) fn strings_value(items: &[String]) -> CapabilityValue {
CapabilityValue::Tuple(items.iter().map(|item| CapabilityValue::String(item.clone())).collect())
}
pub(crate) fn expect_strings(value: CapabilityValue) -> Result<Vec<String>, CapabilityAccessError> {
let CapabilityValue::Tuple(items) = value else {
return Err(CapabilityAccessError::TypeMismatch);
};
items.into_iter().map(|item| expect_string(item).map(|text| text.to_string())).collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn a_rectangle_round_trips_through_the_property_representation() {
let rect = Rect::new(4, 8, 120, 32);
assert_eq!(expect_rect(rect_value(rect)).unwrap(), rect);
}
#[test]
fn a_negative_size_is_refused() {
let value = CapabilityValue::Tuple(vec![
CapabilityValue::Int(0),
CapabilityValue::Int(0),
CapabilityValue::Int(-1),
CapabilityValue::Int(10),
]);
assert!(matches!(expect_rect(value), Err(CapabilityAccessError::OutOfRange)));
}
#[test]
fn rows_round_trip() {
let rows = vec!["one".to_string(), "two".to_string()];
assert_eq!(expect_strings(strings_value(&rows)).unwrap(), rows);
}
}