Skip to main content

boon/entity/
field_value.rs

1use std::fmt;
2
3use crate::error::FieldValueConversionError;
4
5/// Represents a decoded entity field value from the demo's entity system.
6///
7/// Each variant corresponds to a Source 2 network field type. The variant
8/// chosen at runtime depends on the field's serializer metadata.
9#[derive(Clone)]
10pub enum FieldValue {
11    Bool(bool),
12    I32(i32),
13    I64(i64),
14    U32(u32),
15    U64(u64),
16    F32(f32),
17    /// Raw byte string. Stored as `Vec<u8>` rather than `std::string::String`
18    /// because some Source 2 strings are not guaranteed to be valid UTF-8.
19    String(Vec<u8>),
20    Vector2([f32; 2]),
21    Vector3([f32; 3]),
22    Vector4([f32; 4]),
23    /// Arbitrary-length float vector, for multi-component types that don't fit
24    /// the fixed vectors above (e.g. `Quaternion` = 4, `CTransform` = 6).
25    FloatVector(Vec<f32>),
26    /// Euler angles (pitch, yaw, roll) in degrees.
27    QAngle([f32; 3]),
28}
29
30impl fmt::Debug for FieldValue {
31    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
32        match self {
33            Self::Bool(v) => write!(f, "{v}"),
34            Self::I32(v) => write!(f, "{v}"),
35            Self::I64(v) => write!(f, "{v}"),
36            Self::U32(v) => write!(f, "{v}"),
37            Self::U64(v) => write!(f, "{v}"),
38            Self::F32(v) => write!(f, "{v}"),
39            Self::String(v) => write!(f, "{}", String::from_utf8_lossy(v)),
40            Self::Vector2(v) => write!(f, "[{}, {}]", v[0], v[1]),
41            Self::Vector3(v) => write!(f, "[{}, {}, {}]", v[0], v[1], v[2]),
42            Self::Vector4(v) => write!(f, "[{}, {}, {}, {}]", v[0], v[1], v[2], v[3]),
43            Self::FloatVector(v) => write!(f, "{v:?}"),
44            Self::QAngle(v) => write!(f, "QAngle({}, {}, {})", v[0], v[1], v[2]),
45        }
46    }
47}
48
49impl fmt::Display for FieldValue {
50    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
51        fmt::Debug::fmt(self, f)
52    }
53}
54
55impl serde::Serialize for FieldValue {
56    fn serialize<S: serde::Serializer>(
57        &self,
58        serializer: S,
59    ) -> std::result::Result<S::Ok, S::Error> {
60        match self {
61            Self::Bool(v) => serializer.serialize_bool(*v),
62            Self::I32(v) => serializer.serialize_i32(*v),
63            Self::I64(v) => serializer.serialize_i64(*v),
64            Self::U32(v) => serializer.serialize_u32(*v),
65            Self::U64(v) => serializer.serialize_u64(*v),
66            Self::F32(v) => serializer.serialize_f32(*v),
67            Self::String(v) => serializer.serialize_str(&String::from_utf8_lossy(v)),
68            Self::Vector2(v) => v.serialize(serializer),
69            Self::Vector3(v) => v.serialize(serializer),
70            Self::Vector4(v) => v.serialize(serializer),
71            Self::FloatVector(v) => v.serialize(serializer),
72            Self::QAngle(v) => v.serialize(serializer),
73        }
74    }
75}
76
77/// Generates `TryFrom<FieldValue>` for signed integer types.
78/// Accepts `I32` and `I64` variants, with range checking via `try_from`.
79macro_rules! impl_try_from_signed {
80    ($($ty:ty),+) => {
81        $(
82            impl TryFrom<FieldValue> for $ty {
83                type Error = FieldValueConversionError;
84                fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
85                    match v {
86                        FieldValue::I32(val) => <$ty>::try_from(val).map_err(|_| FieldValueConversionError),
87                        FieldValue::I64(val) => <$ty>::try_from(val).map_err(|_| FieldValueConversionError),
88                        _ => Err(FieldValueConversionError),
89                    }
90                }
91            }
92        )+
93    }
94}
95
96/// Generates `TryFrom<FieldValue>` for unsigned integer types.
97/// Accepts `U32` and `U64` variants, with range checking via `try_from`.
98macro_rules! impl_try_from_unsigned {
99    ($($ty:ty),+) => {
100        $(
101            impl TryFrom<FieldValue> for $ty {
102                type Error = FieldValueConversionError;
103                fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
104                    match v {
105                        FieldValue::U32(val) => <$ty>::try_from(val).map_err(|_| FieldValueConversionError),
106                        FieldValue::U64(val) => <$ty>::try_from(val).map_err(|_| FieldValueConversionError),
107                        _ => Err(FieldValueConversionError),
108                    }
109                }
110            }
111        )+
112    }
113}
114
115impl_try_from_signed!(i8, i16, i32, i64);
116impl_try_from_unsigned!(u8, u16, u32, u64);
117
118impl TryFrom<FieldValue> for f32 {
119    type Error = FieldValueConversionError;
120    fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
121        match v {
122            FieldValue::F32(val) => Ok(val),
123            _ => Err(FieldValueConversionError),
124        }
125    }
126}
127
128impl TryFrom<FieldValue> for bool {
129    type Error = FieldValueConversionError;
130    fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
131        match v {
132            FieldValue::Bool(val) => Ok(val),
133            _ => Err(FieldValueConversionError),
134        }
135    }
136}
137
138impl TryFrom<FieldValue> for [f32; 2] {
139    type Error = FieldValueConversionError;
140    fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
141        match v {
142            FieldValue::Vector2(val) => Ok(val),
143            _ => Err(FieldValueConversionError),
144        }
145    }
146}
147
148impl TryFrom<FieldValue> for [f32; 3] {
149    type Error = FieldValueConversionError;
150    fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
151        match v {
152            FieldValue::Vector3(val) | FieldValue::QAngle(val) => Ok(val),
153            _ => Err(FieldValueConversionError),
154        }
155    }
156}
157
158impl TryFrom<FieldValue> for [f32; 4] {
159    type Error = FieldValueConversionError;
160    fn try_from(v: FieldValue) -> std::result::Result<Self, Self::Error> {
161        match v {
162            FieldValue::Vector4(val) => Ok(val),
163            _ => Err(FieldValueConversionError),
164        }
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171
172    // ── TryFrom conversions ──
173
174    #[test]
175    fn try_from_i32_to_i32() {
176        let v = FieldValue::I32(42);
177        assert_eq!(i32::try_from(v).unwrap(), 42);
178    }
179
180    #[test]
181    fn try_from_i64_to_i32() {
182        let v = FieldValue::I64(100);
183        assert_eq!(i32::try_from(v).unwrap(), 100);
184    }
185
186    #[test]
187    fn try_from_u32_to_u32() {
188        let v = FieldValue::U32(999);
189        assert_eq!(u32::try_from(v).unwrap(), 999);
190    }
191
192    #[test]
193    fn try_from_u64_to_u16() {
194        let v = FieldValue::U64(65535);
195        assert_eq!(u16::try_from(v).unwrap(), 65535);
196    }
197
198    #[test]
199    fn try_from_f32() {
200        let v = FieldValue::F32(1.5);
201        assert!((f32::try_from(v).unwrap() - 1.5).abs() < f32::EPSILON);
202    }
203
204    #[test]
205    fn try_from_bool() {
206        let v = FieldValue::Bool(true);
207        assert!(bool::try_from(v).unwrap());
208    }
209
210    #[test]
211    fn try_from_vector2() {
212        let v = FieldValue::Vector2([1.0, 2.0]);
213        assert_eq!(<[f32; 2]>::try_from(v).unwrap(), [1.0, 2.0]);
214    }
215
216    #[test]
217    fn try_from_vector3() {
218        let v = FieldValue::Vector3([1.0, 2.0, 3.0]);
219        assert_eq!(<[f32; 3]>::try_from(v).unwrap(), [1.0, 2.0, 3.0]);
220    }
221
222    #[test]
223    fn try_from_vector4() {
224        let v = FieldValue::Vector4([1.0, 2.0, 3.0, 4.0]);
225        assert_eq!(<[f32; 4]>::try_from(v).unwrap(), [1.0, 2.0, 3.0, 4.0]);
226    }
227
228    #[test]
229    fn try_from_qangle_to_f32_3() {
230        let v = FieldValue::QAngle([10.0, 20.0, 30.0]);
231        assert_eq!(<[f32; 3]>::try_from(v).unwrap(), [10.0, 20.0, 30.0]);
232    }
233
234    #[test]
235    fn try_from_i64_overflow_to_i8() {
236        let v = FieldValue::I64(200);
237        assert!(i8::try_from(v).is_err());
238    }
239
240    #[test]
241    fn try_from_u32_to_signed_fails() {
242        let v = FieldValue::U32(1);
243        assert!(i32::try_from(v).is_err());
244    }
245
246    #[test]
247    fn try_from_i32_to_unsigned_fails() {
248        let v = FieldValue::I32(1);
249        assert!(u32::try_from(v).is_err());
250    }
251
252    // ── Debug / Display formatting ──
253
254    #[test]
255    fn debug_bool() {
256        assert_eq!(format!("{:?}", FieldValue::Bool(true)), "true");
257    }
258
259    #[test]
260    fn debug_i32() {
261        assert_eq!(format!("{:?}", FieldValue::I32(-42)), "-42");
262    }
263
264    #[test]
265    fn debug_string_utf8() {
266        let v = FieldValue::String(b"hello".to_vec());
267        assert_eq!(format!("{:?}", v), "hello");
268    }
269
270    #[test]
271    fn debug_string_invalid_utf8_no_panic() {
272        let v = FieldValue::String(vec![0xFF, 0xFE]);
273        let s = format!("{:?}", v);
274        assert!(!s.is_empty());
275    }
276
277    #[test]
278    fn debug_vector3() {
279        let v = FieldValue::Vector3([1.0, 2.0, 3.0]);
280        assert_eq!(format!("{:?}", v), "[1, 2, 3]");
281    }
282
283    #[test]
284    fn debug_qangle() {
285        let v = FieldValue::QAngle([1.0, 2.0, 3.0]);
286        assert!(format!("{:?}", v).starts_with("QAngle("));
287    }
288
289    #[test]
290    fn display_delegates_to_debug() {
291        let v = FieldValue::Bool(false);
292        assert_eq!(format!("{}", v), format!("{:?}", v));
293    }
294
295    // ── Serialize ──
296
297    #[test]
298    fn serialize_bool_and_numbers() {
299        let b = serde_json::to_value(FieldValue::Bool(true)).unwrap();
300        assert_eq!(b, serde_json::json!(true));
301
302        let n = serde_json::to_value(FieldValue::I32(-7)).unwrap();
303        assert_eq!(n, serde_json::json!(-7));
304
305        let u = serde_json::to_value(FieldValue::U32(42)).unwrap();
306        assert_eq!(u, serde_json::json!(42));
307    }
308
309    #[test]
310    fn serialize_string_and_vector() {
311        let s = serde_json::to_value(FieldValue::String(b"hi".to_vec())).unwrap();
312        assert_eq!(s, serde_json::json!("hi"));
313
314        let v = serde_json::to_value(FieldValue::Vector3([1.0, 2.0, 3.0])).unwrap();
315        assert!(v.is_array());
316        assert_eq!(v.as_array().unwrap().len(), 3);
317    }
318}