1pub type UID = u64;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq)]
21#[repr(C)]
22pub struct SemanticVersion {
23 pub major: u8,
24 pub minor: u8,
25 pub patch: u8,
26 pub prerel: u16,
27}
28
29impl SemanticVersion {
30 #[must_use]
31 pub const fn new(major: u8, minor: u8, patch: u8) -> Self {
32 Self {
33 major,
34 minor,
35 patch,
36 prerel: 0,
37 }
38 }
39
40 #[must_use]
41 pub const fn with_prerel(major: u8, minor: u8, patch: u8, prerel: u16) -> Self {
42 Self {
43 major,
44 minor,
45 patch,
46 prerel,
47 }
48 }
49}
50
51#[derive(Clone, Copy)]
59#[repr(C)]
60pub struct StringView {
61 pub data: *const u8,
62 pub len: usize,
63}
64
65impl StringView {
66 #[must_use]
68 pub fn from_static(s: &'static str) -> Self {
69 Self {
70 data: s.as_ptr(),
71 len: s.len(),
72 }
73 }
74
75 #[must_use]
81 pub unsafe fn as_str<'a>(self) -> &'a str {
82 let slice = unsafe { std::slice::from_raw_parts(self.data, self.len) };
83 unsafe { std::str::from_utf8_unchecked(slice) }
85 }
86
87 pub unsafe fn try_as_str<'a>(self) -> Result<&'a str, std::str::Utf8Error> {
100 let slice = unsafe { std::slice::from_raw_parts(self.data, self.len) };
101 std::str::from_utf8(slice)
102 }
103}
104
105#[derive(Debug, Clone, Copy, PartialEq, Eq)]
109#[repr(C)]
110pub struct Colour {
111 pub r: u8,
112 pub g: u8,
113 pub b: u8,
114 pub a: u8,
115}
116
117impl Colour {
118 #[must_use]
119 pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> Self {
120 Self { r, g, b, a }
121 }
122
123 #[must_use]
124 pub const fn rgb(r: u8, g: u8, b: u8) -> Self {
125 Self { r, g, b, a: 0xFF }
126 }
127
128 #[must_use]
129 pub fn from_rgba_u32(v: u32) -> Self {
130 Self {
131 r: ((v & 0xFF00_0000) >> 24) as u8,
132 g: ((v & 0x00FF_0000) >> 16) as u8,
133 b: ((v & 0x0000_FF00) >> 8) as u8,
134 a: (v & 0x0000_00FF) as u8,
135 }
136 }
137
138 #[must_use]
139 pub fn to_rgba_u32(self) -> u32 {
140 (u32::from(self.r) << 24)
141 | (u32::from(self.g) << 16)
142 | (u32::from(self.b) << 8)
143 | u32::from(self.a)
144 }
145
146 pub const WHITE: Self = Self::rgba(0xFF, 0xFF, 0xFF, 0xFF);
147 pub const BLACK: Self = Self::rgba(0x00, 0x00, 0x00, 0xFF);
148 pub const NONE: Self = Self::rgba(0x00, 0x00, 0x00, 0x00);
149}
150
151#[derive(Debug, Clone, Copy, PartialEq)]
153#[repr(C)]
154pub struct Vector2 {
155 pub x: f32,
156 pub y: f32,
157}
158
159#[derive(Debug, Clone, Copy, PartialEq)]
161#[repr(C)]
162pub struct Vector3 {
163 pub x: f32,
164 pub y: f32,
165 pub z: f32,
166}
167
168#[derive(Debug, Clone, Copy, PartialEq)]
170#[repr(C)]
171pub struct Vector4 {
172 pub x: f32,
173 pub y: f32,
174 pub z: f32,
175 pub w: f32,
176}
177
178#[derive(Debug, Clone, Copy, PartialEq, Eq)]
182#[repr(C)]
183pub enum ComponentType {
184 Other = 0,
185 Network = 1,
186 Pool = 2,
187}
188
189#[cfg(test)]
190mod tests {
191 use super::*;
192
193 #[test]
196 fn semantic_version_new_fields() {
197 let v = SemanticVersion::new(1, 2, 3);
198 assert_eq!(v.major, 1);
199 assert_eq!(v.minor, 2);
200 assert_eq!(v.patch, 3);
201 assert_eq!(v.prerel, 0);
202 }
203
204 #[test]
205 fn semantic_version_with_prerel() {
206 let v = SemanticVersion::with_prerel(1, 0, 0, 5);
207 assert_eq!(v.prerel, 5);
208 }
209
210 #[test]
211 fn semantic_version_equality() {
212 assert_eq!(SemanticVersion::new(1, 2, 3), SemanticVersion::new(1, 2, 3));
213 assert_ne!(SemanticVersion::new(1, 2, 3), SemanticVersion::new(1, 2, 4));
214 }
215
216 #[test]
217 fn semantic_version_clone() {
218 let v = SemanticVersion::new(2, 0, 0);
219 assert_eq!(v, v);
220 }
221
222 #[test]
225 fn stringview_from_static_len() {
226 let sv = StringView::from_static("hello");
227 assert_eq!(sv.len, 5);
228 assert!(!sv.data.is_null());
229 }
230
231 #[test]
232 fn stringview_from_static_empty() {
233 let sv = StringView::from_static("");
234 assert_eq!(sv.len, 0);
235 }
236
237 #[test]
238 fn stringview_as_str_roundtrip() {
239 let sv = StringView::from_static("rust-samp");
240 let s = unsafe { sv.as_str() };
241 assert_eq!(s, "rust-samp");
242 }
243
244 #[test]
245 fn stringview_try_as_str_valid_utf8() {
246 let sv = StringView::from_static("naïve");
247 let result = unsafe { sv.try_as_str() };
248 assert!(result.is_ok());
249 assert_eq!(result.unwrap(), "naïve");
250 }
251
252 #[test]
253 fn stringview_try_as_str_invalid_utf8_returns_err() {
254 let bad = [0xFF_u8, 0xFE];
255 let sv = StringView {
256 data: bad.as_ptr(),
257 len: bad.len(),
258 };
259 let result = unsafe { sv.try_as_str() };
260 assert!(result.is_err());
261 }
262
263 #[test]
266 fn colour_rgba_fields() {
267 let c = Colour::rgba(1, 2, 3, 4);
268 assert_eq!((c.r, c.g, c.b, c.a), (1, 2, 3, 4));
269 }
270
271 #[test]
272 fn colour_rgb_has_full_alpha() {
273 let c = Colour::rgb(10, 20, 30);
274 assert_eq!(c.a, 0xFF);
275 }
276
277 #[test]
278 fn colour_from_to_rgba_u32_roundtrip() {
279 let original = 0xDEAD_BEEF_u32;
280 let c = Colour::from_rgba_u32(original);
281 assert_eq!(c.to_rgba_u32(), original);
282 }
283
284 #[test]
285 fn colour_white_constant() {
286 assert_eq!(Colour::WHITE, Colour::rgba(0xFF, 0xFF, 0xFF, 0xFF));
287 }
288
289 #[test]
290 fn colour_black_constant() {
291 assert_eq!(Colour::BLACK, Colour::rgba(0x00, 0x00, 0x00, 0xFF));
292 }
293
294 #[test]
295 fn colour_none_is_transparent() {
296 assert_eq!(Colour::NONE.a, 0x00);
297 }
298
299 #[test]
302 fn vector2_fields() {
303 let v = Vector2 { x: 1.0, y: 2.0 };
304 assert_eq!((v.x, v.y), (1.0, 2.0));
305 }
306
307 #[test]
308 fn vector3_fields() {
309 let v = Vector3 {
310 x: 1.0,
311 y: 2.0,
312 z: 3.0,
313 };
314 assert_eq!(v.z.to_bits(), 3.0_f32.to_bits());
315 }
316
317 #[test]
318 fn vector4_fields() {
319 let v = Vector4 {
320 x: 1.0,
321 y: 2.0,
322 z: 3.0,
323 w: 4.0,
324 };
325 assert_eq!(v.w.to_bits(), 4.0_f32.to_bits());
326 }
327
328 #[test]
329 fn vectors_clone_and_eq() {
330 let v = Vector3 {
331 x: 1.0,
332 y: 2.0,
333 z: 3.0,
334 };
335 assert_eq!(v, v);
336 }
337
338 #[test]
341 fn component_type_discriminants() {
342 assert_eq!(ComponentType::Other as i32, 0);
343 assert_eq!(ComponentType::Network as i32, 1);
344 assert_eq!(ComponentType::Pool as i32, 2);
345 }
346}