From b5a355c756258c150cf926816367f14426bf60f0 Mon Sep 17 00:00:00 2001 From: Ryan Johnson Date: Sat, 21 Jan 2023 00:17:19 -0800 Subject: [PATCH] Non-Object Text Deserialization (#194) - Extends `Text`'s `Deserialize` impl to allow for deserializing JSON data types other than objects. - Add additional `From` impls for `Text`. - Better `Debug` impl for `Text`. - Tweaked text unit tests. --- crates/valence_protocol/src/text.rs | 131 ++++++++++++++++++++++++---- 1 file changed, 116 insertions(+), 15 deletions(-) diff --git a/crates/valence_protocol/src/text.rs b/crates/valence_protocol/src/text.rs index 592f55d..ce614a6 100644 --- a/crates/valence_protocol/src/text.rs +++ b/crates/valence_protocol/src/text.rs @@ -41,10 +41,70 @@ use crate::{Decode, Encode, Ident, Result}; /// "The text is Red, Green, and also Blue!\nAnd maybe even Italic." /// ); /// ``` -#[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] +#[derive(Clone, PartialEq, Default, Serialize)] #[serde(transparent)] pub struct Text(Box); +impl<'de> Deserialize<'de> for Text { + fn deserialize>(deserializer: D) -> Result { + struct TextVisitor; + + impl<'de> Visitor<'de> for TextVisitor { + type Value = Text; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + write!(formatter, "a text component data type") + } + + fn visit_bool(self, v: bool) -> Result { + Ok(Text::text(v.to_string())) + } + + fn visit_i64(self, v: i64) -> Result { + Ok(Text::text(v.to_string())) + } + + fn visit_u64(self, v: u64) -> Result { + Ok(Text::text(v.to_string())) + } + + fn visit_f64(self, v: f64) -> Result { + Ok(Text::text(v.to_string())) + } + + fn visit_str(self, v: &str) -> Result { + Ok(Text::text(v.to_string())) + } + + fn visit_string(self, v: String) -> Result { + Ok(Text::text(v)) + } + + fn visit_seq>(self, mut seq: A) -> Result { + let Some(mut res) = seq.next_element()? else { + return Ok(Text::default()) + }; + + while let Some(child) = seq.next_element::()? { + res += child; + } + + Ok(res) + } + + fn visit_map>(self, map: A) -> Result { + use de::value::MapAccessDeserializer; + + Ok(Text(Box::new(TextInner::deserialize( + MapAccessDeserializer::new(map), + )?))) + } + } + + deserializer.deserialize_any(TextVisitor) + } +} + #[derive(Clone, PartialEq, Default, Debug, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] struct TextInner { @@ -727,6 +787,42 @@ impl From> for Text { } } +impl From for Text { + fn from(value: i32) -> Self { + Text::text(value.to_string()) + } +} + +impl From for Text { + fn from(value: i64) -> Self { + Text::text(value.to_string()) + } +} + +impl From for Text { + fn from(value: u64) -> Self { + Text::text(value.to_string()) + } +} + +impl From for Text { + fn from(value: f64) -> Self { + Text::text(value.to_string()) + } +} + +impl From for Text { + fn from(value: bool) -> Self { + Text::text(value.to_string()) + } +} + +impl fmt::Debug for Text { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.write_string(f) + } +} + impl fmt::Display for Text { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { self.write_string(f) @@ -845,7 +941,7 @@ fn color_from_str(s: &str) -> Option { #[cfg(test)] mod tests { use super::*; - use crate::ident; + use crate::{ident, translation_key}; #[test] fn text_round_trip() { @@ -882,18 +978,25 @@ mod tests { assert_eq!(color_from_str("blue"), Some(Color::BLUE)); } + #[test] + fn non_object_data_types() { + let input = r#"["foo", true, false, 1.9E10, 9999]"#; + let txt: Text = serde_json::from_str(input).unwrap(); + + assert_eq!(txt, "foo".into_text() + true + false + 1.9E10 + 9999); + } + #[test] fn translate() { let txt = Text::translate( - valence_protocol::translation_key::CHAT_TYPE_ADVANCEMENT_TASK, + translation_key::CHAT_TYPE_ADVANCEMENT_TASK, ["arg1".into(), "arg2".into()], ); let serialized = serde_json::to_string(&txt).unwrap(); let deserialized: Text = serde_json::from_str(&serialized).unwrap(); assert_eq!( serialized, - "{\"translate\":\"chat.type.advancement.task\",\"with\":[{\"text\":\"arg1\"},{\"text\"\ - :\"arg2\"}]}" + r#"{"translate":"chat.type.advancement.task","with":[{"text":"arg1"},{"text":"arg2"}]}"# ); assert_eq!(txt, deserialized); } @@ -905,7 +1008,7 @@ mod tests { let deserialized: Text = serde_json::from_str(&serialized).unwrap(); assert_eq!( serialized, - "{\"score\":{\"name\":\"foo\",\"objective\":\"bar\",\"value\":\"baz\"}}" + r#"{"score":{"name":"foo","objective":"bar","value":"baz"}}"# ); assert_eq!(txt, deserialized); } @@ -918,8 +1021,7 @@ mod tests { let deserialized: Text = serde_json::from_str(&serialized).unwrap(); assert_eq!( serialized, - "{\"selector\":\"foo\",\"separator\":{\"text\":\"bar\",\"color\":\"#ff5555\",\"bold\":\ - true}}" + r##"{"selector":"foo","separator":{"text":"bar","color":"#ff5555","bold":true}}"## ); assert_eq!(txt, deserialized); } @@ -929,7 +1031,7 @@ mod tests { let txt = Text::keybind("foo"); let serialized = serde_json::to_string(&txt).unwrap(); let deserialized: Text = serde_json::from_str(&serialized).unwrap(); - assert_eq!(serialized, "{\"keybind\":\"foo\"}"); + assert_eq!(serialized, r#"{"keybind":"foo"}"#); assert_eq!(txt, deserialized); } @@ -938,8 +1040,7 @@ mod tests { let txt = Text::block_nbt("foo", "bar", Some(true), Some("baz".into())); let serialized = serde_json::to_string(&txt).unwrap(); let deserialized: Text = serde_json::from_str(&serialized).unwrap(); - let expected = "{\"block\":\"foo\",\"nbt\":\"bar\",\"interpret\":true,\"separator\":{\"\ - text\":\"baz\"}}"; + let expected = r#"{"block":"foo","nbt":"bar","interpret":true,"separator":{"text":"baz"}}"#; assert_eq!(serialized, expected); assert_eq!(txt, deserialized); } @@ -949,8 +1050,8 @@ mod tests { let txt = Text::entity_nbt("foo", "bar", Some(true), Some("baz".into())); let serialized = serde_json::to_string(&txt).unwrap(); let deserialized: Text = serde_json::from_str(&serialized).unwrap(); - let expected = "{\"entity\":\"foo\",\"nbt\":\"bar\",\"interpret\":true,\"separator\":{\"\ - text\":\"baz\"}}"; + let expected = + r#"{"entity":"foo","nbt":"bar","interpret":true,"separator":{"text":"baz"}}"#; assert_eq!(serialized, expected); assert_eq!(txt, deserialized); } @@ -960,8 +1061,8 @@ mod tests { let txt = Text::storage_nbt(ident!("foo"), "bar", Some(true), Some("baz".into())); let serialized = serde_json::to_string(&txt).unwrap(); let deserialized: Text = serde_json::from_str(&serialized).unwrap(); - let expected = "{\"storage\":\"foo\",\"nbt\":\"bar\",\"interpret\":true,\"separator\":{\"\ - text\":\"baz\"}}"; + let expected = + r#"{"storage":"foo","nbt":"bar","interpret":true,"separator":{"text":"baz"}}"#; assert_eq!(serialized, expected); assert_eq!(txt, deserialized); }