mcproto_rs/
status.rs

1use crate::types::Chat;
2use crate::uuid::UUID4;
3use crate::{
4    Deserialize as McDeserialize, DeserializeErr, DeserializeResult, Serialize as McSerialize,
5    SerializeErr, SerializeResult,
6};
7use serde::{Deserialize, Deserializer, Serialize, Serializer};
8use alloc::{string::String, fmt, vec::Vec, borrow::ToOwned};
9use alloc::format;
10
11#[cfg(all(test, feature = "std"))]
12use crate::protocol::TestRandom;
13
14#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
15pub struct StatusSpec {
16    pub version: Option<StatusVersionSpec>,
17    pub players: StatusPlayersSpec,
18    pub description: Chat,
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub favicon: Option<StatusFaviconSpec>,
21}
22
23impl McSerialize for StatusSpec {
24    fn mc_serialize<S: crate::Serializer>(&self, to: &mut S) -> SerializeResult {
25        serde_json::to_string(self)
26            .map_err(move |err| {
27                SerializeErr::FailedJsonEncode(format!("failed to serialize json status {}", err))
28            })?
29            .mc_serialize(to)
30    }
31}
32
33impl McDeserialize for StatusSpec {
34    fn mc_deserialize(data: &[u8]) -> DeserializeResult<'_, Self> {
35        String::mc_deserialize(data)?.try_map(move |v| {
36            serde_json::from_str(v.as_str()).map_err(move |err| {
37                DeserializeErr::CannotUnderstandValue(format!(
38                    "failed to deserialize json status {}",
39                    err
40                ))
41            })
42        })
43    }
44}
45
46#[cfg(all(test, feature = "std"))]
47impl TestRandom for StatusSpec {
48    fn test_gen_random() -> Self {
49        Self {
50            version: Some(StatusVersionSpec {
51                protocol: rand::random(),
52                name: String::test_gen_random(),
53            }),
54            players: StatusPlayersSpec {
55                sample: Vec::default(),
56                max: rand::random(),
57                online: rand::random(),
58            },
59            favicon: None,
60            description: Chat::test_gen_random(),
61        }
62    }
63}
64
65#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
66pub struct StatusVersionSpec {
67    pub name: String,
68    pub protocol: i32,
69}
70
71#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
72pub struct StatusPlayersSpec {
73    pub max: i32,
74    pub online: i32,
75    #[serde(skip_serializing_if = "Vec::is_empty")]
76    #[serde(default = "Vec::default")]
77    pub sample: Vec<StatusPlayerSampleSpec>,
78}
79
80#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
81pub struct StatusPlayerSampleSpec {
82    pub name: String,
83    pub id: UUID4,
84}
85
86#[derive(Clone, Debug, PartialEq)]
87pub struct StatusFaviconSpec {
88    pub content_type: String,
89    pub data: Vec<u8>,
90}
91
92impl Serialize for StatusFaviconSpec {
93    fn serialize<S>(&self, serializer: S) -> Result<<S as Serializer>::Ok, <S as Serializer>::Error>
94    where
95        S: Serializer,
96    {
97        let data_base64 = base64::encode(self.data.as_slice());
98        let content = alloc::format!("data:{};base64,{}", self.content_type, data_base64);
99        serializer.serialize_str(content.as_str())
100    }
101}
102
103impl<'de> Deserialize<'de> for StatusFaviconSpec {
104    fn deserialize<D>(deserializer: D) -> Result<Self, <D as Deserializer<'de>>::Error>
105    where
106        D: Deserializer<'de>,
107    {
108        struct Visitor;
109        impl serde::de::Visitor<'_> for Visitor {
110            type Value = StatusFaviconSpec;
111
112            fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
113                write!(formatter, "a string with base64 data for favicon")
114            }
115
116            fn visit_str<E: serde::de::Error>(self, v: &str) -> Result<Self::Value, E> {
117                // favicon syntax data:{content-type};base64,{}
118                let v = str_tag(v, "data:", &self)?;
119                let (content_type, v) = str_until_pat(v, ";", &self)?;
120                let rest = str_tag(v, "base64,", &self)?;
121                match base64::decode(rest.replace('\n', "")) {
122                    Ok(data) => {
123                        Ok(StatusFaviconSpec{
124                            data,
125                            content_type: content_type.to_owned(),
126                        })
127                    },
128                    Err(err) => {
129                        Err(E::custom(format_args!("failed base64 decode {:?}", err)))
130                    }
131                }
132            }
133        }
134
135        deserializer.deserialize_str(Visitor {})
136    }
137}
138
139fn str_tag<'a, E, V>(
140    target: &'a str,
141    expected: &str,
142    v: &V,
143) -> Result<&'a str, E> where
144    E: serde::de::Error,
145    V: serde::de::Visitor<'a>
146{
147    let (front, back) = str_take(target, expected.len(), v)?;
148    if front != expected {
149        Err(E::invalid_value(serde::de::Unexpected::Str(target), v))
150    } else {
151        Ok(back)
152    }
153}
154
155fn str_until_pat<'a, E, V>(
156    target: &'a str,
157    pat: &str,
158    v: &V,
159) -> Result<(&'a str, &'a str), E> where
160    E: serde::de::Error,
161    V: serde::de::Visitor<'a>
162{
163    let n_pat = pat.len();
164    if target.len() < n_pat {
165        return Err(E::invalid_value(serde::de::Unexpected::Str(target), v));
166    }
167
168    for i in 0..=(target.len()-n_pat) {
169        let v = &target[i..i+n_pat];
170        if v == pat {
171            return Ok((&target[..i], &target[i+1..]));
172        }
173    }
174
175    Err(E::invalid_value(serde::de::Unexpected::Str(target), v))
176}
177
178fn str_take<'a, E, V>(
179    target: &'a str,
180    n: usize,
181    v: &V,
182) -> Result<(&'a str, &'a str), E> where
183    E: serde::de::Error,
184    V: serde::de::Visitor<'a>
185{
186    if target.len() < n {
187        Err(E::invalid_value(serde::de::Unexpected::Str(target), v))
188    } else {
189        Ok(target.split_at(n))
190    }
191}