1use crate::{
3 hash::{ComponentHasher, HashComponent, HashEntry, sort_map_entries},
4 serial::ReadFrom,
5 translations_registry::TRANSLATIONS,
6};
7use simdnbt::owned::read_tag;
8use std::io::{self, Cursor};
9use text_components::{
10 TextComponent,
11 content::{Content, NbtSource, Object, Resolvable},
12 custom::CustomData,
13 format::Format,
14 interactivity::{ClickEvent, HoverEvent},
15 resolving::TextResolutor,
16};
17
18pub struct DisplayResolutor;
20impl TextResolutor for DisplayResolutor {
21 fn resolve_content(&self, resolvable: &Resolvable) -> TextComponent {
22 TextComponent {
23 content: Content::Resolvable(resolvable.clone()),
24 ..Default::default()
25 }
26 }
27
28 fn resolve_custom(&self, _data: &CustomData) -> Option<TextComponent> {
29 None
30 }
31
32 fn translate(&self, key: &str) -> Option<String> {
33 TRANSLATIONS.get(key).map(ToString::to_string)
34 }
35}
36
37impl ReadFrom for TextComponent {
38 fn read(data: &mut Cursor<&[u8]>) -> io::Result<Self> {
39 use crate::codec::VarInt;
40
41 let nbt_length = VarInt::read(data)?.0 as usize;
43
44 if nbt_length == 0 {
45 return Ok(Self::new());
47 }
48
49 let nbt_tag =
51 read_tag(data).map_err(|e| io::Error::other(format!("Failed to read NBT: {e:?}")))?;
52
53 Self::from_nbt(&nbt_tag)
54 .ok_or_else(|| io::Error::other("Failed to parse TextComponent from NBT"))
55 }
56}
57
58impl HashComponent for TextComponent {
59 fn hash_component(&self, hasher: &mut ComponentHasher) {
60 if let Content::Text { text } = &self.content
66 && self.format.is_none()
67 && self.interactions.is_none()
68 && self.children.is_empty()
69 {
70 hasher.put_string(text);
71 return;
72 }
73 hash_component_as_map(self, hasher);
75 }
76}
77
78fn hash_component_as_map(component: &TextComponent, hasher: &mut ComponentHasher) {
80 let mut entries: Vec<HashEntry> = Vec::new();
82
83 hash_content_fields(&component.content, &mut entries);
85
86 hash_format_fields(&component.format, &mut entries);
88
89 if let Some(insertion) = &component.interactions.insertion {
90 let mut key_hasher = ComponentHasher::new();
91 key_hasher.put_string("insertion");
92 let mut value_hasher = ComponentHasher::new();
93 value_hasher.put_string(insertion);
94 entries.push(HashEntry::new(key_hasher, value_hasher));
95 }
96
97 if let Some(hover_event) = &component.interactions.hover {
98 let mut key_hasher = ComponentHasher::new();
99 key_hasher.put_string("hover_event");
100 let mut value_hasher = ComponentHasher::new();
101 hash_hover_fields(hover_event, &mut value_hasher);
102 entries.push(HashEntry::new(key_hasher, value_hasher));
103 }
104
105 if let Some(click_event) = &component.interactions.click {
106 let mut key_hasher = ComponentHasher::new();
107 key_hasher.put_string("click_event");
108 let mut value_hasher = ComponentHasher::new();
109 hash_click_fields(click_event, &mut value_hasher);
110 entries.push(HashEntry::new(key_hasher, value_hasher));
111 }
112
113 if !component.children.is_empty() {
115 let mut key_hasher = ComponentHasher::new();
116 key_hasher.put_string("extra");
117 let mut value_hasher = ComponentHasher::new();
118 value_hasher.start_list();
119 for child in &component.children {
120 let child_hash = child.compute_hash();
122 value_hasher.put_raw_bytes(&child_hash.to_le_bytes());
123 }
124 value_hasher.end_list();
125 entries.push(HashEntry::new(key_hasher, value_hasher));
126 }
127
128 sort_map_entries(&mut entries);
130
131 hasher.start_map();
134 for entry in entries {
135 hasher.put_raw_bytes(&entry.key_bytes);
136 hasher.put_raw_bytes(&entry.value_bytes);
137 }
138 hasher.end_map();
139}
140
141#[expect(
142 clippy::too_many_lines,
143 reason = "each Content variant requires distinct hashing logic; splitting would hurt readability"
144)]
145fn hash_content_fields(content: &Content, entries: &mut Vec<HashEntry>) {
146 match content {
147 Content::Text { text } => {
148 let mut key_hasher = ComponentHasher::new();
149 key_hasher.put_string("text");
150 let mut value_hasher = ComponentHasher::new();
151 value_hasher.put_string(text);
152 entries.push(HashEntry::new(key_hasher, value_hasher));
153 }
154 Content::Translate(message) => {
155 {
157 let mut key_hasher = ComponentHasher::new();
158 key_hasher.put_string("translate");
159 let mut value_hasher = ComponentHasher::new();
160 value_hasher.put_string(&message.key);
161 entries.push(HashEntry::new(key_hasher, value_hasher));
162 }
163 if let Some(fallback) = &message.fallback {
165 let mut key_hasher = ComponentHasher::new();
166 key_hasher.put_string("fallback");
167 let mut value_hasher = ComponentHasher::new();
168 value_hasher.put_string(fallback);
169 entries.push(HashEntry::new(key_hasher, value_hasher));
170 }
171 if let Some(args) = &message.args
173 && !args.is_empty()
174 {
175 let mut key_hasher = ComponentHasher::new();
176 key_hasher.put_string("with");
177 let mut value_hasher = ComponentHasher::new();
178 value_hasher.start_list();
179 for arg in args {
180 let arg_hash = arg.compute_hash();
182 value_hasher.put_raw_bytes(&arg_hash.to_le_bytes());
183 }
184 value_hasher.end_list();
185 entries.push(HashEntry::new(key_hasher, value_hasher));
186 }
187 }
188 Content::Keybind { keybind } => {
189 let mut key_hasher = ComponentHasher::new();
190 key_hasher.put_string("keybind");
191 let mut value_hasher = ComponentHasher::new();
192 value_hasher.put_string(keybind);
193 entries.push(HashEntry::new(key_hasher, value_hasher));
194 }
195 Content::Object(Object::Atlas { atlas, sprite }) => {
196 {
197 let mut key_hasher = ComponentHasher::new();
198 key_hasher.put_string("sprite");
199 let mut value_hasher = ComponentHasher::new();
200 value_hasher.put_string(sprite);
201 entries.push(HashEntry::new(key_hasher, value_hasher));
202 }
203 if let Some(atlas) = atlas {
204 let mut key_hasher = ComponentHasher::new();
205 key_hasher.put_string("atlas");
206 let mut value_hasher = ComponentHasher::new();
207 value_hasher.put_string(atlas);
208 entries.push(HashEntry::new(key_hasher, value_hasher));
209 }
210 }
211 Content::Object(Object::Player { player, hat }) => {
212 {
213 let mut inner_entries: Vec<HashEntry> = Vec::new();
214 if let Some(id) = &player.id {
215 let mut key_hasher = ComponentHasher::new();
216 key_hasher.put_string("id");
217 let mut value_hasher = ComponentHasher::new();
218 value_hasher.put_int_array(id);
219 inner_entries.push(HashEntry::new(key_hasher, value_hasher));
220 }
221 if let Some(name) = &player.name {
222 let mut key_hasher = ComponentHasher::new();
223 key_hasher.put_string("name");
224 let mut value_hasher = ComponentHasher::new();
225 value_hasher.put_string(name);
226 inner_entries.push(HashEntry::new(key_hasher, value_hasher));
227 }
228 if let Some(texture) = &player.texture {
229 let mut key_hasher = ComponentHasher::new();
230 key_hasher.put_string("texture");
231 let mut value_hasher = ComponentHasher::new();
232 value_hasher.put_string(texture);
233 inner_entries.push(HashEntry::new(key_hasher, value_hasher));
234 }
235 if !player.properties.is_empty() {
236 let mut key_hasher = ComponentHasher::new();
237 key_hasher.put_string("properties");
238 let mut value_hasher = ComponentHasher::new();
239 value_hasher.start_list();
240 for property in &player.properties {
241 let mut entries: Vec<HashEntry> = Vec::new();
242 {
243 let mut key_hasher = ComponentHasher::new();
244 key_hasher.put_string("name");
245 let mut value_hasher = ComponentHasher::new();
246 value_hasher.put_string(&property.name);
247 entries.push(HashEntry::new(key_hasher, value_hasher));
248 }
249 {
250 let mut key_hasher = ComponentHasher::new();
251 key_hasher.put_string("value");
252 let mut value_hasher = ComponentHasher::new();
253 value_hasher.put_string(&property.value);
254 entries.push(HashEntry::new(key_hasher, value_hasher));
255 }
256 if let Some(signature) = &property.signature {
257 let mut key_hasher = ComponentHasher::new();
258 key_hasher.put_string("signature");
259 let mut value_hasher = ComponentHasher::new();
260 value_hasher.put_string(signature);
261 entries.push(HashEntry::new(key_hasher, value_hasher));
262 }
263
264 sort_map_entries(&mut entries);
266 let mut hasher = ComponentHasher::new();
267 hasher.start_map();
268 for entry in entries {
269 hasher.put_raw_bytes(&entry.key_bytes);
270 hasher.put_raw_bytes(&entry.value_bytes);
271 }
272 hasher.end_map();
273 let property_hash = hasher.finish();
275 value_hasher.put_raw_bytes(&property_hash.to_le_bytes());
276 }
277 value_hasher.end_list();
278 inner_entries.push(HashEntry::new(key_hasher, value_hasher));
279 }
280 sort_map_entries(&mut inner_entries);
282
283 let mut key_hasher = ComponentHasher::new();
285 key_hasher.put_string("player");
286 let mut value_hasher = ComponentHasher::new();
287 value_hasher.start_map();
288 for entry in inner_entries {
289 value_hasher.put_raw_bytes(&entry.key_bytes);
290 value_hasher.put_raw_bytes(&entry.value_bytes);
291 }
292 value_hasher.end_map();
293 entries.push(HashEntry::new(key_hasher, value_hasher));
294 }
295 {
296 let mut key_hasher = ComponentHasher::new();
297 key_hasher.put_string("hat");
298 let mut value_hasher = ComponentHasher::new();
299 value_hasher.put_bool(*hat);
300 entries.push(HashEntry::new(key_hasher, value_hasher));
301 }
302 }
303 Content::Resolvable(Resolvable::Entity {
304 selector,
305 separator,
306 }) => {
307 {
309 let mut key_hasher = ComponentHasher::new();
310 key_hasher.put_string("selector");
311 let mut value_hasher = ComponentHasher::new();
312 value_hasher.put_string(selector);
313 entries.push(HashEntry::new(key_hasher, value_hasher));
314 }
315 {
317 let mut key_hasher = ComponentHasher::new();
318 key_hasher.put_string("separator");
319 let mut value_hasher = ComponentHasher::new();
320 separator.hash_component(&mut value_hasher);
321 entries.push(HashEntry::new(key_hasher, value_hasher));
322 }
323 }
324 Content::Resolvable(Resolvable::Scoreboard {
325 selector,
326 objective,
327 }) => {
328 let mut inner_entries: Vec<HashEntry> = Vec::new();
330 {
331 let mut key_hasher = ComponentHasher::new();
332 key_hasher.put_string("name");
333 let mut value_hasher = ComponentHasher::new();
334 value_hasher.put_string(selector);
335 inner_entries.push(HashEntry::new(key_hasher, value_hasher));
336 }
337 {
338 let mut key_hasher = ComponentHasher::new();
339 key_hasher.put_string("objective");
340 let mut value_hasher = ComponentHasher::new();
341 value_hasher.put_string(objective);
342 inner_entries.push(HashEntry::new(key_hasher, value_hasher));
343 }
344 sort_map_entries(&mut inner_entries);
346
347 let mut key_hasher = ComponentHasher::new();
349 key_hasher.put_string("score");
350 let mut value_hasher = ComponentHasher::new();
351 value_hasher.start_map();
352 for entry in inner_entries {
353 value_hasher.put_raw_bytes(&entry.key_bytes);
354 value_hasher.put_raw_bytes(&entry.value_bytes);
355 }
356 value_hasher.end_map();
357 entries.push(HashEntry::new(key_hasher, value_hasher));
358 }
359 Content::Resolvable(Resolvable::NBT {
360 path,
361 interpret,
362 separator,
363 source,
364 }) => {
365 {
367 let mut key_hasher = ComponentHasher::new();
368 key_hasher.put_string("nbt");
369 let mut value_hasher = ComponentHasher::new();
370 value_hasher.put_string(path);
371 entries.push(HashEntry::new(key_hasher, value_hasher));
372 }
373 if let Some(interpret) = interpret {
375 let mut key_hasher = ComponentHasher::new();
376 key_hasher.put_string("interpret");
377 let mut value_hasher = ComponentHasher::new();
378 value_hasher.put_bool(*interpret);
379 entries.push(HashEntry::new(key_hasher, value_hasher));
380 }
381 {
383 let mut key_hasher = ComponentHasher::new();
384 key_hasher.put_string("separator");
385 let mut value_hasher = ComponentHasher::new();
386 separator.hash_component(&mut value_hasher);
387 entries.push(HashEntry::new(key_hasher, value_hasher));
388 }
389 match source {
391 NbtSource::Entity(selector) => {
392 let mut key_hasher = ComponentHasher::new();
393 key_hasher.put_string("entity");
394 let mut value_hasher = ComponentHasher::new();
395 value_hasher.put_string(selector);
396 entries.push(HashEntry::new(key_hasher, value_hasher));
397 }
398 NbtSource::Block(pos) => {
399 let mut key_hasher = ComponentHasher::new();
400 key_hasher.put_string("block");
401 let mut value_hasher = ComponentHasher::new();
402 value_hasher.put_string(pos);
403 entries.push(HashEntry::new(key_hasher, value_hasher));
404 }
405 NbtSource::Storage(id) => {
406 let mut key_hasher = ComponentHasher::new();
407 key_hasher.put_string("storage");
408 let mut value_hasher = ComponentHasher::new();
409 value_hasher.put_string(id);
410 entries.push(HashEntry::new(key_hasher, value_hasher));
411 }
412 }
413 }
414 Content::Custom(_custom_data) => {
415 let mut key_hasher = ComponentHasher::new();
419 key_hasher.put_string("text");
420 let mut value_hasher = ComponentHasher::new();
421 value_hasher.put_string("");
422 entries.push(HashEntry::new(key_hasher, value_hasher));
423 }
424 }
425}
426
427fn hash_format_fields(format: &Format, entries: &mut Vec<HashEntry>) {
430 if let Some(color) = &format.color {
432 let mut key_hasher = ComponentHasher::new();
433 key_hasher.put_string("color");
434 let mut value_hasher = ComponentHasher::new();
435 value_hasher.put_string(&color.to_string());
436 entries.push(HashEntry::new(key_hasher, value_hasher));
437 }
438
439 if let Some(shadow_color) = &format.shadow_color {
441 let mut key_hasher = ComponentHasher::new();
442 key_hasher.put_string("shadow_color");
443 let mut value_hasher = ComponentHasher::new();
444 value_hasher.put_long(*shadow_color);
445 entries.push(HashEntry::new(key_hasher, value_hasher));
446 }
447
448 if let Some(bold) = format.bold {
450 let mut key_hasher = ComponentHasher::new();
451 key_hasher.put_string("bold");
452 let mut value_hasher = ComponentHasher::new();
453 value_hasher.put_bool(bold);
454 entries.push(HashEntry::new(key_hasher, value_hasher));
455 }
456
457 if let Some(italic) = format.italic {
459 let mut key_hasher = ComponentHasher::new();
460 key_hasher.put_string("italic");
461 let mut value_hasher = ComponentHasher::new();
462 value_hasher.put_bool(italic);
463 entries.push(HashEntry::new(key_hasher, value_hasher));
464 }
465
466 if let Some(underlined) = format.underlined {
468 let mut key_hasher = ComponentHasher::new();
469 key_hasher.put_string("underlined");
470 let mut value_hasher = ComponentHasher::new();
471 value_hasher.put_bool(underlined);
472 entries.push(HashEntry::new(key_hasher, value_hasher));
473 }
474
475 if let Some(strikethrough) = format.strikethrough {
477 let mut key_hasher = ComponentHasher::new();
478 key_hasher.put_string("strikethrough");
479 let mut value_hasher = ComponentHasher::new();
480 value_hasher.put_bool(strikethrough);
481 entries.push(HashEntry::new(key_hasher, value_hasher));
482 }
483
484 if let Some(obfuscated) = format.obfuscated {
486 let mut key_hasher = ComponentHasher::new();
487 key_hasher.put_string("obfuscated");
488 let mut value_hasher = ComponentHasher::new();
489 value_hasher.put_bool(obfuscated);
490 entries.push(HashEntry::new(key_hasher, value_hasher));
491 }
492
493 if let Some(font) = &format.font {
495 let mut key_hasher = ComponentHasher::new();
496 key_hasher.put_string("font");
497 let mut value_hasher = ComponentHasher::new();
498 value_hasher.put_string(font);
499 entries.push(HashEntry::new(key_hasher, value_hasher));
500 }
501}
502
503fn hash_hover_fields(event: &HoverEvent, hasher: &mut ComponentHasher) {
504 let mut entries: Vec<HashEntry> = Vec::new();
505
506 match event {
507 HoverEvent::ShowText { value } => {
508 {
509 let mut key_hasher = ComponentHasher::new();
510 key_hasher.put_string("action");
511 let mut value_hasher = ComponentHasher::new();
512 value_hasher.put_string("show_text");
513 entries.push(HashEntry::new(key_hasher, value_hasher));
514 }
515 {
516 let mut key_hasher = ComponentHasher::new();
517 key_hasher.put_string("value");
518 let mut value_hasher = ComponentHasher::new();
519 hash_component_as_map(value, &mut value_hasher);
520 entries.push(HashEntry::new(key_hasher, value_hasher));
521 }
522 }
523 HoverEvent::ShowItem {
524 id,
525 count,
526 components,
527 } => {
528 {
529 let mut key_hasher = ComponentHasher::new();
530 key_hasher.put_string("action");
531 let mut value_hasher = ComponentHasher::new();
532 value_hasher.put_string("show_item");
533 entries.push(HashEntry::new(key_hasher, value_hasher));
534 }
535 {
536 let mut key_hasher = ComponentHasher::new();
537 key_hasher.put_string("id");
538 let mut value_hasher = ComponentHasher::new();
539 value_hasher.put_string(id);
540 entries.push(HashEntry::new(key_hasher, value_hasher));
541 }
542 if let Some(count) = count {
543 let mut key_hasher = ComponentHasher::new();
544 key_hasher.put_string("count");
545 let mut value_hasher = ComponentHasher::new();
546 value_hasher.put_int(*count);
547 entries.push(HashEntry::new(key_hasher, value_hasher));
548 }
549 if let Some(components) = components {
550 let mut key_hasher = ComponentHasher::new();
551 key_hasher.put_string("components");
552 let mut value_hasher = ComponentHasher::new();
553 value_hasher.put_string(components);
554 entries.push(HashEntry::new(key_hasher, value_hasher));
555 }
556 }
557 HoverEvent::ShowEntity { name, id, uuid } => {
558 {
559 let mut key_hasher = ComponentHasher::new();
560 key_hasher.put_string("action");
561 let mut value_hasher = ComponentHasher::new();
562 value_hasher.put_string("show_entity");
563 entries.push(HashEntry::new(key_hasher, value_hasher));
564 }
565 {
566 let mut key_hasher = ComponentHasher::new();
567 key_hasher.put_string("id");
568 let mut value_hasher = ComponentHasher::new();
569 value_hasher.put_string(id);
570 entries.push(HashEntry::new(key_hasher, value_hasher));
571 }
572 {
573 let mut key_hasher = ComponentHasher::new();
574 key_hasher.put_string("uuid");
575 let mut value_hasher = ComponentHasher::new();
576 value_hasher.put_string(&uuid.to_string());
577 entries.push(HashEntry::new(key_hasher, value_hasher));
578 }
579 if let Some(name) = name {
580 let mut key_hasher = ComponentHasher::new();
581 key_hasher.put_string("name");
582 let mut value_hasher = ComponentHasher::new();
583 hash_component_as_map(name, &mut value_hasher);
584 entries.push(HashEntry::new(key_hasher, value_hasher));
585 }
586 }
587 }
588
589 sort_map_entries(&mut entries);
591
592 hasher.start_map();
594 for entry in entries {
595 hasher.put_raw_bytes(&entry.key_bytes);
596 hasher.put_raw_bytes(&entry.value_bytes);
597 }
598 hasher.end_map();
599}
600
601#[expect(
602 clippy::too_many_lines,
603 reason = "each ClickEvent variant requires distinct hashing logic; splitting would hurt readability"
604)]
605fn hash_click_fields(event: &ClickEvent, hasher: &mut ComponentHasher) {
606 let mut entries: Vec<HashEntry> = Vec::new();
607
608 match event {
609 ClickEvent::OpenUrl { url } => {
610 {
611 let mut key_hasher = ComponentHasher::new();
612 key_hasher.put_string("action");
613 let mut value_hasher = ComponentHasher::new();
614 value_hasher.put_string("open_url");
615 entries.push(HashEntry::new(key_hasher, value_hasher));
616 }
617 {
618 let mut key_hasher = ComponentHasher::new();
619 key_hasher.put_string("url");
620 let mut value_hasher = ComponentHasher::new();
621 value_hasher.put_string(url);
622 entries.push(HashEntry::new(key_hasher, value_hasher));
623 }
624 }
625 ClickEvent::RunCommand { command } => {
626 {
627 let mut key_hasher = ComponentHasher::new();
628 key_hasher.put_string("action");
629 let mut value_hasher = ComponentHasher::new();
630 value_hasher.put_string("run_command");
631 entries.push(HashEntry::new(key_hasher, value_hasher));
632 }
633 {
634 let mut key_hasher = ComponentHasher::new();
635 key_hasher.put_string("command");
636 let mut value_hasher = ComponentHasher::new();
637 value_hasher.put_string(command);
638 entries.push(HashEntry::new(key_hasher, value_hasher));
639 }
640 }
641 ClickEvent::SuggestCommand { command } => {
642 {
643 let mut key_hasher = ComponentHasher::new();
644 key_hasher.put_string("action");
645 let mut value_hasher = ComponentHasher::new();
646 value_hasher.put_string("suggest_command");
647 entries.push(HashEntry::new(key_hasher, value_hasher));
648 }
649 {
650 let mut key_hasher = ComponentHasher::new();
651 key_hasher.put_string("command");
652 let mut value_hasher = ComponentHasher::new();
653 value_hasher.put_string(command);
654 entries.push(HashEntry::new(key_hasher, value_hasher));
655 }
656 }
657 ClickEvent::ChangePage { page } => {
658 {
659 let mut key_hasher = ComponentHasher::new();
660 key_hasher.put_string("action");
661 let mut value_hasher = ComponentHasher::new();
662 value_hasher.put_string("change_page");
663 entries.push(HashEntry::new(key_hasher, value_hasher));
664 }
665 {
666 let mut key_hasher = ComponentHasher::new();
667 key_hasher.put_string("page");
668 let mut value_hasher = ComponentHasher::new();
669 value_hasher.put_int(*page);
670 entries.push(HashEntry::new(key_hasher, value_hasher));
671 }
672 }
673 ClickEvent::CopyToClipboard { value } => {
674 {
675 let mut key_hasher = ComponentHasher::new();
676 key_hasher.put_string("action");
677 let mut value_hasher = ComponentHasher::new();
678 value_hasher.put_string("copy_to_clipboard");
679 entries.push(HashEntry::new(key_hasher, value_hasher));
680 }
681 {
682 let mut key_hasher = ComponentHasher::new();
683 key_hasher.put_string("value");
684 let mut value_hasher = ComponentHasher::new();
685 value_hasher.put_string(value);
686 entries.push(HashEntry::new(key_hasher, value_hasher));
687 }
688 }
689 ClickEvent::ShowDialog { dialog } => {
690 {
691 let mut key_hasher = ComponentHasher::new();
692 key_hasher.put_string("action");
693 let mut value_hasher = ComponentHasher::new();
694 value_hasher.put_string("show_dialog");
695 entries.push(HashEntry::new(key_hasher, value_hasher));
696 }
697 {
698 let mut key_hasher = ComponentHasher::new();
699 key_hasher.put_string("dialog");
700 let mut value_hasher = ComponentHasher::new();
701 value_hasher.put_string(dialog);
702 entries.push(HashEntry::new(key_hasher, value_hasher));
703 }
704 }
705 ClickEvent::Custom(custom_data) => {
706 {
707 let mut key_hasher = ComponentHasher::new();
708 key_hasher.put_string("action");
709 let mut value_hasher = ComponentHasher::new();
710 value_hasher.put_string("custom");
711 entries.push(HashEntry::new(key_hasher, value_hasher));
712 }
713 {
714 let mut key_hasher = ComponentHasher::new();
715 key_hasher.put_string("id");
716 let mut value_hasher = ComponentHasher::new();
717 value_hasher.put_string(&custom_data.id);
718 entries.push(HashEntry::new(key_hasher, value_hasher));
719 }
720 }
721 }
722
723 sort_map_entries(&mut entries);
725
726 hasher.start_map();
728 for entry in entries {
729 hasher.put_raw_bytes(&entry.key_bytes);
730 hasher.put_raw_bytes(&entry.value_bytes);
731 }
732 hasher.end_map();
733}