use serde_json::Value;
use crate::types::Props;
fn str_prop<'a>(props: &'a Props, key: &str) -> Option<&'a str> {
props.get(key).and_then(Value::as_str)
}
pub fn is_vertical(props: &Props) -> bool {
str_prop(props, "orientation") == Some("vertical")
}
pub fn is_rail(props: &Props) -> bool {
is_vertical(props) && str_prop(props, "variant") == Some("bar")
}
pub fn item_count(props: &Props) -> usize {
props.get("items").and_then(Value::as_array).map_or(0, |a| a.iter().filter(|o| o.is_object()).count())
}
pub fn rail_height(props: &Props, tab_bar: f32) -> f32 {
item_count(props) as f32 * tab_bar
}
pub fn rail_item(index: usize, width: f32, tab_bar: f32) -> (f32, f32, f32, f32) {
(0.0, index as f32 * tab_bar, width, tab_bar)
}
pub fn badge_text(count: Option<&Value>) -> Option<String> {
let n = count.and_then(Value::as_f64).filter(|n| n.is_finite())?.floor();
if n <= 0.0 {
None
} else if n > 99.0 {
Some("99+".into())
} else {
Some(format!("{n}"))
}
}
pub fn option_badge(variant: Option<&str>, option: &Value) -> Option<String> {
if variant != Some("bar") {
return None;
}
badge_text(option.get("count"))
}
pub fn item_accessibility_label(label: &str, badge: Option<&str>) -> String {
match badge {
Some(b) => format!("{label}, {b}"),
None => label.to_string(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn badges_hide_at_zero_and_cap_at_99() {
assert_eq!(badge_text(None), None);
assert_eq!(badge_text(Some(&json!(0))), None);
assert_eq!(badge_text(Some(&json!(3))), Some("3".into()));
assert_eq!(badge_text(Some(&json!(99))), Some("99".into()));
assert_eq!(badge_text(Some(&json!(100))), Some("99+".into()));
assert_eq!(badge_text(Some(&json!("3"))), None);
assert_eq!(option_badge(Some("segmented"), &json!({"count": 3})), None);
assert_eq!(option_badge(Some("bar"), &json!({"count": 3})).as_deref(), Some("3"));
assert_eq!(item_accessibility_label("Chats", Some("3")), "Chats, 3");
}
}