elytra_ping/
mc_string.rs

1use bytes::Buf;
2use mc_varint::{VarInt, VarIntRead, VarIntWrite};
3use snafu::{OptionExt, Snafu};
4use std::io::Cursor;
5
6#[derive(Snafu, Debug)]
7pub enum McStringError {
8    #[snafu(display("io error: {source}"), context(false))]
9    Io {
10        source: std::io::Error,
11        backtrace: snafu::Backtrace,
12    },
13    #[snafu(display(
14        "string is too long (is {length} bytes, but expected less than {} bytes)",
15        MAX_LEN
16    ))]
17    TooLong {
18        length: usize,
19        backtrace: snafu::Backtrace,
20    },
21    #[snafu(display("invalid string format"))]
22    InvalidFormat { backtrace: snafu::Backtrace },
23}
24
25pub const MAX_LEN: i32 = i32::MAX;
26
27pub fn encode_mc_string(string: &str) -> Result<Vec<u8>, McStringError> {
28    let len = string.len();
29    // VarInt max length is 5 bytes
30    let mut bytes = Vec::with_capacity(len + 5);
31    bytes.write_var_int(VarInt::from(
32        i32::try_from(len)
33            .ok()
34            .context(TooLongSnafu { length: len })?,
35    ))?;
36    bytes.extend_from_slice(string.as_bytes());
37    Ok(bytes)
38}
39
40pub fn decode_mc_string(cursor: &mut Cursor<&[u8]>) -> Result<String, McStringError> {
41    let len: i32 = cursor.read_var_int()?.into();
42    let len = usize::try_from(len).ok().context(InvalidFormatSnafu)?;
43
44    let bytes = cursor.chunk();
45    if len > bytes.len() {
46        return InvalidFormatSnafu.fail();
47    }
48    let string = std::str::from_utf8(bytes.get(..len).context(InvalidFormatSnafu)?)
49        .ok()
50        .context(InvalidFormatSnafu)?
51        .to_string();
52    cursor.advance(len);
53    Ok(string)
54}
55
56#[cfg(test)]
57mod tests {
58    use super::*;
59
60    #[test]
61    fn encode_and_decode_mc_string() {
62        const STRING: &str = "hello world!!";
63        let bytes = encode_mc_string(STRING).unwrap();
64        let mut cursor = Cursor::new(bytes.as_slice());
65        let decoded_string = decode_mc_string(&mut cursor).unwrap();
66        assert_eq!(decoded_string, STRING);
67    }
68}