1pub use ros_pointcloud2::PointCloud2Msg;
2pub use ros2_interfaces_jazzy_rkyv::nav_msgs::msg::Odometry;
3pub use ros2_interfaces_jazzy_rkyv::sensor_msgs::msg::{Imu, PointCloud2};
4use serde::{Deserialize, Serialize};
5use std::collections::{HashMap, HashSet};
6
7pub const COMPARE_NODE_NAME: &str = "#minot";
8
9#[derive(Clone, Debug, Default, rkyv::Archive, rkyv::Serialize, rkyv::Deserialize)]
10pub enum Qos {
11 Sensor,
12 #[default]
13 SystemDefault,
14 Custom(QosProfile),
15}
16
17#[derive(Deserialize, Debug, Clone, Copy)]
18#[serde(untagged)]
19pub enum HistoryValue {
20 Numeric(i32),
21 String(HistoryPolicy),
22}
23
24#[derive(Deserialize, Debug, Clone, Copy)]
25#[serde(rename_all = "snake_case")]
26pub enum HistoryPolicy {
27 KeepLast,
28 KeepAll,
29}
30
31#[derive(Deserialize, Debug, Clone, Copy)]
32#[serde(untagged)]
33pub enum ReliabilityValue {
34 Numeric(i32),
35 String(ReliabilityPolicy),
36}
37
38#[derive(Deserialize, Debug, Clone, Copy)]
39#[serde(rename_all = "snake_case")]
40pub enum ReliabilityPolicy {
41 Reliable,
42 BestEffort,
43}
44
45#[derive(Deserialize, Debug, Clone, Copy)]
46#[serde(untagged)]
47pub enum DurabilityValue {
48 Numeric(i32),
49 String(DurabilityPolicy),
50}
51
52#[derive(Deserialize, Debug, Clone, Copy)]
53#[serde(rename_all = "snake_case")]
54pub enum DurabilityPolicy {
55 Volatile,
56 TransientLocal,
57}
58
59#[derive(Deserialize, Debug, Clone, Copy)]
60#[serde(untagged)]
61pub enum LivelinessValue {
62 Numeric(i32),
63 String(LivelinessPolicy),
64}
65
66#[derive(Deserialize, Debug, Clone, Copy)]
67#[serde(rename_all = "snake_case")]
68pub enum LivelinessPolicy {
69 Automatic,
70 ManualByTopic,
71}
72
73fn deserialize_history<'de, D>(deserializer: D) -> Result<String, D::Error>
74where
75 D: serde::Deserializer<'de>,
76{
77 let value = HistoryValue::deserialize(deserializer)?;
78 Ok(match value {
79 HistoryValue::Numeric(1) => "keep_last".to_string(),
80 HistoryValue::Numeric(2) => "keep_all".to_string(),
81 HistoryValue::Numeric(_) => "keep_last".to_string(), HistoryValue::String(HistoryPolicy::KeepLast) => "keep_last".to_string(),
83 HistoryValue::String(HistoryPolicy::KeepAll) => "keep_all".to_string(),
84 })
85}
86
87fn deserialize_reliability<'de, D>(deserializer: D) -> Result<String, D::Error>
88where
89 D: serde::Deserializer<'de>,
90{
91 let value = ReliabilityValue::deserialize(deserializer)?;
92 Ok(match value {
93 ReliabilityValue::Numeric(1) => "reliable".to_string(),
94 ReliabilityValue::Numeric(2) => "best_effort".to_string(),
95 ReliabilityValue::Numeric(_) => "reliable".to_string(), ReliabilityValue::String(ReliabilityPolicy::Reliable) => "reliable".to_string(),
97 ReliabilityValue::String(ReliabilityPolicy::BestEffort) => "best_effort".to_string(),
98 })
99}
100
101fn deserialize_durability<'de, D>(deserializer: D) -> Result<String, D::Error>
102where
103 D: serde::Deserializer<'de>,
104{
105 let value = DurabilityValue::deserialize(deserializer)?;
106 Ok(match value {
107 DurabilityValue::Numeric(1) => "transient_local".to_string(),
108 DurabilityValue::Numeric(2) => "volatile".to_string(),
109 DurabilityValue::Numeric(_) => "volatile".to_string(), DurabilityValue::String(DurabilityPolicy::TransientLocal) => "transient_local".to_string(),
111 DurabilityValue::String(DurabilityPolicy::Volatile) => "volatile".to_string(),
112 })
113}
114
115fn deserialize_liveliness<'de, D>(deserializer: D) -> Result<String, D::Error>
116where
117 D: serde::Deserializer<'de>,
118{
119 let value = LivelinessValue::deserialize(deserializer)?;
120 Ok(match value {
121 LivelinessValue::Numeric(1) => "automatic".to_string(),
122 LivelinessValue::Numeric(3) => "manual_by_topic".to_string(),
123 LivelinessValue::Numeric(_) => "automatic".to_string(), LivelinessValue::String(LivelinessPolicy::Automatic) => "automatic".to_string(),
125 LivelinessValue::String(LivelinessPolicy::ManualByTopic) => "manual_by_topic".to_string(),
126 })
127}
128
129#[derive(
130 rkyv::Archive, Deserialize, Debug, Serialize, Clone, rkyv::Deserialize, rkyv::Serialize,
131)]
132pub struct QosProfile {
133 #[serde(deserialize_with = "deserialize_history")]
134 pub history: String,
135 pub depth: i32,
136 #[serde(deserialize_with = "deserialize_reliability")]
137 pub reliability: String,
138 #[serde(deserialize_with = "deserialize_durability")]
139 pub durability: String,
140 pub deadline: QosTime,
141 pub lifespan: QosTime,
142 #[serde(deserialize_with = "deserialize_liveliness")]
143 pub liveliness: String,
144 pub liveliness_lease_duration: QosTime,
145 pub avoid_ros_namespace_conventions: bool,
146}
147
148#[derive(
149 Deserialize, Serialize, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive, Debug, Clone, Copy,
150)]
151pub struct QosTime {
152 pub sec: u64,
153 pub nsec: u64,
154}
155
156#[allow(clippy::large_enum_variant)]
157#[derive(Clone, Debug, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
158pub enum SensorTypeMapped {
159 Lidar(PointCloud2),
160 Imu(Imu),
161 Odometry(Odometry),
162 Any(Vec<u8>),
163}
164
165#[derive(Clone, Debug, rkyv::Serialize, rkyv::Deserialize, rkyv::Archive)]
166pub struct BagMsg {
167 pub topic: String,
168 pub msg_type: String,
169 pub data: SensorTypeMapped,
170 pub qos: Option<Qos>,
171}
172
173#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
174pub enum RatPubRegisterKind {
175 Publish,
176 Subscribe,
177}
178
179#[derive(PartialEq, Eq, Debug, Clone)]
180pub struct Rules {
181 store: HashMap<Variable, Vec<VariableHuman>>,
182 pub cache: HashMap<Variable, HashSet<(Client, RatPubRegisterKind)>>,
183 id_counter: u32,
184}
185
186pub type Client = String;
187pub type Variable = String;
188
189impl Default for Rules {
190 fn default() -> Self {
191 Self::new()
192 }
193}
194
195impl Rules {
196 pub fn new() -> Self {
197 Self {
198 store: HashMap::new(),
199 cache: HashMap::new(),
200 id_counter: 0,
201 }
202 }
203
204 pub fn insert(&mut self, variable: String, strategies: Vec<VariableHuman>) {
205 match self.store.get_mut(&variable) {
206 Some(el) => {
207 el.extend(strategies);
208 }
209 None => {
210 self.store.insert(variable, strategies);
211 }
212 }
213 }
214
215 pub fn new_id(&mut self) -> u32 {
216 self.id_counter += 1;
217 self.id_counter
218 }
219
220 pub fn remove_client(&mut self, client: &str) {
225 let mut variables_to_remove_from_store = Vec::new();
227 let mut cache_registrations_from_store: HashMap<
228 Variable,
229 HashSet<(Client, RatPubRegisterKind)>,
230 > = HashMap::new();
231 let mut store_updates: HashMap<Variable, Vec<VariableHuman>> = HashMap::new();
232
233 for (variable, rules) in self.store.iter_mut() {
234 let mut rules_to_keep_in_variable = Vec::new();
235 let mut variable_has_remaining_rules = false;
236
237 for rule in rules.drain(..) {
238 if rule.ship == client {
240 match &rule.strategy {
242 Some(ActionPlan::Shoot { target, id: _ }) => {
243 if !target.is_empty() {
245 cache_registrations_from_store
246 .entry(variable.clone())
247 .or_default()
248 .extend(
249 target
250 .iter()
251 .cloned()
252 .map(|t| (t, RatPubRegisterKind::Subscribe)),
253 );
254 }
255 }
256 Some(ActionPlan::Catch { source, id: _ }) => {
257 cache_registrations_from_store
259 .entry(variable.clone())
260 .or_default()
261 .insert((source.clone(), RatPubRegisterKind::Publish));
262 }
263 _ => {
264 }
266 }
267 } else if let Some(ActionPlan::Shoot { target, id }) = &rule.strategy {
268 let initial_target_count = target.len();
270 let new_target = target
271 .iter()
272 .filter(|t| t != &client)
273 .cloned()
274 .collect::<Vec<_>>();
275
276 if new_target.len() < initial_target_count {
277 if new_target.is_empty() {
279 cache_registrations_from_store
282 .entry(variable.clone())
283 .or_default()
284 .insert((rule.ship.clone(), RatPubRegisterKind::Publish));
285 } else {
286 rules_to_keep_in_variable.push(VariableHuman {
288 ship: rule.ship.clone(),
289 strategy: Some(ActionPlan::Shoot {
290 target: new_target,
291 id: *id,
292 }),
293 });
294 variable_has_remaining_rules = true;
295 }
296 } else {
297 rules_to_keep_in_variable.push(rule);
299 variable_has_remaining_rules = true;
300 }
301 } else if let Some(ActionPlan::Catch { source, id: _ }) = &rule.strategy {
302 if source == client {
304 cache_registrations_from_store
307 .entry(variable.clone())
308 .or_default()
309 .insert((rule.ship.clone(), RatPubRegisterKind::Subscribe));
310 } else {
311 rules_to_keep_in_variable.push(rule);
313 variable_has_remaining_rules = true;
314 }
315 } else {
316 rules_to_keep_in_variable.push(rule);
318 variable_has_remaining_rules = true;
319 }
320 }
321
322 if variable_has_remaining_rules {
324 store_updates.insert(variable.clone(), rules_to_keep_in_variable);
325 } else {
326 variables_to_remove_from_store.push(variable.clone());
327 }
328 }
329
330 for (variable, updated_rules) in store_updates {
332 self.store.insert(variable, updated_rules);
333 }
334 for var in variables_to_remove_from_store {
335 self.store.remove(&var);
336 }
337
338 for (variable, registrations) in cache_registrations_from_store {
340 self.cache
341 .entry(variable)
342 .or_default()
343 .extend(registrations);
344 }
345
346 let mut variables_to_remove_from_cache = Vec::new();
348 for (variable, registrations) in self.cache.iter_mut() {
349 let initial_count = registrations.len();
350 registrations.retain(|(c, _)| c != client);
351 if registrations.is_empty() && initial_count > 0 {
352 variables_to_remove_from_cache.push(variable.clone());
353 }
354 }
355
356 for var in variables_to_remove_from_cache {
358 self.cache.remove(&var);
359 }
360
361 self.resolve_cache();
364 }
365
366 pub fn clear(&mut self) {
367 self.store.clear();
368 }
369
370 pub fn raw(&self) -> &std::collections::HashMap<String, Vec<VariableHuman>> {
371 &self.store
372 }
373
374 pub fn resolve_cache(&mut self) {
386 let vars_to_process: Vec<Variable> = self.cache.keys().cloned().collect();
388
389 for var_name in vars_to_process {
390 if let Some(registrations) = self.cache.remove(&var_name) {
392 let mut new_pubs: HashSet<String> = HashSet::new();
393 let mut new_subs: HashSet<String> = HashSet::new();
394
395 for (client, kind) in registrations.iter() {
396 match kind {
397 RatPubRegisterKind::Publish => {
398 new_pubs.insert(client.clone());
399 }
400 RatPubRegisterKind::Subscribe => {
401 new_subs.insert(client.clone());
402 }
403 }
404 }
405
406 let existing_rules_option = self.store.remove(&var_name);
408
409 let had_existing_shoot = &existing_rules_option.as_ref().and_then(|rules| {
412 rules
413 .iter()
414 .find(|vh| matches!(vh.strategy, Some(ActionPlan::Shoot { .. })))
415 .map(|v| match v.strategy.as_ref() {
416 Some(ap) => match ap {
418 ActionPlan::Sail => unreachable!(),
419 ActionPlan::Shoot { target: _, id } => id,
420 ActionPlan::Catch { .. } => unreachable!(),
421 },
422 None => unreachable!(),
423 })
424 });
425
426 let mut generated_rules: Vec<VariableHuman> = Vec::new();
427
428 match had_existing_shoot {
429 Some(id) => {
430 let mut all_pubs: HashSet<String> = HashSet::new();
433 let mut all_subs: HashSet<String> = HashSet::new();
434 let mut other_vh: Vec<VariableHuman> = Vec::new();
435 let id = **id;
436 if let Some(existing_rules) = existing_rules_option {
437 for vh in existing_rules {
438 match vh.strategy {
439 Some(ActionPlan::Shoot { target, id: _ }) => {
440 all_pubs.insert(vh.ship);
441 all_subs.extend(target.into_iter());
443 }
444 Some(ActionPlan::Catch { source: _, id: _ }) => {
446 all_subs.insert(vh.ship);
447 }
448 _ => {
449 other_vh.push(vh);
450 }
451 }
452 }
453 }
454 all_pubs.extend(new_pubs.into_iter());
456 all_subs.extend(new_subs.into_iter());
457
458 let mut sorted_all_pubs: Vec<String> = all_pubs.into_iter().collect();
459 sorted_all_pubs.sort();
460 let mut sorted_all_subs: Vec<String> = all_subs.into_iter().collect();
461 sorted_all_subs.sort();
462 generated_rules.extend(other_vh);
463
464 if !sorted_all_pubs.is_empty() && !sorted_all_subs.is_empty() {
465 for pub_client in &sorted_all_pubs {
467 generated_rules.push(VariableHuman {
468 ship: pub_client.clone(),
469 strategy: Some(ActionPlan::Shoot {
470 target: sorted_all_subs.clone(),
471 id,
472 }),
473 });
474 }
475 }
476
477 self.store.insert(var_name.clone(), generated_rules);
479 }
480 None => {
481 if !new_pubs.is_empty() && !new_subs.is_empty() {
484 let mut sorted_new_pubs: Vec<String> = new_pubs.into_iter().collect();
486 sorted_new_pubs.sort();
487 let mut sorted_new_subs: Vec<String> = new_subs.into_iter().collect();
488 sorted_new_subs.sort();
489
490 let mut pair_generated_rules: Vec<VariableHuman> = Vec::new();
491 let pair_id = self.new_id();
492 for pub_client in &sorted_new_pubs {
493 pair_generated_rules.push(VariableHuman {
494 ship: pub_client.clone(),
495 strategy: Some(ActionPlan::Shoot {
496 target: sorted_new_subs.clone(),
497 id: pair_id,
498 }),
499 });
500 }
501
502 if let Some(mut existing_rules) = existing_rules_option {
503 existing_rules.extend(pair_generated_rules);
505 self.store.insert(var_name.clone(), existing_rules);
506 } else {
507 self.store.insert(var_name.clone(), pair_generated_rules);
509 }
510 } else {
511 if let Some(existing_rules) = existing_rules_option {
514 self.store.insert(var_name.clone(), existing_rules);
515 }
516 self.cache.insert(var_name.clone(), registrations);
518 }
519 }
520 }
521 }
522 }
523 }
524
525 pub fn register(&mut self, var: String, client: String, kind: RatPubRegisterKind) {
526 match self.cache.get_mut(&var) {
527 Some(el) => {
528 el.insert((client, kind));
529 }
530 None => {
531 let mut set = HashSet::new();
532 set.insert((client, kind));
533 self.cache.insert(var, set);
534 }
535 }
536
537 self.resolve_cache();
538 }
539}
540
541#[derive(rkyv::Archive, rkyv::Deserialize, rkyv::Serialize, Clone, Debug, PartialEq, Eq)]
542pub struct VariableHuman {
543 pub ship: String,
544 pub strategy: Option<ActionPlan>,
545}
546
547#[derive(
548 rkyv::Archive, rkyv::Serialize, rkyv::Deserialize, Debug, Clone, Default, PartialEq, Eq,
549)]
550pub enum ActionPlan {
551 #[default]
552 Sail,
553 Shoot {
554 target: Vec<String>,
555 id: u32,
556 },
557 Catch {
558 source: String,
559 id: u32,
560 },
561}
562
563#[cfg(test)]
564mod tests {
565 use super::*;
566
567 use std::collections::{HashMap, HashSet};
568
569 fn sort_variable_humans(humans: &mut [VariableHuman]) {
571 humans.sort_by(|a, b| {
573 a.ship
574 .cmp(&b.ship)
575 .then(format!("{:?}", a.strategy).cmp(&format!("{:?}", b.strategy)))
576 });
577 for human in humans.iter_mut() {
579 if let Some(ActionPlan::Shoot { target, id: _ }) = &mut human.strategy {
580 target.sort();
581 }
582 }
583 }
584 fn create_rules() -> Rules {
585 Rules::new()
586 }
587
588 #[test]
589 fn test_remove_client_main_ship_no_partner() {
590 let mut rules = create_rules();
591 rules.insert(
592 "var1".to_string(),
593 vec![VariableHuman {
594 ship: "client1".to_string(),
595 strategy: Some(ActionPlan::Sail),
596 }],
597 );
598
599 rules.remove_client("client1");
600
601 assert_eq!(rules.store.len(), 0);
603 }
604
605 #[test]
606 fn test_remove_client_main_ship_catch() {
607 let mut rules = create_rules();
608 rules.insert(
609 "var1".to_string(),
610 vec![VariableHuman {
611 ship: "client1".to_string(),
612 strategy: Some(ActionPlan::Catch {
613 source: "client2".to_string(),
614 id: 0,
615 }),
616 }],
617 );
618
619 rules.remove_client("client1");
620
621 assert_eq!(rules.store.len(), 0);
623 assert_eq!(rules.cache.len(), 1); let generated_rules = rules.cache.get("var1").unwrap();
626 assert_eq!(generated_rules.len(), 1); let mut rules = create_rules();
630 rules.insert(
631 "var1".to_string(),
632 vec![VariableHuman {
633 ship: "client1".to_string(),
634 strategy: Some(ActionPlan::Catch {
635 source: "client2".to_string(),
636 id: 0,
637 }),
638 }],
639 );
640 rules
641 .cache
642 .entry("var1".to_string())
643 .or_default()
644 .insert(("client3".to_string(), RatPubRegisterKind::Subscribe));
645
646 rules.remove_client("client1");
647 assert_eq!(rules.store.len(), 1);
648 assert!(rules.store.contains_key("var1"));
649 let generated_rules = rules.store.get("var1").unwrap();
650 assert_eq!(generated_rules.len(), 1);
651 assert_eq!(generated_rules[0].ship, "client2");
652 assert_eq!(
653 generated_rules[0].strategy,
654 Some(ActionPlan::Shoot {
655 target: vec!["client3".to_string()],
656 id: 1
657 })
658 );
659 assert_eq!(rules.cache.len(), 0); }
661
662 #[test]
663 fn test_remove_client_main_ship_shoot() {
664 let mut rules = create_rules();
665 rules.insert(
666 "var1".to_string(),
667 vec![VariableHuman {
668 ship: "client1".to_string(),
669 strategy: Some(ActionPlan::Shoot {
670 target: vec!["client2".to_string(), "client3".to_string()],
671 id: 0,
672 }),
673 }],
674 );
675
676 rules.remove_client("client1");
677
678 assert_eq!(rules.store.len(), 0);
680 let generated_rules = rules.cache.get("var1").unwrap();
682 assert_eq!(generated_rules.len(), 2); }
684
685 #[test]
686 fn test_remove_client_target_in_shoot_partial_removal() {
687 let mut rules = create_rules();
688 rules.insert(
689 "var1".to_string(),
690 vec![
691 VariableHuman {
692 ship: "shooter1".to_string(),
693 strategy: Some(ActionPlan::Shoot {
694 target: vec!["client_to_remove".to_string(), "client2".to_string()],
695 id: 0,
696 }),
697 },
698 VariableHuman {
699 ship: "shooter2".to_string(),
700 strategy: Some(ActionPlan::Shoot {
701 target: vec!["client2".to_string(), "client_to_remove".to_string()],
702 id: 0,
703 }),
704 },
705 ],
706 );
707
708 rules.remove_client("client_to_remove");
709
710 assert_eq!(rules.store.len(), 1);
712 assert!(rules.store.contains_key("var1"));
713 let remaining_rules = rules.store.get("var1").unwrap();
714 assert_eq!(remaining_rules.len(), 2);
715
716 let rule1 = &remaining_rules[0];
717 let rule2 = &remaining_rules[1];
718
719 if rule1.ship == "shooter1" {
721 assert_eq!(
722 rule1.strategy,
723 Some(ActionPlan::Shoot {
724 target: vec!["client2".to_string()],
725 id: 0
726 })
727 );
728 assert_eq!(rule2.ship, "shooter2");
729 assert_eq!(
730 rule2.strategy,
731 Some(ActionPlan::Shoot {
732 target: vec!["client2".to_string()],
733 id: 0
734 })
735 );
736 } else {
737 assert_eq!(rule1.ship, "shooter2");
738 assert_eq!(
739 rule1.strategy,
740 Some(ActionPlan::Shoot {
741 target: vec!["client2".to_string()],
742 id: 0
743 })
744 );
745 assert_eq!(rule2.ship, "shooter1");
746 assert_eq!(
747 rule2.strategy,
748 Some(ActionPlan::Shoot {
749 target: vec!["client2".to_string()],
750 id: 0
751 })
752 );
753 }
754
755 assert_eq!(rules.cache.len(), 0); }
757
758 #[test]
759 fn test_remove_client_target_in_shoot_full_removal() {
760 let mut rules = create_rules();
761 rules.insert(
762 "var1".to_string(),
763 vec![VariableHuman {
764 ship: "shooter1".to_string(),
765 strategy: Some(ActionPlan::Shoot {
766 target: vec!["client_to_remove".to_string()],
767 id: 0,
768 }),
769 }],
770 );
771
772 rules.remove_client("client_to_remove");
773
774 assert_eq!(rules.cache.len(), 1);
775 assert_eq!(rules.store.len(), 0);
776
777 let mut rules = create_rules();
779 rules.insert(
780 "var1".to_string(),
781 vec![VariableHuman {
782 ship: "shooter1".to_string(),
783 strategy: Some(ActionPlan::Shoot {
784 target: vec!["client_to_remove".to_string()],
785 id: 0,
786 }),
787 }],
788 );
789 rules
790 .cache
791 .entry("var1".to_string())
792 .or_default()
793 .insert(("client2".to_string(), RatPubRegisterKind::Subscribe));
794
795 rules.remove_client("client_to_remove");
796
797 assert_eq!(rules.store.len(), 1);
798 assert!(rules.store.contains_key("var1"));
799 let generated_rules = rules.store.get("var1").unwrap();
800 assert_eq!(generated_rules.len(), 1);
801 assert_eq!(generated_rules[0].ship, "shooter1");
802 assert_eq!(
803 generated_rules[0].strategy,
804 Some(ActionPlan::Shoot {
805 target: vec!["client2".to_string()],
806 id: 1
807 })
808 );
809 assert_eq!(rules.cache.len(), 0); }
811
812 #[test]
813 fn test_remove_client_from_cache_partial_removal() {
814 let mut rules = create_rules();
815 rules
816 .cache
817 .entry("var1".to_string())
818 .or_default()
819 .insert(("client_to_remove".to_string(), RatPubRegisterKind::Publish));
820 rules
821 .cache
822 .entry("var1".to_string())
823 .or_default()
824 .insert(("client2".to_string(), RatPubRegisterKind::Subscribe));
825 rules.cache.entry("var2".to_string()).or_default().insert((
826 "client_to_remove".to_string(),
827 RatPubRegisterKind::Subscribe,
828 ));
829 rules
830 .cache
831 .entry("var2".to_string())
832 .or_default()
833 .insert(("client3".to_string(), RatPubRegisterKind::Publish));
834
835 rules.remove_client("client_to_remove");
836
837 assert_eq!(rules.cache.len(), 2); assert_eq!(rules.store.len(), 0);
844 }
845
846 #[test]
847 fn test_remove_client_from_cache_full_removal() {
848 let mut rules = create_rules();
849 rules
850 .cache
851 .entry("var1".to_string())
852 .or_default()
853 .insert(("client_to_remove".to_string(), RatPubRegisterKind::Publish));
854
855 rules.remove_client("client_to_remove");
856
857 assert_eq!(rules.cache.len(), 0);
859 assert_eq!(rules.store.len(), 0); }
861
862 #[test]
863 fn test_remove_client_involved_in_multiple_rules_and_variables() {
864 let mut rules = create_rules();
865 rules.insert(
866 "var1".to_string(),
867 vec![
868 VariableHuman {
869 ship: "client_to_remove".to_string(),
870 strategy: Some(ActionPlan::Sail),
871 },
872 VariableHuman {
873 ship: "shooter1".to_string(),
874 strategy: Some(ActionPlan::Shoot {
875 target: vec!["client_to_remove".to_string(), "client2".to_string()],
876 id: 0,
877 }),
878 },
879 ],
880 );
881 rules.insert(
882 "var2".to_string(),
883 vec![VariableHuman {
884 ship: "client3".to_string(),
885 strategy: Some(ActionPlan::Catch {
886 source: "client_to_remove".to_string(),
887 id: 0,
888 }),
889 }],
890 );
891 rules.cache.entry("var1".to_string()).or_default().insert((
892 "client_to_remove".to_string(),
893 RatPubRegisterKind::Subscribe,
894 ));
895 rules
896 .cache
897 .entry("var3".to_string())
898 .or_default()
899 .insert(("client_to_remove".to_string(), RatPubRegisterKind::Publish));
900 rules
901 .cache
902 .entry("var3".to_string())
903 .or_default()
904 .insert(("client4".to_string(), RatPubRegisterKind::Subscribe));
905
906 rules.remove_client("client_to_remove");
907
908 assert_eq!(rules.cache.len(), 2); assert_eq!(rules.store.len(), 1);
911 assert!(rules.store.contains_key("var1"));
912
913 let var1_rules = rules.store.get("var1").unwrap();
914 assert_eq!(var1_rules.len(), 1);
915 assert_eq!(var1_rules[0].ship, "shooter1");
916 assert_eq!(
917 var1_rules[0].strategy,
918 Some(ActionPlan::Shoot {
919 target: vec!["client2".to_string()],
920 id: 0
921 })
922 );
923 }
924
925 #[test]
926 fn test_remove_client_not_present() {
927 let mut rules = create_rules();
928 rules.insert(
929 "var1".to_string(),
930 vec![VariableHuman {
931 ship: "client1".to_string(),
932 strategy: Some(ActionPlan::Sail),
933 }],
934 );
935 rules
936 .cache
937 .entry("var2".to_string())
938 .or_default()
939 .insert(("client2".to_string(), RatPubRegisterKind::Publish));
940
941 let initial_store = rules.store.clone();
942 let initial_cache = rules.cache.clone();
943
944 rules.remove_client("client_not_present");
945
946 assert_eq!(rules.store, initial_store);
948 assert_eq!(rules.cache, initial_cache);
949 }
950 #[test]
951 fn test_resolve_cache_basic_pair() {
952 let mut rules = Rules::new();
953 rules.register(
954 "position".to_string(),
955 "ShipA".to_string(),
956 RatPubRegisterKind::Publish,
957 );
958 rules.register(
959 "position".to_string(),
960 "ShipB".to_string(),
961 RatPubRegisterKind::Subscribe,
962 );
963
964 rules.resolve_cache();
965
966 let mut expected_store: HashMap<Variable, Vec<VariableHuman>> = {
968 let mut map = HashMap::new();
969 let var_humans = vec![VariableHuman {
970 ship: "ShipA".to_string(),
971 strategy: Some(ActionPlan::Shoot {
972 target: vec!["ShipB".to_string()],
973 id: 1,
974 }),
975 }];
976 map.insert("position".to_string(), var_humans);
977 map
978 };
979
980 let mut actual_store = rules.store.clone();
981
982 if let Some(humans) = actual_store.get_mut("position") {
984 sort_variable_humans(humans);
985 }
986 if let Some(humans) = expected_store.get_mut("position") {
987 sort_variable_humans(humans);
988 }
989
990 assert_eq!(actual_store, expected_store);
991
992 assert!(rules.cache.is_empty());
994 }
995
996 #[test]
997 fn test_resolve_cache_multiple_pubs_subs() {
998 let mut rules = Rules::new();
999 rules.register(
1000 "velocity".to_string(),
1001 "ShipX".to_string(),
1002 RatPubRegisterKind::Publish,
1003 );
1004 rules.register(
1005 "velocity".to_string(),
1006 "ShipY".to_string(),
1007 RatPubRegisterKind::Publish,
1008 );
1009 rules.register(
1010 "velocity".to_string(),
1011 "ShipZ".to_string(),
1012 RatPubRegisterKind::Subscribe,
1013 );
1014 rules.register(
1015 "velocity".to_string(),
1016 "ShipW".to_string(),
1017 RatPubRegisterKind::Subscribe,
1018 );
1019
1020 rules.resolve_cache();
1021
1022 let expected_store: HashMap<Variable, Vec<VariableHuman>> = {
1024 let mut map = HashMap::new();
1025 let var_humans = vec![
1027 VariableHuman {
1029 ship: "ShipX".to_string(),
1030 strategy: Some(ActionPlan::Shoot {
1031 target: vec!["ShipW".to_string(), "ShipZ".to_string()],
1032 id: 1,
1033 }),
1034 },
1035 VariableHuman {
1036 ship: "ShipY".to_string(),
1037 strategy: Some(ActionPlan::Shoot {
1038 target: vec!["ShipW".to_string(), "ShipZ".to_string()],
1039 id: 1,
1040 }),
1041 },
1042 ];
1043 map.insert("velocity".to_string(), var_humans);
1044 map
1045 };
1046
1047 let mut actual_store_sorted = rules.store.clone();
1049 if let Some(humans) = actual_store_sorted.get_mut("velocity") {
1050 sort_variable_humans(humans); }
1052
1053 let mut expected_store_sorted = expected_store.clone();
1055 if let Some(humans) = expected_store_sorted.get_mut("velocity") {
1056 sort_variable_humans(humans); }
1058
1059 assert_eq!(actual_store_sorted, expected_store_sorted);
1060
1061 assert!(rules.cache.is_empty());
1063 }
1064
1065 #[test]
1066 fn test_resolve_cache_no_pair_pubs_only() {
1067 let mut rules = Rules::new();
1068 rules.register(
1069 "fuel".to_string(),
1070 "ShipA".to_string(),
1071 RatPubRegisterKind::Publish,
1072 );
1073 rules.register(
1074 "fuel".to_string(),
1075 "ShipC".to_string(),
1076 RatPubRegisterKind::Publish,
1077 );
1078
1079 rules.resolve_cache();
1080
1081 assert!(rules.store.is_empty());
1083
1084 let expected_cache: HashMap<Variable, HashSet<(Client, RatPubRegisterKind)>> = {
1086 let mut map = HashMap::new();
1087 let mut set = HashSet::new();
1088 set.insert(("ShipA".to_string(), RatPubRegisterKind::Publish));
1089 set.insert(("ShipC".to_string(), RatPubRegisterKind::Publish));
1090 map.insert("fuel".to_string(), set);
1091 map
1092 };
1093 assert_eq!(rules.cache, expected_cache);
1094 }
1095
1096 #[test]
1097 fn test_resolve_cache_no_pair_subs_only() {
1098 let mut rules = Rules::new();
1099 rules.register(
1100 "cargo".to_string(),
1101 "ShipB".to_string(),
1102 RatPubRegisterKind::Subscribe,
1103 );
1104 rules.register(
1105 "cargo".to_string(),
1106 "ShipD".to_string(),
1107 RatPubRegisterKind::Subscribe,
1108 );
1109
1110 rules.resolve_cache();
1111
1112 assert!(rules.store.is_empty());
1114
1115 let expected_cache: HashMap<Variable, HashSet<(Client, RatPubRegisterKind)>> = {
1117 let mut map = HashMap::new();
1118 let mut set = HashSet::new();
1119 set.insert(("ShipB".to_string(), RatPubRegisterKind::Subscribe));
1120 set.insert(("ShipD".to_string(), RatPubRegisterKind::Subscribe));
1121 map.insert("cargo".to_string(), set);
1122 map
1123 };
1124 assert_eq!(rules.cache, expected_cache);
1125 }
1126
1127 #[test]
1128 fn test_resolve_cache_mixed_variables() {
1129 let mut rules = Rules::new();
1130 rules.register(
1132 "shields".to_string(),
1133 "ShipF".to_string(),
1134 RatPubRegisterKind::Publish,
1135 );
1136 rules.register(
1137 "shields".to_string(),
1138 "ShipG".to_string(),
1139 RatPubRegisterKind::Subscribe,
1140 );
1141
1142 rules.register(
1144 "waypoints".to_string(),
1145 "ShipH".to_string(),
1146 RatPubRegisterKind::Publish,
1147 );
1148
1149 rules.resolve_cache();
1150
1151 let mut expected_store: HashMap<Variable, Vec<VariableHuman>> = {
1153 let mut map = HashMap::new();
1154 let var_humans = vec![VariableHuman {
1155 ship: "ShipF".to_string(),
1156 strategy: Some(ActionPlan::Shoot {
1157 target: vec!["ShipG".to_string()],
1158 id: 1,
1159 }),
1160 }];
1161 map.insert("shields".to_string(), var_humans);
1162 map
1163 };
1164
1165 let mut actual_store = rules.store.clone();
1166
1167 if let Some(humans) = actual_store.get_mut("shields") {
1169 sort_variable_humans(humans);
1170 }
1171 if let Some(humans) = expected_store.get_mut("shields") {
1172 sort_variable_humans(humans);
1173 }
1174
1175 assert_eq!(actual_store, expected_store);
1176
1177 let expected_cache: HashMap<Variable, HashSet<(Client, RatPubRegisterKind)>> = {
1179 let mut map = HashMap::new();
1180 let mut set = HashSet::new();
1181 set.insert(("ShipH".to_string(), RatPubRegisterKind::Publish));
1182 map.insert("waypoints".to_string(), set);
1183 map
1184 };
1185 assert_eq!(rules.cache, expected_cache);
1186 }
1187
1188 #[test]
1189 fn test_resolve_cache_state_after_mixed_plus_new_subs() {
1190 let mut rules = Rules::new();
1191
1192 let shields_rules = vec![VariableHuman {
1195 ship: "ShipF".to_string(),
1196 strategy: Some(ActionPlan::Shoot {
1197 target: vec!["ShipG".to_string()],
1198 id: 0,
1199 }),
1200 }];
1201 rules.insert("shields".to_string(), shields_rules.clone());
1202
1203 rules.register(
1205 "waypoints".to_string(),
1206 "ShipH".to_string(),
1207 RatPubRegisterKind::Publish,
1208 );
1209
1210 rules.register(
1212 "status".to_string(),
1213 "ShipI".to_string(),
1214 RatPubRegisterKind::Subscribe,
1215 );
1216 rules.register(
1217 "status".to_string(),
1218 "ShipJ".to_string(),
1219 RatPubRegisterKind::Subscribe,
1220 );
1221
1222 let initial_store = rules.store.clone();
1224 let initial_cache = rules.cache.clone();
1225
1226 rules.resolve_cache();
1227
1228 let mut initial_store_sorted = initial_store.clone();
1231 if let Some(humans) = initial_store_sorted.get_mut("shields") {
1232 sort_variable_humans(humans);
1233 }
1234 let mut actual_store_sorted = rules.store.clone();
1235 if let Some(humans) = actual_store_sorted.get_mut("shields") {
1236 sort_variable_humans(humans);
1237 }
1238
1239 assert_eq!(actual_store_sorted, initial_store_sorted);
1240
1241 assert_eq!(rules.cache, initial_cache);
1243 }
1244
1245 #[test]
1246 fn test_resolve_cache_state_after_prev_plus_new_pub() {
1247 let mut rules = Rules::new();
1248
1249 let shields_rules = vec![VariableHuman {
1252 ship: "ShipF".to_string(),
1253 strategy: Some(ActionPlan::Shoot {
1254 target: vec!["ShipG".to_string()],
1255 id: 0,
1256 }),
1257 }];
1258 rules.insert("shields".to_string(), shields_rules.clone());
1259
1260 rules.register(
1262 "waypoints".to_string(),
1263 "ShipH".to_string(),
1264 RatPubRegisterKind::Publish,
1265 );
1266 rules.register(
1268 "status".to_string(),
1269 "ShipI".to_string(),
1270 RatPubRegisterKind::Subscribe,
1271 );
1272 rules.register(
1273 "status".to_string(),
1274 "ShipJ".to_string(),
1275 RatPubRegisterKind::Subscribe,
1276 );
1277
1278 rules.register(
1280 "status".to_string(),
1281 "ShipK".to_string(),
1282 RatPubRegisterKind::Publish,
1283 );
1284
1285 rules.resolve_cache();
1286
1287 let mut expected_store: HashMap<Variable, Vec<VariableHuman>> = HashMap::new();
1289 expected_store.insert("shields".to_string(), shields_rules); let status_rules = vec![VariableHuman {
1293 ship: "ShipK".to_string(),
1294 strategy: Some(ActionPlan::Shoot {
1295 target: vec!["ShipI".to_string(), "ShipJ".to_string()],
1296 id: 1,
1297 }),
1298 }];
1299 expected_store.insert("status".to_string(), status_rules);
1300
1301 let mut actual_store_sorted = rules.store.clone();
1302 if let Some(humans) = actual_store_sorted.get_mut("shields") {
1303 sort_variable_humans(humans);
1304 }
1305 if let Some(humans) = actual_store_sorted.get_mut("status") {
1306 sort_variable_humans(humans);
1307 }
1308
1309 let mut expected_store_sorted = expected_store.clone();
1310 if let Some(humans) = expected_store_sorted.get_mut("shields") {
1311 sort_variable_humans(humans);
1312 }
1313 if let Some(humans) = expected_store_sorted.get_mut("status") {
1314 sort_variable_humans(humans);
1315 }
1316
1317 assert_eq!(actual_store_sorted, expected_store_sorted);
1318
1319 let expected_cache: HashMap<Variable, HashSet<(Client, RatPubRegisterKind)>> = {
1321 let mut map = HashMap::new();
1322 let mut set = HashSet::new();
1323 set.insert(("ShipH".to_string(), RatPubRegisterKind::Publish));
1324 map.insert("waypoints".to_string(), set);
1325 map
1326 };
1327 assert_eq!(rules.cache, expected_cache);
1328 }
1329
1330 #[test]
1331 fn test_resolve_cache_empty_cache() {
1332 let mut rules = Rules::new();
1333 rules.insert(
1335 "initial".to_string(),
1336 vec![VariableHuman {
1337 ship: "ShipI".to_string(),
1338 strategy: Some(ActionPlan::Sail),
1339 }],
1340 );
1341
1342 rules.resolve_cache();
1343
1344 let expected_store: HashMap<Variable, Vec<VariableHuman>> = {
1346 let mut map = HashMap::new();
1347 map.insert(
1348 "initial".to_string(),
1349 vec![VariableHuman {
1350 ship: "ShipI".to_string(),
1351 strategy: Some(ActionPlan::Sail),
1352 }],
1353 );
1354 map
1355 };
1356 assert_eq!(rules.store, expected_store);
1357
1358 assert!(rules.cache.is_empty());
1360 }
1361
1362 #[test]
1363 fn test_resolve_cache_store_with_rules_add_sub() {
1364 let mut rules = Rules::new();
1365 let var_name = "data_stream".to_string();
1366
1367 rules.insert(
1369 var_name.clone(),
1370 vec![
1371 VariableHuman {
1372 ship: "ShipA".to_string(),
1373 strategy: Some(ActionPlan::Shoot {
1374 target: vec!["ShipB".to_string()],
1375 id: 0,
1376 }),
1377 },
1378 VariableHuman {
1379 ship: "MonitorShip".to_string(),
1380 strategy: Some(ActionPlan::Sail),
1381 }, ],
1383 );
1384
1385 rules.register(
1387 var_name.clone(),
1388 "ShipC".to_string(),
1389 RatPubRegisterKind::Subscribe,
1390 );
1391
1392 rules.resolve_cache();
1393
1394 let expected_store: HashMap<Variable, Vec<VariableHuman>> = {
1396 let mut map = HashMap::new();
1397 let var_humans = vec![
1400 VariableHuman {
1401 ship: "MonitorShip".to_string(),
1402 strategy: Some(ActionPlan::Sail),
1403 }, VariableHuman {
1405 ship: "ShipA".to_string(),
1406 strategy: Some(ActionPlan::Shoot {
1407 target: vec!["ShipB".to_string(), "ShipC".to_string()],
1408 id: 0,
1409 }),
1410 }, ];
1412 map.insert(var_name.clone(), var_humans);
1413 map
1414 };
1415
1416 let mut actual_store_sorted = rules.store.clone();
1417 if let Some(humans) = actual_store_sorted.get_mut(&var_name) {
1418 sort_variable_humans(humans);
1419 }
1420
1421 let mut expected_store_sorted = expected_store.clone();
1422 if let Some(humans) = expected_store_sorted.get_mut(&var_name) {
1423 sort_variable_humans(humans);
1424 }
1425
1426 assert_eq!(actual_store_sorted, expected_store_sorted);
1427
1428 assert!(!rules.cache.contains_key(&var_name));
1430 assert!(rules.cache.is_empty()); }
1432
1433 #[test]
1434 fn test_resolve_cache_store_with_rules_add_pub() {
1435 let mut rules = Rules::new();
1436 let var_name = "data_stream".to_string();
1437
1438 rules.insert(
1440 var_name.clone(),
1441 vec![
1442 VariableHuman {
1443 ship: "ShipA".to_string(),
1444 strategy: Some(ActionPlan::Shoot {
1445 target: vec!["ShipB".to_string()],
1446 id: 0,
1447 }),
1448 },
1449 VariableHuman {
1450 ship: "MonitorShip".to_string(),
1451 strategy: Some(ActionPlan::Sail),
1452 }, ],
1454 );
1455
1456 rules.register(
1458 var_name.clone(),
1459 "ShipC".to_string(),
1460 RatPubRegisterKind::Publish,
1461 );
1462
1463 rules.resolve_cache();
1464
1465 let expected_store: HashMap<Variable, Vec<VariableHuman>> = {
1466 let mut map = HashMap::new();
1467 let var_humans = vec![
1468 VariableHuman {
1469 ship: "MonitorShip".to_string(),
1470 strategy: Some(ActionPlan::Sail),
1471 }, VariableHuman {
1473 ship: "ShipA".to_string(),
1474 strategy: Some(ActionPlan::Shoot {
1475 target: vec!["ShipB".to_string()],
1476 id: 0,
1477 }),
1478 }, VariableHuman {
1480 ship: "ShipC".to_string(),
1481 strategy: Some(ActionPlan::Shoot {
1482 target: vec!["ShipB".to_string()],
1483 id: 0,
1484 }),
1485 },
1486 ];
1487 map.insert(var_name.clone(), var_humans);
1488 map
1489 };
1490
1491 let mut actual_store_sorted = rules.store.clone();
1492 if let Some(humans) = actual_store_sorted.get_mut(&var_name) {
1493 sort_variable_humans(humans);
1494 }
1495
1496 let mut expected_store_sorted = expected_store.clone();
1497 if let Some(humans) = expected_store_sorted.get_mut(&var_name) {
1498 sort_variable_humans(humans);
1499 }
1500
1501 assert_eq!(actual_store_sorted, expected_store_sorted);
1502
1503 assert!(!rules.cache.contains_key(&var_name));
1505 assert!(rules.cache.is_empty()); }
1507
1508 #[test]
1509 fn test_resolve_cache_store_with_rules_add_pub_sub() {
1510 let mut rules = Rules::new();
1511 let var_name = "telemetry".to_string();
1512
1513 rules.insert(
1515 var_name.clone(),
1516 vec![VariableHuman {
1517 ship: "BaseStation".to_string(),
1518 strategy: Some(ActionPlan::Shoot {
1519 target: vec!["Rover1".to_string()],
1520 id: 0,
1521 }),
1522 }],
1523 );
1524
1525 rules.register(
1527 var_name.clone(),
1528 "BaseStationBackup".to_string(),
1529 RatPubRegisterKind::Publish,
1530 );
1531 rules.register(
1532 var_name.clone(),
1533 "Rover2".to_string(),
1534 RatPubRegisterKind::Subscribe,
1535 );
1536
1537 rules.resolve_cache();
1538
1539 let expected_store: HashMap<Variable, Vec<VariableHuman>> = {
1541 let mut map = HashMap::new();
1542 let all_subs = vec!["Rover1".to_string(), "Rover2".to_string()]; let var_humans = vec![
1547 VariableHuman {
1549 ship: "BaseStation".to_string(),
1550 strategy: Some(ActionPlan::Shoot {
1551 target: all_subs.clone(),
1552 id: 0,
1553 }),
1554 },
1555 VariableHuman {
1556 ship: "BaseStationBackup".to_string(),
1557 strategy: Some(ActionPlan::Shoot {
1558 target: all_subs.clone(),
1559 id: 0,
1560 }),
1561 },
1562 ];
1563 map.insert(var_name.clone(), var_humans);
1564 map
1565 };
1566
1567 let mut actual_store_sorted = rules.store.clone();
1568 if let Some(humans) = actual_store_sorted.get_mut(&var_name) {
1569 sort_variable_humans(humans);
1570 }
1571
1572 let mut expected_store_sorted = expected_store.clone();
1573 if let Some(humans) = expected_store_sorted.get_mut(&var_name) {
1574 sort_variable_humans(humans);
1575 }
1576
1577 assert_eq!(actual_store_sorted, expected_store_sorted);
1578
1579 assert!(!rules.cache.contains_key(&var_name));
1581 assert!(rules.cache.is_empty()); }
1583}