1use css_parse::{Build, Cursor, Parser, Peek, T, keyword_set};
2use csskit_derives::{IntoCursor, ToCursors, Visitable};
3
4#[derive(ToCursors, IntoCursor, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
6#[visit]
7pub enum Tag {
8 Html(HtmlTag),
9 HtmlNonConforming(HtmlNonConformingTag),
10 HtmlNonStandard(HtmlNonStandardTag),
11 Svg(SvgTag),
12 Mathml(MathmlTag),
13 CustomElement(CustomElementTag),
14 Unknown(UnknownTag),
15}
16
17impl<'a> Peek<'a> for Tag {
18 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
19 <T![Ident]>::peek(p, c)
20 }
21}
22
23impl<'a> Build<'a> for Tag {
24 fn build(p: &Parser<'a>, c: Cursor) -> Self {
25 if HtmlTag::peek(p, c) {
26 Self::Html(HtmlTag::build(p, c))
27 } else if SvgTag::peek(p, c) {
28 Self::Svg(SvgTag::build(p, c))
29 } else if MathmlTag::peek(p, c) {
30 Self::Mathml(MathmlTag::build(p, c))
31 } else if CustomElementTag::peek(p, c) {
32 Self::CustomElement(CustomElementTag::build(p, c))
33 } else if HtmlNonConformingTag::peek(p, c) {
34 Self::HtmlNonConforming(HtmlNonConformingTag::build(p, c))
35 } else if HtmlNonStandardTag::peek(p, c) {
36 Self::HtmlNonStandard(HtmlNonStandardTag::build(p, c))
37 } else {
38 Self::Unknown(UnknownTag::build(p, c))
39 }
40 }
41}
42
43#[derive(ToCursors, IntoCursor, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
44#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
45#[visit(self)]
46pub struct CustomElementTag(T![Ident]);
47
48impl CustomElementTag {
49 const INVALID: phf::Map<&'static str, bool> = phf::phf_map! {
50 "annotation-xml" => true,
51 "color-profile" => true,
52 "font-face" => true,
53 "font-face-src" => true,
54 "font-face-uri" => true,
55 "font-face-format" => true,
56 "font-face-name" => true,
57 "missing-glyph" => true,
58 };
59}
60
61impl<'a> Peek<'a> for CustomElementTag {
62 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
63 let str = p.parse_str_lower(c);
64 if *Self::INVALID.get(str).unwrap_or(&false) {
65 return false;
66 }
67 let mut chars = str.chars();
68 if !matches!(chars.next(), Some('a'..='z')) {
69 return false;
70 }
71 let mut has_dash = false;
72 for char in chars {
73 if char == '-' {
74 has_dash = true;
75 continue;
76 }
77 if !matches!(char,
78 '.' |
79 '_' |
80 '0'..='9' |
81 'a'..='z' |
82 '\u{b7}' |
83 '\u{c0}'..='\u{d6}' |
84 '\u{d8}'..='\u{f6}' |
85 '\u{f8}'..='\u{37d}' |
86 '\u{37F}'..='\u{1fff}' |
87 '\u{200c}'..='\u{200d}' |
88 '\u{203f}'..='\u{2040}' |
89 '\u{2070}'..='\u{218f}' |
90 '\u{2c00}'..='\u{2fef}' |
91 '\u{3001}'..='\u{d7ff}' |
92 '\u{f900}'..='\u{fdcf}' |
93 '\u{fdf0}'..='\u{fffd}' |
94 '\u{10000}'..='\u{effff}'
95 ) {
96 return false;
97 }
98 }
99 has_dash
100 }
101}
102
103impl<'a> Build<'a> for CustomElementTag {
104 fn build(p: &Parser<'a>, c: Cursor) -> Self {
105 Self(<T![Ident]>::build(p, c))
106 }
107}
108
109keyword_set!(
110 #[derive(Visitable)]
112 #[visit(self)]
113 pub enum HtmlTag {
114 A: "a",
115 Abbr: "abbr",
116 Address: "address",
117 Area: "area",
118 Article: "article",
119 Aside: "aside",
120 Audio: "audio",
121 B: "b",
122 Base: "base",
123 Bdi: "bdi",
124 Bdo: "bdo",
125 Big: "big",
126 Blockquote: "blockquote",
127 Body: "body",
128 Br: "br",
129 Button: "button",
130 Canvas: "canvas",
131 Caption: "caption",
132 Center: "center",
133 Cite: "cite",
134 Code: "code",
135 Col: "col",
136 Colgroup: "colgroup",
137 Data: "data",
138 Datalist: "datalist",
139 Dd: "dd",
140 Del: "del",
141 Details: "details",
142 Dfn: "dfn",
143 Dialog: "dialog",
144 Dir: "dir",
145 Div: "div",
146 Dl: "dl",
147 Dt: "dt",
148 Em: "em",
149 Embed: "embed",
150 Fieldset: "fieldset",
151 Figcaption: "figcaption",
152 Figure: "figure",
153 Font: "font",
154 Footer: "footer",
155 Form: "form",
156 Frame: "frame",
157 Frameset: "frameset",
158 H1: "h1",
159 H2: "h2",
160 H3: "h3",
161 H4: "h4",
162 H5: "h5",
163 H6: "h6",
164 Head: "head",
165 Header: "header",
166 Hgroup: "hgroup",
167 Hr: "hr",
168 Html: "html",
169 I: "i",
170 Iframe: "iframe",
171 Img: "img",
172 Input: "input",
173 Ins: "ins",
174 Kbd: "kbd",
175 Label: "label",
176 Legend: "legend",
177 Li: "li",
178 Link: "link",
179 Main: "main",
180 Map: "map",
181 Mark: "mark",
182 Marquee: "marquee",
183 Menu: "menu",
184 Menuitem: "menuitem",
185 Meta: "meta",
186 Meter: "meter",
187 Nav: "nav",
188 Nobr: "nobr",
189 Noembed: "noembed",
190 Noframes: "noframes",
191 Noscript: "noscript",
192 Object: "object",
193 Ol: "ol",
194 Optgroup: "optgroup",
195 Option: "option",
196 Output: "output",
197 P: "p",
198 Param: "param",
199 Picture: "picture",
200 Plaintext: "plaintext",
201 Pre: "pre",
202 Progress: "progress",
203 Q: "q",
204 Rb: "rb",
205 Rp: "rp",
206 Rt: "rt",
207 Rtc: "rtc",
208 Ruby: "ruby",
209 S: "s",
210 Samp: "samp",
211 Script: "script",
212 Search: "search",
213 Section: "section",
214 Select: "select",
215 Slot: "slot",
216 Small: "small",
217 Source: "source",
218 Span: "span",
219 Strike: "strike",
220 Strong: "strong",
221 Style: "style",
222 Sub: "sub",
223 Summary: "summary",
224 Sup: "sup",
225 Table: "table",
226 Tbody: "tbody",
227 Td: "td",
228 Template: "template",
229 Textarea: "textarea",
230 Tfoot: "tfoot",
231 Th: "th",
232 Thead: "thead",
233 Time: "time",
234 Title: "title",
235 Tr: "tr",
236 Track: "track",
237 Tt: "tt",
238 U: "u",
239 Ul: "ul",
240 Var: "var",
241 Video: "video",
242 Wbr: "wbr",
243 Xmp: "xmp",
244 }
245);
246
247keyword_set!(
248 #[derive(Visitable)]
250 #[visit(self)]
251 pub enum HtmlNonConformingTag {
252 Acronym: "acronym",
253 Applet: "applet",
254 Basefont: "basefont",
255 Bgsound: "bgsound",
256 Big: "big",
257 Blink: "blink",
258 Center: "center",
259 Dir: "dir",
260 Font: "font",
261 Frame: "frame",
262 Frameset: "frameset",
263 Isindex: "isindex",
264 Keygen: "keygen",
265 Listing: "listing",
266 Marquee: "marquee",
267 Menuitem: "menuitem",
268 Multicol: "multicol",
269 Nextid: "nextid",
270 Nobr: "nobr",
271 Noembed: "noembed",
272 Noframes: "noframes",
273 Param: "param",
274 Plaintext: "plaintext",
275 Rb: "rb",
276 Rtc: "rtc",
277 Spacer: "spacer",
278 Strike: "strike",
279 Tt: "tt",
280 Xmp: "xmp",
281 }
282);
283
284keyword_set!(
285 #[derive(Visitable)]
286 #[visit(self)]
287 pub enum HtmlNonStandardTag {
288 Fencedframe: "fencedframe",
290 Portal: "portal",
292 Permission: "permission",
294 Selectedcontent: "selectedcontent",
296 }
297);
298
299keyword_set!(
300 #[derive(Visitable)]
302 #[visit(self)]
303 pub enum SvgTag {
304 A: "a",
305 Animate: "animate",
306 Animatemotion: "animatemotion",
307 Animatetransform: "animatetransform",
308 Circle: "circle",
309 Clippath: "clippath",
310 Defs: "defs",
311 Desc: "desc",
312 Discard: "discard",
313 Ellipse: "ellipse",
314 Feblend: "feblend",
315 Fecolormatrix: "fecolormatrix",
316 Fecomponenttransfer: "fecomponenttransfer",
317 Fecomposite: "fecomposite",
318 Feconvolvematrix: "feconvolvematrix",
319 Fediffuselighting: "fediffuselighting",
320 Fedisplacementmap: "fedisplacementmap",
321 Fedistantlight: "fedistantlight",
322 Fedropshadow: "fedropshadow",
323 Feflood: "feflood",
324 Fefunca: "fefunca",
325 Fefuncb: "fefuncb",
326 Fefuncg: "fefuncg",
327 Fefuncr: "fefuncr",
328 Fegaussianblur: "fegaussianblur",
329 Feimage: "feimage",
330 Femerge: "femerge",
331 Femergenode: "femergenode",
332 Femorphology: "femorphology",
333 Feoffset: "feoffset",
334 Fepointlight: "fepointlight",
335 Fespecularlighting: "fespecularlighting",
336 Fespotlight: "fespotlight",
337 Fetile: "fetile",
338 Feturbulence: "feturbulence",
339 Filter: "filter",
340 Foreignobject: "foreignobject",
341 G: "g",
342 Image: "image",
343 Line: "line",
344 Lineargradient: "lineargradient",
345 Marker: "marker",
346 Mask: "mask",
347 Metadata: "metadata",
348 Mpath: "mpath",
349 Path: "path",
350 Pattern: "pattern",
351 Polygon: "polygon",
352 Polyline: "polyline",
353 Radialgradient: "radialgradient",
354 Rect: "rect",
355 Script: "script",
356 Set: "set",
357 Stop: "stop",
358 Style: "style",
359 Svg: "svg",
360 Switch: "switch",
361 Symbol: "symbol",
362 Text: "text",
363 Textpath: "textpath",
364 Title: "title",
365 Tspan: "tspan",
366 Use: "use",
367 View: "view",
368 }
369);
370
371keyword_set!(
372 #[derive(Visitable)]
374 #[visit(self)]
375 pub enum MathmlTag {
376 Abs: "abs",
377 And: "and",
378 Annotation: "annotation",
379 AnnotationXml: "annotation-xml",
380 Apply: "apply",
381 Approx: "approx",
382 Arg: "arg",
383 Bind: "bind",
384 Bvar: "bvar",
385 Card: "card",
386 Cartesianproduct: "cartesianproduct",
387 Cbytes: "cbytes",
388 Ceiling: "ceiling",
389 Cerror: "cerror",
390 Ci: "ci",
391 Cn: "cn",
392 Codomain: "codomain",
393 Compose: "compose",
394 Condition: "condition",
395 Conjugate: "conjugate",
396 Cs: "cs",
397 Csymbol: "csymbol",
398 Curl: "curl",
399 Declare: "declare",
400 Degree: "degree",
401 Determinant: "determinant",
402 Diff: "diff",
403 Divergence: "divergence",
404 Divide: "divide",
405 Domain: "domain",
406 Domainofapplication: "domainofapplication",
407 Emptyset: "emptyset",
408 Eq: "eq",
409 Equivalent: "equivalent",
410 Exists: "exists",
411 Exp: "exp",
412 Factorial: "factorial",
413 Factorof: "factorof",
414 Floor: "floor",
415 Fn: "fn",
416 Forall: "forall",
417 Gcd: "gcd",
418 Geq: "geq",
419 Grad: "grad",
420 Gt: "gt",
421 Ident: "ident",
422 Image: "image",
423 Imaginary: "imaginary",
424 Img: "img",
425 Implies: "implies",
426 In: "in",
427 Int: "int",
428 Intersect: "intersect",
429 Interval: "interval",
430 Inverse: "inverse",
431 Lambda: "lambda",
432 Laplacian: "laplacian",
433 Lcm: "lcm",
434 Leq: "leq",
435 Limit: "limit",
436 List: "list",
437 Ln: "ln",
438 Log: "log",
439 Logbase: "logbase",
440 Lowlimit: "lowlimit",
441 Lt: "lt",
442 Maction: "maction",
443 Maligngroup: "maligngroup",
444 Malignmark: "malignmark",
445 Math: "math",
446 Matrix: "matrix",
447 Matrixrow: "matrixrow",
448 Max: "max",
449 Mean: "mean",
450 Median: "median",
451 Menclose: "menclose",
452 Merror: "merror",
453 Mfenced: "mfenced",
454 Mfrac: "mfrac",
455 Mfraction: "mfraction",
456 Mglyph: "mglyph",
457 Mi: "mi",
458 Min: "min",
459 Minus: "minus",
460 Mlabeledtr: "mlabeledtr",
461 Mlongdiv: "mlongdiv",
462 Mmultiscripts: "mmultiscripts",
463 Mn: "mn",
464 Mo: "mo",
465 Mode: "mode",
466 Moment: "moment",
467 Momentabout: "momentabout",
468 Mover: "mover",
469 Mpadded: "mpadded",
470 Mphantom: "mphantom",
471 Mprescripts: "mprescripts",
472 Mroot: "mroot",
473 Mrow: "mrow",
474 Ms: "ms",
475 Mscarries: "mscarries",
476 Mscarry: "mscarry",
477 Msgroup: "msgroup",
478 Msline: "msline",
479 Mspace: "mspace",
480 Msqrt: "msqrt",
481 Msrow: "msrow",
482 Mstack: "mstack",
483 Mstyle: "mstyle",
484 Msub: "msub",
485 Msubsup: "msubsup",
486 Msup: "msup",
487 Mtable: "mtable",
488 Mtd: "mtd",
489 Mtext: "mtext",
490 Mtr: "mtr",
491 Munder: "munder",
492 Munderover: "munderover",
493 Neq: "neq",
494 None: "none",
495 Not: "not",
496 Notin: "notin",
497 Notprsubset: "notprsubset",
498 Notsubset: "notsubset",
499 Or: "or",
500 Otherwise: "otherwise",
501 Outerproduct: "outerproduct",
502 Partialdiff: "partialdiff",
503 Piece: "piece",
504 Piecewise: "piecewise",
505 Plus: "plus",
506 Power: "power",
507 Product: "product",
508 Prsubset: "prsubset",
509 Quotient: "quotient",
510 Real: "real",
511 Reln: "reln",
512 Rem: "rem",
513 Root: "root",
514 Scalarproduct: "scalarproduct",
515 Sdev: "sdev",
516 Selector: "selector",
517 Semantics: "semantics",
518 Sep: "sep",
519 Set: "set",
520 Setdiff: "setdiff",
521 Share: "share",
522 Sin: "sin",
523 Subset: "subset",
524 Sum: "sum",
525 Tendsto: "tendsto",
526 Times: "times",
527 Transpose: "transpose",
528 Union: "union",
529 Uplimit: "uplimit",
530 Variance: "variance",
531 Vector: "vector",
532 Vectorproduct: "vectorproduct",
533 Xo: "xo",
534 }
535);
536
537#[derive(ToCursors, IntoCursor, Visitable, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
538#[cfg_attr(feature = "serde", derive(serde::Serialize), serde())]
539#[visit(self)]
540pub struct UnknownTag(T![Ident]);
541
542impl<'a> Peek<'a> for UnknownTag {
543 fn peek(p: &Parser<'a>, c: Cursor) -> bool {
544 <T![Ident]>::peek(p, c)
545 }
546}
547
548impl<'a> Build<'a> for UnknownTag {
549 fn build(p: &Parser<'a>, c: Cursor) -> Self {
550 Self(<T![Ident]>::build(p, c))
551 }
552}
553
554#[cfg(test)]
555mod tests {
556 use super::*;
557 use css_parse::assert_parse;
558
559 #[test]
560 fn size_test() {
561 assert_eq!(std::mem::size_of::<Tag>(), 20);
562 assert_eq!(std::mem::size_of::<HtmlTag>(), 16);
563 assert_eq!(std::mem::size_of::<SvgTag>(), 16);
564 assert_eq!(std::mem::size_of::<MathmlTag>(), 16);
565 assert_eq!(std::mem::size_of::<CustomElementTag>(), 12);
566 assert_eq!(std::mem::size_of::<HtmlNonConformingTag>(), 16);
567 assert_eq!(std::mem::size_of::<HtmlNonStandardTag>(), 16);
568 }
569
570 #[test]
571 fn test_writes() {
572 assert_parse!(Tag, "div");
573 }
574}