1use std::borrow::Cow;
2use crate::formatting::{Color};
3use crate::identifier::Identifier;
4use serde::{Serialize, Deserialize};
5use uuid::Uuid;
6
7#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
8#[serde(rename_all = "snake_case", tag = "action", content = "value")]
9pub enum ClickEvent<'a> {
10 OpenUrl(Cow<'a, str>),
11 RunCommand(Cow<'a, str>),
12 SuggestCommand(Cow<'a, str>),
13 ChangePage(usize),
14 CopyToClipboard(Cow<'a, str>),
15}
16
17#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
18#[serde(rename_all = "snake_case", tag = "action", content = "value")]
19pub enum HoverEvent<'a> {
20 ShowText(either::Either<Box<TextComponent<'a>>, Cow<'a, str>>),
21 ShowItem(Cow<'a, str>),
22 ShowEntity(Cow<'a, str>),
23}
24
25#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
26#[serde(untagged)]
27pub enum Component<'a> {
28 Text(TextComponent<'a>),
29 Translatable(TranslatableComponent<'a>),
30 KeyBind(KeyBindComponent<'a>),
31 Score(ScoreComponent<'a>),
32 Selector(SelectorComponent<'a>),
33 Base(BaseComponent<'a>),
34}
35
36#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
37#[serde(rename_all = "camelCase")]
38pub struct BaseComponent<'a> {
39 #[serde(skip_serializing_if = "Option::is_none")]
40 pub bold: Option<bool>,
41 #[serde(skip_serializing_if = "Option::is_none")]
42 pub italic: Option<bool>,
43 #[serde(skip_serializing_if = "Option::is_none")]
44 pub underlined: Option<bool>,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub strikethrough: Option<bool>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub obfuscated: Option<bool>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub font: Option<Identifier<'a>>,
51 #[serde(skip_serializing_if = "Option::is_none")]
52 pub color: Option<Color<'a>>,
53 #[serde(skip_serializing_if = "Option::is_none")]
54 pub insertion: Option<Cow<'a, str>>,
55 #[serde(skip_serializing_if = "is_cow_empty")]
56 pub extra: Cow<'a, [Component<'a>]>,
57 #[serde(skip_serializing_if = "Option::is_none")]
58 pub click_event: Option<ClickEvent<'a>>,
59 #[serde(skip_serializing_if = "Option::is_none")]
60 pub hover_event: Option<HoverEvent<'a>>,
61}
62
63#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
64#[serde(rename_all = "camelCase")]
65pub struct TextComponent<'a> {
66 pub text: Cow<'a, str>,
67 #[serde(flatten)]
68 pub base: BaseComponent<'a>,
69}
70
71#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
72#[serde(rename_all = "camelCase")]
73pub struct TranslatableComponent<'a> {
74 pub translate: Cow<'a, str>,
75 #[serde(skip_serializing_if = "is_cow_empty")]
76 pub with: Cow<'a, [Component<'a>]>,
77 #[serde(flatten)]
78 pub base: BaseComponent<'a>,
79}
80
81#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
82#[serde(rename_all = "camelCase")]
83pub struct KeyBindComponent<'a> {
84 #[serde(rename = "keybind")]
85 pub key_bind: Cow<'a, str>,
86 #[serde(flatten)]
87 pub base: BaseComponent<'a>,
88}
89
90#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
91#[serde(rename_all = "camelCase")]
92pub struct ScoreComponent<'a> {
93 pub score: Score<'a>,
94 #[serde(flatten)]
95 pub base: BaseComponent<'a>,
96}
97
98#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
99#[serde(rename_all = "camelCase")]
100pub struct Score<'a> {
101 pub name: either::Either<Cow<'a, str>, Uuid>,
102 pub objective: Cow<'a, str>,
103 #[serde(skip_serializing_if = "serde_json::Value::is_null")]
104 pub value: serde_json::Value,
105}
106
107#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
108#[serde(rename_all = "camelCase")]
109pub struct SelectorComponent<'a> {
110 pub selector: Cow<'a, str>,
111 #[serde(flatten)]
112 pub base: BaseComponent<'a>,
113}
114
115fn is_cow_empty<T: Clone>(value: &Cow<[T]>) -> bool {
116 match value {
117 Cow::Borrowed(ref data) => data.is_empty(),
118 Cow::Owned(ref vec) => vec.is_empty(),
119 }
120}
121
122fn make_owned<T: ToOwned + ?Sized>(cow: &mut Cow<T>) {
123 if let Cow::Borrowed(borrowed) = cow {
124 *cow = Cow::Owned(borrowed.to_owned())
125 }
126}
127
128fn add<T: ToOwned + Clone>(into: &mut Cow<[T]>, to_add: T) {
129 match into.is_empty() {
130 true => *into = Cow::Owned(vec![to_add]),
131 false => {
132 make_owned(into);
133 match into {
134 Cow::Owned(ref mut owned) => owned.push(to_add),
135 _ => unsafe { std::hint::unreachable_unchecked() }
137 }
138 }
139 }
140}
141
142fn add_values<'a, T: ToOwned + Clone>(into: &mut Cow<'a, [T]>, to_add: Cow<'a, [T]>) {
143 match into.is_empty() {
144 true => *into = to_add,
145 false => {
146 make_owned(into);
147 match into {
148 Cow::Owned(ref mut owned) => {
149 let mut to_add = to_add.into();
150 make_owned(&mut to_add);
151 match to_add {
152 Cow::Owned(push) => for to_add in push {
153 owned.push(to_add)
154 },
155 _ => unsafe { std::hint::unreachable_unchecked() }
157 }
158 }
159 _ => unsafe { std::hint::unreachable_unchecked() }
161 }
162 }
163 }
164}
165
166impl<'a> BaseComponent<'a> {
167 pub fn add_extra(&mut self, extra: impl Into<Component<'a>>) {
168 add(&mut self.extra, extra.into())
169 }
170
171 pub fn add_extras(&mut self, extras: impl Into<Cow<'a, [Component<'a>]>>) {
172 add_values(&mut self.extra, extras.into());
173 }
174}
175
176impl <'a> TranslatableComponent<'a> {
177 pub fn add_arg(&mut self, arg: impl Into<Component<'a>>) {
178 add(&mut self.with, arg.into())
179 }
180
181 pub fn add_args(&mut self, args: impl Into<Cow<'a, [Component<'a>]>>) {
182 add_values(&mut self.with, args.into());
183 }
184}
185
186impl<'a> From<TextComponent<'a>> for Component<'a> {
187 fn from(component: TextComponent<'a>) -> Self {
188 Self::Text(component)
189 }
190}
191
192impl<'a> From<TranslatableComponent<'a>> for Component<'a> {
193 fn from(component: TranslatableComponent<'a>) -> Self {
194 Self::Translatable(component)
195 }
196}
197
198impl<'a> From<ScoreComponent<'a>> for Component<'a> {
199 fn from(component: ScoreComponent<'a>) -> Self {
200 Self::Score(component)
201 }
202}
203
204impl<'a> From<SelectorComponent<'a>> for Component<'a> {
205 fn from(component: SelectorComponent<'a>) -> Self {
206 Self::Selector(component)
207 }
208}
209
210impl<'a> From<KeyBindComponent<'a>> for Component<'a> {
211 fn from(component: KeyBindComponent<'a>) -> Self {
212 Self::KeyBind(component)
213 }
214}
215
216impl<'a> From<BaseComponent<'a>> for Component<'a> {
217 fn from(component: BaseComponent<'a>) -> Self {
218 Self::Base(component)
219 }
220}