1use super::{DecodedTemplateOp, TemplateAnchor, TemplatePath};
2use crate::op::TemplateOp;
3
4#[cfg_attr(feature = "serialize", derive(serde::Serialize, serde::Deserialize))]
9#[derive(Clone, Copy, Eq)]
10pub struct Template {
11 #[cfg_attr(
13 feature = "serialize",
14 serde(deserialize_with = "super::serialization::deserialize_leaky")
15 )]
16 ops: &'static [TemplateOp],
17
18 #[cfg_attr(
20 feature = "serialize",
21 serde(deserialize_with = "super::serialization::deserialize_strings_leaky")
22 )]
23 strings: &'static [&'static str],
24
25 #[cfg_attr(
27 feature = "serialize",
28 serde(deserialize_with = "super::serialization::deserialize_leaky")
29 )]
30 anchors: &'static [TemplateAnchor],
31
32 hash: u64,
45}
46
47#[derive(Clone, Copy)]
49pub enum StaticTemplateNode<'a> {
50 Element(StaticTemplateElement<'a>),
52 Text(StaticTemplateText<'a>),
54}
55
56#[derive(Clone, Copy)]
58pub struct StaticTemplateElement<'a> {
59 template: &'a Template,
60 op: usize,
61}
62
63impl<'a> StaticTemplateElement<'a> {
64 pub fn op(self) -> usize {
66 self.op
67 }
68
69 pub fn tag(self) -> &'static str {
71 self.template
72 .element_meta_at_op(self.op)
73 .expect("static element")
74 .0
75 }
76
77 pub fn namespace(self) -> Option<&'static str> {
79 self.template
80 .element_meta_at_op(self.op)
81 .expect("static element")
82 .1
83 }
84
85 pub fn attributes(self) -> StaticTemplateAttributeIter<'a> {
87 let (cursor, end, _) = self
88 .template
89 .element_attr_child_ops(self.op)
90 .expect("static element");
91 StaticTemplateAttributeIter {
92 template: self.template,
93 cursor,
94 end,
95 }
96 }
97
98 pub fn children(self) -> StaticTemplateNodeIter<'a> {
100 let (_, cursor, end) = self
101 .template
102 .element_attr_child_ops(self.op)
103 .expect("static element");
104 StaticTemplateNodeIter {
105 template: self.template,
106 cursor,
107 end,
108 }
109 }
110
111 pub fn attribute_value(
113 self,
114 key: (&'static str, Option<&'static str>),
115 ) -> Option<&'static str> {
116 self.attributes()
117 .filter(|attr| (attr.name, attr.namespace) == key)
118 .map(|attr| attr.value)
119 .last()
120 }
121}
122
123#[derive(Clone, Copy)]
125pub struct StaticTemplateText<'a> {
126 template: &'a Template,
127 op: usize,
128}
129
130impl StaticTemplateText<'_> {
131 pub fn op(self) -> usize {
133 self.op
134 }
135
136 pub fn text(self) -> &'static str {
138 self.template
139 .static_text_at_op(self.op)
140 .expect("static text")
141 }
142}
143
144#[derive(Clone, Copy, Debug, PartialEq, Eq)]
146pub struct StaticTemplateAttribute {
147 pub name: &'static str,
149 pub value: &'static str,
151 pub namespace: Option<&'static str>,
153}
154
155#[derive(Clone, Copy)]
157pub struct StaticTemplateNodeIter<'a> {
158 template: &'a Template,
159 cursor: usize,
160 end: usize,
161}
162
163impl<'a> Iterator for StaticTemplateNodeIter<'a> {
164 type Item = StaticTemplateNode<'a>;
165
166 fn next(&mut self) -> Option<Self::Item> {
167 while self.cursor < self.end {
168 let op = self.cursor;
169 self.cursor = self.template.next_sibling_op(op);
170 if let Some(node) = self.template.static_node(op) {
171 return Some(node);
172 }
173 }
174 None
175 }
176}
177
178#[derive(Clone, Copy)]
180pub struct StaticTemplateAttributeIter<'a> {
181 template: &'a Template,
182 cursor: usize,
183 end: usize,
184}
185
186impl Iterator for StaticTemplateAttributeIter<'_> {
187 type Item = StaticTemplateAttribute;
188
189 fn next(&mut self) -> Option<Self::Item> {
190 while self.cursor < self.end {
191 let op = self.cursor;
192 self.cursor += self.template.attr_op_len(op).unwrap_or(1);
193 if let Some((name, value, namespace)) = self.template.static_attr_at_op(op) {
194 return Some(StaticTemplateAttribute {
195 name,
196 value,
197 namespace,
198 });
199 }
200 }
201 None
202 }
203}
204
205impl std::fmt::Debug for Template {
206 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
207 f.debug_struct("Template").finish_non_exhaustive()
208 }
209}
210
211impl Template {
212 pub(crate) const fn new(
214 ops: &'static [TemplateOp],
215 strings: &'static [&'static str],
216 anchors: &'static [TemplateAnchor],
217 ) -> Self {
218 Self {
219 ops,
220 strings,
221 anchors,
222 hash: Self::compute_hash(ops, strings, anchors),
223 }
224 }
225
226 pub const fn strings(&self) -> &'static [&'static str] {
228 self.strings
229 }
230
231 pub fn decoded_ops(&self) -> impl ExactSizeIterator<Item = DecodedTemplateOp> + '_ {
233 self.ops.iter().map(|op| op.decode())
234 }
235
236 pub const fn anchors(&self) -> &'static [TemplateAnchor] {
238 self.anchors
239 }
240
241 pub fn static_node(&self, op: usize) -> Option<StaticTemplateNode<'_>> {
243 self.static_element(op)
244 .map(StaticTemplateNode::Element)
245 .or_else(|| self.static_text(op).map(StaticTemplateNode::Text))
246 }
247
248 pub fn static_element(&self, op: usize) -> Option<StaticTemplateElement<'_>> {
250 self.element_meta_at_op(op)
251 .is_some()
252 .then_some(StaticTemplateElement { template: self, op })
253 }
254
255 pub fn static_text(&self, op: usize) -> Option<StaticTemplateText<'_>> {
257 self.static_text_at_op(op)
258 .is_some()
259 .then_some(StaticTemplateText { template: self, op })
260 }
261
262 fn string(&self, id: u16) -> &'static str {
264 self.strings[id as usize]
265 }
266
267 fn enter_meta(&self, op: usize) -> Option<(usize, bool)> {
269 match self.ops.get(op).map(|op| op.decode()) {
270 Some(DecodedTemplateOp::Enter { skip, namespace }) => Some((skip as usize, namespace)),
271 _ => None,
272 }
273 }
274
275 fn static_string_at_op(&self, op: usize) -> Option<&'static str> {
277 match self.ops.get(op).map(|op| op.decode()) {
278 Some(DecodedTemplateOp::Static(id)) => Some(self.string(id)),
279 _ => None,
280 }
281 }
282
283 fn element_meta_at_op(&self, op: usize) -> Option<(&'static str, Option<&'static str>)> {
284 let (_, has_namespace) = self.enter_meta(op)?;
285 let tag = self.static_string_at_op(op + 1)?;
286 let namespace = has_namespace
287 .then(|| self.static_string_at_op(op + 2))
288 .flatten();
289 Some((tag, namespace))
290 }
291
292 fn element_children_start(&self, op: usize) -> Option<usize> {
293 let (_, has_namespace) = self.enter_meta(op)?;
294 Some(op + if has_namespace { 3 } else { 2 })
295 }
296
297 fn static_attr_at_op(
298 &self,
299 op: usize,
300 ) -> Option<(&'static str, &'static str, Option<&'static str>)> {
301 let namespace = match self.ops.get(op).map(|op| op.decode()) {
302 Some(DecodedTemplateOp::Attr { namespace }) => namespace,
303 _ => return None,
304 };
305 let name = self.static_string_at_op(op + 1)?;
306 let value = self.static_string_at_op(op + 2)?;
307 let namespace = namespace
308 .then(|| self.static_string_at_op(op + 3))
309 .flatten();
310 Some((name, value, namespace))
311 }
312
313 fn static_text_at_op(&self, op: usize) -> Option<&'static str> {
314 (self.ops.get(op).map(|op| op.decode()) == Some(DecodedTemplateOp::Text))
315 .then(|| self.static_string_at_op(op + 1))
316 .flatten()
317 }
318
319 fn attr_op_len(&self, op: usize) -> Option<usize> {
320 match self.ops.get(op).map(|op| op.decode()) {
321 Some(DecodedTemplateOp::Attr { namespace: true }) => Some(4),
322 Some(DecodedTemplateOp::Attr { .. }) => Some(3),
323 _ => None,
324 }
325 }
326
327 fn element_end(&self, op: usize) -> Option<usize> {
328 let (skip, _) = self.enter_meta(op)?;
329 Some(op + skip)
330 }
331
332 fn element_attr_child_ops(&self, element_op: usize) -> Option<(usize, usize, usize)> {
333 let attr_start = self.element_children_start(element_op)?;
334 let mut cursor = attr_start;
335 let end = self.element_end(element_op)?;
336 while cursor < end {
337 if let Some(len) = self.attr_op_len(cursor) {
338 cursor += len;
339 } else {
340 break;
341 }
342 }
343 Some((attr_start, cursor, end))
344 }
345
346 pub fn root_count(&self) -> usize {
348 let mut count = 0;
349 for anchor in self.anchors.iter().copied() {
350 if anchor.parent_element_op_index().is_some() {
351 continue;
352 }
353
354 let path = anchor.static_path();
355 if !path.is_root() && !path.is_empty() {
356 continue;
357 }
358
359 if !anchor.nodes().is_empty() {
360 count += 1;
361 }
362
363 if !anchor.is_last_static_node() && path.is_root() {
364 count += 1;
365 }
366 }
367
368 count
369 }
370
371 pub fn static_node_at_path(&self, path: TemplatePath) -> Option<StaticTemplateNode<'_>> {
373 if path.is_empty() {
374 return None;
375 }
376
377 let mut start = 0;
378 let mut end = self.ops.len();
379 let path_depth = path.depth();
380
381 for (depth, segment) in path.segments().enumerate() {
382 let op = self.static_node_op_in_range(start, end, segment)?;
383 if depth + 1 == path_depth {
384 return self.static_node(op);
385 }
386
387 let (_, child_start, child_end) = self.element_attr_child_ops(op)?;
388 start = child_start;
389 end = child_end;
390 }
391
392 None
393 }
394
395 fn static_node_op_in_range(&self, start: usize, end: usize, target: usize) -> Option<usize> {
396 let mut cursor = start;
397 let mut index = 0;
398 while cursor < end {
399 let op = cursor;
400 cursor = self.next_sibling_op(op);
401 if self.static_node(op).is_none() {
402 continue;
403 }
404
405 if index == target {
406 return Some(op);
407 }
408 index += 1;
409 }
410
411 None
412 }
413
414 fn next_sibling_op(&self, op: usize) -> usize {
415 Self::next_sibling_op_in(self.ops, op)
416 }
417
418 const fn next_sibling_op_in(ops: &[TemplateOp], op: usize) -> usize {
419 match ops[op].decode() {
420 DecodedTemplateOp::Enter { skip, .. } => op + skip as usize,
421 DecodedTemplateOp::Text => op + 2,
422 DecodedTemplateOp::Attr { namespace: true } => op + 4,
423 DecodedTemplateOp::Attr { .. } => op + 3,
424 _ => op + 1,
425 }
426 }
427
428 const fn compute_hash(
431 ops: &[TemplateOp],
432 strings: &'static [&'static str],
433 anchors: &[TemplateAnchor],
434 ) -> u64 {
435 use xxhash_rust::const_xxh64::xxh64;
436
437 let mut hash = 0u64;
438
439 let mut i = 0;
441 while i < ops.len() {
442 hash = xxh64(&ops[i].bits().to_le_bytes(), hash);
443 i += 1;
444 }
445
446 hash = xxh64(&[0xA0], hash);
448 let mut i = 0;
449 while i < strings.len() {
450 let string = strings[i];
451 let bytes = string.as_bytes();
452 hash = xxh64(bytes, hash);
453 i += 1;
454 }
455
456 hash = xxh64(&[0xA1], hash);
458 let mut i = 0;
459 while i < anchors.len() {
460 let anchor = anchors[i];
461 hash = xxh64(&anchor.parent_op_index.to_le_bytes(), hash);
462 hash = xxh64(&anchor.slot_path().bits().to_le_bytes(), hash);
463 hash = xxh64(&anchor.node_start.to_le_bytes(), hash);
464 hash = xxh64(&anchor.node_end.to_le_bytes(), hash);
465 hash = xxh64(&anchor.attr_start.to_le_bytes(), hash);
466 hash = xxh64(&anchor.attr_end.to_le_bytes(), hash);
467 i += 1;
468 }
469
470 hash
471 }
472}
473
474impl std::hash::Hash for Template {
475 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
476 self.hash.hash(state);
477 }
478}
479
480impl PartialEq for Template {
481 fn eq(&self, other: &Self) -> bool {
482 self.hash == other.hash
483 }
484}
485
486impl PartialOrd for Template {
487 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
488 Some(self.cmp(other))
489 }
490}
491
492impl Ord for Template {
493 fn cmp(&self, other: &Self) -> std::cmp::Ordering {
494 self.hash.cmp(&other.hash)
495 }
496}