1use alloc::string::String;
7use alloc::vec::Vec;
8
9use crate::session::SessionId;
10use crate::status::ReturnStatus;
11
12#[derive(Debug, Default)]
14pub struct WebDdsRoot {
15 pub applications: Vec<Application>,
17 pub clients: Vec<Client>,
19}
20
21impl WebDdsRoot {
22 pub fn create_application(&mut self, app: Application) -> Result<SessionId, ReturnStatus> {
29 if self.applications.iter().any(|a| a.name == app.name) {
30 return Err(ReturnStatus::ObjectAlreadyExists);
31 }
32 let token = alloc::format!("session-{}", self.applications.len() + 1);
33 self.applications.push(app);
34 Ok(SessionId::new(token))
35 }
36
37 pub fn delete_application(&mut self, name: &str) -> Result<(), ReturnStatus> {
42 let idx = self
43 .applications
44 .iter()
45 .position(|a| a.name == name)
46 .ok_or(ReturnStatus::InvalidObject)?;
47 self.applications.remove(idx);
48 Ok(())
49 }
50
51 #[must_use]
57 pub fn get_applications(&self, expression: &str) -> Vec<&Application> {
58 self.applications
59 .iter()
60 .filter(|a| fnmatch_simple(expression, &a.name))
61 .collect()
62 }
63}
64
65#[derive(Debug, Clone, PartialEq, Eq)]
67pub struct Application {
68 pub name: String,
70 pub participants: Vec<DomainParticipant>,
72}
73
74#[derive(Debug, Clone, PartialEq, Eq)]
76pub struct Client {
77 pub client_api_key: String,
79}
80
81#[derive(Debug, Clone, PartialEq, Eq)]
83pub struct DomainParticipant {
84 pub name: String,
86 pub domain_id: i32,
88}
89
90fn fnmatch_simple(pattern: &str, name: &str) -> bool {
92 if pattern == "*" {
93 return true;
94 }
95 if let Some(idx) = pattern.find('*') {
97 let (prefix, suffix) = pattern.split_at(idx);
98 let suffix = &suffix[1..]; return name.starts_with(prefix) && name.ends_with(suffix);
100 }
101 pattern == name
102}
103
104#[cfg(test)]
105#[allow(clippy::expect_used)]
106mod tests {
107 use super::*;
108
109 fn app(name: &str) -> Application {
110 Application {
111 name: String::from(name),
112 participants: Vec::new(),
113 }
114 }
115
116 #[test]
117 fn create_application_returns_session_id() {
118 let mut root = WebDdsRoot::default();
120 let s = root.create_application(app("app1")).expect("ok");
121 assert!(s.is_valid());
122 }
123
124 #[test]
125 fn duplicate_application_yields_object_already_exists() {
126 let mut root = WebDdsRoot::default();
128 root.create_application(app("dup")).expect("first ok");
129 assert_eq!(
130 root.create_application(app("dup")),
131 Err(ReturnStatus::ObjectAlreadyExists)
132 );
133 }
134
135 #[test]
136 fn delete_application_removes_existing() {
137 let mut root = WebDdsRoot::default();
138 root.create_application(app("a")).expect("ok");
139 assert_eq!(root.delete_application("a"), Ok(()));
140 assert!(root.applications.is_empty());
141 }
142
143 #[test]
144 fn delete_unknown_application_yields_invalid_object() {
145 let mut root = WebDdsRoot::default();
146 assert_eq!(
147 root.delete_application("missing"),
148 Err(ReturnStatus::InvalidObject)
149 );
150 }
151
152 #[test]
153 fn get_applications_with_wildcard_matches_all() {
154 let mut root = WebDdsRoot::default();
156 root.create_application(app("a1")).expect("ok");
157 root.create_application(app("a2")).expect("ok");
158 let all = root.get_applications("*");
159 assert_eq!(all.len(), 2);
160 }
161
162 #[test]
163 fn get_applications_with_prefix_pattern_matches_subset() {
164 let mut root = WebDdsRoot::default();
165 root.create_application(app("temp_sensor")).expect("ok");
166 root.create_application(app("temp_actuator")).expect("ok");
167 root.create_application(app("pressure_sensor")).expect("ok");
168 let temps = root.get_applications("temp_*");
169 assert_eq!(temps.len(), 2);
170 }
171
172 #[test]
173 fn get_applications_with_exact_match() {
174 let mut root = WebDdsRoot::default();
175 root.create_application(app("exactly")).expect("ok");
176 root.create_application(app("else")).expect("ok");
177 let one = root.get_applications("exactly");
178 assert_eq!(one.len(), 1);
179 assert_eq!(one[0].name, "exactly");
180 }
181
182 #[test]
183 fn application_carries_participants() {
184 let a = Application {
185 name: String::from("trader"),
186 participants: alloc::vec![
187 DomainParticipant {
188 name: String::from("p0"),
189 domain_id: 0,
190 },
191 DomainParticipant {
192 name: String::from("p1"),
193 domain_id: 1,
194 },
195 ],
196 };
197 assert_eq!(a.participants.len(), 2);
198 assert_eq!(a.participants[0].domain_id, 0);
199 }
200}