use crate::semantic::identity::SemanticRef;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SemanticKind {
Landmark,
Heading,
Text,
List,
ListItem,
Link,
Image,
Input,
Textarea,
Select,
Button,
Group,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LandmarkRole {
Main,
Aside,
Header,
Nav,
Section,
Footer,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SemanticComponent {
pub semantic_ref: SemanticRef,
pub kind: SemanticKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(default, skip_serializing_if = "SemanticAttrs::is_empty")]
pub attrs: SemanticAttrs,
#[serde(skip)]
pub(crate) interaction_selector: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub children: Vec<SemanticComponent>,
}
impl SemanticComponent {
pub fn is_focusable(&self) -> bool {
matches!(
self.kind,
SemanticKind::Link
| SemanticKind::Input
| SemanticKind::Textarea
| SemanticKind::Select
| SemanticKind::Button
)
}
pub fn walk<'a>(&'a self, visit: &mut dyn FnMut(&'a SemanticComponent)) {
visit(self);
for child in &self.children {
child.walk(visit);
}
}
}
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize, Deserialize)]
pub struct SemanticAttrs {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub landmark: Option<LandmarkRole>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub heading_level: Option<u8>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ordered: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub href: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub src: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub alt: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub value: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input_type: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub placeholder: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub checked: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub disabled: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub required: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub readonly: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub multiple: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub button_type: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub options: Vec<SelectOption>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tag: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub element_id: Option<String>,
}
impl SemanticAttrs {
pub fn is_empty(&self) -> bool {
self.landmark.is_none()
&& self.heading_level.is_none()
&& self.ordered.is_none()
&& self.href.is_none()
&& self.src.is_none()
&& self.alt.is_none()
&& self.name.is_none()
&& self.value.is_none()
&& self.input_type.is_none()
&& self.placeholder.is_none()
&& self.checked.is_none()
&& self.disabled.is_none()
&& self.required.is_none()
&& self.readonly.is_none()
&& self.multiple.is_none()
&& self.button_type.is_none()
&& self.options.is_empty()
&& self.tag.is_none()
&& self.element_id.is_none()
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct SelectOption {
pub value: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub selected: bool,
#[serde(default, skip_serializing_if = "std::ops::Not::not")]
pub disabled: bool,
}