aboutsummaryrefslogtreecommitdiff
path: root/minecraft-chat/src
diff options
context:
space:
mode:
authormat <github@matdoes.dev>2021-12-11 15:17:42 -0600
committermat <github@matdoes.dev>2021-12-11 15:17:42 -0600
commitba911a8a207eb47df7a055410570767b2e33c2ae (patch)
tree4a53d384f08b7272ba287bdb326f3d7fcb09f289 /minecraft-chat/src
parent6026c74430f311c9217b77e7ac07d183efde5bce (diff)
downloadazalea-drasl-ba911a8a207eb47df7a055410570767b2e33c2ae.tar.xz
correct minecraft-chat :tada:
Diffstat (limited to 'minecraft-chat/src')
-rw-r--r--minecraft-chat/src/component.rs234
-rw-r--r--minecraft-chat/src/style.rs162
-rw-r--r--minecraft-chat/src/text_component.rs91
3 files changed, 279 insertions, 208 deletions
diff --git a/minecraft-chat/src/component.rs b/minecraft-chat/src/component.rs
index 300a1228..2ff6111a 100644
--- a/minecraft-chat/src/component.rs
+++ b/minecraft-chat/src/component.rs
@@ -1,8 +1,11 @@
-use serde_json;
+use serde::{
+ de::{self, Error},
+ Deserialize, Deserializer,
+};
use crate::{
base_component::BaseComponent,
- style::Style,
+ style::{ChatFormatting, Style},
text_component::TextComponent,
translatable_component::{StringOrComponent, TranslatableComponent},
};
@@ -13,9 +16,105 @@ pub enum Component {
Translatable(TranslatableComponent),
}
+lazy_static! {
+ pub static ref DEFAULT_STYLE: Style = Style {
+ color: Some(ChatFormatting::WHITE.try_into().unwrap()),
+ ..Style::default()
+ };
+}
+
/// A chat component
impl Component {
- pub fn new(json: &serde_json::Value) -> Result<Component, String> {
+ // TODO: is it possible to use a macro so this doesn't have to be duplicated?
+
+ pub fn get_base_mut(&mut self) -> &mut BaseComponent {
+ match self {
+ Self::Text(c) => &mut c.base,
+ Self::Translatable(c) => &mut c.base,
+ }
+ }
+
+ pub fn get_base(&self) -> &BaseComponent {
+ match self {
+ Self::Text(c) => &c.base,
+ Self::Translatable(c) => &c.base,
+ }
+ }
+
+ /// Add a component as a sibling of this one
+ fn append(&mut self, sibling: Component) {
+ self.get_base_mut().siblings.push(sibling);
+ }
+
+ /// Get the "separator" component from the json
+ fn parse_separator(json: &serde_json::Value) -> Result<Option<Component>, serde_json::Error> {
+ if json.get("separator").is_some() {
+ return Ok(Some(Component::deserialize(
+ json.get("separator").unwrap(),
+ )?));
+ }
+ Ok(None)
+ }
+
+ /// Convert this component into an ansi string
+ pub fn to_ansi(&self, default_style: Option<&Style>) -> String {
+ // default the default_style to white if it's not set
+ let default_style: &Style = default_style.unwrap_or_else(|| &DEFAULT_STYLE);
+
+ // this contains the final string will all the ansi escape codes
+ let mut built_string = String::new();
+ // this style will update as we visit components
+ let mut running_style = Style::default();
+
+ for component in self.clone().into_iter() {
+ let component_text = match &component {
+ Self::Text(c) => &c.text,
+ Self::Translatable(c) => &c.key,
+ };
+ let component_style = &component.get_base().style;
+
+ let ansi_text = running_style.compare_ansi(component_style, default_style);
+ built_string.push_str(&ansi_text);
+ built_string.push_str(component_text);
+
+ running_style.apply(component_style);
+ }
+
+ if !running_style.is_empty() {
+ built_string.push_str("\u{1b}[m");
+ }
+
+ built_string
+ }
+}
+
+impl IntoIterator for Component {
+ /// Recursively call the function for every component in this component
+ fn into_iter(self) -> Self::IntoIter {
+ let base = self.get_base();
+ let siblings = base.siblings.clone();
+ let mut v: Vec<Component> = Vec::with_capacity(siblings.len() + 1);
+ v.push(self);
+ for sibling in siblings {
+ v.extend(sibling.into_iter());
+ }
+
+ v.into_iter()
+ }
+
+ type Item = Component;
+ type IntoIter = std::vec::IntoIter<Self::Item>;
+}
+
+impl<'de> Deserialize<'de> for Component {
+ fn deserialize<D>(de: D) -> Result<Self, D::Error>
+ where
+ D: Deserializer<'de>,
+ {
+ println!("deserializing component");
+ let json: serde_json::Value = serde::Deserialize::deserialize(de)?;
+ println!("made json");
+
// we create a component that we might add siblings to
let mut component: Component;
@@ -38,7 +137,7 @@ impl Component {
for i in 0..with.len() {
// if it's a string component with no styling and no siblings, just add a string to with_array
// otherwise add the component to the array
- let c = Component::new(&with[i])?;
+ let c = Component::deserialize(&with[i]).map_err(de::Error::custom)?;
if let Component::Text(text_component) = c {
if text_component.base.siblings.is_empty()
&& text_component.base.style.is_empty()
@@ -47,7 +146,9 @@ impl Component {
break;
}
}
- with_array.push(StringOrComponent::Component(Component::new(&with[i])?));
+ with_array.push(StringOrComponent::Component(
+ Component::deserialize(&with[i]).map_err(de::Error::custom)?,
+ ));
}
component =
Component::Translatable(TranslatableComponent::new(translate, with_array));
@@ -62,33 +163,41 @@ impl Component {
// if (!object.has("name") || !object.has("objective")) throw new JsonParseException("A score component needs a least a name and an objective");
// ScoreComponent scoreComponent = new ScoreComponent(GsonHelper.getAsString((JsonObject)object, "name"), GsonHelper.getAsString((JsonObject)object, "objective"));
if score_json.get("name").is_none() || score_json.get("objective").is_none() {
- return Err(
- "A score component needs at least a name and an objective".to_string()
- );
+ return Err(de::Error::missing_field(
+ "A score component needs at least a name and an objective",
+ ));
}
// TODO
- return Err("score text components aren't yet supported".to_string());
+ return Err(de::Error::custom(
+ "score text components aren't yet supported",
+ ));
// component = ScoreComponent
} else if json.get("selector").is_some() {
// } else if (jsonObject.has("selector")) {
// object = this.parseSeparator(type, jsonDeserializationContext, jsonObject);
// SelectorComponent selectorComponent = new SelectorComponent(GsonHelper.getAsString(jsonObject, "selector"), (Optional<Component>)object);
- return Err("selector text components aren't yet supported".to_string());
+ return Err(de::Error::custom(
+ "selector text components aren't yet supported",
+ ));
// } else if (jsonObject.has("keybind")) {
// KeybindComponent keybindComponent = new KeybindComponent(GsonHelper.getAsString(jsonObject, "keybind"));
} else if json.get("keybind").is_some() {
- return Err("keybind text components aren't yet supported".to_string());
+ return Err(de::Error::custom(
+ "keybind text components aren't yet supported",
+ ));
} else {
// } else {
// if (!jsonObject.has("nbt")) throw new JsonParseException("Don't know how to turn " + jsonElement + " into a Component");
if json.get("nbt").is_none() {
- return Err(format!("Don't know how to turn {} into a Component", json));
+ return Err(de::Error::custom(
+ format!("Don't know how to turn {} into a Component", json).as_str(),
+ ));
}
// object = GsonHelper.getAsString(jsonObject, "nbt");
let _nbt = json.get("nbt").unwrap().to_string();
// Optional<Component> optional = this.parseSeparator(type, jsonDeserializationContext, jsonObject);
- let _separator = Component::parse_separator(json)?;
+ let _separator = Component::parse_separator(&json).map_err(de::Error::custom)?;
let _interpret = match json.get("interpret") {
Some(v) => v.as_bool().ok_or(Some(false)).unwrap(),
@@ -97,7 +206,9 @@ impl Component {
// boolean bl = GsonHelper.getAsBoolean(jsonObject, "interpret", false);
// if (jsonObject.has("block")) {
if json.get("block").is_some() {}
- return Err("nbt text components aren't yet supported".to_string());
+ return Err(de::Error::custom(
+ "nbt text components aren't yet supported",
+ ));
// NbtComponent.BlockNbtComponent blockNbtComponent = new NbtComponent.BlockNbtComponent((String)object, bl, GsonHelper.getAsString(jsonObject, "block"), optional);
// } else if (jsonObject.has("entity")) {
// NbtComponent.EntityNbtComponent entityNbtComponent = new NbtComponent.EntityNbtComponent((String)object, bl, GsonHelper.getAsString(jsonObject, "entity"), optional);
@@ -120,106 +231,37 @@ impl Component {
if json.get("extra").is_some() {
let extra = match json.get("extra").unwrap().as_array() {
Some(r) => r,
- None => return Err("Extra isn't an array".to_string()),
+ None => return Err(de::Error::custom("Extra isn't an array")),
};
if extra.is_empty() {
- return Err("Unexpected empty array of components".to_string());
+ return Err(de::Error::custom("Unexpected empty array of components"));
}
for extra_component in extra {
- component.append(Component::new(extra_component)?);
+ let sibling =
+ Component::deserialize(extra_component).map_err(de::Error::custom)?;
+ component.append(sibling);
}
}
- let style = Style::deserialize(json);
+ let style = Style::deserialize(&json);
component.get_base_mut().style = style;
return Ok(component);
}
// ok so it's not an object, if it's an array deserialize every item
else if !json.is_array() {
- return Err(format!("Don't know how to turn {} into a Component", json));
+ return Err(de::Error::custom(
+ format!("Don't know how to turn {} into a Component", json).as_str(),
+ ));
}
let json_array = json.as_array().unwrap();
// the first item in the array is the one that we're gonna return, the others are siblings
- let mut component = Component::new(&json_array[0])?;
+ let mut component = Component::deserialize(&json_array[0]).map_err(de::Error::custom)?;
for i in 1..json_array.len() {
- component.append(Component::new(json_array.get(i).unwrap())?);
+ component.append(
+ Component::deserialize(json_array.get(i).unwrap()).map_err(de::Error::custom)?,
+ );
}
Ok(component)
}
-
- // TODO: is it possible to use a macro so this doesn't have to be duplicated?
-
- pub fn get_base_mut(&mut self) -> &mut BaseComponent {
- match self {
- Self::Text(c) => &mut c.base,
- Self::Translatable(c) => &mut c.base,
- }
- }
-
- pub fn get_base(&self) -> &BaseComponent {
- match self {
- Self::Text(c) => &c.base,
- Self::Translatable(c) => &c.base,
- }
- }
-
- /// Add a component as a sibling of this one
- fn append(&mut self, sibling: Component) {
- self.get_base_mut().siblings.push(sibling);
- }
-
- /// Get the "separator" component from the json
- fn parse_separator(json: &serde_json::Value) -> Result<Option<Component>, String> {
- if json.get("separator").is_some() {
- return Ok(Some(Component::new(json.get("separator").unwrap())?));
- }
- Ok(None)
- }
-
- /// Convert this component into an ansi string
- pub fn to_ansi(&self) -> String {
- // this contains the final string will all the ansi escape codes
- let mut built_string = String::new();
- // this style will update as we visit components
- let mut running_style = Style::default();
-
- for component in self.clone().into_iter() {
- let component_text = match &component {
- Self::Text(c) => &c.text,
- Self::Translatable(c) => &c.key,
- };
- let component_style = &component.get_base().style;
-
- let ansi_text = running_style.compare_ansi(component_style);
- built_string.push_str(&ansi_text);
- built_string.push_str(component_text);
-
- running_style.apply(component_style);
- }
-
- if !running_style.is_empty() {
- built_string.push_str("\u{1b}[m");
- }
-
- built_string
- }
-}
-
-impl IntoIterator for Component {
- /// Recursively call the function for every component in this component
- fn into_iter(self) -> Self::IntoIter {
- let base = self.get_base();
- let siblings = base.siblings.clone();
- let mut v: Vec<Component> = Vec::with_capacity(siblings.len() + 1);
- v.push(self);
- for sibling in siblings {
- v.extend(sibling.into_iter());
- }
-
- v.into_iter()
- }
-
- type Item = Component;
- type IntoIter = std::vec::IntoIter<Self::Item>;
}
diff --git a/minecraft-chat/src/style.rs b/minecraft-chat/src/style.rs
index 14fabec5..7b333e5f 100644
--- a/minecraft-chat/src/style.rs
+++ b/minecraft-chat/src/style.rs
@@ -188,29 +188,21 @@ impl TextColor {
}
}
+// from ChatFormatting to TextColor
+impl TryFrom<ChatFormatting<'_>> for TextColor {
+ type Error = String;
+
+ fn try_from(formatter: ChatFormatting<'_>) -> Result<Self, Self::Error> {
+ if formatter.is_format {
+ return Err(format!("{} is not a color", formatter.name));
+ }
+ let color = formatter.color.unwrap_or(0);
+ Ok(Self::new(color, Some(formatter.name.to_string())))
+ }
+}
+
#[derive(Clone, Debug)]
pub struct Style {
- // @Nullable
- // final TextColor color;
- // @Nullable
- // final Boolean bold;
- // @Nullable
- // final Boolean italic;
- // @Nullable
- // final Boolean underlined;
- // @Nullable
- // final Boolean strikethrough;
- // @Nullable
- // final Boolean obfuscated;
- // @Nullable
- // final ClickEvent clickEvent;
- // @Nullable
- // final HoverEvent hoverEvent;
- // @Nullable
- // final String insertion;
- // @Nullable
- // final ResourceLocation font;
-
// these are options instead of just bools because None is different than false in this case
pub color: Option<TextColor>,
pub bold: Option<bool>,
@@ -218,17 +210,24 @@ pub struct Style {
pub underlined: Option<bool>,
pub strikethrough: Option<bool>,
pub obfuscated: Option<bool>,
+ /// Whether it should reset the formatting before applying these styles
+ pub reset: bool,
}
impl Style {
- pub fn default() -> Style {
- Style {
+ pub fn default() -> Self {
+ Self::empty()
+ }
+
+ pub fn empty() -> Self {
+ Self {
color: None,
bold: None,
italic: None,
underlined: None,
strikethrough: None,
obfuscated: None,
+ reset: false,
}
}
@@ -251,6 +250,7 @@ impl Style {
underlined,
strikethrough,
obfuscated,
+ ..Style::default()
}
} else {
Style::default()
@@ -268,43 +268,36 @@ impl Style {
}
/// find the necessary ansi code to get from this style to another
- pub fn compare_ansi(&self, after: &Style) -> String {
- let should_reset = {
+ pub fn compare_ansi(&self, after: &Style, default_style: &Style) -> String {
+ let should_reset = after.reset ||
// if it used to be bold and now it's not, reset
- if self.bold.unwrap_or(false) && !after.bold.unwrap_or(true) {
- true
- }
+ (self.bold.unwrap_or(false) && !after.bold.unwrap_or(true)) ||
// if it used to be italic and now it's not, reset
- else if self.italic.unwrap_or(false) && !after.italic.unwrap_or(true) {
- true
+ (self.italic.unwrap_or(false) && !after.italic.unwrap_or(true)) ||
// if it used to be underlined and now it's not, reset
- } else if self.underlined.unwrap_or(false) && !after.underlined.unwrap_or(true) {
- true
+ (self.underlined.unwrap_or(false) && !after.underlined.unwrap_or(true)) ||
// if it used to be strikethrough and now it's not, reset
- } else if self.strikethrough.unwrap_or(false) && !after.strikethrough.unwrap_or(true) {
- true
+ (self.strikethrough.unwrap_or(false) && !after.strikethrough.unwrap_or(true)) ||
// if it used to be obfuscated and now it's not, reset
- } else {
- self.obfuscated.unwrap_or(false) && !after.obfuscated.unwrap_or(true)
- }
- };
+ (self.obfuscated.unwrap_or(false) && !after.obfuscated.unwrap_or(true));
let mut ansi_codes = String::new();
+ let empty_style = Style::empty();
+
let (before, after) = if should_reset {
- // if it's true before and none after, make it true after
- // if it's false before and none after, make it false after
- // we should apply after into before and use that as after
ansi_codes.push_str(Ansi::RESET);
- let mut updated_after = self.clone();
+ let mut updated_after = if after.reset {
+ default_style.clone()
+ } else {
+ self.clone()
+ };
updated_after.apply(after);
- (Style::default(), updated_after)
+ (&empty_style, updated_after)
} else {
- (self.clone(), after.clone())
+ (self, after.clone())
};
- println!("should_reset {:?}", should_reset);
-
// if bold used to be false/default and now it's true, set bold
if !before.bold.unwrap_or(false) && after.bold.unwrap_or(false) {
ansi_codes.push_str(Ansi::BOLD);
@@ -331,7 +324,7 @@ impl Style {
if before.color.is_none() && after.color.is_some() {
true
} else if before.color.is_some() && after.color.is_some() {
- before.color.unwrap().value != after.color.as_ref().unwrap().value
+ before.color.clone().unwrap().value != after.color.as_ref().unwrap().value
} else {
false
}
@@ -369,21 +362,14 @@ impl Style {
/// Apply a ChatFormatting to this style
pub fn apply_formatting(&mut self, formatting: &ChatFormatting) {
- match formatting {
- &ChatFormatting::BOLD => self.bold = Some(true),
- &ChatFormatting::ITALIC => self.italic = Some(true),
- &ChatFormatting::UNDERLINE => self.underlined = Some(true),
- &ChatFormatting::STRIKETHROUGH => self.strikethrough = Some(true),
- &ChatFormatting::OBFUSCATED => self.obfuscated = Some(true),
- &ChatFormatting::RESET => {
- self.color = None;
- self.bold = None;
- self.italic = None;
- self.underlined = None;
- self.strikethrough = None;
- self.obfuscated = None;
- }
- &ChatFormatting {
+ match *formatting {
+ ChatFormatting::BOLD => self.bold = Some(true),
+ ChatFormatting::ITALIC => self.italic = Some(true),
+ ChatFormatting::UNDERLINE => self.underlined = Some(true),
+ ChatFormatting::STRIKETHROUGH => self.strikethrough = Some(true),
+ ChatFormatting::OBFUSCATED => self.obfuscated = Some(true),
+ ChatFormatting::RESET => self.reset = true,
+ ChatFormatting {
name: _,
code: _,
is_format: _,
@@ -401,6 +387,8 @@ impl Style {
#[cfg(test)]
mod tests {
+ use crate::component::DEFAULT_STYLE;
+
use super::*;
#[test]
@@ -418,22 +406,15 @@ mod tests {
#[test]
fn ansi_difference_should_reset() {
let style_a = Style {
- color: None,
bold: Some(true),
italic: Some(true),
- underlined: None,
- strikethrough: None,
- obfuscated: None,
+ ..Style::default()
};
let style_b = Style {
- color: None,
bold: Some(false),
- italic: None,
- underlined: None,
- strikethrough: None,
- obfuscated: None,
+ ..Style::default()
};
- let ansi_difference = style_a.compare_ansi(&style_b);
+ let ansi_difference = style_a.compare_ansi(&style_b, &Style::default());
assert_eq!(
ansi_difference,
format!(
@@ -446,26 +427,41 @@ mod tests {
#[test]
fn ansi_difference_shouldnt_reset() {
let style_a = Style {
- color: None,
bold: Some(true),
- italic: None,
- underlined: None,
- strikethrough: None,
- obfuscated: None,
+ ..Style::default()
};
let style_b = Style {
- color: None,
- bold: None,
italic: Some(true),
- underlined: None,
- strikethrough: None,
- obfuscated: None,
+ ..Style::default()
};
- let ansi_difference = style_a.compare_ansi(&style_b);
+ let ansi_difference = style_a.compare_ansi(&style_b, &Style::default());
assert_eq!(ansi_difference, Ansi::ITALIC)
}
#[test]
+ fn ansi_difference_explicit_reset() {
+ let style_a = Style {
+ bold: Some(true),
+ ..Style::empty()
+ };
+ let style_b = Style {
+ italic: Some(true),
+ reset: true,
+ ..Style::empty()
+ };
+ let ansi_difference = style_a.compare_ansi(&style_b, &DEFAULT_STYLE);
+ assert_eq!(
+ ansi_difference,
+ format!(
+ "{reset}{italic}{white}",
+ reset = Ansi::RESET,
+ white = Ansi::rgb(ChatFormatting::WHITE.color.unwrap()),
+ italic = Ansi::ITALIC
+ )
+ )
+ }
+
+ #[test]
fn test_from_code() {
assert_eq!(
ChatFormatting::from_code('a').unwrap(),
diff --git a/minecraft-chat/src/text_component.rs b/minecraft-chat/src/text_component.rs
index 66bde690..a5030fa1 100644
--- a/minecraft-chat/src/text_component.rs
+++ b/minecraft-chat/src/text_component.rs
@@ -1,4 +1,10 @@
-use crate::{base_component::BaseComponent, component::Component, style::ChatFormatting};
+use std::fmt;
+
+use crate::{
+ base_component::BaseComponent,
+ component::Component,
+ style::{ChatFormatting, Style, TextColor},
+};
#[derive(Clone, Debug)]
pub struct TextComponent {
@@ -10,7 +16,7 @@ const LEGACY_FORMATTING_CODE_SYMBOL: char = '§';
/// Convert a legacy color code string into a Component
/// Technically in Minecraft this is done when displaying the text, but AFAIK it's the same as just doing it in TextComponent
-pub fn legacy_color_code_to_component(legacy_color_code: &str) -> Component {
+pub fn legacy_color_code_to_text_component(legacy_color_code: &str) -> TextComponent {
let mut components: Vec<TextComponent> = Vec::with_capacity(1);
// iterate over legacy_color_code, if it starts with LEGACY_COLOR_CODE_SYMBOL then read the next character and get the style from that
// otherwise, add the character to the text
@@ -21,24 +27,15 @@ pub fn legacy_color_code_to_component(legacy_color_code: &str) -> Component {
if legacy_color_code.chars().nth(i).unwrap() == LEGACY_FORMATTING_CODE_SYMBOL {
let formatting_code = legacy_color_code.chars().nth(i + 1).unwrap();
if let Ok(formatter) = ChatFormatting::from_code(formatting_code) {
- if components.is_empty() || components.last().unwrap().text.is_empty() {
+ if components.is_empty() {
+ components.push(TextComponent::new("".to_string()));
+ } else if !components.last().unwrap().text.is_empty() {
components.push(TextComponent::new("".to_string()));
}
- println!(
- "applying formatter {:?} {:?}",
- components.last_mut().unwrap().base.style,
- formatter
- );
- components
- .last_mut()
- .unwrap()
- .base
- .style
- .apply_formatting(formatter);
- println!(
- "applied formatter {:?}",
- components.last_mut().unwrap().base.style
- );
+
+ let style = &mut components.last_mut().unwrap().base.style;
+ // if the formatter is a reset, then we need to reset the style to the default
+ style.apply_formatting(formatter);
}
i += 1;
} else {
@@ -60,36 +57,72 @@ pub fn legacy_color_code_to_component(legacy_color_code: &str) -> Component {
final_component.base.siblings.push(component.get());
}
- final_component.get()
+ final_component
}
impl<'a> TextComponent {
pub fn new(text: String) -> Self {
- Self {
- base: BaseComponent::new(),
- text,
+ // if it contains a LEGACY_FORMATTING_CODE_SYMBOL, format it
+ if text.contains(LEGACY_FORMATTING_CODE_SYMBOL) {
+ legacy_color_code_to_text_component(&text)
+ } else {
+ Self {
+ base: BaseComponent::new(),
+ text,
+ }
}
}
- pub fn to_string(&self) -> String {
- self.text.clone()
- }
-
fn get(self) -> Component {
Component::Text(self)
}
}
+impl fmt::Display for TextComponent {
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+ write!(f, "{}", self.text.clone())
+ }
+}
+
#[cfg(test)]
mod tests {
+ use crate::style::Ansi;
+
use super::*;
#[test]
+ fn test_hypixel_motd() {
+ let component =
+ TextComponent::new("§aHypixel Network §c[1.8-1.18]\n§b§lHAPPY HOLIDAYS".to_string())
+ .get();
+ assert_eq!(
+ component.to_ansi(None),
+ format!(
+ "{GREEN}Hypixel Network {RED}[1.8-1.18]\n{BOLD}{AQUA}HAPPY HOLIDAYS{RESET}",
+ GREEN = Ansi::rgb(ChatFormatting::GREEN.color.unwrap()),
+ RED = Ansi::rgb(ChatFormatting::RED.color.unwrap()),
+ AQUA = Ansi::rgb(ChatFormatting::AQUA.color.unwrap()),
+ BOLD = Ansi::BOLD,
+ RESET = Ansi::RESET
+ )
+ );
+ }
+
+ #[test]
fn test_legacy_color_code_to_component() {
- let component = legacy_color_code_to_component("§lHello §r§1w§2o§3r§4l§5d");
+ let component = TextComponent::new("§lHello §r§1w§2o§3r§4l§5d".to_string()).get();
assert_eq!(
- component.to_ansi(),
- "\u{1b}[38;2;170;0;170mHello world\u{1b}[m"
+ component.to_ansi(None),
+ format!(
+ "{BOLD}Hello {RESET}{DARK_BLUE}w{DARK_GREEN}o{DARK_AQUA}r{DARK_RED}l{DARK_PURPLE}d{RESET}",
+ BOLD = Ansi::BOLD,
+ RESET = Ansi::RESET,
+ DARK_BLUE = Ansi::rgb(ChatFormatting::DARK_BLUE.color.unwrap()),
+ DARK_GREEN = Ansi::rgb(ChatFormatting::DARK_GREEN.color.unwrap()),
+ DARK_AQUA = Ansi::rgb(ChatFormatting::DARK_AQUA.color.unwrap()),
+ DARK_RED = Ansi::rgb(ChatFormatting::DARK_RED.color.unwrap()),
+ DARK_PURPLE = Ansi::rgb(ChatFormatting::DARK_PURPLE.color.unwrap())
+ )
);
}
}