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};
8use serde::{Deserialize, Serialize};
9
10use crate::{TypeCodec, component::JsonComponent};
11
12pub use serde_json::Value as JsonValue;
13
14/// A VarInt-prefixed JSON representation of the current Java text component schema.
15#[derive(Debug, Clone, PartialEq)]
16pub struct JsonTextComponent(pub JsonComponent);
17
18impl JsonTextComponent {
19    pub const MAX_DECODE_UTF16_CODE_UNITS: usize = 262_144;
20    pub const MAX_DECODE_BYTES: usize = Self::MAX_DECODE_UTF16_CODE_UNITS * 3;
21    pub const MAX_DECODE_ENCODED_BYTES: usize = Self::MAX_DECODE_BYTES + 3;
22
23    // Vanilla 1.20.3+ still refuses to encode larger JSON components.
24    pub const MAX_ENCODE_UTF16_CODE_UNITS: usize = 32_767;
25    pub const MAX_ENCODE_BYTES: usize = Self::MAX_ENCODE_UTF16_CODE_UNITS * 3;
26    pub const MAX_ENCODE_ENCODED_BYTES: usize = Self::MAX_ENCODE_BYTES + 3;
27
28    const MAX_COMPONENT_DEPTH: usize = 512;
29
30    pub fn text(value: impl Into<String>) -> Self {
31        Self(JsonComponent::text(value))
32    }
33
34    pub fn from_json_str(value: &str) -> Result<Self, serde_json::Error> {
35        let component = deserialize_json(value)?;
36        validate_component(&component).map_err(json_validation_error)?;
37        Ok(Self(component))
38    }
39
40    fn validate_length(
41        value: &str,
42        max_code_units: usize,
43        max_bytes: usize,
44        operation: CodecOperation,
45        bytes_processed: usize,
46    ) -> Result<(), CodecError> {
47        if value.len() > max_bytes {
48            return Err(CodecError::invalid_encoding_for_operation(
49                CodecKind::JsonTextComponent,
50                operation,
51                bytes_processed,
52                InvalidEncodingReason::StringTooLong { max_bytes },
53            ));
54        }
55        if value.encode_utf16().count() > max_code_units {
56            return Err(CodecError::invalid_encoding_for_operation(
57                CodecKind::JsonTextComponent,
58                operation,
59                bytes_processed,
60                InvalidEncodingReason::TooManyUtf16CodeUnits { max_code_units },
61            ));
62        }
63        Ok(())
64    }
65
66    fn invalid_json(
67        operation: CodecOperation,
68        bytes_processed: usize,
69        source: impl std::error::Error + Send + Sync + 'static,
70    ) -> CodecError {
71        CodecError::invalid_encoding_for_operation_with_source(
72            CodecKind::JsonTextComponent,
73            operation,
74            bytes_processed,
75            InvalidEncodingReason::InvalidJson,
76            source,
77        )
78    }
79}
80
81impl Default for JsonTextComponent {
82    fn default() -> Self {
83        Self::text("")
84    }
85}
86
87impl From<JsonComponent> for JsonTextComponent {
88    fn from(value: JsonComponent) -> Self {
89        Self(value)
90    }
91}
92
93impl From<String> for JsonTextComponent {
94    fn from(value: String) -> Self {
95        Self::text(value)
96    }
97}
98
99impl From<&str> for JsonTextComponent {
100    fn from(value: &str) -> Self {
101        Self::text(value)
102    }
103}
104
105impl TypeCodec for JsonTextComponent {
106    fn encode(&self, writer: &mut impl Write) -> Result<(), CodecError> {
107        validate_component(&self.0)
108            .map_err(|source| Self::invalid_json(CodecOperation::Write, 0, source))?;
109        let mut bytes = Vec::new();
110        let mut serializer = serde_json::Serializer::new(&mut bytes);
111        self.0
112            .serialize(serde_stacker::Serializer::new(&mut serializer))
113            .map_err(|source| Self::invalid_json(CodecOperation::Write, 0, source))?;
114        let json = String::from_utf8(bytes)
115            .map_err(|source| Self::invalid_json(CodecOperation::Write, 0, source))?;
116        Self::validate_length(
117            &json,
118            Self::MAX_ENCODE_UTF16_CODE_UNITS,
119            Self::MAX_ENCODE_BYTES,
120            CodecOperation::Write,
121            0,
122        )?;
123
124        let bytes = json.as_bytes();
125        let prefix_size = writer
126            .write_varint_with_size(bytes.len() as i32)
127            .map_err(|error| error.with_context(CodecKind::JsonTextComponent))?;
128        write_all_counted(writer, bytes, CodecKind::JsonTextComponent, prefix_size)
129    }
130
131    fn decode(reader: &mut impl Read) -> Result<Self, CodecError> {
132        let (byte_length, prefix_size) = reader
133            .read_varint_with_size()
134            .map_err(|error| error.with_context(CodecKind::JsonTextComponent))?;
135        let byte_length = usize::try_from(byte_length).map_err(|_| {
136            CodecError::invalid_encoding(
137                CodecKind::JsonTextComponent,
138                prefix_size,
139                InvalidEncodingReason::NegativeLength { value: byte_length },
140            )
141        })?;
142        if byte_length > Self::MAX_DECODE_BYTES {
143            return Err(CodecError::invalid_encoding(
144                CodecKind::JsonTextComponent,
145                prefix_size,
146                InvalidEncodingReason::StringTooLong {
147                    max_bytes: Self::MAX_DECODE_BYTES,
148                },
149            ));
150        }
151
152        let mut bytes = vec![0; byte_length];
153        read_exact_counted(
154            reader,
155            &mut bytes,
156            CodecKind::JsonTextComponent,
157            prefix_size,
158        )?;
159        let bytes_processed = prefix_size + byte_length;
160        let json = String::from_utf8(bytes).map_err(|error| {
161            let utf8_error = error.utf8_error();
162            CodecError::invalid_encoding(
163                CodecKind::JsonTextComponent,
164                bytes_processed,
165                InvalidEncodingReason::InvalidUtf8 {
166                    valid_up_to: utf8_error.valid_up_to(),
167                    error_len: utf8_error.error_len(),
168                },
169            )
170        })?;
171        Self::validate_length(
172            &json,
173            Self::MAX_DECODE_UTF16_CODE_UNITS,
174            Self::MAX_DECODE_BYTES,
175            CodecOperation::Read,
176            bytes_processed,
177        )?;
178
179        let component = deserialize_json(&json)
180            .map_err(|source| Self::invalid_json(CodecOperation::Read, bytes_processed, source))?;
181        validate_component(&component)
182            .map_err(|source| Self::invalid_json(CodecOperation::Read, bytes_processed, source))?;
183        Ok(Self(component))
184    }
185}
186
187fn deserialize_json(value: &str) -> Result<JsonComponent, serde_json::Error> {
188    validate_json_syntax_depth(value, JsonTextComponent::MAX_COMPONENT_DEPTH * 2 + 8)
189        .map_err(json_validation_error)?;
190    let mut deserializer = serde_json::Deserializer::from_str(value);
191    deserializer.disable_recursion_limit();
192    let component =
193        JsonComponent::deserialize(serde_stacker::Deserializer::new(&mut deserializer))?;
194    deserializer.end()?;
195    Ok(component)
196}
197
198fn validate_json_syntax_depth(value: &str, max_depth: usize) -> Result<(), JsonSyntaxDepthError> {
199    let mut depth = 0_usize;
200    let mut in_string = false;
201    let mut escaped = false;
202    for byte in value.bytes() {
203        if in_string {
204            if escaped {
205                escaped = false;
206            } else if byte == b'\\' {
207                escaped = true;
208            } else if byte == b'"' {
209                in_string = false;
210            }
211            continue;
212        }
213        match byte {
214            b'"' => in_string = true,
215            b'{' | b'[' => {
216                depth += 1;
217                if depth > max_depth {
218                    return Err(JsonSyntaxDepthError { max_depth });
219                }
220            }
221            b'}' | b']' => depth = depth.saturating_sub(1),
222            _ => {}
223        }
224    }
225    Ok(())
226}
227
228#[derive(Debug)]
229struct JsonSyntaxDepthError {
230    max_depth: usize,
231}
232
233impl std::fmt::Display for JsonSyntaxDepthError {
234    fn fmt(&self, formatter: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235        write!(
236            formatter,
237            "JSON nesting exceeds the {}-container limit",
238            self.max_depth
239        )
240    }
241}
242
243impl std::error::Error for JsonSyntaxDepthError {}
244
245fn validate_component(
246    component: &JsonComponent,
247) -> Result<(), crate::component::ComponentDepthError> {
248    component.validate_depth(JsonTextComponent::MAX_COMPONENT_DEPTH)?;
249    component.validate_dynamic_depth(JsonTextComponent::MAX_COMPONENT_DEPTH)
250}
251
252fn json_validation_error(
253    source: impl std::error::Error + Send + Sync + 'static,
254) -> serde_json::Error {
255    serde_json::Error::io(std::io::Error::new(std::io::ErrorKind::InvalidData, source))
256}