minify-html/src/spec/tag/omission.rs

165 lines
5.3 KiB
Rust

use phf::{Map, phf_map, phf_set, Set};
// Rules sourced from https://html.spec.whatwg.org/multipage/syntax.html#syntax-tag-omission.
// TODO html, head, body
// TODO Opening tags
pub enum ClosingTagOmissionRuleIfLast {
// Closing tag can always be omitted if it's the last node of its parent's children.
Always,
// Closing tag can never be omitted if it's the last node of its parent's children.
Never,
// Closing tag can be omitted if it's the last node of its parent's children and the parent tag name is not one of these.
ParentIsNot(Set<&'static [u8]>),
}
pub struct ClosingTagOmissionRule {
// Closing tag can be omitted if immediately followed by an element node with one of these tag names.
followed_by: Set<&'static [u8]>,
// Closing tag can be omitted if it's the last node of its parent's children.
is_last: ClosingTagOmissionRuleIfLast,
}
impl ClosingTagOmissionRule {
pub fn can_omit_as_last_node(&self, parent: Option<&[u8]>) -> bool {
match &self.is_last {
ClosingTagOmissionRuleIfLast::Always => true,
ClosingTagOmissionRuleIfLast::Never => false,
ClosingTagOmissionRuleIfLast::ParentIsNot(parents) => match parent {
Some(tag) => !parents.contains(tag),
None => true,
},
}
}
pub fn can_omit_as_before(&self, after: &[u8]) -> bool {
self.followed_by.contains(after)
}
}
static LI_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"li"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static DT_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"dt", b"dd"),
is_last: ClosingTagOmissionRuleIfLast::Never,
};
static DD_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"dd", b"dt"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static P_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(
b"address",
b"article",
b"aside",
b"blockquote",
b"details",
b"div",
b"dl",
b"fieldset",
b"figcaption",
b"figure",
b"footer",
b"form",
b"h1",
b"h2",
b"h3",
b"h4",
b"h5",
b"h6",
b"header",
b"hgroup",
b"hr",
b"main",
b"menu",
b"nav",
b"ol",
b"p",
b"pre",
b"section",
b"table",
b"ul",
),
is_last: ClosingTagOmissionRuleIfLast::ParentIsNot(phf_set!(
b"a",
b"audio",
b"del",
b"ins",
b"map",
b"noscript",
b"video",
)),
};
static RT_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"rt", b"rp"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static RP_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"rt", b"rp"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static OPTGROUP_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"optgroup"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static OPTION_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"option", b"optgroup"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static THEAD_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"tbody", b"tfoot"),
is_last: ClosingTagOmissionRuleIfLast::Never,
};
static TBODY_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"tbody", b"tfoot"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static TFOOT_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static TR_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"tr"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static TD_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"td", b"th"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
static TH_CLOSING_TAG_OMISSION_RULE: &ClosingTagOmissionRule = &ClosingTagOmissionRule {
followed_by: phf_set!(b"td", b"th"),
is_last: ClosingTagOmissionRuleIfLast::Always,
};
pub static CLOSING_TAG_OMISSION_RULES: Map<&'static [u8], &ClosingTagOmissionRule> = phf_map! {
b"li" => LI_CLOSING_TAG_OMISSION_RULE,
b"dt" => DT_CLOSING_TAG_OMISSION_RULE,
b"dd" => DD_CLOSING_TAG_OMISSION_RULE,
b"p" => P_CLOSING_TAG_OMISSION_RULE,
b"rt" => RT_CLOSING_TAG_OMISSION_RULE,
b"rp" => RP_CLOSING_TAG_OMISSION_RULE,
b"optgroup" => OPTGROUP_CLOSING_TAG_OMISSION_RULE,
b"option" => OPTION_CLOSING_TAG_OMISSION_RULE,
b"thead" => THEAD_CLOSING_TAG_OMISSION_RULE,
b"tbody" => TBODY_CLOSING_TAG_OMISSION_RULE,
b"tfoot" => TFOOT_CLOSING_TAG_OMISSION_RULE,
b"tr" => TR_CLOSING_TAG_OMISSION_RULE,
b"td" => TD_CLOSING_TAG_OMISSION_RULE,
b"th" => TH_CLOSING_TAG_OMISSION_RULE,
};