1use chumsky::{IterParser, Parser as ChumskyParser};
2
3use crate::{
4 impl_block_properties_parser,
5 parser::{
6 close_block, key_value, key_value_boolean, key_value_numeric, open_block, InternalParser,
7 TokenError, TokenSource,
8 },
9 types::point::{key_value_point3d, Point3D},
10 Parser,
11};
12
13#[derive(Debug)]
15pub struct Cameras<'a> {
16 pub activecamera: i32,
17 pub cameras: Vec<Camera<'a>>,
18}
19
20impl<'a> Cameras<'a> {
21 pub fn new(activecamera: i32, cameras: Vec<Camera<'a>>) -> Self {
22 Self {
23 activecamera,
24 cameras,
25 }
26 }
27}
28
29#[derive(Debug, Default)]
31pub struct Camera<'a> {
32 pub id: u32,
33 pub classname: &'a str,
34 pub origin: Point3D,
35 pub angles: Point3D,
36 pub targetname: &'a str,
37
38 pub spawnflags: Option<u32>,
40 pub wait: Option<f32>,
41 pub acceleration: Option<f32>,
42 pub deceleration: Option<f32>,
43 pub speed: Option<f32>,
44 pub fov: Option<f32>,
45 pub fov_rate: Option<f32>,
46 pub use_screen_aspect_ratio: Option<bool>,
47 pub interp_time: Option<f32>,
48}
49
50#[derive(Debug, Clone)]
52enum CamerasProperty {
53 ActiveCamera(i32),
54}
55
56#[derive(Debug, Clone)]
58enum CameraProperty<'a> {
59 Id(u32),
60 Classname(&'a str),
61 Origin(Point3D),
62 Angles(Point3D),
63 Targetname(&'a str),
64 SpawnFlags(u32),
65 Wait(f32),
66 Acceleration(f32),
67 Deceleration(f32),
68 Speed(f32),
69 Fov(f32),
70 FovRate(f32),
71 UseScreenAspectRatio(bool),
72 InterpTime(f32),
73}
74
75impl<'src> Parser<'src> for Cameras<'src> {}
77
78impl<'src> InternalParser<'src> for Cameras<'src> {
94 fn parser<I>() -> impl ChumskyParser<'src, I, Self, TokenError<'src>>
95 where
96 I: TokenSource<'src>,
97 {
98 impl_block_properties_parser! {
99 property_list: CamerasProperty = {
100 p_activecamera = key_value_numeric("activecamera") => CamerasProperty::ActiveCamera,
101 }
102 }
103
104 open_block("cameras")
105 .ignore_then(
106 property_list
107 .repeated()
108 .collect::<Vec<CamerasProperty>>()
109 .then(
110 Camera::parser::<I>()
111 .repeated()
112 .collect::<Vec<Camera<'src>>>(),
113 ),
114 )
115 .then_ignore(close_block())
116 .map(
117 |(properties, cameras): (Vec<CamerasProperty>, Vec<Camera<'src>>)| {
118 let mut activecamera = -1;
119 for prop in properties {
120 match prop {
121 CamerasProperty::ActiveCamera(val) => activecamera = val,
122 }
123 }
124 Cameras::new(activecamera, cameras)
125 },
126 )
127 .boxed()
128 }
129}
130
131impl<'src> Parser<'src> for Camera<'src> {}
133
134impl<'src> InternalParser<'src> for Camera<'src> {
152 fn parser<I>() -> impl ChumskyParser<'src, I, Self, TokenError<'src>>
153 where
154 I: TokenSource<'src>,
155 {
156 impl_block_properties_parser! {
157 property_list: CameraProperty = {
158 p_id = key_value_numeric("id") => CameraProperty::Id,
159 p_classname = key_value("classname") => CameraProperty::Classname,
160 p_origin = key_value_point3d("origin") => CameraProperty::Origin,
161 p_angles = key_value_point3d("angles") => CameraProperty::Angles,
162 p_targetname = key_value("targetname") => CameraProperty::Targetname,
163 p_spawnflags = key_value_numeric("spawnflags") => CameraProperty::SpawnFlags,
164 p_wait = key_value_numeric("wait") => CameraProperty::Wait,
165 p_acceleration = key_value_numeric("acceleration") => CameraProperty::Acceleration,
166 p_deceleration = key_value_numeric("deceleration") => CameraProperty::Deceleration,
167 p_speed = key_value_numeric("speed") => CameraProperty::Speed,
168 p_fov = key_value_numeric("fov") => CameraProperty::Fov,
169 p_fov_rate = key_value_numeric("fov_rate") => CameraProperty::FovRate,
170 p_use_screen_aspect_ratio = key_value_boolean("use_screen_aspect_ratio") => CameraProperty::UseScreenAspectRatio,
171 p_interp_time = key_value_numeric("interp_time") => CameraProperty::InterpTime,
172 }
173 }
174
175 open_block("camera")
176 .ignore_then(property_list.repeated().collect::<Vec<CameraProperty>>())
177 .then_ignore(close_block())
178 .map(|properties: Vec<CameraProperty>| {
179 let mut camera = Camera::default();
180 for prop in properties {
181 match prop {
182 CameraProperty::Id(val) => camera.id = val,
183 CameraProperty::Classname(val) => camera.classname = val,
184 CameraProperty::Origin(val) => camera.origin = val,
185 CameraProperty::Angles(val) => camera.angles = val,
186 CameraProperty::Targetname(val) => camera.targetname = val,
187 CameraProperty::SpawnFlags(val) => camera.spawnflags = Some(val),
188 CameraProperty::Wait(val) => camera.wait = Some(val),
189 CameraProperty::Acceleration(val) => camera.acceleration = Some(val),
190 CameraProperty::Deceleration(val) => camera.deceleration = Some(val),
191 CameraProperty::Speed(val) => camera.speed = Some(val),
192 CameraProperty::Fov(val) => camera.fov = Some(val),
193 CameraProperty::FovRate(val) => camera.fov_rate = Some(val),
194 CameraProperty::UseScreenAspectRatio(val) => {
195 camera.use_screen_aspect_ratio = Some(val)
196 }
197 CameraProperty::InterpTime(val) => camera.interp_time = Some(val),
198 }
199 }
200 camera
201 })
202 .boxed()
203 }
204}
205
206#[cfg(test)]
207mod tests {
208 use super::*;
209 use crate::util::lex;
210
211 #[test]
212 fn test_cameras_empty() {
213 let input = r#"
214 cameras
215 {
216 "activecamera" "-1"
217 }
218 "#;
219
220 let stream = lex(input);
221 let result = Cameras::parse(stream);
222 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
223
224 let cameras = result.unwrap();
225 assert_eq!(cameras.activecamera, -1);
226 assert_eq!(cameras.cameras.len(), 0);
227 }
228
229 #[test]
230 fn test_cameras_with_active_camera() {
231 let input = r#"
232 cameras
233 {
234 "activecamera" "0"
235 }
236 "#;
237
238 let stream = lex(input);
239 let result = Cameras::parse(stream);
240 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
241
242 let cameras = result.unwrap();
243 assert_eq!(cameras.activecamera, 0);
244 assert_eq!(cameras.cameras.len(), 0);
245 }
246
247 #[test]
248 fn test_cameras_missing_activecamera() {
249 let input = r#"
250 cameras
251 {
252 }
253 "#;
254
255 let stream = lex(input);
256 let result = Cameras::parse(stream);
257 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
258
259 let cameras = result.unwrap();
260 assert_eq!(cameras.activecamera, -1); assert_eq!(cameras.cameras.len(), 0);
262 }
263
264 #[test]
265 fn test_cameras_invalid_block_name() {
266 let input = r#"
267 wrongname
268 {
269 "activecamera" "-1"
270 }
271 "#;
272
273 let stream = lex(input);
274 let result = Cameras::parse(stream);
275 assert!(result.is_err(), "Parser should fail on invalid block name");
276 }
277
278 #[test]
279 fn test_cameras_missing_closing_brace() {
280 let input = r#"
281 cameras
282 {
283 "activecamera" "-1"
284 "#;
285
286 let stream = lex(input);
287 let result = Cameras::parse(stream);
288 assert!(
289 result.is_err(),
290 "Parser should fail on missing closing brace"
291 );
292 }
293
294 #[test]
295 fn test_cameras_invalid_activecamera_type() {
296 let input = r#"
297 cameras
298 {
299 "activecamera" "not_a_number"
300 }
301 "#;
302
303 let stream = lex(input);
304 let result = Cameras::parse(stream);
305 assert!(
306 result.is_err(),
307 "Parser should fail on invalid activecamera type"
308 );
309 }
310
311 #[test]
312 fn test_camera_complete_valid() {
313 let input = r#"
314 camera
315 {
316 "id" "42"
317 "classname" "point_viewcontrol"
318 "origin" "100 200 64"
319 "angles" "0 90 0"
320 "targetname" "camera_main"
321 "spawnflags" "8"
322 "fov" "75"
323 "speed" "100"
324 "acceleration" "500"
325 "deceleration" "500"
326 }
327 "#;
328
329 let stream = lex(input);
330 let result = Camera::parse(stream);
331 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
332
333 let camera = result.unwrap();
334 assert_eq!(camera.id, 42);
335 assert_eq!(camera.classname, "point_viewcontrol");
336 assert_eq!(camera.origin.x, 100.0);
337 assert_eq!(camera.origin.y, 200.0);
338 assert_eq!(camera.origin.z, 64.0);
339 assert_eq!(camera.angles.x, 0.0);
340 assert_eq!(camera.angles.y, 90.0);
341 assert_eq!(camera.angles.z, 0.0);
342 assert_eq!(camera.targetname, "camera_main");
343 assert_eq!(camera.spawnflags, Some(8));
344 assert_eq!(camera.fov, Some(75.0));
345 assert_eq!(camera.speed, Some(100.0));
346 assert_eq!(camera.acceleration, Some(500.0));
347 assert_eq!(camera.deceleration, Some(500.0));
348 }
349
350 #[test]
351 fn test_camera_properties_out_of_order() {
352 let input = r#"
353 camera
354 {
355 "targetname" "test_camera"
356 "angles" "45 180 0"
357 "id" "10"
358 "fov" "60"
359 "origin" "0 0 128"
360 "classname" "point_viewcontrol"
361 }
362 "#;
363
364 let stream = lex(input);
365 let result = Camera::parse(stream);
366 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
367
368 let camera = result.unwrap();
369 assert_eq!(camera.id, 10);
370 assert_eq!(camera.classname, "point_viewcontrol");
371 assert_eq!(camera.targetname, "test_camera");
372 assert_eq!(camera.fov, Some(60.0));
373 }
374
375 #[test]
376 fn test_camera_minimal_properties() {
377 let input = r#"
378 camera
379 {
380 "id" "1"
381 "classname" "point_viewcontrol"
382 "origin" "0 0 0"
383 "angles" "0 0 0"
384 "targetname" "cam"
385 }
386 "#;
387
388 let stream = lex(input);
389 let result = Camera::parse(stream);
390 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
391
392 let camera = result.unwrap();
393 assert_eq!(camera.id, 1);
394 assert_eq!(camera.classname, "point_viewcontrol");
395 assert_eq!(camera.targetname, "cam");
396 assert_eq!(camera.spawnflags, None);
397 assert_eq!(camera.fov, None);
398 }
399
400 #[test]
401 fn test_camera_empty_block() {
402 let input = r#"
403 camera
404 {
405 }
406 "#;
407
408 let stream = lex(input);
409 let result = Camera::parse(stream);
410 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
411
412 let camera = result.unwrap();
413 let default = Camera::default();
414 assert_eq!(camera.id, default.id);
415 }
416
417 #[test]
418 fn test_camera_invalid_origin() {
419 let input = r#"
420 camera
421 {
422 "id" "1"
423 "origin" "not valid"
424 }
425 "#;
426
427 let stream = lex(input);
428 let result = Camera::parse(stream);
429 assert!(result.is_err(), "Parser should fail on invalid origin");
430 }
431
432 #[test]
433 fn test_camera_invalid_angles() {
434 let input = r#"
435 camera
436 {
437 "id" "1"
438 "angles" "0 90"
439 }
440 "#;
441
442 let stream = lex(input);
443 let result = Camera::parse(stream);
444 assert!(
445 result.is_err(),
446 "Parser should fail on invalid angles (too few values)"
447 );
448 }
449
450 #[test]
451 fn test_cameras_with_camera_blocks() {
452 let input = r#"
453 cameras
454 {
455 "activecamera" "0"
456 camera
457 {
458 "id" "1"
459 "classname" "point_viewcontrol"
460 "origin" "0 0 64"
461 "angles" "0 0 0"
462 "targetname" "camera1"
463 }
464 camera
465 {
466 "id" "2"
467 "classname" "point_viewcontrol"
468 "origin" "100 100 64"
469 "angles" "0 90 0"
470 "targetname" "camera2"
471 }
472 }
473 "#;
474
475 let stream = lex(input);
476 let result = Cameras::parse(stream);
477 assert!(result.is_ok(), "Parsing failed: {:?}", result.err());
478
479 let cameras = result.unwrap();
480 assert_eq!(cameras.activecamera, 0);
481 assert_eq!(cameras.cameras.len(), 2);
482 assert_eq!(cameras.cameras[0].id, 1);
483 assert_eq!(cameras.cameras[0].targetname, "camera1");
484 assert_eq!(cameras.cameras[1].id, 2);
485 assert_eq!(cameras.cameras[1].targetname, "camera2");
486 }
487}