1use serde::de::DeserializeOwned;
2use serde::Serialize;
3
4use crate::config::NbtReadConfig;
5use crate::error::Result;
6use crate::protocol_adapter::{ProtocolNbtAdapter, ProtocolNbtEncoding};
7use crate::tag::TagType;
8
9pub trait NbtCodecProfile {
11 const NBT_ENCODING: ProtocolNbtEncoding;
12
13 fn nbt_read_config() -> NbtReadConfig {
14 NbtReadConfig::default()
15 }
16}
17
18pub trait NbtCodecFacade: Serialize + DeserializeOwned + NbtCodecProfile + Sized {
23 fn nbt_adapter() -> ProtocolNbtAdapter {
24 ProtocolNbtAdapter {
25 encoding: Self::NBT_ENCODING,
26 read_config: Self::nbt_read_config(),
27 }
28 }
29
30 fn encode_nbt_root(&self, root_name: impl Into<String>) -> Result<Vec<u8>> {
31 Self::nbt_adapter().encode_root(root_name, self)
32 }
33
34 fn decode_nbt_root(bytes: &[u8]) -> Result<Self> {
35 Self::nbt_adapter().decode_root(bytes)
36 }
37
38 fn decode_nbt_root_named(bytes: &[u8]) -> Result<(String, Self)> {
39 Self::nbt_adapter().decode_root_named(bytes)
40 }
41
42 fn encode_nbt_prefixed(&self) -> Result<Vec<u8>> {
43 Self::nbt_adapter().encode_prefixed(self)
44 }
45
46 fn decode_nbt_prefixed(bytes: &[u8]) -> Result<Self> {
47 Self::nbt_adapter().decode_prefixed(bytes)
48 }
49
50 fn encode_nbt_headless(&self) -> Result<(TagType, Vec<u8>)> {
51 Self::nbt_adapter().encode_headless(self)
52 }
53
54 fn decode_nbt_headless(tag_type: TagType, bytes: &[u8]) -> Result<Self> {
55 Self::nbt_adapter().decode_headless(tag_type, bytes)
56 }
57}
58
59impl<T> NbtCodecFacade for T where T: Serialize + DeserializeOwned + NbtCodecProfile {}
60
61#[macro_export]
65macro_rules! nbt_profile {
66 ($ty:ty, net) => {
67 impl $crate::codec_bridge::NbtCodecProfile for $ty {
68 const NBT_ENCODING: $crate::ProtocolNbtEncoding = $crate::ProtocolNbtEncoding::Network;
69 }
70 };
71 ($ty:ty, le) => {
72 impl $crate::codec_bridge::NbtCodecProfile for $ty {
73 const NBT_ENCODING: $crate::ProtocolNbtEncoding =
74 $crate::ProtocolNbtEncoding::LittleEndian;
75 }
76 };
77 ($ty:ty, be) => {
78 impl $crate::codec_bridge::NbtCodecProfile for $ty {
79 const NBT_ENCODING: $crate::ProtocolNbtEncoding =
80 $crate::ProtocolNbtEncoding::BigEndian;
81 }
82 };
83}
84
85#[macro_export]
87macro_rules! nbt_profile_with_config {
88 ($ty:ty, net, $config:expr) => {
89 impl $crate::codec_bridge::NbtCodecProfile for $ty {
90 const NBT_ENCODING: $crate::ProtocolNbtEncoding = $crate::ProtocolNbtEncoding::Network;
91 fn nbt_read_config() -> $crate::NbtReadConfig {
92 $config
93 }
94 }
95 };
96 ($ty:ty, le, $config:expr) => {
97 impl $crate::codec_bridge::NbtCodecProfile for $ty {
98 const NBT_ENCODING: $crate::ProtocolNbtEncoding =
99 $crate::ProtocolNbtEncoding::LittleEndian;
100 fn nbt_read_config() -> $crate::NbtReadConfig {
101 $config
102 }
103 }
104 };
105 ($ty:ty, be, $config:expr) => {
106 impl $crate::codec_bridge::NbtCodecProfile for $ty {
107 const NBT_ENCODING: $crate::ProtocolNbtEncoding =
108 $crate::ProtocolNbtEncoding::BigEndian;
109 fn nbt_read_config() -> $crate::NbtReadConfig {
110 $config
111 }
112 }
113 };
114}
115
116#[cfg(test)]
117mod tests {
118 use serde::{Deserialize, Serialize};
119
120 use crate::config::NbtReadConfig;
121 use crate::limits::NbtLimits;
122 use crate::{Error, TagType};
123
124 use super::*;
125
126 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
127 struct NetPacket {
128 username: String,
129 hp: i32,
130 flags: Vec<u8>,
131 }
132
133 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
134 struct LePacket {
135 id: i32,
136 values: Vec<i32>,
137 }
138
139 #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
140 struct StrictListPacket {
141 entries: Vec<i32>,
142 }
143
144 crate::nbt_profile!(NetPacket, net);
145 crate::nbt_profile!(LePacket, le);
146 crate::nbt_profile_with_config!(
147 StrictListPacket,
148 be,
149 NbtReadConfig::strict(NbtLimits::default())
150 );
151
152 #[test]
153 fn net_profile_root_roundtrip() {
154 let input = NetPacket {
155 username: "Steve".to_string(),
156 hp: 20,
157 flags: vec![1, 0, 1],
158 };
159 let bytes = input.encode_nbt_root("Packet").unwrap();
160 let (root_name, output) = NetPacket::decode_nbt_root_named(&bytes).unwrap();
161 assert_eq!(root_name, "Packet");
162 assert_eq!(output, input);
163 }
164
165 #[test]
166 fn le_profile_prefixed_roundtrip() {
167 let input = LePacket {
168 id: 7,
169 values: vec![2, 4, 6, 8],
170 };
171 let bytes = input.encode_nbt_prefixed().unwrap();
172 let output = LePacket::decode_nbt_prefixed(&bytes).unwrap();
173 assert_eq!(output, input);
174 }
175
176 #[test]
177 fn headless_profile_roundtrip() {
178 let input = LePacket {
179 id: 99,
180 values: vec![1, 3, 5],
181 };
182 let (tag_type, bytes) = input.encode_nbt_headless().unwrap();
183 let output = LePacket::decode_nbt_headless(tag_type, &bytes).unwrap();
184 assert_eq!(output, input);
185 }
186
187 #[test]
188 fn profile_with_config_uses_declared_parse_mode() {
189 let bytes = vec![0x00, 0x00, 0x00, 0x00, 0x01];
191 let err = StrictListPacket::decode_nbt_headless(TagType::List, &bytes).unwrap_err();
192 assert!(matches!(err.innermost(), Error::InvalidListHeader { .. }));
193 }
194}