chunkedge_binary/impls/
string.rs1use std::io::Write;
2
3use anyhow::{ensure, Context};
4use chunkedge_nbt::compound::NetworkCompound;
5use chunkedge_nbt::serde::ser::CompoundSerializer;
6use chunkedge_text::{JsonText, Text};
7use serde::de::IntoDeserializer;
8use serde::{Deserialize, Serialize};
9
10use crate::{Bounded, Decode, Encode, VarInt};
11
12const DEFAULT_MAX_STRING_CHARS: usize = 32767;
13const MAX_TEXT_CHARS: usize = 262144;
14
15impl Encode for str {
16 fn encode(&self, w: impl Write) -> anyhow::Result<()> {
17 Bounded::<_, DEFAULT_MAX_STRING_CHARS>(self).encode(w)
18 }
19}
20
21impl<const MAX_CHARS: usize> Encode for Bounded<&'_ str, MAX_CHARS> {
22 fn encode(&self, mut w: impl Write) -> anyhow::Result<()> {
23 let char_count = self.encode_utf16().count();
24
25 ensure!(
26 char_count <= MAX_CHARS,
27 "char count of string exceeds maximum (expected <= {MAX_CHARS}, got {char_count})"
28 );
29
30 VarInt(self.len() as i32).encode(&mut w)?;
31 Ok(w.write_all(self.as_bytes())?)
32 }
33}
34
35impl<'a> Decode<'a> for &'a str {
36 fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
37 Ok(Bounded::<_, DEFAULT_MAX_STRING_CHARS>::decode(r)?.0)
38 }
39}
40
41impl<'a, const MAX_CHARS: usize> Decode<'a> for Bounded<&'a str, MAX_CHARS> {
42 fn decode(r: &mut &'a [u8]) -> anyhow::Result<Self> {
43 let len = VarInt::decode(r)?.0;
44 ensure!(len >= 0, "attempt to decode string with negative length");
45 let len = len as usize;
46 ensure!(
47 len <= r.len(),
48 "not enough data remaining ({} bytes) to decode string of {len} bytes",
49 r.len()
50 );
51
52 let (res, remaining) = r.split_at(len);
53 let res = std::str::from_utf8(res)?;
54
55 let char_count = res.encode_utf16().count();
56 ensure!(
57 char_count <= MAX_CHARS,
58 "char count of string exceeds maximum (expected <= {MAX_CHARS}, got {char_count})"
59 );
60
61 *r = remaining;
62
63 Ok(Bounded(res))
64 }
65}
66
67impl Encode for String {
68 fn encode(&self, w: impl Write) -> anyhow::Result<()> {
69 self.as_str().encode(w)
70 }
71}
72
73impl<const MAX_CHARS: usize> Encode for Bounded<String, MAX_CHARS> {
74 fn encode(&self, w: impl Write) -> anyhow::Result<()> {
75 Bounded::<_, MAX_CHARS>(self.as_str()).encode(w)
76 }
77}
78
79impl Decode<'_> for String {
80 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
81 Ok(<&str>::decode(r)?.into())
82 }
83}
84
85impl<const MAX_CHARS: usize> Decode<'_> for Bounded<String, MAX_CHARS> {
86 fn decode(r: &mut &'_ [u8]) -> anyhow::Result<Self> {
87 Ok(Bounded(Bounded::<&str, MAX_CHARS>::decode(r)?.0.into()))
88 }
89}
90
91impl Decode<'_> for Box<str> {
92 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
93 Ok(<&str>::decode(r)?.into())
94 }
95}
96
97impl<const MAX_CHARS: usize> Decode<'_> for Bounded<Box<str>, MAX_CHARS> {
98 fn decode(r: &mut &'_ [u8]) -> anyhow::Result<Self> {
99 Ok(Bounded(Bounded::<&str, MAX_CHARS>::decode(r)?.0.into()))
100 }
101}
102
103impl Decode<'_> for Text {
104 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
105 let c = NetworkCompound::decode(r)
106 .context("decoding text from NBT")?
107 .compound;
108 Text::deserialize(c.into_deserializer()).context("deserializing text NBT")
109 }
110}
111
112impl Encode for Text {
113 fn encode(&self, w: impl Write) -> anyhow::Result<()> {
114 let c = self
115 .serialize(CompoundSerializer)
116 .context("serializing text as compound")?;
117
118 NetworkCompound::from(c).encode(w)
119 }
120}
121
122impl Encode for JsonText {
123 fn encode(&self, w: impl Write) -> anyhow::Result<()> {
124 let s = serde_json::to_string(self).context("serializing text JSON")?;
125
126 Bounded::<_, MAX_TEXT_CHARS>(s).encode(w)
127 }
128}
129
130impl Decode<'_> for JsonText {
131 fn decode(r: &mut &[u8]) -> anyhow::Result<Self> {
132 let str = Bounded::<&str, MAX_TEXT_CHARS>::decode(r)?.0;
133
134 serde_json::from_str(str).context("deserializing text JSON")
135 }
136}
137
138#[cfg(test)]
139mod tests {
140 use chunkedge_text::{Color, IntoText};
141
142 use super::*;
143
144 #[test]
145 fn test_text_encoding() {
146 let text = Text::from("Hello, world!").color(Color::RED);
147 let mut encoded = Vec::new();
148 text.encode(&mut encoded).unwrap();
149
150 let decoded: Text = Text::decode(&mut encoded.as_slice()).unwrap();
151 assert_eq!(decoded, text);
152
153 let text = "Italic text".italic();
154 let mut encoded = Vec::new();
155 text.encode(&mut encoded).unwrap();
156 let decoded: Text = Text::decode(&mut encoded.as_slice()).unwrap();
157 assert_eq!(decoded, text);
158 }
159
160 #[test]
161 fn test_json_text_encoding() {
162 let json_text = JsonText(Text::from("Hello, world!").color(Color::RED));
163 let mut encoded = Vec::new();
164 json_text.encode(&mut encoded).unwrap();
165
166 let decoded: JsonText = JsonText::decode(&mut encoded.as_slice()).unwrap();
167 assert_eq!(decoded, json_text);
168 }
169}