Skip to main content

mcproto_types/
json_text_component.rs

1use std::io::{Read, Write};
2
3use mcproto_codec::{
4    error::{CodecError, CodecKind, CodecOperation, InvalidEncodingReason},
5    io::{read_exact_counted, write_all_counted},
6    varint::{VarIntRead, VarIntWrite},
7};
8
9use crate::{TypeCodec, component::JsonComponent};
10
11pub use serde_json::Value as JsonValue;
12
13/// A VarInt-prefixed JSON representation of the current Java text component schema.
14#[derive(Debug, Clone, PartialEq)]
15pub struct JsonTextComponent(pub JsonComponent);
16
17impl JsonTextComponent {
18    pub const MAX_DECODE_UTF16_CODE_UNITS: usize = 262_144;
19    pub const MAX_DECODE_BYTES: usize = Self::MAX_DECODE_UTF16_CODE_UNITS * 3;
20    pub const MAX_DECODE_ENCODED_BYTES: usize = Self::MAX_DECODE_BYTES + 3;
21
22    // Vanilla 1.20.3+ still refuses to encode larger JSON components.
23    pub const MAX_ENCODE_UTF16_CODE_UNITS: usize = 32_767;
24    pub const MAX_ENCODE_BYTES: usize = Self::MAX_ENCODE_UTF16_CODE_UNITS * 3;
25    pub const MAX_ENCODE_ENCODED_BYTES: usize = Self::MAX_ENCODE_BYTES + 3;
26
27    const MAX_COMPONENT_DEPTH: usize = 512;
28
29    pub fn text(value: impl Into<String>) -> Self {
30        Self(JsonComponent::text(value))
31    }
32
33    pub fn from_json_str(value: &str) -> Result<Self, serde_json::Error> {
34        serde_json::from_str(value).map(Self)
35    }
36
37    fn validate_length(
38        value: &str,
39        max_code_units: usize,
40        max_bytes: usize,
41        operation: CodecOperation,
42        bytes_processed: usize,
43    ) -> Result<(), CodecError> {
44        if value.len() > max_bytes {
45            return Err(CodecError::invalid_encoding_for_operation(
46                CodecKind::JsonTextComponent,
47                operation,
48                bytes_processed,
49                InvalidEncodingReason::StringTooLong { max_bytes },
50            ));
51        }
52        if value.encode_utf16().count() > max_code_units {
53            return Err(CodecError::invalid_encoding_for_operation(
54                CodecKind::JsonTextComponent,
55                operation,
56                bytes_processed,
57                InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
58            ));
59        }
60        Ok(())
61    }
62
63    fn invalid_json(
64        operation: CodecOperation,
65        bytes_processed: usize,
66        source: impl std::error::Error + Send + Sync + 'static,
67    ) -> CodecError {
68        CodecError::invalid_encoding_for_operation_with_source(
69            CodecKind::JsonTextComponent,
70            operation,
71            bytes_processed,
72            InvalidEncodingReason::InvalidJson,
73            source,
74        )
75    }
76}
77
78impl Default for JsonTextComponent {
79    fn default() -> Self {
80        Self::text("")
81    }
82}
83
84impl From<JsonComponent> for JsonTextComponent {
85    fn from(value: JsonComponent) -> Self {
86        Self(value)
87    }
88}
89
90impl From<String> for JsonTextComponent {
91    fn from(value: String) -> Self {
92        Self::text(value)
93    }
94}
95
96impl From<&str> for JsonTextComponent {
97    fn from(value: &str) -> Self {
98        Self::text(value)
99    }
100}
101
102impl TypeCodec for JsonTextComponent {
103    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
104        self.0
105            .validate_depth(Self::MAX_COMPONENT_DEPTH)
106            .map_err(|source| Self::invalid_json(CodecOperation::Write, 0, source))?;
107        let json = serde_json::to_string(&self.0)
108            .map_err(|source| Self::invalid_json(CodecOperation::Write, 0, source))?;
109        Self::validate_length(
110            &json,
111            Self::MAX_ENCODE_UTF16_CODE_UNITS,
112            Self::MAX_ENCODE_BYTES,
113            CodecOperation::Write,
114            0,
115        )?;
116
117        let bytes = json.as_bytes();
118        let prefix_size = writer
119            .write_varint_with_size(bytes.len() as i32)
120            .map_err(|error| error.with_context(CodecKind::JsonTextComponent))?;
121        write_all_counted(writer, bytes, CodecKind::JsonTextComponent, prefix_size)
122    }
123
124    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
125        let (byte_length, prefix_size) = reader
126            .read_varint_with_size()
127            .map_err(|error| error.with_context(CodecKind::JsonTextComponent))?;
128        let byte_length = usize::try_from(byte_length).map_err(|_| {
129            CodecError::invalid_encoding(
130                CodecKind::JsonTextComponent,
131                prefix_size,
132                InvalidEncodingReason::NegativeLength { value: byte_length },
133            )
134        })?;
135        if byte_length > Self::MAX_DECODE_BYTES {
136            return Err(CodecError::invalid_encoding(
137                CodecKind::JsonTextComponent,
138                prefix_size,
139                InvalidEncodingReason::StringTooLong {
140                    max_bytes: Self::MAX_DECODE_BYTES,
141                },
142            ));
143        }
144
145        let mut bytes = vec![0; byte_length];
146        read_exact_counted(
147            reader,
148            &mut bytes,
149            CodecKind::JsonTextComponent,
150            prefix_size,
151        )?;
152        let bytes_processed = prefix_size + byte_length;
153        let json = String::from_utf8(bytes).map_err(|error| {
154            let utf8_error = error.utf8_error();
155            CodecError::invalid_encoding(
156                CodecKind::JsonTextComponent,
157                bytes_processed,
158                InvalidEncodingReason::InvalidUtf8 {
159                    valid_up_to: utf8_error.valid_up_to(),
160                    error_len: utf8_error.error_len(),
161                },
162            )
163        })?;
164        Self::validate_length(
165            &json,
166            Self::MAX_DECODE_UTF16_CODE_UNITS,
167            Self::MAX_DECODE_BYTES,
168            CodecOperation::Read,
169            bytes_processed,
170        )?;
171
172        let component: JsonComponent = serde_json::from_str(&json)
173            .map_err(|source| Self::invalid_json(CodecOperation::Read, bytes_processed, source))?;
174        component
175            .validate_depth(Self::MAX_COMPONENT_DEPTH)
176            .map_err(|source| Self::invalid_json(CodecOperation::Read, bytes_processed, source))?;
177        Ok(Self(component))
178    }
179}