sauron_core/vdom/
attribute.rs1#![allow(clippy::type_complexity)]
2
3use crate::vdom::ComponentEventCallback;
4use crate::vdom::EventCallback;
5use derive_where::derive_where;
6use indexmap::IndexMap;
7
8pub use attribute_value::AttributeValue;
9pub use callback::Callback;
10pub use style::Style;
11pub use value::Value;
12
13mod attribute_value;
14pub mod callback;
15pub(crate) mod special;
16mod style;
17mod value;
18
19pub type Namespace = &'static str;
21
22pub type Tag = &'static str;
24
25pub type AttributeName = &'static str;
27
28#[derive_where(Clone, Debug, PartialEq, Eq)]
30pub struct Attribute<MSG> {
31 pub namespace: Option<Namespace>,
35 pub name: AttributeName,
38 pub value: Vec<AttributeValue<MSG>>,
40}
41
42pub struct GroupedAttributeValues<'a, MSG> {
44 pub listeners: Vec<&'a EventCallback<MSG>>,
46 pub component_callbacks: Vec<&'a ComponentEventCallback>,
48 pub plain_values: Vec<&'a Value>,
50 pub styles: Vec<&'a Style>,
52}
53
54impl<MSG> Attribute<MSG> {
55 pub fn new(
57 namespace: Option<Namespace>,
58 name: AttributeName,
59 value: AttributeValue<MSG>,
60 ) -> Self {
61 Attribute {
62 name,
63 value: vec![value],
64 namespace,
65 }
66 }
67
68 pub fn with_multiple_values(
70 namespace: Option<Namespace>,
71 name: AttributeName,
72 value: impl IntoIterator<Item = AttributeValue<MSG>>,
73 ) -> Self {
74 Attribute {
75 name,
76 value: value.into_iter().collect(),
77 namespace,
78 }
79 }
80
81 pub fn name(&self) -> &AttributeName {
83 &self.name
84 }
85
86 pub fn value(&self) -> &[AttributeValue<MSG>] {
88 &self.value
89 }
90
91 pub fn namespace(&self) -> Option<&Namespace> {
93 self.namespace.as_ref()
94 }
95
96 pub fn is_event_listener(&self) -> bool {
98 self.value
99 .first()
100 .map(|v| v.is_event_listener())
101 .unwrap_or(false)
102 }
103
104 pub(crate) fn group_values(attr: &Attribute<MSG>) -> GroupedAttributeValues<MSG> {
106 let mut listeners = vec![];
107 let mut component_callbacks = vec![];
108 let mut plain_values = vec![];
109 let mut styles = vec![];
110 for av in attr.value() {
111 match av {
112 AttributeValue::Simple(v) => {
113 plain_values.push(v);
114 }
115 AttributeValue::Style(v) => {
116 styles.extend(v);
117 }
118 AttributeValue::EventListener(cb) => {
119 listeners.push(cb);
120 }
121 AttributeValue::ComponentEventListener(cb) => {
122 component_callbacks.push(cb);
123 }
124 AttributeValue::Empty => (),
125 }
126 }
127 GroupedAttributeValues {
128 listeners,
129 component_callbacks,
130 plain_values,
131 styles,
132 }
133 }
134
135 fn is_just_empty(&self) -> bool {
136 self.value
137 .first()
138 .map(|av| av.is_just_empty())
139 .unwrap_or(false)
140 }
141
142 pub(crate) fn is_mount_callback(&self) -> bool {
143 self.name == "mount"
144 }
145
146 pub fn merge_attributes_of_same_name<'a>(
149 attributes: impl IntoIterator<Item = &'a Attribute<MSG>> + Iterator,
150 ) -> Vec<Attribute<MSG>>
151 where
152 MSG: 'a,
153 {
154 let mut merged: IndexMap<&AttributeName, Attribute<MSG>> =
155 IndexMap::with_capacity(attributes.size_hint().0);
156 for att in attributes.into_iter() {
157 if !att.is_just_empty() {
158 if let Some(existing) = merged.get_mut(&att.name) {
159 existing.value.extend(att.value.clone());
160 } else {
161 merged.insert(
162 &att.name,
163 Attribute {
164 namespace: att.namespace,
165 name: att.name,
166 value: att.value.clone(),
167 },
168 );
169 }
170 }
171 }
172 merged.into_values().collect()
173 }
174
175 #[doc(hidden)]
177 pub fn group_attributes_per_name<'a>(
178 attributes: impl IntoIterator<Item = &'a Attribute<MSG>> + Iterator,
179 ) -> IndexMap<&'a AttributeName, Vec<&'a Attribute<MSG>>> {
180 let mut grouped: IndexMap<&'a AttributeName, Vec<&'a Attribute<MSG>>> =
181 IndexMap::with_capacity(attributes.size_hint().0);
182 for attr in attributes {
183 if let Some(existing) = grouped.get_mut(&attr.name) {
184 existing.push(attr);
185 } else {
186 grouped.insert(&attr.name, vec![attr]);
187 }
188 }
189 grouped
190 }
191}
192
193#[inline]
200pub fn attr<MSG>(name: AttributeName, value: impl Into<AttributeValue<MSG>>) -> Attribute<MSG> {
201 attr_ns(None, name, value)
202}
203
204#[inline]
212pub fn attr_ns<MSG>(
213 namespace: Option<Namespace>,
214 name: AttributeName,
215 value: impl Into<AttributeValue<MSG>>,
216) -> Attribute<MSG> {
217 Attribute::new(namespace, name, value.into())
218}