use crate::types::Chat; use crate::uuid::UUID4; use crate::{ Deserialize as McDeserialize, DeserializeErr, DeserializeResult, Serialize as McSerialize, SerializeErr, SerializeResult, }; use serde::{Deserialize, Deserializer, Serialize, Serializer}; use alloc::{string::String, fmt, vec::Vec, borrow::ToOwned}; use alloc::format; #[cfg(all(test, feature = "std"))] use crate::protocol::TestRandom; #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct StatusSpec { pub version: Option, pub players: StatusPlayersSpec, pub description: Chat, #[serde(skip_serializing_if = "Option::is_none")] pub favicon: Option, } impl McSerialize for StatusSpec { fn mc_serialize(&self, to: &mut S) -> SerializeResult { serde_json::to_string(self) .map_err(move |err| { SerializeErr::FailedJsonEncode(format!("failed to serialize json status {}", err)) })? .mc_serialize(to) } } impl McDeserialize for StatusSpec { fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> { String::mc_deserialize(data)?.try_map(move |v| { serde_json::from_str(v.as_str()).map_err(move |err| { DeserializeErr::CannotUnderstandValue(format!( "failed to deserialize json status {}", err )) }) }) } } #[cfg(all(test, feature = "std"))] impl TestRandom for StatusSpec { fn test_gen_random() -> Self { Self { version: Some(StatusVersionSpec { protocol: rand::random(), name: String::test_gen_random(), }), players: StatusPlayersSpec { sample: Vec::default(), max: rand::random(), online: rand::random(), }, favicon: None, description: Chat::test_gen_random(), } } } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct StatusVersionSpec { pub name: String, pub protocol: i32, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct StatusPlayersSpec { pub max: i32, pub online: i32, #[serde(skip_serializing_if = "Vec::is_empty")] #[serde(default = "Vec::default")] pub sample: Vec, } #[derive(Serialize, Deserialize, Clone, Debug, PartialEq)] pub struct StatusPlayerSampleSpec { pub name: String, pub id: UUID4, } #[derive(Clone, Debug, PartialEq)] pub struct StatusFaviconSpec { pub content_type: String, pub data: Vec, } impl Serialize for StatusFaviconSpec { fn serialize(&self, serializer: S) -> Result<::Ok, ::Error> where S: Serializer, { let data_base64 = base64::encode(self.data.as_slice()); let content = alloc::format!("data:{};base64,{}", self.content_type, data_base64); serializer.serialize_str(content.as_str()) } } impl<'de> Deserialize<'de> for StatusFaviconSpec { fn deserialize(deserializer: D) -> Result>::Error> where D: Deserializer<'de>, { struct Visitor; impl serde::de::Visitor<'_> for Visitor { type Value = StatusFaviconSpec; fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result { write!(formatter, "a string with base64 data for favicon") } fn visit_str(self, v: &str) -> Result { // favicon syntax data:{content-type};base64,{} let v = str_tag(v, "data:", &self)?; let (content_type, v) = str_until_pat(v, ";", &self)?; let rest = str_tag(v, "base64,", &self)?; match base64::decode(rest.replace('\n', "")) { Ok(data) => { Ok(StatusFaviconSpec{ data, content_type: content_type.to_owned(), }) }, Err(err) => { Err(E::custom(format_args!("failed base64 decode {:?}", err))) } } } } deserializer.deserialize_str(Visitor {}) } } fn str_tag<'a, E, V>( target: &'a str, expected: &str, v: &V, ) -> Result<&'a str, E> where E: serde::de::Error, V: serde::de::Visitor<'a> { let (front, back) = str_take(target, expected.len(), v)?; if front != expected { Err(E::invalid_value(serde::de::Unexpected::Str(target), v)) } else { Ok(back) } } fn str_until_pat<'a, E, V>( target: &'a str, pat: &str, v: &V, ) -> Result<(&'a str, &'a str), E> where E: serde::de::Error, V: serde::de::Visitor<'a> { let n_pat = pat.len(); if target.len() < n_pat { return Err(E::invalid_value(serde::de::Unexpected::Str(target), v)); } for i in 0..=(target.len()-n_pat) { let v = &target[i..i+n_pat]; if v == pat { return Ok((&target[..i], &target[i+1..])); } } Err(E::invalid_value(serde::de::Unexpected::Str(target), v)) } fn str_take<'a, E, V>( target: &'a str, n: usize, v: &V, ) -> Result<(&'a str, &'a str), E> where E: serde::de::Error, V: serde::de::Visitor<'a> { if target.len() < n { Err(E::invalid_value(serde::de::Unexpected::Str(target), v)) } else { Ok(target.split_at(n)) } }