mcproto_rs/
chat.rs

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    // state
65    text: String,
66    color: Option<ColorCode>,
67    bold: bool,
68    italic: bool,
69    underlined: bool,
70    strikethrough: bool,
71    obfuscated: bool,
72
73    // all the parts we've already seen
74    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            //noinspection ALL
589            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                // string component
868                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                // translate
881                if let Some(raw_translate) = additional.remove("translate") {
882                    return if let Some(translate) = raw_translate.as_str() {
883                        // need "with"
884                        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                // keybind
914                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                // score
926                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                // selector (SKIP)
938
939                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}