use crate::err::ProcessingResult;
use crate::proc::checkpoint::WriteCheckpoint;
use crate::proc::range::ProcessorRange;
use crate::proc::MatchAction::*;
use crate::proc::MatchMode::*;
use crate::proc::Processor;
use crate::unit::attr::value::process_attr_value;
use crate::unit::attr::value::skip_attr_value;
use crate::unit::attr::value::DelimiterType;
use crate::unit::attr::value::ProcessedAttrValue;
use minify_html_common::gen::attrs::ATTRS;
use minify_html_common::gen::codepoints::WHATWG_ATTR_NAME_CHAR;
use minify_html_common::gen::codepoints::WHITESPACE;
use minify_html_common::spec::tag::ns::Namespace;
mod value;
#[derive(Clone, Copy, Eq, PartialEq)]
pub enum AttrType {
Quoted,
Unquoted,
NoValue,
}
pub struct ProcessedAttr {
pub name: ProcessorRange,
pub typ: AttrType,
pub value: Option<ProcessorRange>,
}
pub fn process_attr(
proc: &mut Processor,
ns: Namespace,
element: ProcessorRange,
) -> ProcessingResult<ProcessedAttr> {
let name = proc
.m(WhileInLookup(WHATWG_ATTR_NAME_CHAR), Keep)
.require("attribute name")?;
proc.make_lowercase(name);
let attr_cfg = ATTRS.get(ns, &proc[element], &proc[name]);
let is_boolean = attr_cfg.filter(|attr| attr.boolean).is_some();
let after_name = WriteCheckpoint::new(proc);
let should_collapse_and_trim_value_ws =
attr_cfg.filter(|attr| attr.collapse && attr.trim).is_some();
proc.m(WhileInLookup(WHITESPACE), Discard);
let has_value = proc.m(IsChar(b'='), Keep).nonempty();
let (typ, value) = if !has_value {
(AttrType::NoValue, None)
} else {
proc.m(WhileInLookup(WHITESPACE), Discard);
if is_boolean {
skip_attr_value(proc)?;
debug_assert_eq!(after_name.written_count(proc), 1);
after_name.erase_written(proc);
(AttrType::NoValue, None)
} else {
match process_attr_value(proc, should_collapse_and_trim_value_ws)? {
ProcessedAttrValue { value: None, .. } => {
debug_assert_eq!(after_name.written_count(proc), 1);
after_name.erase_written(proc);
(AttrType::NoValue, None)
}
ProcessedAttrValue {
delimiter: DelimiterType::Unquoted,
value,
} => (AttrType::Unquoted, value),
ProcessedAttrValue {
delimiter: DelimiterType::Double,
value,
}
| ProcessedAttrValue {
delimiter: DelimiterType::Single,
value,
} => (AttrType::Quoted, value),
}
}
};
Ok(ProcessedAttr { name, typ, value })
}