minify_html_onepass/unit/attr/
mod.rs1use crate::err::ProcessingResult;
2use crate::proc::checkpoint::WriteCheckpoint;
3use crate::proc::range::ProcessorRange;
4use crate::proc::MatchAction::*;
5use crate::proc::MatchMode::*;
6use crate::proc::Processor;
7use crate::unit::attr::value::process_attr_value;
8use crate::unit::attr::value::skip_attr_value;
9use crate::unit::attr::value::DelimiterType;
10use crate::unit::attr::value::ProcessedAttrValue;
11use minify_html_common::gen::attrs::ATTRS;
12use minify_html_common::gen::codepoints::WHATWG_ATTR_NAME_CHAR;
13use minify_html_common::gen::codepoints::WHITESPACE;
14use minify_html_common::spec::tag::ns::Namespace;
15
16mod value;
17
18#[derive(Clone, Copy, Eq, PartialEq)]
19pub enum AttrType {
20 Quoted,
21 Unquoted,
22 NoValue,
23}
24
25pub struct ProcessedAttr {
26 pub name: ProcessorRange,
27 pub typ: AttrType,
28 pub value: Option<ProcessorRange>,
29}
30
31pub fn process_attr(
32 proc: &mut Processor,
33 ns: Namespace,
34 element: ProcessorRange,
35) -> ProcessingResult<ProcessedAttr> {
36 let name = proc
39 .m(WhileInLookup(WHATWG_ATTR_NAME_CHAR), Keep)
40 .require("attribute name")?;
41 proc.make_lowercase(name);
42 let attr_cfg = ATTRS.get(ns, &proc[element], &proc[name]);
43 let is_boolean = attr_cfg.filter(|attr| attr.boolean).is_some();
44 let after_name = WriteCheckpoint::new(proc);
45
46 let should_collapse_and_trim_value_ws =
48 attr_cfg.filter(|attr| attr.collapse && attr.trim).is_some();
49 proc.m(WhileInLookup(WHITESPACE), Discard);
50 let has_value = proc.m(IsChar(b'='), Keep).nonempty();
51
52 let (typ, value) = if !has_value {
53 (AttrType::NoValue, None)
54 } else {
55 proc.m(WhileInLookup(WHITESPACE), Discard);
56 if is_boolean {
57 skip_attr_value(proc)?;
58 debug_assert_eq!(after_name.written_count(proc), 1);
60 after_name.erase_written(proc);
61 (AttrType::NoValue, None)
62 } else {
63 match process_attr_value(proc, should_collapse_and_trim_value_ws)? {
64 ProcessedAttrValue { value: None, .. } => {
65 debug_assert_eq!(after_name.written_count(proc), 1);
67 after_name.erase_written(proc);
68 (AttrType::NoValue, None)
69 }
70 ProcessedAttrValue {
71 delimiter: DelimiterType::Unquoted,
72 value,
73 } => (AttrType::Unquoted, value),
74 ProcessedAttrValue {
75 delimiter: DelimiterType::Double,
76 value,
77 }
78 | ProcessedAttrValue {
79 delimiter: DelimiterType::Single,
80 value,
81 } => (AttrType::Quoted, value),
82 }
83 }
84 };
85
86 Ok(ProcessedAttr { name, typ, value })
87}