use crate::ElementId;
use derive_more::{Display, Error};
#[derive(Debug, Clone, PartialEq, Eq, Hash, Display)]
pub enum VerificationErrorKind {
#[display("Element {} missing accessible label", _0)]
MissingLabel(ElementId),
#[display("Element {} has empty label", _0)]
EmptyLabel(ElementId),
#[display("Element {} has invalid role: {:?}", _0, _1)]
InvalidRole(ElementId, accesskit::Role),
#[display(
"Element {} below minimum target size: {}x{} (minimum 44x44)",
_0,
_1,
_2
)]
BelowMinTargetSize(ElementId, u32, u32),
#[display(
"Element {} overflows viewport: position ({}, {}) + size ({}x{}) exceeds viewport ({}x{})",
_0,
_1,
_2,
_3,
_4,
_5,
_6
)]
OverflowsViewport(ElementId, i32, i32, u32, u32, u32, u32),
#[display("Element {} not keyboard accessible (missing focusable role)", _0)]
NotKeyboardAccessible(ElementId),
#[display("Tree error: {}", _0)]
TreeError(String),
#[display("Node {} not found in tree", _0)]
NodeNotFound(ElementId),
}
#[derive(Debug, Clone, Display, Error)]
#[display("Verification error: {} at {}:{}", kind, file, line)]
pub struct VerificationError {
pub kind: VerificationErrorKind,
pub file: &'static str,
pub line: u32,
}
impl VerificationError {
#[track_caller]
pub fn new(kind: VerificationErrorKind) -> Self {
let loc = std::panic::Location::caller();
Self {
kind,
file: loc.file(),
line: loc.line(),
}
}
#[track_caller]
pub fn missing_label(element_id: ElementId) -> Self {
Self::new(VerificationErrorKind::MissingLabel(element_id))
}
#[track_caller]
pub fn empty_label(element_id: ElementId) -> Self {
Self::new(VerificationErrorKind::EmptyLabel(element_id))
}
#[track_caller]
pub fn invalid_role(element_id: ElementId, role: accesskit::Role) -> Self {
Self::new(VerificationErrorKind::InvalidRole(element_id, role))
}
#[track_caller]
pub fn below_min_target_size(element_id: ElementId, width: u32, height: u32) -> Self {
Self::new(VerificationErrorKind::BelowMinTargetSize(
element_id, width, height,
))
}
#[track_caller]
pub fn overflows_viewport(
element_id: ElementId,
x: i32,
y: i32,
width: u32,
height: u32,
viewport_width: u32,
viewport_height: u32,
) -> Self {
Self::new(VerificationErrorKind::OverflowsViewport(
element_id,
x,
y,
width,
height,
viewport_width,
viewport_height,
))
}
#[track_caller]
pub fn not_keyboard_accessible(element_id: ElementId) -> Self {
Self::new(VerificationErrorKind::NotKeyboardAccessible(element_id))
}
#[track_caller]
pub fn tree_error(message: impl Into<String>) -> Self {
Self::new(VerificationErrorKind::TreeError(message.into()))
}
#[track_caller]
pub fn node_not_found(element_id: ElementId) -> Self {
Self::new(VerificationErrorKind::NodeNotFound(element_id))
}
}
#[derive(Debug, Clone, Default)]
pub struct VerificationReport {
pub errors: Vec<VerificationError>,
}
impl VerificationReport {
pub fn new() -> Self {
Self::default()
}
pub fn add_error(&mut self, error: VerificationError) {
self.errors.push(error);
}
pub fn has_errors(&self) -> bool {
!self.errors.is_empty()
}
pub fn error_count(&self) -> usize {
self.errors.len()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Display)]
pub enum UiErrorKind {
#[display("Missing label: {}", _0)]
MissingLabel(String),
#[display("Target too small: {}", _0)]
TargetTooSmall(String),
#[display("Insufficient contrast: {}", _0)]
InsufficientContrast(String),
#[display("Widget not found: {}", _0)]
WidgetNotFound(String),
#[display("Verification failed: {}", _0)]
VerificationFailed(String),
#[display("Unsupported operation: {}", _0)]
Unsupported(String),
}
#[derive(Debug, Clone, Display, Error)]
#[display("{} at {}:{}", kind, file, line)]
pub struct UiError {
pub kind: UiErrorKind,
pub file: &'static str,
pub line: u32,
}
impl UiError {
#[track_caller]
pub fn new(kind: UiErrorKind) -> Self {
let loc = std::panic::Location::caller();
Self {
kind,
file: loc.file(),
line: loc.line(),
}
}
}
pub type UiResult<T> = Result<T, UiError>;