1use crate::stats::analysis_graph::{AnalysisDependency, AnalysisGraph};
2use crate::*;
3pub use ::derive_new;
5pub use ::paste;
7use boxcars;
8use std::any::type_name;
9use std::marker::PhantomData;
10use std::sync::Arc;
11
12pub struct AnalysisFeatureContext<'a> {
14 graph: &'a AnalysisGraph,
15}
16
17impl<'a> AnalysisFeatureContext<'a> {
18 pub(crate) fn new(graph: &'a AnalysisGraph) -> Self {
19 Self { graph }
20 }
21
22 pub fn maybe_state<T: 'static>(&self) -> Option<&'a T> {
23 self.graph.state::<T>()
24 }
25
26 pub fn state<T: 'static>(&self) -> SubtrActorResult<&'a T> {
27 self.maybe_state::<T>().ok_or_else(|| {
28 SubtrActorError::new(SubtrActorErrorVariant::CallbackError(format!(
29 "missing analysis state {}",
30 type_name::<T>(),
31 )))
32 })
33 }
34}
35
36pub trait FeatureAdder<F> {
38 fn features_added(&self) -> usize {
39 self.get_column_headers().len()
40 }
41
42 fn get_column_headers(&self) -> &[&str];
43
44 fn add_features(
45 &self,
46 processor: &dyn ProcessorView,
47 frame: &boxcars::Frame,
48 frame_count: usize,
49 current_time: f32,
50 vector: &mut Vec<F>,
51 ) -> SubtrActorResult<()>;
52}
53
54pub trait AnalysisFeatureAdder<F> {
56 fn features_added(&self) -> usize {
57 self.get_column_headers().len()
58 }
59
60 fn get_column_headers(&self) -> &[&str];
61
62 fn analysis_dependencies(&self) -> Vec<AnalysisDependency>;
63
64 fn add_features(
65 &self,
66 context: &AnalysisFeatureContext<'_>,
67 processor: &dyn ProcessorView,
68 frame: &boxcars::Frame,
69 frame_count: usize,
70 current_time: f32,
71 vector: &mut Vec<F>,
72 ) -> SubtrActorResult<()>;
73}
74
75pub trait LengthCheckedAnalysisFeatureAdder<F, const N: usize> {
77 fn get_column_headers_array(&self) -> &[&str; N];
78
79 fn analysis_dependencies(&self) -> Vec<AnalysisDependency>;
80
81 fn get_features(
82 &self,
83 context: &AnalysisFeatureContext<'_>,
84 processor: &dyn ProcessorView,
85 frame: &boxcars::Frame,
86 frame_count: usize,
87 current_time: f32,
88 ) -> SubtrActorResult<[F; N]>;
89}
90
91#[macro_export]
93macro_rules! impl_analysis_feature_adder {
94 ($struct_name:ident) => {
95 impl<F: TryFrom<f32>> AnalysisFeatureAdder<F> for $struct_name<F>
96 where
97 <F as TryFrom<f32>>::Error: std::fmt::Debug,
98 {
99 fn add_features(
100 &self,
101 context: &AnalysisFeatureContext<'_>,
102 processor: &dyn ProcessorView,
103 frame: &boxcars::Frame,
104 frame_count: usize,
105 current_time: f32,
106 vector: &mut Vec<F>,
107 ) -> SubtrActorResult<()> {
108 Ok(vector.extend(self.get_features(
109 context,
110 processor,
111 frame,
112 frame_count,
113 current_time,
114 )?))
115 }
116
117 fn get_column_headers(&self) -> &[&str] {
118 self.get_column_headers_array()
119 }
120
121 fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
122 LengthCheckedAnalysisFeatureAdder::analysis_dependencies(self)
123 }
124 }
125 };
126}
127
128pub trait LengthCheckedFeatureAdder<F, const N: usize> {
130 fn get_column_headers_array(&self) -> &[&str; N];
131
132 fn get_features(
133 &self,
134 processor: &dyn ProcessorView,
135 frame: &boxcars::Frame,
136 frame_count: usize,
137 current_time: f32,
138 ) -> SubtrActorResult<[F; N]>;
139}
140
141#[macro_export]
143macro_rules! impl_feature_adder {
144 ($struct_name:ident) => {
145 impl<F: TryFrom<f32>> FeatureAdder<F> for $struct_name<F>
146 where
147 <F as TryFrom<f32>>::Error: std::fmt::Debug,
148 {
149 fn add_features(
150 &self,
151 processor: &dyn ProcessorView,
152 frame: &boxcars::Frame,
153 frame_count: usize,
154 current_time: f32,
155 vector: &mut Vec<F>,
156 ) -> SubtrActorResult<()> {
157 Ok(
158 vector.extend(self.get_features(
159 processor,
160 frame,
161 frame_count,
162 current_time,
163 )?),
164 )
165 }
166
167 fn get_column_headers(&self) -> &[&str] {
168 self.get_column_headers_array()
169 }
170 }
171 };
172}
173
174pub trait PlayerFeatureAdder<F> {
176 fn features_added(&self) -> usize {
177 self.get_column_headers().len()
178 }
179
180 fn get_column_headers(&self) -> &[&str];
181
182 fn add_features(
183 &self,
184 player_id: &PlayerId,
185 processor: &dyn ProcessorView,
186 frame: &boxcars::Frame,
187 frame_count: usize,
188 current_time: f32,
189 vector: &mut Vec<F>,
190 ) -> SubtrActorResult<()>;
191}
192
193#[derive(Clone, Copy)]
195pub struct AnalysisPlayerFeatureInput<'a, 'ctx> {
196 pub context: &'a AnalysisFeatureContext<'ctx>,
197 pub player_id: &'a PlayerId,
198 pub processor: &'a dyn ProcessorView,
199 pub frame: &'a boxcars::Frame,
200 pub frame_count: usize,
201 pub current_time: f32,
202}
203
204pub trait AnalysisPlayerFeatureAdder<F> {
206 fn features_added(&self) -> usize {
207 self.get_column_headers().len()
208 }
209
210 fn get_column_headers(&self) -> &[&str];
211
212 fn analysis_dependencies(&self) -> Vec<AnalysisDependency>;
213
214 fn add_features(
215 &self,
216 input: AnalysisPlayerFeatureInput<'_, '_>,
217 vector: &mut Vec<F>,
218 ) -> SubtrActorResult<()>;
219}
220
221pub trait LengthCheckedAnalysisPlayerFeatureAdder<F, const N: usize> {
223 fn get_column_headers_array(&self) -> &[&str; N];
224
225 fn analysis_dependencies(&self) -> Vec<AnalysisDependency>;
226
227 fn get_features(
228 &self,
229 context: &AnalysisFeatureContext<'_>,
230 player_id: &PlayerId,
231 processor: &dyn ProcessorView,
232 frame: &boxcars::Frame,
233 frame_count: usize,
234 current_time: f32,
235 ) -> SubtrActorResult<[F; N]>;
236}
237
238#[macro_export]
240macro_rules! impl_analysis_player_feature_adder {
241 ($struct_name:ident) => {
242 impl<F: TryFrom<f32>> AnalysisPlayerFeatureAdder<F> for $struct_name<F>
243 where
244 <F as TryFrom<f32>>::Error: std::fmt::Debug,
245 {
246 fn add_features(
247 &self,
248 input: AnalysisPlayerFeatureInput<'_, '_>,
249 vector: &mut Vec<F>,
250 ) -> SubtrActorResult<()> {
251 Ok(vector.extend(self.get_features(
252 input.context,
253 input.player_id,
254 input.processor,
255 input.frame,
256 input.frame_count,
257 input.current_time,
258 )?))
259 }
260
261 fn get_column_headers(&self) -> &[&str] {
262 self.get_column_headers_array()
263 }
264
265 fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
266 LengthCheckedAnalysisPlayerFeatureAdder::analysis_dependencies(self)
267 }
268 }
269 };
270}
271
272pub struct DynamicAnalysisFeatureAdder<F, G, const N: usize> {
273 get_features: G,
274 column_headers: &'static [&'static str; N],
275 dependencies: Vec<AnalysisDependency>,
276 _marker: PhantomData<F>,
277}
278
279impl<F, G, const N: usize> DynamicAnalysisFeatureAdder<F, G, N> {
280 pub fn new(
281 column_headers: &'static [&'static str; N],
282 dependencies: Vec<AnalysisDependency>,
283 get_features: G,
284 ) -> Self {
285 Self {
286 get_features,
287 column_headers,
288 dependencies,
289 _marker: PhantomData,
290 }
291 }
292}
293
294impl<F, G, const N: usize> AnalysisFeatureAdder<F> for DynamicAnalysisFeatureAdder<F, G, N>
295where
296 F: Send + Sync + 'static,
297 G: Fn(
298 &AnalysisFeatureContext<'_>,
299 &dyn ProcessorView,
300 &boxcars::Frame,
301 usize,
302 f32,
303 ) -> SubtrActorResult<[F; N]>
304 + Send
305 + Sync
306 + 'static,
307{
308 fn get_column_headers(&self) -> &[&str] {
309 self.column_headers.as_slice()
310 }
311
312 fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
313 self.dependencies.clone()
314 }
315
316 fn add_features(
317 &self,
318 context: &AnalysisFeatureContext<'_>,
319 processor: &dyn ProcessorView,
320 frame: &boxcars::Frame,
321 frame_count: usize,
322 current_time: f32,
323 vector: &mut Vec<F>,
324 ) -> SubtrActorResult<()> {
325 vector.extend((self.get_features)(
326 context,
327 processor,
328 frame,
329 frame_count,
330 current_time,
331 )?);
332 Ok(())
333 }
334}
335
336pub struct DynamicAnalysisPlayerFeatureAdder<F, G, const N: usize> {
337 get_features: G,
338 column_headers: &'static [&'static str; N],
339 dependencies: Vec<AnalysisDependency>,
340 _marker: PhantomData<F>,
341}
342
343impl<F, G, const N: usize> DynamicAnalysisPlayerFeatureAdder<F, G, N> {
344 pub fn new(
345 column_headers: &'static [&'static str; N],
346 dependencies: Vec<AnalysisDependency>,
347 get_features: G,
348 ) -> Self {
349 Self {
350 get_features,
351 column_headers,
352 dependencies,
353 _marker: PhantomData,
354 }
355 }
356}
357
358impl<F, G, const N: usize> AnalysisPlayerFeatureAdder<F>
359 for DynamicAnalysisPlayerFeatureAdder<F, G, N>
360where
361 F: Send + Sync + 'static,
362 G: Fn(
363 &AnalysisFeatureContext<'_>,
364 &PlayerId,
365 &dyn ProcessorView,
366 &boxcars::Frame,
367 usize,
368 f32,
369 ) -> SubtrActorResult<[F; N]>
370 + Send
371 + Sync
372 + 'static,
373{
374 fn get_column_headers(&self) -> &[&str] {
375 self.column_headers.as_slice()
376 }
377
378 fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
379 self.dependencies.clone()
380 }
381
382 fn add_features(
383 &self,
384 input: AnalysisPlayerFeatureInput<'_, '_>,
385 vector: &mut Vec<F>,
386 ) -> SubtrActorResult<()> {
387 vector.extend((self.get_features)(
388 input.context,
389 input.player_id,
390 input.processor,
391 input.frame,
392 input.frame_count,
393 input.current_time,
394 )?);
395 Ok(())
396 }
397}
398
399pub fn dynamic_analysis_feature_adder<F, G, const N: usize>(
400 column_headers: &'static [&'static str; N],
401 dependencies: Vec<AnalysisDependency>,
402 get_features: G,
403) -> Arc<dyn AnalysisFeatureAdder<F> + Send + Sync + 'static>
404where
405 F: Send + Sync + 'static,
406 G: Fn(
407 &AnalysisFeatureContext<'_>,
408 &dyn ProcessorView,
409 &boxcars::Frame,
410 usize,
411 f32,
412 ) -> SubtrActorResult<[F; N]>
413 + Send
414 + Sync
415 + 'static,
416{
417 Arc::new(DynamicAnalysisFeatureAdder::new(
418 column_headers,
419 dependencies,
420 get_features,
421 ))
422}
423
424pub fn dynamic_analysis_player_feature_adder<F, G, const N: usize>(
425 column_headers: &'static [&'static str; N],
426 dependencies: Vec<AnalysisDependency>,
427 get_features: G,
428) -> Arc<dyn AnalysisPlayerFeatureAdder<F> + Send + Sync + 'static>
429where
430 F: Send + Sync + 'static,
431 G: Fn(
432 &AnalysisFeatureContext<'_>,
433 &PlayerId,
434 &dyn ProcessorView,
435 &boxcars::Frame,
436 usize,
437 f32,
438 ) -> SubtrActorResult<[F; N]>
439 + Send
440 + Sync
441 + 'static,
442{
443 Arc::new(DynamicAnalysisPlayerFeatureAdder::new(
444 column_headers,
445 dependencies,
446 get_features,
447 ))
448}
449
450#[derive(Clone)]
451pub enum NDArrayFeatureAdder<F> {
452 Plain(Arc<dyn FeatureAdder<F> + Send + Sync>),
453 Analysis(Arc<dyn AnalysisFeatureAdder<F> + Send + Sync>),
454}
455
456impl<F> NDArrayFeatureAdder<F> {
457 pub fn plain(adder: Arc<dyn FeatureAdder<F> + Send + Sync>) -> Self {
458 Self::Plain(adder)
459 }
460
461 pub fn analysis(adder: Arc<dyn AnalysisFeatureAdder<F> + Send + Sync>) -> Self {
462 Self::Analysis(adder)
463 }
464
465 pub fn features_added(&self) -> usize {
466 match self {
467 Self::Plain(adder) => adder.features_added(),
468 Self::Analysis(adder) => adder.features_added(),
469 }
470 }
471
472 pub fn get_column_headers(&self) -> &[&str] {
473 match self {
474 Self::Plain(adder) => adder.get_column_headers(),
475 Self::Analysis(adder) => adder.get_column_headers(),
476 }
477 }
478
479 pub fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
480 match self {
481 Self::Plain(_) => Vec::new(),
482 Self::Analysis(adder) => adder.analysis_dependencies(),
483 }
484 }
485
486 pub fn is_analysis_backed(&self) -> bool {
487 matches!(self, Self::Analysis(_))
488 }
489}
490
491impl<F> From<Arc<dyn FeatureAdder<F> + Send + Sync>> for NDArrayFeatureAdder<F> {
492 fn from(adder: Arc<dyn FeatureAdder<F> + Send + Sync>) -> Self {
493 Self::plain(adder)
494 }
495}
496
497impl<F> From<Arc<dyn AnalysisFeatureAdder<F> + Send + Sync>> for NDArrayFeatureAdder<F> {
498 fn from(adder: Arc<dyn AnalysisFeatureAdder<F> + Send + Sync>) -> Self {
499 Self::analysis(adder)
500 }
501}
502
503pub type NDArrayFeatureAdders<F> = Vec<NDArrayFeatureAdder<F>>;
504
505#[derive(Clone)]
506pub enum NDArrayPlayerFeatureAdder<F> {
507 Plain(Arc<dyn PlayerFeatureAdder<F> + Send + Sync>),
508 Analysis(Arc<dyn AnalysisPlayerFeatureAdder<F> + Send + Sync>),
509}
510
511impl<F> NDArrayPlayerFeatureAdder<F> {
512 pub fn plain(adder: Arc<dyn PlayerFeatureAdder<F> + Send + Sync>) -> Self {
513 Self::Plain(adder)
514 }
515
516 pub fn analysis(adder: Arc<dyn AnalysisPlayerFeatureAdder<F> + Send + Sync>) -> Self {
517 Self::Analysis(adder)
518 }
519
520 pub fn features_added(&self) -> usize {
521 match self {
522 Self::Plain(adder) => adder.features_added(),
523 Self::Analysis(adder) => adder.features_added(),
524 }
525 }
526
527 pub fn get_column_headers(&self) -> &[&str] {
528 match self {
529 Self::Plain(adder) => adder.get_column_headers(),
530 Self::Analysis(adder) => adder.get_column_headers(),
531 }
532 }
533
534 pub fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
535 match self {
536 Self::Plain(_) => Vec::new(),
537 Self::Analysis(adder) => adder.analysis_dependencies(),
538 }
539 }
540
541 pub fn is_analysis_backed(&self) -> bool {
542 matches!(self, Self::Analysis(_))
543 }
544}
545
546impl<F> From<Arc<dyn PlayerFeatureAdder<F> + Send + Sync>> for NDArrayPlayerFeatureAdder<F> {
547 fn from(adder: Arc<dyn PlayerFeatureAdder<F> + Send + Sync>) -> Self {
548 Self::plain(adder)
549 }
550}
551
552impl<F> From<Arc<dyn AnalysisPlayerFeatureAdder<F> + Send + Sync>>
553 for NDArrayPlayerFeatureAdder<F>
554{
555 fn from(adder: Arc<dyn AnalysisPlayerFeatureAdder<F> + Send + Sync>) -> Self {
556 Self::analysis(adder)
557 }
558}
559
560pub type NDArrayPlayerFeatureAdders<F> = Vec<NDArrayPlayerFeatureAdder<F>>;
561
562pub trait LengthCheckedPlayerFeatureAdder<F, const N: usize> {
564 fn get_column_headers_array(&self) -> &[&str; N];
565
566 fn get_features(
567 &self,
568 player_id: &PlayerId,
569 processor: &dyn ProcessorView,
570 frame: &boxcars::Frame,
571 frame_count: usize,
572 current_time: f32,
573 ) -> SubtrActorResult<[F; N]>;
574}
575
576#[macro_export]
578macro_rules! impl_player_feature_adder {
579 ($struct_name:ident) => {
580 impl<F: TryFrom<f32>> PlayerFeatureAdder<F> for $struct_name<F>
581 where
582 <F as TryFrom<f32>>::Error: std::fmt::Debug,
583 {
584 fn add_features(
585 &self,
586 player_id: &PlayerId,
587 processor: &dyn ProcessorView,
588 frame: &boxcars::Frame,
589 frame_count: usize,
590 current_time: f32,
591 vector: &mut Vec<F>,
592 ) -> SubtrActorResult<()> {
593 Ok(vector.extend(self.get_features(
594 player_id,
595 processor,
596 frame,
597 frame_count,
598 current_time,
599 )?))
600 }
601
602 fn get_column_headers(&self) -> &[&str] {
603 self.get_column_headers_array()
604 }
605 }
606 };
607}
608
609impl<G, F, const N: usize> FeatureAdder<F> for (G, &[&str; N])
610where
611 G: Fn(&dyn ProcessorView, &boxcars::Frame, usize, f32) -> SubtrActorResult<[F; N]>,
612{
613 fn add_features(
614 &self,
615 processor: &dyn ProcessorView,
616 frame: &boxcars::Frame,
617 frame_count: usize,
618 current_time: f32,
619 vector: &mut Vec<F>,
620 ) -> SubtrActorResult<()> {
621 vector.extend(self.0(processor, frame, frame_count, current_time)?);
622 Ok(())
623 }
624
625 fn get_column_headers(&self) -> &[&str] {
626 self.1.as_slice()
627 }
628}
629
630impl<G, F, const N: usize> PlayerFeatureAdder<F> for (G, &[&str; N])
631where
632 G: Fn(&PlayerId, &dyn ProcessorView, &boxcars::Frame, usize, f32) -> SubtrActorResult<[F; N]>,
633{
634 fn add_features(
635 &self,
636 player_id: &PlayerId,
637 processor: &dyn ProcessorView,
638 frame: &boxcars::Frame,
639 frame_count: usize,
640 current_time: f32,
641 vector: &mut Vec<F>,
642 ) -> SubtrActorResult<()> {
643 vector.extend(self.0(
644 player_id,
645 processor,
646 frame,
647 frame_count,
648 current_time,
649 )?);
650 Ok(())
651 }
652
653 fn get_column_headers(&self) -> &[&str] {
654 self.1.as_slice()
655 }
656}
657
658#[macro_export]
660macro_rules! build_global_feature_adder {
661 ($struct_name:ident, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
662
663 #[derive(derive_new::new)]
664 pub struct $struct_name<F> {
665 _zero: std::marker::PhantomData<F>,
666 }
667
668 impl<F: Sync + Send + TryFrom<f32> + 'static> $struct_name<F> where
669 <F as TryFrom<f32>>::Error: std::fmt::Debug,
670 {
671 pub fn arc_new() -> std::sync::Arc<dyn FeatureAdder<F> + Send + Sync + 'static> {
672 std::sync::Arc::new(Self::new())
673 }
674 }
675
676 global_feature_adder!(
677 $struct_name,
678 $prop_getter,
679 $( $column_names ),*
680 );
681 }
682}
683
684#[macro_export]
686macro_rules! global_feature_adder {
687 ($struct_name:ident, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
688 macro_rules! _global_feature_adder {
689 ($count:ident) => {
690 impl<F: TryFrom<f32>> LengthCheckedFeatureAdder<F, $count> for $struct_name<F>
691 where
692 <F as TryFrom<f32>>::Error: std::fmt::Debug,
693 {
694 fn get_column_headers_array(&self) -> &[&str; $count] {
695 &[$( $column_names ),*]
696 }
697
698 fn get_features(
699 &self,
700 processor: &dyn ProcessorView,
701 frame: &boxcars::Frame,
702 frame_count: usize,
703 current_time: f32,
704 ) -> SubtrActorResult<[F; $count]> {
705 $prop_getter(self, processor, frame, frame_count, current_time)
706 }
707 }
708
709 impl_feature_adder!($struct_name);
710 };
711 }
712 paste::paste! {
713 const [<$struct_name:snake:upper _LENGTH>]: usize = [$($column_names),*].len();
714 _global_feature_adder!([<$struct_name:snake:upper _LENGTH>]);
715 }
716 }
717}
718
719#[macro_export]
721macro_rules! build_analysis_global_feature_adder {
722 ($struct_name:ident, $dependency_getter:expr, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
723
724 #[derive(derive_new::new)]
725 pub struct $struct_name<F> {
726 _zero: std::marker::PhantomData<F>,
727 }
728
729 impl<F: Sync + Send + TryFrom<f32> + 'static> $struct_name<F> where
730 <F as TryFrom<f32>>::Error: std::fmt::Debug,
731 {
732 pub fn arc_new() -> std::sync::Arc<dyn AnalysisFeatureAdder<F> + Send + Sync + 'static> {
733 std::sync::Arc::new(Self::new())
734 }
735 }
736
737 analysis_global_feature_adder!(
738 $struct_name,
739 $dependency_getter,
740 $prop_getter,
741 $( $column_names ),*
742 );
743 }
744}
745
746#[macro_export]
748macro_rules! analysis_global_feature_adder {
749 ($struct_name:ident, $dependency_getter:expr, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
750 macro_rules! _analysis_global_feature_adder {
751 ($count:ident) => {
752 impl<F: TryFrom<f32>> LengthCheckedAnalysisFeatureAdder<F, $count> for $struct_name<F>
753 where
754 <F as TryFrom<f32>>::Error: std::fmt::Debug,
755 {
756 fn get_column_headers_array(&self) -> &[&str; $count] {
757 &[$( $column_names ),*]
758 }
759
760 fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
761 $dependency_getter(self)
762 }
763
764 fn get_features(
765 &self,
766 context: &AnalysisFeatureContext<'_>,
767 processor: &dyn ProcessorView,
768 frame: &boxcars::Frame,
769 frame_count: usize,
770 current_time: f32,
771 ) -> SubtrActorResult<[F; $count]> {
772 $prop_getter(self, context, processor, frame, frame_count, current_time)
773 }
774 }
775
776 impl_analysis_feature_adder!($struct_name);
777 };
778 }
779 paste::paste! {
780 const [<$struct_name:snake:upper _LENGTH>]: usize = [$($column_names),*].len();
781 _analysis_global_feature_adder!([<$struct_name:snake:upper _LENGTH>]);
782 }
783 }
784}
785
786#[macro_export]
788macro_rules! build_player_feature_adder {
789 ($struct_name:ident, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
790 #[derive(derive_new::new)]
791 pub struct $struct_name<F> {
792 _zero: std::marker::PhantomData<F>,
793 }
794
795 impl<F: Sync + Send + TryFrom<f32> + 'static> $struct_name<F> where
796 <F as TryFrom<f32>>::Error: std::fmt::Debug,
797 {
798 pub fn arc_new() -> std::sync::Arc<dyn PlayerFeatureAdder<F> + Send + Sync + 'static> {
799 std::sync::Arc::new(Self::new())
800 }
801 }
802
803 player_feature_adder!(
804 $struct_name,
805 $prop_getter,
806 $( $column_names ),*
807 );
808 }
809}
810
811#[macro_export]
813macro_rules! player_feature_adder {
814 ($struct_name:ident, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
815 macro_rules! _player_feature_adder {
816 ($count:ident) => {
817 impl<F: TryFrom<f32>> LengthCheckedPlayerFeatureAdder<F, $count> for $struct_name<F>
818 where
819 <F as TryFrom<f32>>::Error: std::fmt::Debug,
820 {
821 fn get_column_headers_array(&self) -> &[&str; $count] {
822 &[$( $column_names ),*]
823 }
824
825 fn get_features(
826 &self,
827 player_id: &PlayerId,
828 processor: &dyn ProcessorView,
829 frame: &boxcars::Frame,
830 frame_count: usize,
831 current_time: f32,
832 ) -> SubtrActorResult<[F; $count]> {
833 $prop_getter(self, player_id, processor, frame, frame_count, current_time)
834 }
835 }
836
837 impl_player_feature_adder!($struct_name);
838 };
839 }
840 paste::paste! {
841 const [<$struct_name:snake:upper _LENGTH>]: usize = [$($column_names),*].len();
842 _player_feature_adder!([<$struct_name:snake:upper _LENGTH>]);
843 }
844 }
845}
846
847#[macro_export]
849macro_rules! build_analysis_player_feature_adder {
850 ($struct_name:ident, $dependency_getter:expr, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
851 #[derive(derive_new::new)]
852 pub struct $struct_name<F> {
853 _zero: std::marker::PhantomData<F>,
854 }
855
856 impl<F: Sync + Send + TryFrom<f32> + 'static> $struct_name<F> where
857 <F as TryFrom<f32>>::Error: std::fmt::Debug,
858 {
859 pub fn arc_new() -> std::sync::Arc<dyn AnalysisPlayerFeatureAdder<F> + Send + Sync + 'static> {
860 std::sync::Arc::new(Self::new())
861 }
862 }
863
864 analysis_player_feature_adder!(
865 $struct_name,
866 $dependency_getter,
867 $prop_getter,
868 $( $column_names ),*
869 );
870 }
871}
872
873#[macro_export]
875macro_rules! analysis_player_feature_adder {
876 ($struct_name:ident, $dependency_getter:expr, $prop_getter:expr, $( $column_names:expr ),* $(,)?) => {
877 macro_rules! _analysis_player_feature_adder {
878 ($count:ident) => {
879 impl<F: TryFrom<f32>> LengthCheckedAnalysisPlayerFeatureAdder<F, $count> for $struct_name<F>
880 where
881 <F as TryFrom<f32>>::Error: std::fmt::Debug,
882 {
883 fn get_column_headers_array(&self) -> &[&str; $count] {
884 &[$( $column_names ),*]
885 }
886
887 fn analysis_dependencies(&self) -> Vec<AnalysisDependency> {
888 $dependency_getter(self)
889 }
890
891 fn get_features(
892 &self,
893 context: &AnalysisFeatureContext<'_>,
894 player_id: &PlayerId,
895 processor: &dyn ProcessorView,
896 frame: &boxcars::Frame,
897 frame_count: usize,
898 current_time: f32,
899 ) -> SubtrActorResult<[F; $count]> {
900 $prop_getter(self, context, player_id, processor, frame, frame_count, current_time)
901 }
902 }
903
904 impl_analysis_player_feature_adder!($struct_name);
905 };
906 }
907 paste::paste! {
908 const [<$struct_name:snake:upper _LENGTH>]: usize = [$($column_names),*].len();
909 _analysis_player_feature_adder!([<$struct_name:snake:upper _LENGTH>]);
910 }
911 }
912}
913
914pub fn convert_float_conversion_error<T>(_: T) -> SubtrActorError {
916 SubtrActorError::new(SubtrActorErrorVariant::FloatConversionError)
917}
918
919#[macro_export]
921macro_rules! convert_all {
922 ($err:expr, $( $item:expr ),* $(,)?) => {{
923 Ok([
924 $( $item.try_into().map_err($err)? ),*
925 ])
926 }};
927}
928
929#[macro_export]
931macro_rules! convert_all_floats {
932 ($( $item:expr ),* $(,)?) => {{
933 convert_all!(convert_float_conversion_error, $( $item ),*)
934 }};
935}