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