1use alloc::{vec::Vec, string::{String, ToString}, collections::{BTreeMap}, boxed::Box, borrow::ToOwned, fmt, format};
2use serde::{Serialize, Deserialize, Deserializer, de, Serializer};
3use serde::de::{Visitor, Error, IntoDeserializer, MapAccess};
4use serde::ser::SerializeMap;
5use serde_json::Value;
6use crate::{SerializeResult, DeserializeResult};
7
8pub type BoxedChat = Box<Chat>;
9
10#[derive(Clone, Debug, PartialEq)]
11pub enum Chat {
12 Text(TextComponent),
13 Translation(TranslationComponent),
14 Keybind(KeybindComponent),
15 Score(ScoreComponent),
16}
17
18impl Chat {
19 pub fn base(&self) -> &BaseComponent {
20 use Chat::*;
21
22 match self {
23 Text(body) => &body.base,
24 Translation(body) => &body.base,
25 Keybind(body) => &body.base,
26 Score(body) => &body.base,
27 }
28 }
29
30 pub fn siblings(&self) -> &Vec<BoxedChat> {
31 &self.base().extra
32 }
33
34 pub fn boxed(self) -> BoxedChat {
35 Box::new(self)
36 }
37
38 pub fn from_text(text: &str) -> Chat {
39 Chat::Text(TextComponent {
40 base: BaseComponent::default(),
41 text: text.to_owned(),
42 })
43 }
44
45 pub fn from_traditional(orig: &str, translate_colorcodes: bool) -> Chat {
46 TraditionalParser::new(orig, translate_colorcodes).parse()
47 }
48
49 pub fn to_traditional(&self) -> Option<String> {
50 use Chat::*;
51
52 match self {
53 Text(body) => Some(body.to_traditional()),
54 _ => None
55 }
56 }
57}
58
59struct TraditionalParser {
60 source: Vec<char>,
61 at: usize,
62 translate_colorcodes: bool,
63
64 text: String,
66 color: Option<ColorCode>,
67 bold: bool,
68 italic: bool,
69 underlined: bool,
70 strikethrough: bool,
71 obfuscated: bool,
72
73 done: Vec<TextComponent>,
75}
76
77impl TraditionalParser {
78
79 fn new(source: &str, translate_colorcodes: bool) -> Self {
80 Self {
81 source: source.chars().collect(),
82 at: 0,
83 translate_colorcodes,
84
85 text: String::new(),
86 color: None,
87 bold: false,
88 italic: false,
89 underlined: false,
90 strikethrough: false,
91 obfuscated: false,
92
93 done: Vec::new(),
94 }
95 }
96
97 fn parse(mut self) -> Chat {
98 loop {
99 if let Some(formatter) = self.consume_formatter() {
100 self.handle_formatter(formatter)
101 } else if let Some(next) = self.consume_char() {
102 self.push_next(next)
103 } else {
104 return self.finalize()
105 }
106 }
107 }
108
109 fn handle_formatter(&mut self, formatter: Formatter) {
110 use Formatter::*;
111
112 if self.has_text() {
113 self.finish_current();
114 }
115
116 match formatter {
117 Color(color) => {
118 self.finish_current();
119 self.color = Some(color);
120 }
121 Obfuscated => self.obfuscated = true,
122 Bold => self.bold = true,
123 Strikethrough => self.strikethrough = true,
124 Underline => self.underlined = true,
125 Italic => self.italic = true,
126 _ => {}
127 }
128 }
129
130 fn push_next(&mut self, next: char) {
131 self.text.push(next);
132 }
133
134 fn finish_current(&mut self) {
135 if self.has_text() {
136 let current = TextComponent {
137 text: self.text.clone(),
138 base: BaseComponent {
139 color: self.color.clone(),
140 bold: self.bold,
141 italic: self.italic,
142 underlined: self.underlined,
143 strikethrough: self.strikethrough,
144 obfuscated: self.obfuscated,
145 hover_event: None,
146 click_event: None,
147 insertion: None,
148 extra: Vec::default()
149 }
150 };
151 self.text.clear();
152 self.done.push(current);
153 }
154
155 self.reset_style();
156 }
157
158 fn reset_style(&mut self) {
159 self.bold = false;
160 self.italic = false;
161 self.underlined = false;
162 self.strikethrough = false;
163 self.obfuscated = false;
164 self.color = None;
165 }
166
167 fn has_text(&self) -> bool {
168 return !self.text.is_empty()
169 }
170
171 fn is_on_formatter(&self) -> bool {
172 self.source.get(self.at).map(move |c| {
173 let c = *c;
174 c == SECTION_SYMBOL || (self.translate_colorcodes && c == '&')
175 }).unwrap_or(false)
176 }
177
178 fn consume_char(&mut self) -> Option<char> {
179 if let Some(c) = self.source.get(self.at) {
180 self.at += 1;
181 Some(*c)
182 } else {
183 None
184 }
185 }
186
187 fn consume_formatter(&mut self) -> Option<Formatter> {
188 if self.is_on_formatter() {
189 self.consume_char()?;
190 let c = self.consume_char()?;
191 let out = Formatter::from_code(&c);
192 if out.is_none() {
193 self.at -= 1;
194 }
195
196 out
197 } else {
198 None
199 }
200 }
201
202 fn finalize(mut self) -> Chat {
203 self.finish_current();
204 self.simplify();
205 let n_components = self.done.len();
206 if n_components == 1 {
207 return Chat::Text(self.done.remove(0));
208 }
209
210 let mut top_level = TextComponent {
211 text: String::default(),
212 base: BaseComponent::default(),
213 };
214
215 if n_components > 0 {
216 top_level.base.extra.extend(
217 self.done.into_iter()
218 .map(move |component| Chat::Text(component).boxed()));
219 }
220
221 Chat::Text(top_level)
222 }
223
224 fn simplify(&mut self) {
225 let mut updated = Vec::with_capacity(self.done.len());
226 let mut last: Option<TextComponent> = None;
227 while !self.done.is_empty() {
228 let cur = self.done.remove(0);
229 if let Some(mut la) = last.take() {
230 if la.base.has_same_style_as(&cur.base) {
231 la.text.extend(cur.text.chars());
232 last = Some(la);
233 continue;
234 } else {
235 updated.push(la);
236 }
237 }
238
239 last = Some(cur);
240 }
241
242 if let Some(l) = last.take() {
243 updated.push(l);
244 }
245
246 self.done = updated;
247 }
248}
249
250#[derive(Serialize, Clone, Debug, PartialEq)]
251pub struct BaseComponent {
252 #[serde(skip_serializing_if = "should_skip_flag_field")]
253 pub bold: bool,
254 #[serde(skip_serializing_if = "should_skip_flag_field")]
255 pub italic: bool,
256 #[serde(skip_serializing_if = "should_skip_flag_field")]
257 pub underlined: bool,
258 #[serde(skip_serializing_if = "should_skip_flag_field")]
259 pub strikethrough: bool,
260 #[serde(skip_serializing_if = "should_skip_flag_field")]
261 pub obfuscated: bool,
262 #[serde(skip_serializing_if = "Option::is_none")]
263 pub color: Option<ColorCode>,
264 #[serde(skip_serializing_if = "Option::is_none")]
265 pub insertion: Option<String>,
266 #[serde(skip_serializing_if = "Option::is_none")]
267 pub click_event: Option<ChatClickEvent>,
268 #[serde(skip_serializing_if = "Option::is_none")]
269 pub hover_event: Option<ChatHoverEvent>,
270 #[serde(skip_serializing_if = "Vec::is_empty")]
271 pub extra: Vec<BoxedChat>,
272}
273
274fn should_skip_flag_field(flag: &bool) -> bool {
275 !*flag
276}
277
278impl BaseComponent {
279
280 fn has_same_style_as(&self, other: &Self) -> bool {
281 other.bold == self.bold &&
282 other.italic == self.italic &&
283 other.underlined == self.underlined &&
284 other.strikethrough == self.strikethrough &&
285 other.obfuscated == self.obfuscated &&
286 other.color.eq(&self.color)
287 }
288}
289
290impl Into<BaseComponent> for JsonComponentBase {
291 fn into(self) -> BaseComponent {
292 BaseComponent {
293 bold: self.bold.unwrap_or(false),
294 italic: self.italic.unwrap_or(false),
295 underlined: self.underlined.unwrap_or(false),
296 strikethrough: self.strikethrough.unwrap_or(false),
297 obfuscated: self.obfuscated.unwrap_or(false),
298 color: self.color,
299 insertion: self.insertion,
300 click_event: self.click_event,
301 hover_event: self.hover_event,
302 extra: self.extra.into_iter().map(move |elem| elem.boxed()).collect(),
303 }
304 }
305}
306
307#[derive(Deserialize)]
308struct JsonComponentBase {
309 pub bold: Option<bool>,
310 pub italic: Option<bool>,
311 pub underlined: Option<bool>,
312 pub strikethrough: Option<bool>,
313 pub obfuscated: Option<bool>,
314 pub color: Option<ColorCode>,
315 pub insertion: Option<String>,
316 #[serde(rename = "clickEvent")]
317 pub click_event: Option<ChatClickEvent>,
318 #[serde(rename = "hoverEvent")]
319 pub hover_event: Option<ChatHoverEvent>,
320 #[serde(default = "Vec::default")]
321 pub extra: Vec<Chat>,
322
323 #[serde(flatten)]
324 _additional: BTreeMap<String, serde_json::Value>
325}
326
327impl Default for BaseComponent {
328 fn default() -> Self {
329 Self {
330 bold: false,
331 italic: false,
332 underlined: false,
333 strikethrough: false,
334 obfuscated: false,
335 color: None,
336 insertion: None,
337 click_event: None,
338 hover_event: None,
339 extra: Vec::default(),
340 }
341 }
342}
343
344#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
345pub struct TextComponent {
346 pub text: String,
347
348 #[serde(flatten)]
349 #[serde(skip_deserializing)]
350 pub base: BaseComponent,
351}
352
353impl TextComponent {
354 pub fn to_traditional(&self) -> String {
355 let b = &self.base;
356 let text = &self.text;
357
358 let (mut buf, has_formatters) = if !text.is_empty() {
359 let formatters = self.traditional_formatters(false);
360 let has_formatters = formatters.is_some();
361 let mut buf = formatters.unwrap_or_else(|| String::new());
362 buf.extend(text.chars());
363 (buf, has_formatters)
364 } else {
365 (String::default(), false)
366 };
367
368 let mut last_had_formatters = has_formatters;
369 for extra in b.extra.iter() {
370 if let Chat::Text(child) = extra.as_ref() {
371 match child.traditional_formatters(last_had_formatters) {
372 Some(child_fmts) => {
373 last_had_formatters = true;
374 buf.extend(child_fmts.chars())
375 },
376 None => {
377 last_had_formatters = false;
378 }
379 }
380
381 buf.extend(child.text.chars());
382 }
383 }
384
385 buf
386 }
387
388 fn traditional_formatters(&self, prev_colored: bool) -> Option<String> {
389 let b = &self.base;
390 let mut buf = String::default();
391
392 if let Some(c) = b.color {
393 buf.push(SECTION_SYMBOL);
394 buf.push(c.code());
395 }
396
397 let mut apply_formatter = |b: bool, formatter: Formatter| {
398 if b {
399 buf.push(SECTION_SYMBOL);
400 buf.push(formatter.code());
401 }
402 };
403
404 apply_formatter(b.bold, Formatter::Bold);
405 apply_formatter(b.italic, Formatter::Italic);
406 apply_formatter(b.strikethrough, Formatter::Strikethrough);
407 apply_formatter(b.underlined, Formatter::Underline);
408 apply_formatter(b.obfuscated, Formatter::Obfuscated);
409
410 if buf.is_empty() {
411 if prev_colored {
412 buf.push(SECTION_SYMBOL);
413 buf.push('r');
414 Some(buf)
415 } else {
416 None
417 }
418 } else {
419 Some(buf)
420 }
421 }
422}
423
424#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
425pub struct TranslationComponent {
426 pub translate: String,
427 #[serde(skip_serializing_if = "Vec::is_empty")]
428 pub with: Vec<BoxedChat>,
429
430 #[serde(flatten)]
431 #[serde(skip_deserializing)]
432 pub base: BaseComponent,
433}
434
435#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
436pub struct KeybindComponent {
437 pub keybind: String,
438
439 #[serde(flatten)]
440 #[serde(skip_deserializing)]
441 pub base: BaseComponent
442}
443#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
444pub struct ScoreComponent {
445 pub score: ScoreComponentObjective,
446
447 #[serde(flatten)]
448 #[serde(skip_deserializing)]
449 pub base: BaseComponent
450}
451
452#[derive(Deserialize, Serialize, Clone, Debug, PartialEq)]
453pub struct ScoreComponentObjective {
454 pub name: String,
455 #[serde(skip_serializing_if = "Option::is_none")]
456 pub objective: Option<String>,
457 #[serde(skip_serializing_if = "Option::is_none")]
458 pub value: Option<String>,
459}
460
461#[derive(Clone, Debug, PartialEq)]
462pub enum ChatClickEvent {
463 OpenUrl(String),
464 RunCommand(String),
465 SuggestCommand(String),
466 ChangePage(i32)
467}
468
469impl Serialize for ChatClickEvent {
470 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
471 S: Serializer
472 {
473 let mut m = serializer.serialize_map(Some(2))?;
474
475 use ChatClickEvent::*;
476
477 m.serialize_entry("action", match self {
478 OpenUrl(_) => "open_url",
479 RunCommand(_) => "run_command",
480 SuggestCommand(_) => "suggest_command",
481 ChangePage(_) => "change_page",
482 })?;
483
484 m.serialize_key("value")?;
485
486 match self {
487 OpenUrl(body) => m.serialize_value(body),
488 RunCommand(body) => m.serialize_value(body),
489 SuggestCommand(body) => m.serialize_value(body),
490 ChangePage(body) => m.serialize_value(body),
491 }?;
492
493 m.end()
494 }
495}
496
497impl<'de> Deserialize<'de> for ChatClickEvent {
498 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where
499 D: Deserializer<'de>
500 {
501 struct V;
502
503 impl<'de> Visitor<'de> for V {
504 type Value = ChatClickEvent;
505
506 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
507 write!(f, "an event object for ChatClickEvent")
508 }
509
510 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, <A as MapAccess<'de>>::Error> where
511 A: MapAccess<'de>
512 {
513 let (action, value) = read_event(&mut map)?;
514
515 use ChatClickEvent::*;
516 match action {
517 "open_url" => match value.as_str() {
518 Some(url) => Ok(OpenUrl(url.to_owned())),
519 None => Err(A::Error::custom(format!("open_url requires string body, got {}", value)))
520 },
521 "run_command" => match value.as_str() {
522 Some(cmd) => Ok(RunCommand(cmd.to_owned())),
523 None => Err(A::Error::custom(format!("run_command requires string body, got {}", value)))
524 },
525 "suggest_command" => match value.as_str() {
526 Some(cmd) => Ok(SuggestCommand(cmd.to_owned())),
527 None => Err(A::Error::custom(format!("suggest_command requires string body, got {}", value)))
528 },
529 "change_page" => match value.as_i64() {
530 Some(v) => Ok(ChangePage(v as i32)),
531 None => Err(A::Error::custom(format!("change_page requires integer body, got {}", value)))
532 },
533 other => Err(A::Error::custom(format!("invalid click action kind {}", other)))
534 }
535 }
536 }
537
538 deserializer.deserialize_map(V)
539 }
540}
541
542#[derive(Clone, Debug, PartialEq)]
543pub enum ChatHoverEvent {
544 ShowText(BoxedChat),
545 ShowItem(Value),
546 ShowEntity(Value)
547}
548
549impl Serialize for ChatHoverEvent {
550 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
551 S: Serializer
552 {
553 let mut m = serializer.serialize_map(Some(2))?;
554
555 use ChatHoverEvent::*;
556
557 m.serialize_entry("action", match self {
558 ShowText(_) => "show_text",
559 ShowItem(_) => "show_item",
560 ShowEntity(_) => "show_entity",
561 })?;
562
563 m.serialize_key("value")?;
564
565 match self {
566 ShowText(body) => m.serialize_value(body),
567 ShowItem(body) => m.serialize_value(body),
568 ShowEntity(body) => m.serialize_value(body),
569 }?;
570
571 m.end()
572 }
573}
574
575impl<'de> Deserialize<'de> for ChatHoverEvent {
576 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where
577 D: Deserializer<'de>
578 {
579 struct V;
580
581 impl<'de> Visitor<'de> for V {
582 type Value = ChatHoverEvent;
583
584 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
585 write!(f, "an event object for ChatClickEvent")
586 }
587
588 fn visit_map<A>(self, mut map: A) -> Result<Self::Value, <A as MapAccess<'de>>::Error> where
590 A: MapAccess<'de>
591 {
592 let (action, value) = read_event(&mut map)?;
593
594 use ChatHoverEvent::*;
595 match action {
596 "show_text" => Ok(ShowText(
597 Chat::deserialize(value.into_deserializer())
598 .map_err(move |err| A::Error::custom(
599 format!("error deserializing text to show {:?}", err)))?
600 .boxed())),
601 "show_item" => Ok(ShowItem(value)),
602 "show_entity" => Ok(ShowEntity(value)),
603 other => Err(A::Error::custom(format!("invalid hover action kind {}", other)))
604 }
605 }
606 }
607
608 deserializer.deserialize_map(V)
609 }
610}
611
612pub const SECTION_SYMBOL: char = '§';
613
614#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)]
615pub enum ColorCode {
616 Black,
617 DarkBlue,
618 DarkGreen,
619 DarkAqua,
620 DarkRed,
621 DarkPurple,
622 Gold,
623 Gray,
624 DarkGray,
625 Blue,
626 Green,
627 Aqua,
628 Red,
629 LightPurple,
630 Yellow,
631 White,
632}
633
634impl ColorCode {
635 pub fn from_code(i: &char) -> Option<Self> {
636 match i {
637 '0' => Some(ColorCode::Black),
638 '1' => Some(ColorCode::DarkBlue),
639 '2' => Some(ColorCode::DarkGreen),
640 '3' => Some(ColorCode::DarkAqua),
641 '4' => Some(ColorCode::DarkRed),
642 '5' => Some(ColorCode::DarkPurple),
643 '6' => Some(ColorCode::Gold),
644 '7' => Some(ColorCode::Gray),
645 '8' => Some(ColorCode::DarkGray),
646 '9' => Some(ColorCode::Blue),
647 'a' => Some(ColorCode::Green),
648 'b' => Some(ColorCode::Aqua),
649 'c' => Some(ColorCode::Red),
650 'd' => Some(ColorCode::LightPurple),
651 'e' => Some(ColorCode::Yellow),
652 'f' => Some(ColorCode::White),
653 _ => None,
654 }
655 }
656
657 pub fn code(&self) -> char {
658 match self {
659 ColorCode::Black => '0',
660 ColorCode::DarkBlue => '1',
661 ColorCode::DarkGreen => '2',
662 ColorCode::DarkAqua => '3',
663 ColorCode::DarkRed => '4',
664 ColorCode::DarkPurple => '5',
665 ColorCode::Gold => '6',
666 ColorCode::Gray => '7',
667 ColorCode::DarkGray => '8',
668 ColorCode::Blue => '9',
669 ColorCode::Green => 'a',
670 ColorCode::Aqua => 'b',
671 ColorCode::Red => 'c',
672 ColorCode::LightPurple => 'd',
673 ColorCode::Yellow => 'e',
674 ColorCode::White => 'f',
675 }
676 }
677
678 pub fn from_name(name: &str) -> Option<Self> {
679 match name.to_ascii_lowercase().as_str() {
680 "black" => Some(ColorCode::Black),
681 "dark_blue" => Some(ColorCode::DarkBlue),
682 "dark_green" => Some(ColorCode::DarkGreen),
683 "dark_aqua" => Some(ColorCode::DarkAqua),
684 "dark_red" => Some(ColorCode::DarkRed),
685 "dark_purple" => Some(ColorCode::DarkPurple),
686 "gold" => Some(ColorCode::Gold),
687 "gray" => Some(ColorCode::Gray),
688 "dark_gray" => Some(ColorCode::DarkGray),
689 "blue" => Some(ColorCode::Blue),
690 "green" => Some(ColorCode::Green),
691 "aqua" => Some(ColorCode::Aqua),
692 "red" => Some(ColorCode::Red),
693 "light_purple" => Some(ColorCode::LightPurple),
694 "yellow" => Some(ColorCode::Yellow),
695 "white" => Some(ColorCode::White),
696 _ => None,
697 }
698 }
699
700 pub fn name(&self) -> &str {
701 match self {
702 ColorCode::Black => "black",
703 ColorCode::DarkBlue => "dark_blue",
704 ColorCode::DarkGreen => "dark_green",
705 ColorCode::DarkAqua => "dark_aqua",
706 ColorCode::DarkRed => "dark_red",
707 ColorCode::DarkPurple => "dark_purple",
708 ColorCode::Gold => "gold",
709 ColorCode::Gray => "gray",
710 ColorCode::DarkGray => "dark_gray",
711 ColorCode::Blue => "blue",
712 ColorCode::Green => "green",
713 ColorCode::Aqua => "aqua",
714 ColorCode::Red => "red",
715 ColorCode::LightPurple => "light_purple",
716 ColorCode::Yellow => "yellow",
717 ColorCode::White => "white",
718 }
719 }
720}
721
722impl fmt::Display for ColorCode {
723 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
724 write!(f, "{}{}", SECTION_SYMBOL, self.code())
725 }
726}
727
728impl Serialize for ColorCode {
729 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
730 S: Serializer {
731 serializer.serialize_str(self.name())
732 }
733}
734
735impl<'de> Deserialize<'de> for ColorCode {
736 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where
737 D: Deserializer<'de>
738 {
739 struct V;
740
741 impl<'de> Visitor<'de> for V {
742 type Value = ColorCode;
743
744 fn expecting(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
745 write!(fmt, "a string representing a color code")
746 }
747
748 fn visit_str<E>(self, v: &str) -> Result<Self::Value, E> where
749 E: de::Error,
750 {
751 if let Some(code) = ColorCode::from_name(v) {
752 Ok(code)
753 } else {
754 Err(E::custom(format!("invalid color code name {}", v)))
755 }
756 }
757 }
758
759 deserializer.deserialize_str(V)
760 }
761}
762
763#[derive(PartialOrd, PartialEq, Debug, Copy, Clone)]
764pub enum Formatter {
765 Color(ColorCode),
766 Obfuscated,
767 Bold,
768 Strikethrough,
769 Underline,
770 Italic,
771 Reset,
772}
773
774impl Formatter {
775 pub fn from_code(i: &char) -> Option<Self> {
776 match i.to_ascii_lowercase() {
777 'k' => Some(Formatter::Obfuscated),
778 'l' => Some(Formatter::Bold),
779 'm' => Some(Formatter::Strikethrough),
780 'n' => Some(Formatter::Underline),
781 'o' => Some(Formatter::Italic),
782 'r' => Some(Formatter::Reset),
783 _ => ColorCode::from_code(i).map(Formatter::Color),
784 }
785 }
786
787 pub fn code(&self) -> char {
788 match self {
789 Formatter::Color(c) => c.code(),
790 Formatter::Obfuscated => 'k',
791 Formatter::Bold => 'l',
792 Formatter::Strikethrough => 'm',
793 Formatter::Underline => 'n',
794 Formatter::Italic => 'o',
795 Formatter::Reset => 'r',
796 }
797 }
798
799 pub fn from_name(name: &str) -> Option<Self> {
800 match name.to_ascii_lowercase().as_str() {
801 "obfuscated" => Some(Formatter::Obfuscated),
802 "bold" => Some(Formatter::Bold),
803 "strikethrough" => Some(Formatter::Strikethrough),
804 "underline" => Some(Formatter::Underline),
805 "italic" => Some(Formatter::Italic),
806 "reset" => Some(Formatter::Reset),
807 _ => ColorCode::from_name(name).map(Formatter::Color),
808 }
809 }
810
811 pub fn name(&self) -> &str {
812 match self {
813 Formatter::Obfuscated => "obfuscated",
814 Formatter::Bold => "bold",
815 Formatter::Strikethrough => "strikethrough",
816 Formatter::Underline => "underline",
817 Formatter::Italic => "italic",
818 Formatter::Reset => "reset",
819 Formatter::Color(c) => c.name(),
820 }
821 }
822}
823
824impl fmt::Display for Formatter {
825 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
826 write!(f, "{}{}", SECTION_SYMBOL, self.code())
827 }
828}
829
830
831impl<'de> Deserialize<'de> for Chat {
832 fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error> where
833 D: Deserializer<'de>
834 {
835 struct V;
836
837 impl<'de> Visitor<'de> for V {
838 type Value = Chat;
839
840 fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
841 write!(formatter, "any primitive or a JSON object specifying the component")
842 }
843
844 fn visit_bool<E>(self, value: bool) -> Result<Self::Value, E> where E: de::Error {
845 self.visit_string(value.to_string())
846 }
847
848 fn visit_i64<E>(self, value: i64) -> Result<Self::Value, E> where E: de::Error {
849 self.visit_string(value.to_string())
850 }
851
852 fn visit_f64<E>(self, value: f64) -> Result<Self::Value, E> where E: de::Error {
853 self.visit_string(value.to_string())
854 }
855
856 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E> where E: de::Error {
857 Ok(Chat::Text(TextComponent {
858 base: BaseComponent::default(),
859 text: value.to_owned(),
860 }))
861 }
862
863 fn visit_map<M>(self, map: M) -> Result<Self::Value, M::Error> where M: de::MapAccess<'de> {
864 let mut base: JsonComponentBase = de::Deserialize::deserialize(de::value::MapAccessDeserializer::new(map))?;
865 let additional = &mut base._additional;
866
867 if let Some(raw_text) = additional.remove("text") {
869 return if let Some(text) = raw_text.as_str() {
870 additional.clear();
871 Ok(Chat::Text(TextComponent {
872 text: text.to_owned(),
873 base: base.into(),
874 }))
875 } else {
876 Err(M::Error::custom(format!("have text but it's not a string - {:?}", raw_text)))
877 };
878 }
879
880 if let Some(raw_translate) = additional.remove("translate") {
882 return if let Some(translate) = raw_translate.as_str() {
883 if let Some(raw_with) = additional.remove("with") {
885 if let Some(withs) = raw_with.as_array() {
886 let mut withs_out = Vec::with_capacity(withs.len());
887 for with in withs {
888 withs_out.push(with.deserialize_any(V)
889 .map_err(move |err| M::Error::custom(
890 format!("unable to parse one of the translation with entries :: {}", err)))?
891 .boxed());
892 }
893 Ok(Chat::Translation(TranslationComponent{
894 base: base.into(),
895 translate: translate.to_owned(),
896 with: withs_out,
897 }))
898 } else {
899 Err(M::Error::custom(format!("have with but it's not an array - {:?}", raw_with)))
900 }
901 } else {
902 Ok(Chat::Translation(TranslationComponent{
903 base: base.into(),
904 translate: translate.to_owned(),
905 with: Vec::default(),
906 }))
907 }
908 } else {
909 Err(M::Error::custom(format!("have translate but it's not a string - {:?}", raw_translate)))
910 }
911 }
912
913 if let Some(raw_keybind) = additional.remove("keybind") {
915 return if let Some(keybind) = raw_keybind.as_str() {
916 Ok(Chat::Keybind(KeybindComponent{
917 keybind: keybind.to_owned(),
918 base: base.into()
919 }))
920 } else {
921 Err(M::Error::custom(format!("have keybind but it's not a string! {:?}", raw_keybind)))
922 }
923 }
924
925 if let Some(raw_score) = additional.remove("score") {
927 let score = ScoreComponentObjective::deserialize(raw_score.into_deserializer())
928 .map_err(move |err| M::Error::custom(
929 format!("failed to deserialize scoreboard objective for score chat component :: {:?}", err)))?;
930
931 return Ok(Chat::Score(ScoreComponent{
932 score,
933 base: base.into(),
934 }));
935 }
936
937 Err(M::Error::custom("not able to parse chat component, not a valid chat component kind"))
940 }
941 }
942
943 deserializer.deserialize_any(V)
944 }
945}
946
947impl Serialize for Chat {
948 fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error> where
949 S: Serializer
950 {
951 use Chat::*;
952
953 match self {
954 Text(body) => body.serialize(serializer),
955 Translation(body) => body.serialize(serializer),
956 Keybind(body) => body.serialize(serializer),
957 Score(body) => body.serialize(serializer)
958 }
959 }
960}
961
962impl super::Serialize for Chat {
963 fn mc_serialize<S: super::Serializer>(&self, to: &mut S) -> SerializeResult {
964 serde_json::to_string(self)
965 .map_err(move |err| super::SerializeErr::FailedJsonEncode(
966 format!("error while encoding chat :: {:?} -> {:?}", self, err)))?
967 .mc_serialize(to)
968 }
969}
970
971impl super::Deserialize for Chat {
972 fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> {
973 String::mc_deserialize(data)?.try_map(move |raw| {
974 serde_json::from_str(raw.as_str()).map_err(move |err|
975 super::DeserializeErr::FailedJsonDeserialize(format!(
976 "failed to deserialize chat from JSON '{}' :: {:?}", raw, err
977 )))
978 })
979 }
980}
981
982#[cfg(all(test, feature = "std"))]
983use super::protocol::TestRandom;
984
985#[cfg(all(test, feature = "std"))]
986impl TestRandom for Chat {
987 fn test_gen_random() -> Self {
988 let str = String::test_gen_random();
989 Chat::from_text(str.as_str())
990 }
991}
992
993fn read_event<'de, A>(
994 access: &mut A,
995) -> Result<(&'de str, Value), <A as MapAccess<'de>>::Error>
996 where A: MapAccess<'de>
997{
998 let mut action: Option<&str> = None;
999 let mut value: Option<Value> = None;
1000 while action.is_none() || value.is_none() {
1001 if let Some(key) = access.next_key()? {
1002 match key {
1003 "action" => {
1004 action = access.next_value()?;
1005 if action.is_none() {
1006 return Err(A::Error::custom("none for value key=action"));
1007 }
1008 },
1009 "value" => {
1010 value = access.next_value()?;
1011 if value.is_none() {
1012 return Err(A::Error::custom("none for value key=value"));
1013 }
1014 },
1015 other => {
1016 return Err(A::Error::custom(format!("unexpected key in event {}", other)));
1017 }
1018 }
1019 } else {
1020 return Err(A::Error::custom(format!("event needs action and value")));
1021 }
1022 }
1023
1024 Ok((action.expect("checked"), value.expect("checked")))
1025}
1026
1027#[cfg(test)]
1028pub mod tests {
1029
1030 use super::*;
1031
1032 #[test]
1033 fn test_from_traditional_simple() {
1034 let out = Chat::from_traditional("&cthis &cis red, and &rthis is &e&lyellow", true);
1035 assert_eq!(out, Chat::Text(TextComponent{
1036 text: String::default(),
1037 base: {
1038 let mut b = BaseComponent::default();
1039 b.extra = alloc::vec!(
1040 Chat::Text(TextComponent{
1041 text: "this is red, and ".to_owned(),
1042 base: {
1043 let mut b = BaseComponent::default();
1044 b.color = Some(ColorCode::Red);
1045 b
1046 },
1047 }).boxed(),
1048 Chat::Text(TextComponent{
1049 text: "this is ".to_owned(),
1050 base: BaseComponent::default(),
1051 }).boxed(),
1052 Chat::Text(TextComponent{
1053 text: "yellow".to_owned(),
1054 base: {
1055 let mut b = BaseComponent::default();
1056 b.color = Some(ColorCode::Yellow);
1057 b.bold = true;
1058 b
1059 }
1060 }).boxed()
1061 );
1062 b
1063 }
1064 }));
1065
1066 let traditional = out.to_traditional().expect("is text");
1067 assert_eq!(traditional.as_str(), "§cthis is red, and §rthis is §e§lyellow");
1068 #[cfg(feature="std")]
1069 println!("{}", serde_json::to_string_pretty(&out).expect("should serialize fine"));
1070 }
1071}