use crate::widget::display_widgets::icon::{IconPaintError, IconRef};
use crate::widget::display_widgets::icon_data_set::{lookup_registered, register_icon_data};
pub const MAX_REGISTERED_ICONS: usize = 16;
pub fn register_icon(name: &'static str, paths: &'static [&'static str]) -> bool {
try_register_icon(name, paths).is_ok()
}
pub fn register_icon_on_grid(
name: &'static str,
paths: &'static [&'static str],
grid: u16,
) -> bool {
try_register_icon_on_grid(name, paths, grid).is_ok()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IconRegistrationError {
EmptyName,
NoPaths,
ZeroGrid,
BlankPath,
RegistryFull {
capacity: usize,
},
Undrawable(IconPaintError),
}
pub fn try_register_icon(
name: &'static str,
paths: &'static [&'static str],
) -> Result<(), IconRegistrationError> {
try_register_icon_on_grid(name, paths, 960)
}
pub fn try_register_icon_on_grid(
name: &'static str,
paths: &'static [&'static str],
grid: u16,
) -> Result<(), IconRegistrationError> {
use IconRegistrationError as Refused;
if name.is_empty() {
return Err(Refused::EmptyName);
}
if paths.is_empty() {
return Err(Refused::NoPaths);
}
if grid == 0 {
return Err(Refused::ZeroGrid);
}
if paths.iter().any(|d| d.trim().is_empty()) {
return Err(Refused::BlankPath);
}
let data = crate::widget::IconData { name, grid, paths };
crate::widget::display_widgets::icon::validate_icon(IconRef::StaticGeometry(&data))
.map_err(Refused::Undrawable)?;
if !is_registered_icon(name) && registered_icon_count() >= MAX_REGISTERED_ICONS {
return Err(Refused::RegistryFull { capacity: MAX_REGISTERED_ICONS });
}
if register_icon_data(name, paths, grid) {
Ok(())
} else {
Err(Refused::RegistryFull { capacity: MAX_REGISTERED_ICONS })
}
}
pub fn clear_registered_icons() -> usize {
crate::widget::display_widgets::icon_data_set::clear_registered_icon_data()
}
pub fn registered_icon_count() -> usize {
crate::widget::display_widgets::icon_data_set::registered_icon_data_count()
}
pub fn is_registered_icon(name: &str) -> bool {
lookup_registered(name).is_some()
}
pub fn registered_icon(name: &str) -> Option<RegisteredIcon> {
lookup_registered(name).map(|data| RegisteredIcon {
name: data.name,
grid: data.grid,
paths: data.paths,
})
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RegisteredIcon {
pub name: &'static str,
pub grid: u16,
pub paths: &'static [&'static str],
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn registration_refuses_junk_and_honours_capacity_and_replacement() {
clear_registered_icons();
assert_eq!(registered_icon_count(), 0);
assert!(!register_icon("", &["M0 0Z"]));
assert!(!register_icon("empty", &[]));
assert!(!register_icon("blank", &[" "]));
assert!(!register_icon_on_grid("zerogrid", &["M0 0Z"], 0));
assert_eq!(registered_icon_count(), 0, "a refused icon must not occupy a slot");
assert!(register_icon("mine", &["M0 0L10 0L10 10Z"]));
assert!(is_registered_icon("mine"));
assert!(registered_icon("mine").is_some());
assert_eq!(registered_icon("mine").map(|icon| icon.grid), Some(960));
assert!(register_icon_on_grid("mine", &["M0 0L20 0L20 20Z"], 24));
assert_eq!(registered_icon_count(), 1, "a same-named registration replaces");
let stored = registered_icon("mine").expect("registered");
assert_eq!(stored.grid, 24, "the replacement's grid wins");
assert_eq!(stored.paths, &["M0 0L20 0L20 20Z"]);
for index in 1..MAX_REGISTERED_ICONS {
let name: &'static str = Box::leak(format!("icon{index}").into_boxed_str());
assert!(register_icon(name, &["M0 0L1 0L1 1Z"]), "slot {index} must be accepted");
}
assert_eq!(registered_icon_count(), MAX_REGISTERED_ICONS);
assert!(
!register_icon("overflow", &["M0 0L1 0L1 1Z"]),
"a full registry must refuse, not evict"
);
assert!(
try_register_icon("mine", &["M0 0L1 0L1 1Z"]).is_ok(),
"re-registering an *existing* name replaces in place, so a full registry still accepts it"
);
assert_eq!(
try_register_icon("overflow-new", &["M0 0L1 0L1 1Z"]),
Err(IconRegistrationError::RegistryFull { capacity: MAX_REGISTERED_ICONS })
);
assert_eq!(
try_register_icon("", &["M0 0L1 0L1 1Z"]),
Err(IconRegistrationError::EmptyName)
);
assert_eq!(try_register_icon("x", &[]), Err(IconRegistrationError::NoPaths));
assert_eq!(
try_register_icon_on_grid("x", &["M0 0L1 0L1 1Z"], 0),
Err(IconRegistrationError::ZeroGrid)
);
assert_eq!(try_register_icon("x", &[" "]), Err(IconRegistrationError::BlankPath));
assert!(matches!(
try_register_icon("broken", &["M0 0L oops"]),
Err(IconRegistrationError::Undrawable(
crate::widget::display_widgets::icon::IconPaintError::InvalidGeometry(_)
))
));
assert!(matches!(
try_register_icon("no-contour", &["M0 0"]),
Err(IconRegistrationError::Undrawable(
crate::widget::display_widgets::icon::IconPaintError::EmptyGeometry
))
));
assert_eq!(
registered_icon_count(),
MAX_REGISTERED_ICONS,
"a refused registration must not occupy a slot"
);
assert_eq!(clear_registered_icons(), MAX_REGISTERED_ICONS);
assert_eq!(registered_icon_count(), 0);
assert!(!is_registered_icon("mine"), "clearing removes the lookups too");
}
}