zerodds_corba_dnc/
node.rs1use alloc::collections::BTreeMap;
11use alloc::string::String;
12use alloc::vec::Vec;
13
14use crate::plan::InstanceDeploymentDescription;
15
16#[derive(Debug, Clone, PartialEq, Eq, Default)]
18pub struct NodeApplication {
19 pub node: String,
21 pub instances: Vec<InstanceDeploymentDescription>,
23 pub active: Vec<String>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
29pub struct NodeApplicationManager {
30 node: String,
31 instances: Vec<InstanceDeploymentDescription>,
32}
33
34impl NodeApplicationManager {
35 #[must_use]
37 pub fn new(node: String, instances: Vec<InstanceDeploymentDescription>) -> Self {
38 Self { node, instances }
39 }
40
41 #[must_use]
43 pub fn start_launch(&self) -> NodeApplication {
44 NodeApplication {
45 node: self.node.clone(),
46 instances: self.instances.clone(),
47 active: self.instances.iter().map(|i| i.name.clone()).collect(),
48 }
49 }
50
51 pub fn destroy_application(app: &mut NodeApplication) {
53 app.active.clear();
54 }
55}
56
57#[derive(Debug, Default, Clone, PartialEq, Eq)]
59pub struct NodeManager {
60 pub name: String,
62 apps: BTreeMap<String, NodeApplication>,
64}
65
66impl NodeManager {
67 #[must_use]
69 pub fn new(name: String) -> Self {
70 Self {
71 name,
72 apps: BTreeMap::new(),
73 }
74 }
75
76 #[must_use]
79 pub fn prepare_plan(
80 &self,
81 instances: Vec<InstanceDeploymentDescription>,
82 ) -> NodeApplicationManager {
83 NodeApplicationManager::new(self.name.clone(), instances)
84 }
85
86 pub fn register_application(&mut self, plan_label: String, app: NodeApplication) {
89 self.apps.insert(plan_label, app);
90 }
91
92 #[must_use]
94 pub fn active_plans(&self) -> Vec<String> {
95 self.apps.keys().cloned().collect()
96 }
97
98 pub fn unregister_application(&mut self, plan_label: &str) -> Option<NodeApplication> {
100 self.apps.remove(plan_label)
101 }
102}
103
104#[cfg(test)]
105#[allow(clippy::expect_used, clippy::unwrap_used, clippy::panic)]
106mod tests {
107 use super::*;
108 use alloc::string::ToString;
109
110 fn make_inst(name: &str) -> InstanceDeploymentDescription {
111 InstanceDeploymentDescription {
112 name: name.into(),
113 implementation: "I".into(),
114 node: "N1".into(),
115 ..InstanceDeploymentDescription::default()
116 }
117 }
118
119 #[test]
120 fn prepare_plan_then_start_launch() {
121 let nm = NodeManager::new("N1".into());
122 let app_mgr = nm.prepare_plan(alloc::vec![make_inst("a"), make_inst("b")]);
123 let app = app_mgr.start_launch();
124 assert_eq!(app.node, "N1");
125 assert_eq!(app.active.len(), 2);
126 assert!(app.active.iter().any(|s| s == "a"));
127 assert!(app.active.iter().any(|s| s == "b"));
128 }
129
130 #[test]
131 fn destroy_application_clears_active() {
132 let nm = NodeManager::new("N1".into());
133 let mut app = nm.prepare_plan(alloc::vec![make_inst("a")]).start_launch();
134 NodeApplicationManager::destroy_application(&mut app);
135 assert!(app.active.is_empty());
136 assert_eq!(app.instances.len(), 1, "instance metadata is preserved");
137 }
138
139 #[test]
140 fn register_and_unregister_application() {
141 let mut nm = NodeManager::new("N1".into());
142 let app = nm.prepare_plan(alloc::vec![make_inst("a")]).start_launch();
143 nm.register_application("Plan1".into(), app);
144 assert_eq!(nm.active_plans(), alloc::vec!["Plan1".to_string()]);
145 let popped = nm.unregister_application("Plan1");
146 assert!(popped.is_some());
147 assert!(nm.active_plans().is_empty());
148 }
149
150 #[test]
151 fn unregister_unknown_returns_none() {
152 let mut nm = NodeManager::new("N".into());
153 assert!(nm.unregister_application("nope").is_none());
154 }
155}