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 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}