virtual_node/velement/
special_attributes.rs1use crate::event::RealDom;
2use std::borrow::Cow;
3use std::cell::RefCell;
4
5pub struct SpecialAttributes<Dom: RealDom> {
7 key: Option<Cow<'static, str>>,
8 on_create_element: Option<CreateOrRemoveElementFn<Dom>>,
12 on_remove_element: Option<CreateOrRemoveElementFn<Dom>>,
16 pub dangerous_inner_html: Option<String>,
22}
23
24#[derive(Debug)]
27pub struct KeyNotSetError;
28
29impl<Dom: RealDom> SpecialAttributes<Dom> {
30 pub fn key(&self) -> Option<&str> {
35 self.key.as_ref().map(|key| key.as_ref())
36 }
37
38 pub fn set_key<Key>(&mut self, key: Key)
40 where
41 Key: Into<Cow<'static, str>>,
42 {
43 self.key = Some(key.into());
44 }
45
46 pub fn on_create_element_key(&self) -> Option<&Cow<'static, str>> {
48 if self.on_create_element.is_some() {
49 return self.key.as_ref();
50 }
51 None
52 }
53
54 pub fn set_key_and_on_create_element<Key, Func>(&mut self, key: Key, func: Func)
56 where
57 Key: Into<Cow<'static, str>>,
58 Func: FnMut(Dom::Element) + 'static,
59 {
60 self.set_key(key);
61 self.set_on_create_element(func).unwrap()
62 }
63
64 pub fn set_on_create_element<Func>(&mut self, func: Func) -> Result<(), KeyNotSetError>
96 where
97 Func: FnMut(Dom::Element) + 'static,
98 {
99 if self.key.is_none() {
100 return Err(KeyNotSetError);
101 }
102
103 self.on_create_element = Some(CreateOrRemoveElementFn {
104 func: RefCell::new(ElementFunc::OneArg(Box::new(func))),
105 });
106 Ok(())
107 }
108
109 #[doc(hidden)]
111 pub fn set_on_create_element_no_args<Func>(&mut self, func: Func) -> Result<(), KeyNotSetError>
112 where
113 Func: FnMut() + 'static,
114 {
115 if self.key.is_none() {
116 return Err(KeyNotSetError);
117 }
118
119 self.on_create_element = Some(CreateOrRemoveElementFn {
120 func: RefCell::new(ElementFunc::NoArgs(Box::new(func))),
121 });
122 Ok(())
123 }
124
125 pub fn maybe_call_on_create_element(&self, element: &Dom::Element) {
127 if let Some(on_create_elem) = &self.on_create_element {
128 on_create_elem.call(element.clone());
129 }
130 }
131}
132
133impl<Dom: RealDom> SpecialAttributes<Dom> {
134 pub fn on_remove_element_key(&self) -> Option<&Cow<'static, str>> {
136 if self.on_remove_element.is_some() {
137 return self.key.as_ref();
138 }
139 None
140 }
141
142 pub fn set_on_remove_element<Func>(&mut self, func: Func) -> Result<(), KeyNotSetError>
174 where
175 Func: FnMut(Dom::Element) + 'static,
176 {
177 if self.key.is_none() {
178 return Err(KeyNotSetError);
179 }
180
181 self.on_remove_element = Some(CreateOrRemoveElementFn {
182 func: std::cell::RefCell::new(ElementFunc::OneArg(Box::new(func))),
183 });
184 Ok(())
185 }
186
187 #[doc(hidden)]
189 pub fn set_on_remove_element_no_args<Func>(&mut self, func: Func) -> Result<(), KeyNotSetError>
190 where
191 Func: FnMut() + 'static,
192 {
193 if self.key.is_none() {
194 return Err(KeyNotSetError);
195 }
196
197 self.on_remove_element = Some(CreateOrRemoveElementFn {
198 func: RefCell::new(ElementFunc::NoArgs(Box::new(func))),
199 });
200 Ok(())
201 }
202
203 pub fn maybe_call_on_remove_element(&self, element: &Dom::Element) {
205 if let Some(on_remove_elem) = &self.on_remove_element {
206 on_remove_elem.call(element.clone());
207 }
208
209 let _ = element;
210 }
211
212 pub(crate) fn map_dom<New: RealDom>(
213 self,
214 map: &dyn Fn(Box<dyn FnMut(Dom::Element)>) -> Box<dyn FnMut(New::Element)>,
215 ) -> SpecialAttributes<New> {
216 SpecialAttributes {
217 key: self.key,
218 on_create_element: self.on_create_element.map(|func| func.map_dom(map)),
219 on_remove_element: self.on_remove_element.map(|func| func.map_dom(map)),
220 dangerous_inner_html: self.dangerous_inner_html,
221 }
222 }
223}
224
225struct CreateOrRemoveElementFn<Dom: RealDom> {
226 func: RefCell<ElementFunc<Dom>>,
227}
228
229enum ElementFunc<Dom: RealDom> {
230 NoArgs(Box<dyn FnMut()>),
231 OneArg(Box<dyn FnMut(Dom::Element)>),
232}
233
234impl<Dom: RealDom> CreateOrRemoveElementFn<Dom> {
235 fn call(&self, element: Dom::Element) {
236 use std::ops::DerefMut;
237
238 match self.func.borrow_mut().deref_mut() {
239 ElementFunc::NoArgs(func) => func(),
240 ElementFunc::OneArg(func) => func(element),
241 };
242 }
243
244 fn map_dom<New: RealDom>(
245 self,
246 map: &dyn Fn(Box<dyn FnMut(Dom::Element)>) -> Box<dyn FnMut(New::Element)>,
247 ) -> CreateOrRemoveElementFn<New> {
248 let func = self.func.into_inner();
249 match func {
250 ElementFunc::NoArgs(func) => CreateOrRemoveElementFn {
251 func: RefCell::new(ElementFunc::NoArgs(func)),
252 },
253 ElementFunc::OneArg(func) => {
254 let func = map(func);
255 CreateOrRemoveElementFn {
256 func: RefCell::new(ElementFunc::OneArg(func)),
257 }
258 }
259 }
260 }
261}
262
263impl<Dom: RealDom> PartialEq for CreateOrRemoveElementFn<Dom> {
264 fn eq(&self, rhs: &Self) -> bool {
265 let _ = rhs;
266 true
268 }
269}
270
271impl<Dom: RealDom> Default for SpecialAttributes<Dom> {
272 fn default() -> Self {
273 Self {
274 key: None,
275 on_create_element: None,
276 on_remove_element: None,
277 dangerous_inner_html: None,
278 }
279 }
280}
281
282impl<Dom: RealDom> PartialEq for SpecialAttributes<Dom> {
283 fn eq(&self, other: &Self) -> bool {
284 let SpecialAttributes {
285 key: key_lhs,
286 on_create_element: on_create_element_lhs,
287 on_remove_element: on_remove_element_lhs,
288 dangerous_inner_html: dangerous_inner_html_lhs,
289 } = self;
290 let SpecialAttributes {
291 key: key_rhs,
292 on_create_element: on_create_element_rhs,
293 on_remove_element: on_remove_element_rhs,
294 dangerous_inner_html: dangerous_inner_html_rhs,
295 } = other;
296
297 key_lhs == key_rhs
298 && on_create_element_lhs == on_create_element_rhs
299 && on_remove_element_lhs == on_remove_element_rhs
300 && dangerous_inner_html_lhs == dangerous_inner_html_rhs
301 }
302}
303
304#[cfg(test)]
305mod tests {
306 use super::*;
307 use crate::VirtualElement;
308
309 #[test]
311 fn error_setting_on_create_element_if_no_key() {
312 let mut elem = create_div();
313
314 let err1 = elem
315 .special_attributes
316 .set_on_create_element(|_| {})
317 .err()
318 .unwrap();
319 let err2 = elem
320 .special_attributes
321 .set_on_create_element_no_args(|| {})
322 .err()
323 .unwrap();
324
325 assert!(matches!(err1, KeyNotSetError));
326 assert!(matches!(err2, KeyNotSetError));
327 }
328
329 #[test]
331 fn error_setting_on_remove_element_if_no_key() {
332 let mut elem = create_div();
333 let special = &mut elem.special_attributes;
334
335 let err1 = special.set_on_remove_element(|_| {}).err().unwrap();
336 let err2 = special.set_on_remove_element_no_args(|| {}).err().unwrap();
337
338 assert!(matches!(err1, KeyNotSetError));
339 assert!(matches!(err2, KeyNotSetError));
340 }
341
342 #[test]
345 fn on_create_element_key() {
346 let mut elem = create_div();
347 let special = &mut elem.special_attributes;
348
349 special.set_key("hello");
350 assert_eq!(special.on_create_element_key(), None);
351
352 special.set_on_create_element(|_| {}).unwrap();
353 assert_eq!(
354 special.on_create_element_key().map(|key| key.as_ref()),
355 Some("hello")
356 );
357 }
358
359 #[test]
362 fn on_remove_element_key() {
363 let mut elem = create_div();
364 let special = &mut elem.special_attributes;
365
366 special.set_key("hello");
367 assert_eq!(special.on_remove_element_key(), None);
368
369 special.set_on_remove_element(|_| {}).unwrap();
370 assert_eq!(
371 special.on_remove_element_key().map(|key| key.as_ref()),
372 Some("hello")
373 );
374 }
375
376 fn create_div() -> VirtualElement<()> {
377 VirtualElement::new("div")
378 }
379}