1use simdnbt::{borrow::NbtTag as BorrowedNbtTag, owned::NbtTag as OwnedNbtTag};
4
5pub trait NbtNumeric {
8 fn codec_bool(&self) -> Option<bool>;
10
11 fn codec_i32(&self) -> Option<i32>;
13
14 fn codec_f32(&self) -> Option<f32>;
16
17 fn codec_f64(&self) -> Option<f64>;
19}
20
21impl NbtNumeric for OwnedNbtTag {
22 fn codec_bool(&self) -> Option<bool> {
23 match self {
24 Self::Byte(value) => Some(*value != 0),
25 Self::Short(value) => Some(*value != 0),
26 Self::Int(value) => Some(*value != 0),
27 Self::Long(value) => Some(*value != 0),
28 Self::Float(value) => Some(*value != 0.0),
29 Self::Double(value) => Some(*value != 0.0),
30 _ => None,
31 }
32 }
33
34 fn codec_i32(&self) -> Option<i32> {
35 match self {
36 Self::Byte(value) => Some(i32::from(*value)),
37 Self::Short(value) => Some(i32::from(*value)),
38 Self::Int(value) => Some(*value),
39 Self::Long(value) => Some(*value as i32),
40 Self::Float(value) => Some(*value as i32),
41 Self::Double(value) => Some(*value as i32),
42 _ => None,
43 }
44 }
45
46 fn codec_f32(&self) -> Option<f32> {
47 match self {
48 Self::Byte(value) => Some(f32::from(*value)),
49 Self::Short(value) => Some(f32::from(*value)),
50 Self::Int(value) => Some(*value as f32),
51 Self::Long(value) => Some(*value as f32),
52 Self::Float(value) => Some(*value),
53 Self::Double(value) => Some(*value as f32),
54 _ => None,
55 }
56 }
57
58 fn codec_f64(&self) -> Option<f64> {
59 match self {
60 Self::Byte(value) => Some(f64::from(*value)),
61 Self::Short(value) => Some(f64::from(*value)),
62 Self::Int(value) => Some(f64::from(*value)),
63 Self::Long(value) => Some(*value as f64),
64 Self::Float(value) => Some(f64::from(*value)),
65 Self::Double(value) => Some(*value),
66 _ => None,
67 }
68 }
69}
70
71impl NbtNumeric for BorrowedNbtTag<'_, '_> {
72 fn codec_bool(&self) -> Option<bool> {
73 self.byte()
74 .map(|value| value != 0)
75 .or_else(|| self.short().map(|value| value != 0))
76 .or_else(|| self.int().map(|value| value != 0))
77 .or_else(|| self.long().map(|value| value != 0))
78 .or_else(|| self.float().map(|value| value != 0.0))
79 .or_else(|| self.double().map(|value| value != 0.0))
80 }
81
82 fn codec_i32(&self) -> Option<i32> {
83 self.byte()
84 .map(i32::from)
85 .or_else(|| self.short().map(i32::from))
86 .or_else(|| self.int())
87 .or_else(|| self.long().map(|value| value as i32))
88 .or_else(|| self.float().map(|value| value as i32))
89 .or_else(|| self.double().map(|value| value as i32))
90 }
91
92 fn codec_f32(&self) -> Option<f32> {
93 self.byte()
94 .map(f32::from)
95 .or_else(|| self.short().map(f32::from))
96 .or_else(|| self.int().map(|value| value as f32))
97 .or_else(|| self.long().map(|value| value as f32))
98 .or_else(|| self.float())
99 .or_else(|| self.double().map(|value| value as f32))
100 }
101
102 fn codec_f64(&self) -> Option<f64> {
103 self.byte()
104 .map(f64::from)
105 .or_else(|| self.short().map(f64::from))
106 .or_else(|| self.int().map(f64::from))
107 .or_else(|| self.long().map(|value| value as f64))
108 .or_else(|| self.float().map(f64::from))
109 .or_else(|| self.double())
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use std::io::Cursor;
116
117 use simdnbt::{borrow::read_tag, owned::NbtTag};
118
119 use super::NbtNumeric;
120
121 #[test]
122 fn owned_numeric_tags_use_codec_coercions() {
123 assert_eq!(NbtTag::Double(5.5).codec_f32(), Some(5.5));
124 assert_eq!(NbtTag::Float(5.9).codec_i32(), Some(5));
125 assert_eq!(
126 NbtTag::Long(i64::from(i32::MAX) + 1).codec_i32(),
127 Some(i32::MIN)
128 );
129 assert_eq!(NbtTag::Double(f64::NAN).codec_bool(), Some(true));
130 assert_eq!(NbtTag::String("1".into()).codec_f64(), None);
131 }
132
133 #[test]
134 fn borrowed_numeric_tags_use_codec_coercions() {
135 let tag = NbtTag::Double(5.5);
136 let mut bytes = Vec::new();
137 tag.write(&mut bytes);
138 let borrowed =
139 read_tag(&mut Cursor::new(bytes.as_slice())).expect("owned test tag should parse");
140
141 assert_eq!(borrowed.as_tag().codec_f32(), Some(5.5));
142 }
143}