1use simdnbt::{borrow::NbtTag as BorrowedNbtTag, owned::NbtTag as OwnedNbtTag};
4
5pub trait NbtNumeric {
12 fn codec_bool(&self) -> Option<bool>;
14
15 fn codec_i8(&self) -> Option<i8>;
17
18 fn codec_i32(&self) -> Option<i32>;
20
21 fn codec_i64(&self) -> Option<i64>;
23
24 fn codec_f32(&self) -> Option<f32>;
26
27 fn codec_f64(&self) -> Option<f64>;
29
30 fn int_value(&self) -> Option<i32>;
32
33 fn short_value(&self) -> Option<i16> {
35 self.int_value().map(|value| value as i16)
36 }
37
38 fn byte_value(&self) -> Option<i8> {
40 self.int_value().map(|value| value as i8)
41 }
42}
43
44impl NbtNumeric for OwnedNbtTag {
45 fn codec_bool(&self) -> Option<bool> {
46 match self {
47 Self::Byte(value) => Some(*value != 0),
48 Self::Short(value) => Some(*value != 0),
49 Self::Int(value) => Some(*value != 0),
50 Self::Long(value) => Some(*value != 0),
51 Self::Float(value) => Some(*value != 0.0),
52 Self::Double(value) => Some(*value != 0.0),
53 _ => None,
54 }
55 }
56
57 fn codec_i8(&self) -> Option<i8> {
58 match self {
59 Self::Byte(value) => Some(*value),
60 Self::Short(value) => Some(*value as i8),
61 Self::Int(value) => Some(*value as i8),
62 Self::Long(value) => Some(*value as i8),
63 Self::Float(value) => Some(*value as i32 as i8),
64 Self::Double(value) => Some(*value as i32 as i8),
65 _ => None,
66 }
67 }
68
69 fn codec_i32(&self) -> Option<i32> {
70 match self {
71 Self::Byte(value) => Some(i32::from(*value)),
72 Self::Short(value) => Some(i32::from(*value)),
73 Self::Int(value) => Some(*value),
74 Self::Long(value) => Some(*value as i32),
75 Self::Float(value) => Some(*value as i32),
76 Self::Double(value) => Some(*value as i32),
77 _ => None,
78 }
79 }
80
81 fn codec_i64(&self) -> Option<i64> {
82 match self {
83 Self::Byte(value) => Some(i64::from(*value)),
84 Self::Short(value) => Some(i64::from(*value)),
85 Self::Int(value) => Some(i64::from(*value)),
86 Self::Long(value) => Some(*value),
87 Self::Float(value) => Some(*value as i64),
88 Self::Double(value) => Some(*value as i64),
89 _ => None,
90 }
91 }
92
93 fn codec_f32(&self) -> Option<f32> {
94 match self {
95 Self::Byte(value) => Some(f32::from(*value)),
96 Self::Short(value) => Some(f32::from(*value)),
97 Self::Int(value) => Some(*value as f32),
98 Self::Long(value) => Some(*value as f32),
99 Self::Float(value) => Some(*value),
100 Self::Double(value) => Some(*value as f32),
101 _ => None,
102 }
103 }
104
105 fn codec_f64(&self) -> Option<f64> {
106 match self {
107 Self::Byte(value) => Some(f64::from(*value)),
108 Self::Short(value) => Some(f64::from(*value)),
109 Self::Int(value) => Some(f64::from(*value)),
110 Self::Long(value) => Some(*value as f64),
111 Self::Float(value) => Some(f64::from(*value)),
112 Self::Double(value) => Some(*value),
113 _ => None,
114 }
115 }
116
117 fn int_value(&self) -> Option<i32> {
118 match self {
119 Self::Byte(value) => Some(i32::from(*value)),
120 Self::Short(value) => Some(i32::from(*value)),
121 Self::Int(value) => Some(*value),
122 Self::Long(value) => Some(*value as i32),
123 Self::Float(value) => Some(value.floor() as i32),
124 Self::Double(value) => Some(value.floor() as i32),
125 _ => None,
126 }
127 }
128}
129
130impl NbtNumeric for BorrowedNbtTag<'_, '_> {
131 fn codec_bool(&self) -> Option<bool> {
132 self.byte()
133 .map(|value| value != 0)
134 .or_else(|| self.short().map(|value| value != 0))
135 .or_else(|| self.int().map(|value| value != 0))
136 .or_else(|| self.long().map(|value| value != 0))
137 .or_else(|| self.float().map(|value| value != 0.0))
138 .or_else(|| self.double().map(|value| value != 0.0))
139 }
140
141 fn codec_i8(&self) -> Option<i8> {
142 self.byte()
143 .or_else(|| self.short().map(|value| value as i8))
144 .or_else(|| self.int().map(|value| value as i8))
145 .or_else(|| self.long().map(|value| value as i8))
146 .or_else(|| self.float().map(|value| value as i32 as i8))
147 .or_else(|| self.double().map(|value| value as i32 as i8))
148 }
149
150 fn codec_i32(&self) -> Option<i32> {
151 self.byte()
152 .map(i32::from)
153 .or_else(|| self.short().map(i32::from))
154 .or_else(|| self.int())
155 .or_else(|| self.long().map(|value| value as i32))
156 .or_else(|| self.float().map(|value| value as i32))
157 .or_else(|| self.double().map(|value| value as i32))
158 }
159
160 fn codec_i64(&self) -> Option<i64> {
161 self.byte()
162 .map(i64::from)
163 .or_else(|| self.short().map(i64::from))
164 .or_else(|| self.int().map(i64::from))
165 .or_else(|| self.long())
166 .or_else(|| self.float().map(|value| value as i64))
167 .or_else(|| self.double().map(|value| value as i64))
168 }
169
170 fn codec_f32(&self) -> Option<f32> {
171 self.byte()
172 .map(f32::from)
173 .or_else(|| self.short().map(f32::from))
174 .or_else(|| self.int().map(|value| value as f32))
175 .or_else(|| self.long().map(|value| value as f32))
176 .or_else(|| self.float())
177 .or_else(|| self.double().map(|value| value as f32))
178 }
179
180 fn codec_f64(&self) -> Option<f64> {
181 self.byte()
182 .map(f64::from)
183 .or_else(|| self.short().map(f64::from))
184 .or_else(|| self.int().map(f64::from))
185 .or_else(|| self.long().map(|value| value as f64))
186 .or_else(|| self.float().map(f64::from))
187 .or_else(|| self.double())
188 }
189
190 fn int_value(&self) -> Option<i32> {
191 self.byte()
192 .map(i32::from)
193 .or_else(|| self.short().map(i32::from))
194 .or_else(|| self.int())
195 .or_else(|| self.long().map(|value| value as i32))
196 .or_else(|| self.float().map(|value| value.floor() as i32))
197 .or_else(|| self.double().map(|value| value.floor() as i32))
198 }
199}
200
201#[cfg(test)]
202mod tests {
203 use std::io::Cursor;
204
205 use simdnbt::{borrow::read_tag, owned::NbtTag};
206
207 use super::NbtNumeric;
208
209 #[test]
210 fn owned_numeric_tags_use_codec_coercions() {
211 assert_eq!(NbtTag::Double(5.5).codec_f32(), Some(5.5));
212 assert_eq!(NbtTag::Float(5.9).codec_i32(), Some(5));
213 assert_eq!(NbtTag::Int(0x1FF).codec_i8(), Some(-1));
214 assert_eq!(NbtTag::Double(-1.5).codec_i64(), Some(-1));
215 assert_eq!(NbtTag::Float(-1.5).codec_i8(), Some(-1));
216 assert_eq!(
217 NbtTag::Long(i64::from(i32::MAX) + 1).codec_i32(),
218 Some(i32::MIN)
219 );
220 assert_eq!(NbtTag::Double(f64::NAN).codec_bool(), Some(true));
221 assert_eq!(NbtTag::String("1".into()).codec_f64(), None);
222 }
223
224 #[test]
225 fn borrowed_numeric_tags_use_codec_coercions() {
226 let tag = NbtTag::Double(5.5);
227 let mut bytes = Vec::new();
228 tag.write(&mut bytes);
229 let borrowed =
230 read_tag(&mut Cursor::new(bytes.as_slice())).expect("owned test tag should parse");
231
232 assert_eq!(borrowed.as_tag().codec_f32(), Some(5.5));
233 }
234}