accessibility_rs/engine/rules/
wcag_rule_map.rs

1use crate::engine::rules::rule::{Rule, Validation};
2use crate::engine::rules::techniques::Techniques;
3use crate::engine::rules::utils::nodes::{
4    get_unique_selector, has_alt, has_alt_prop, has_prop, has_prop_value, validate_empty_nodes,
5    validate_missing_attr,
6};
7use crate::engine::rules::wcag_base::{Guideline, IssueType, Principle};
8use crate::i18n::locales::get_message_i18n_str_raw;
9use accessibility_scraper::{ElementRef, Selector};
10use selectors::Element;
11use std::collections::BTreeMap;
12use std::collections::HashMap;
13use std::ops::Add;
14
15lazy_static! {
16    /// a list of rules that should be applied for WCAG2.0 A-AAA
17    pub static ref RULES_A: BTreeMap<&'static str, Vec<Rule>> =
18        vec![
19            ("html", Vec::from([
20                Rule::new(Techniques::H57.into(), IssueType::Error, Principle::Understandable, Guideline::Readable, "1", |nodes, _auditor| {
21                    let n = nodes[0].0;
22                    Validation::new_issue(!n.attr("lang").unwrap_or_default().is_empty() || !n.attr("xml:lang").unwrap_or_default().is_empty(), "2").into()
23                }),
24                Rule::new(Techniques::H57.into(), IssueType::Error, Principle::Understandable, Guideline::Readable, "1", |nodes, _auditor| {
25                    let lang = nodes[0].0.attr("lang").unwrap_or_default();
26                    let alphabetic = lang.chars().all(|x| x.is_alphabetic());
27                    // <https://www.rfc-editor.org/rfc/bcp/bcp47.txt>
28                    Validation::new_issue(if lang.len() > 3 {
29                        let mut c = lang.chars();
30                        let has_underscore = c.nth(2).unwrap_or_default() == '_' || lang.len() >= 4 && c.nth(1).unwrap_or_default() == '_';
31                        alphabetic && has_underscore && lang.len() < 12
32                    } else {
33                        alphabetic && lang.len() < 12
34                    }, "3.Lang").into()
35                }),
36                Rule::new(Techniques::H57.into(), IssueType::Error, Principle::Understandable, Guideline::Readable, "1", |nodes, _auditor| {
37                    let lang = nodes[0].0.attr("xml:lang").unwrap_or_default();
38                    let alphabetic = lang.chars().all(|x| x == '_' || x.is_alphabetic());
39                   // <https://www.rfc-editor.org/rfc/bcp/bcp47.txt>
40                   Validation::new_issue(if lang.len() > 3 {
41                        let mut c = lang.chars();
42                        let has_underscore = c.nth(2).unwrap_or_default() == '_' || lang.len() >= 4 && c.nth(1).unwrap_or_default() == '_';
43                        alphabetic && has_underscore && lang.len() < 12
44                    } else {
45                        alphabetic && lang.len() < 12
46                    }, "3.XmlLang").into()
47                }),
48                Rule::new(Techniques::H25.into(), IssueType::Error, Principle::Operable, Guideline::Navigable, "2", |nodes, _auditor| {
49                    let selector = unsafe { Selector::parse("head > title").unwrap_unchecked() };
50
51                    Validation::new_issue(nodes[0].0.select(&selector).count() >= 1, "1.NoTitleEl").into()
52                })
53            ])),
54            ("meta", Vec::from([
55                Rule::new(Techniques::F40.into(), IssueType::Error, Principle::Operable, Guideline::EnoughTime, "1", |nodes, _auditor| {
56                    let mut valid = true;
57
58                    for node in nodes {
59                        let element = node.0;
60                        let meta_refresh = element.attr("http-equiv").unwrap_or_default();
61                        if meta_refresh == "refresh" {
62                            let content = element.attr("content").unwrap_or_default();
63                            if content.contains("url") {
64                                valid = content.starts_with("0;");
65                            }
66                        }
67                    }
68
69                    Validation::new_issue(valid, "2").into()
70                }),
71                Rule::new(Techniques::F41.into(), IssueType::Error, Principle::Understandable, Guideline::EnoughTime, "1", |nodes, _auditor| {
72                    let mut valid = true;
73
74                    for node in nodes {
75                        let element = node.0;
76                        let meta_refresh = element.attr("http-equiv").unwrap_or_default();
77                        if meta_refresh == "refresh" {
78                            let content = element.attr("content").unwrap_or_default();
79                            if !content.is_empty() {
80                                valid = content == "0";
81                            }
82                        }
83                    }
84
85                    Validation::new_issue(valid, "2").into()
86                }),
87            ])),
88            ("title", Vec::from([
89                Rule::new(Techniques::H25.into(), IssueType::Error, Principle::Operable, Guideline::Navigable, "2", |nodes, _auditor| {
90                    let mut valid = true;
91                    for node in nodes {
92                        let e = node.0.inner_html().is_empty();
93                        if e {
94                            valid = false;
95                        }
96                    }
97                    Validation::new_issue(!nodes.is_empty() || valid, "1.EmptyTitle").into()
98                }),
99            ])),
100            ("body", Vec::from([
101                Rule::new(Techniques::G18.into(), IssueType::Error, Principle::Perceivable, Guideline::Distinguishable, "1", |nodes, auditor| {
102                    use rgb::RGB8;
103                    let mut validation_errors = Vec::new();
104
105                    // todo: test for multiple contrast rules at once.
106                    for node in nodes {
107                        if node.0.has_children() {
108                            let mut children = node.0.children();
109
110                            while let Some(el) = children.next() {
111                                match ElementRef::wrap(el) {
112                                    Some(element) => {
113                                        if vec![
114                                            "h1",
115                                            "h2",
116                                            "h3",
117                                            "h4",
118                                            "h5",
119                                            "h6",
120                                            "a",
121                                            "button",
122                                            "p",
123                                            "img",
124                                            "span",
125                                            "div",
126                                            "li",
127                                            "ol",
128                                            "td",
129                                            "th",
130                                            "tr",
131                                            "textarea",
132                                            "select",
133                                            "input"].contains(&element.value().name()) {
134                                            let style = accessibility_tree::style::cascade::style_for_element_ref(
135                                                &element,
136                                                &auditor.author,
137                                                &auditor.document
138                                            );
139
140                                            let font_size = style.font.font_size.0;
141                                            let text_color = style.color.color;
142
143                                            match element.parent() {
144                                                Some(parent_node) => {
145                                                    match ElementRef::wrap(parent_node) {
146                                                        Some(parent_element) => {
147                                                            let parent_style = accessibility_tree::style::cascade::style_for_element_ref(
148                                                                &parent_element,
149                                                                &auditor.author,
150                                                                &auditor.document,
151                                                            );
152
153                                                            match parent_style.background.background_color {
154                                                                cssparser::Color::RGBA(c) => {
155                                                                    let parent_element_background_color = RGB8::from([c.red, c.green, c.blue]);
156                                                                    let current_element_text_color = RGB8::from([text_color.red, text_color.green, text_color.blue]);
157                                                                    let contrast_ratio = contrast::contrast::<_, f32>(parent_element_background_color, current_element_text_color);
158                                                                    let min_contrast = if font_size.px <= 16.00 { 4.00 } else { 3.00 };
159
160                                                                    if contrast_ratio <= min_contrast {
161                                                                        let message =  t!(
162                                                                            &get_message_i18n_str_raw(
163                                                                                &Guideline::Distinguishable,
164                                                                                "",
165                                                                                "3_G18_or_G145.Fail",
166                                                                                ""),
167                                                                            locale = auditor.locale,
168                                                                            required = min_contrast.to_string(),
169                                                                            value = contrast_ratio.to_string());
170
171                                                                        validation_errors.push(Validation::new_custom_issue(false, "", message).into())
172                                                                    }
173                                                                }
174                                                                _ => ()
175                                                            }
176                                                        }
177                                                        _ => ()
178                                                    }
179                                                }
180                                                _ => ()
181                                            }
182                                        }
183                                    }
184                                    _ => ()
185                                }
186                            }
187                        }
188                    }
189
190                    crate::engine::rules::rule::RuleValidation::Multi(validation_errors)
191                }),
192            ])),
193            ("iframe", Vec::from([
194                Rule::new(Techniques::H64.into(), IssueType::Error, Principle::Operable, Guideline::Navigable, "1", |nodes, _auditor| {
195                    validate_missing_attr(nodes, "title", "1").into()
196                }),
197            ])),
198            ("frame", Vec::from([
199                Rule::new(Techniques::H64.into(), IssueType::Error, Principle::Operable, Guideline::Navigable, "1", |nodes, _auditor| {
200                    validate_missing_attr(nodes, "title", "1").into()
201                }),
202            ])),
203            ("form", Vec::from([
204                Rule::new(Techniques::H32.into(), IssueType::Error, Principle::Operable, Guideline::Predictable, "2", |nodes, _auditor| {
205                    let mut valid = false;
206                    let mut elements = Vec::new();
207                    let selector = unsafe { Selector::parse("button[type=submit]").unwrap_unchecked() };
208
209                    for ele in nodes {
210                        let ele = ele.0;
211                        let e = ele.select(&selector);
212                        let c = e.count();
213
214                       if c == 1 {
215                            valid = true;
216                       } else {
217                            valid = false;
218                            elements.push(get_unique_selector(&ele))
219                        }
220                    }
221
222                    Validation::new(valid, "2", elements, Default::default()).into()
223                }),
224                Rule::new(Techniques::H36.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
225                    let mut valid = false;
226                    let mut elements = Vec::new();
227                    let selector = unsafe { Selector::parse("input[type=image][name=submit]").unwrap_unchecked() };
228
229                    for ele in nodes {
230                        let ele = ele.0;
231                        let mut e = ele.select(&selector);
232
233                        while let Some(el) = e.next() {
234                            let alt = has_alt(el);
235                            if !alt {
236                                elements.push(get_unique_selector(&ele))
237                            }
238                            valid = alt;
239                        }
240                    }
241
242                    Validation::new(valid, "", elements, Default::default()).into()
243                }),
244            ])),
245            ("a", Vec::from([
246                Rule::new(Techniques::H2.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
247                    let mut valid = true;
248                    let selector = unsafe { Selector::parse("img").unwrap_unchecked() };
249                    let mut elements = Vec::new();
250
251                    for ele in nodes {
252                        let ele = ele.0;
253                        let mut e = ele.select(&selector);
254
255                        while let Some(el) = e.next() {
256                            let alt  = match el.attr("alt") {
257                                Some(s) => s,
258                                _ => "",
259                            };
260
261                            let text = ele.text().collect::<Vec<_>>().join("");
262                            let text = text.trim();
263
264                            if alt == text {
265                                valid = false;
266                                elements.push(get_unique_selector(&ele))
267                            }
268                        }
269                    }
270
271                    Validation::new(valid, "EG5", elements, Default::default()).into()
272                }),
273                Rule::new(Techniques::H30.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
274                    let mut valid = true;
275                    let selector = unsafe { Selector::parse("img").unwrap_unchecked() };
276                    let mut elements = Vec::new();
277
278                    for ele in nodes {
279                        let ele = ele.0;
280                        let mut e = ele.select(&selector);
281
282                        while let Some(el) = e.next() {
283                            let alt = has_alt(el);
284                            if !alt {
285                                elements.push(get_unique_selector(&ele))
286                            }
287                            valid = alt;
288                        }
289                    }
290
291                    Validation::new(valid, "2", elements, Default::default()).into()
292                }),
293                Rule::new(Techniques::H91.into(), IssueType::Error, Principle::Robust, Guideline::Compatible, "2", |nodes, _auditor| {
294                    let mut valid = true;
295                    let mut elements = Vec::new();
296
297                    for ele in nodes {
298                        let ele = ele.0;
299                        match ele.attr("href") {
300                            Some(_) => {
301                                let empty = ele.inner_html().trim().is_empty();
302                                if empty {
303                                    elements.push(get_unique_selector(&ele))
304                                }
305                                valid = !empty
306                            }
307                            _ => ()
308                        }
309                    }
310                    Validation::new(valid, "A.NoContent", elements, Default::default()).into()
311                }),
312                Rule::new(Techniques::H91.into(), IssueType::Error, Principle::Robust, Guideline::Compatible, "2", |nodes, _auditor| {
313                    let mut valid = true;
314                    let mut elements = Vec::new();
315                    for ele in nodes {
316                        let ele = ele.0;
317                        let v = !ele.is_empty() || ele.has_attribute("id") || ele.has_attribute("href");
318                        if !v {
319                            elements.push(get_unique_selector(&ele))
320                        }
321                        valid = v;
322                    }
323                    Validation::new(valid, "A.EmptyNoId", elements, Default::default()).into()
324                }),
325            ])),
326            ("img", Vec::from([
327                Rule::new(Techniques::H37.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
328                    let mut valid = true;
329                    let mut elements = Vec::new();
330
331                    for ele in nodes {
332                        let ele = ele.0;
333                        let alt = has_alt(ele);
334                        if !alt {
335                            elements.push(get_unique_selector(&ele))
336                        }
337                        valid = alt;
338                    }
339
340                    Validation::new(valid, "", elements, Default::default()).into()
341                }),
342                Rule::new(Techniques::H67.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
343                    let mut valid = true;
344                    let mut elements = Vec::new();
345
346                    for ele in nodes {
347                        let ele = ele.0;
348                        if has_prop(ele, "alt") && has_prop_value(ele, "title") {
349                            valid = false;
350                            elements.push(get_unique_selector(&ele))
351                        }
352                    }
353
354                    Validation::new(valid, "1", elements, Default::default()).into()
355                }),
356            ])),
357            ("h1", Vec::from([
358                Rule::new(Techniques::H42.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
359                    validate_empty_nodes(nodes, "2").into()
360                }),
361            ])),
362            ("h2", Vec::from([
363                Rule::new(Techniques::H42.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
364                    validate_empty_nodes(nodes, "2").into()
365                }),
366            ])),
367            ("h3", Vec::from([
368                Rule::new(Techniques::H42.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
369                    validate_empty_nodes(nodes, "2").into()
370                }),
371            ])),
372            ("h4", Vec::from([
373                Rule::new(Techniques::H42.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
374                    validate_empty_nodes(nodes, "2").into()
375                }),
376            ])),
377            ("h5", Vec::from([
378                Rule::new(Techniques::H42.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
379                    validate_empty_nodes(nodes, "2").into()
380                }),
381            ])),
382            ("h6", Vec::from([
383                Rule::new(Techniques::H42.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
384                    validate_empty_nodes(nodes, "2").into()
385                }),
386            ])),
387            ("label", Vec::from([
388                Rule::new(Techniques::H93.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
389                    let mut valid = true;
390                    let mut elements = Vec::new();
391                    let mut id_map: HashMap<&str, u8> = HashMap::new();
392
393                    for ele in nodes {
394                        match ele.0.attr("for") {
395                            Some(s) => {
396                                if id_map.contains_key(s) {
397                                    let u = id_map.get(s);
398                                    match u {
399                                        Some(u) => {
400                                            valid = false;
401                                            id_map.insert(s, u.add(1));
402                                            elements.push(get_unique_selector(&ele.0))
403                                        }
404                                        _ => ()
405                                    }
406                                } else {
407                                    id_map.insert(s, 1);
408                                }
409                            }
410                            _ => ()
411                        }
412                    }
413
414                    Validation::new(valid, "1", elements, Default::default()).into()
415                }),
416                Rule::new(Techniques::H44.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
417                    let mut valid = true;
418                    let mut elements = Vec::new();
419
420                    for ele in nodes {
421                        let has_valid_aria_label = ele.0.attr("aria-label").map_or(false, |s| !s.trim().is_empty());
422                        let mut has_valid_text_match = false;
423
424                        if !has_valid_aria_label && ele.0.text().next().is_some() {
425                            for child in ele.0.children() {
426                                match ElementRef::wrap(child) {
427                                    Some(child_element) => {
428                                        let name = child_element.value().name();
429
430                                        if vec!["textareas", "select"].contains(&name) {
431                                            has_valid_text_match = true;
432                                        } else if name == "input" {
433                                            match child_element.attr("type") {
434                                                Some(s) => {
435                                                     if vec!["text", "file", "password"].contains(&s) {
436                                                        has_valid_text_match = true;
437                                                     }
438                                                }
439                                                _ => ()
440                                            }
441                                        }
442
443                                        if has_valid_text_match {
444                                            break;
445                                        }
446                                    }
447                                    _ => ()
448                                }
449                            }
450                        }
451
452                        if !has_valid_aria_label && !has_valid_text_match {
453                             match ele.0.attr("for") {
454                                 Some(s) => {
455                                     let selector = unsafe { Selector::parse(&("#".to_string() + &s)).unwrap_unchecked() };
456                                     let root_tree = ele.0.tree().root();
457
458                                     match ElementRef::new(root_tree) {
459                                         t => {
460                                             let e = t.select(&selector);
461
462                                             if e.count() == 0 {
463                                                 valid = false;
464                                                 elements.push(get_unique_selector(&ele.0))
465                                             }
466                                         }
467                                     }
468                                 }
469                                 _ => ()
470                             }
471                         }
472                    }
473
474                    Validation::new(valid, "NonExistent", elements, Default::default()).into()
475                })
476            ])),
477            ("input", Vec::from([
478                Rule::new(Techniques::H91.into(), IssueType::Error, Principle::Robust, Guideline::Compatible, "2", |nodes, auditor| {
479                    let mut valid = true;
480                    let mut elements = Vec::new();
481
482                    for ele in nodes {
483                        let ele = ele.0;
484                        match ele.attr("type") {
485                            Some(t) => {
486                                if t == "submit" || t == "reset" || t == "button" {
487                                    let is_valid = match ele.attr("value") {
488                                        Some(_) => true,
489                                        _ => false
490                                    };
491
492                                    if !is_valid {
493                                        valid = false;
494                                        elements.push(get_unique_selector(&ele))
495                                    }
496                                }
497                            }
498                            _ => ()
499                        }
500                    }
501
502                    let message =  if !valid { t!(&get_message_i18n_str_raw( &Guideline::Compatible, "", "2_msg_pattern", ""), locale = auditor.locale, msgNodeType = r#""input""#, builtAttrs = r#""value""#) } else { Default::default() };
503
504                    Validation::new(valid, "", elements, message).into()
505                }),
506                Rule::new(Techniques::H91.into(), IssueType::Error, Principle::Robust, Guideline::Compatible, "2", |nodes, auditor| {
507                    let mut valid = true;
508                    let mut elements = Vec::new();
509
510                    for ele in nodes {
511                        let ele = ele.0;
512                        match ele.attr("type") {
513                            Some(t) => {
514                                if t == "submit" || t == "reset" || t == "button" {
515                                    let is_valid = match ele.attr("value") {
516                                        Some(v) => {
517                                          if v.trim().is_empty() {
518                                            false
519                                          } else {
520                                            true
521                                          }
522                                        }
523                                        _ => false
524                                    };
525
526                                    if !is_valid {
527                                        valid = false;
528                                        elements.push(get_unique_selector(&ele))
529                                    }
530                                }
531                            }
532                            _ => ()
533                        }
534                    }
535
536                    let message =  if !valid { t!(&get_message_i18n_str_raw( &Guideline::Compatible, "", "2_msg_pattern2", ""), locale = auditor.locale, msgNodeType = r#""input""#, builtAttrs = r#""value="something" ""#) } else { Default::default() };
537
538                    Validation::new(valid, "", elements, message).into()
539                }),
540            ])),
541            ("blink", Vec::from([
542                Rule::new(Techniques::F47.into(), IssueType::Error, Principle::Operable, Guideline::EnoughTime, "2", |nodes, _auditor| {
543                    Validation::new_issue(nodes.is_empty(), "").into()
544                }),
545            ])),
546            ("object", Vec::from([
547                Rule::new(Techniques::F47.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
548                    let mut valid = true;
549                    let mut elements = Vec::new();
550
551                    for ele in nodes {
552                        let ele = ele.0;
553                        let empty = ele.text();
554                        if empty.count() >= 1 {
555                            valid = false;
556                            elements.push(get_unique_selector(&ele))
557                        }
558                    }
559
560                    Validation::new(valid, "", elements, Default::default()).into()
561                }),
562            ])),
563            ("area",Vec::from([
564                Rule::new(Techniques::H24.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
565                    let mut valid = true;
566                    let mut elements = Vec::new();
567
568                    for ele in nodes {
569                        let ele = ele.0;
570                        if !has_alt_prop(ele) {
571                            valid = false;
572                            elements.push(get_unique_selector(&ele));
573                        }
574                    }
575
576                    Validation::new(valid, "ImageMapAreaNoAlt", elements, Default::default()).into()
577                })
578            ])),
579            ("map",Vec::from([
580                Rule::new(Techniques::H24.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor|{
581                    let mut valid = true;
582                    let mut elements = Vec::new();
583
584                    for ele in nodes{
585                        let ele = ele.0;
586                        if !has_alt_prop(ele){
587                            valid =  false;
588                            elements.push(get_unique_selector(&ele));
589                        }
590                    }
591
592                    Validation::new(valid,"ImageMapNoAlt",elements, Default::default()).into()
593                })
594            ])),
595            ("fieldset", Vec::from([
596                Rule::new(Techniques::H71.into(), IssueType::Error, Principle::Perceivable, Guideline::Adaptable, "1", |nodes, _auditor| {
597                    let mut valid = true;
598                    let selector = unsafe { Selector::parse("legend").unwrap_unchecked() };
599                    let mut elements = Vec::new();
600
601                    for ele in nodes {
602                        let ele = ele.0;
603                        let mut e = ele.select(&selector);
604                        let mut has_legend = false;
605
606                        while let Some(el) = e.next() {
607                            has_legend = true;
608                            if el.text().count() == 0 {
609                                valid = false;
610                                elements.push(get_unique_selector(&ele))
611                            }
612                        }
613                        if valid && !has_legend {
614                            valid = false;
615                        }
616                    }
617
618                    Validation::new(valid, "NoLegend", elements, Default::default()).into()
619                }),
620            ])),
621            ("applet", Vec::from([
622                Rule::new(Techniques::H35.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
623                    let mut valid = true;
624                    let mut elements = Vec::new();
625
626                    for ele in nodes {
627                        let ele = ele.0;
628                        if !has_alt_prop(ele) {
629                            valid = false;
630                            elements.push(get_unique_selector(&ele))
631                        }
632                    }
633
634                    Validation::new(valid, "2", elements, Default::default()).into()
635                }),
636                Rule::new(Techniques::H35.into(), IssueType::Error, Principle::Perceivable, Guideline::TextAlternatives, "1", |nodes, _auditor| {
637                    let mut valid = true;
638                    let mut elements = Vec::new();
639
640                    for ele in nodes {
641                        let ele = ele.0;
642                        let empty = ele.has_children() || !ele.inner_html().trim().is_empty();
643                        if !empty {
644                            valid = false;
645                            elements.push(get_unique_selector(&ele))
646                        }
647                    }
648
649                    Validation::new(valid, "3", elements, Default::default()).into()
650                }),
651            ])),
652        ]
653        .into_iter()
654        .collect();
655}