1use std::{future::Future, path::Path};
20
21use astarte_device_sdk::aggregate::AstarteObject;
22use astarte_device_sdk::astarte_interfaces::Interface;
23use astarte_device_sdk::client::{ClientDisconnect, RecvError};
24use astarte_device_sdk::properties::PropAccess;
25use astarte_device_sdk::store::StoredProp;
26use astarte_device_sdk::transport::Connection;
27use astarte_device_sdk::{AstarteData, DeviceEvent, Error};
28use mockall::mock;
29
30pub use mockall;
32
33pub use astarte_device_sdk::Client;
35
36pub trait DeviceIntrospection {
37 fn get_interface<F, O>(&self, interface_name: &str, f: F) -> impl Future<Output = O> + Send
38 where
39 F: FnMut(Option<&Interface>) -> O + Send + 'static,
40 O: 'static;
41}
42
43pub trait DynamicIntrospection {
44 fn add_interface(
45 &mut self,
46 interface: Interface,
47 ) -> impl Future<Output = Result<bool, Error>> + Send;
48
49 fn extend_interfaces<I>(
50 &mut self,
51 interfaces: I,
52 ) -> impl Future<Output = Result<Vec<String>, Error>> + Send
53 where
54 I: IntoIterator<Item = Interface> + Send + 'static;
55
56 fn add_interface_from_file<P>(
57 &mut self,
58 file_path: P,
59 ) -> impl Future<Output = Result<bool, Error>> + Send
60 where
61 P: AsRef<Path> + Send + Sync + 'static;
62
63 fn add_interface_from_str(
64 &mut self,
65 json_str: &str,
66 ) -> impl Future<Output = Result<bool, Error>> + Send;
67
68 fn remove_interface(
69 &mut self,
70 interface_name: &str,
71 ) -> impl Future<Output = Result<bool, Error>> + Send;
72
73 fn remove_interfaces<I>(
74 &mut self,
75 interfaces_name: I,
76 ) -> impl Future<Output = Result<Vec<String>, Error>> + Send
77 where
78 I: IntoIterator<Item = String> + Send + 'static,
79 I::IntoIter: Send;
80}
81
82mock! {
83 pub DeviceClient<C: Connection + 'static> { }
84
85 impl<C: Connection> Clone for DeviceClient<C> {
86 fn clone(&self) -> Self;
87 }
88
89 impl<C: Connection> Client for DeviceClient<C> {
90 async fn send_object_with_timestamp(
91 &mut self,
92 interface_name: &str,
93 interface_path: &str,
94 data: AstarteObject,
95 timestamp: chrono::DateTime<chrono::Utc>,
96 ) -> Result<(), Error>;
97
98 async fn send_object(
99 &mut self,
100 interface_name: &str,
101 interface_path: &str,
102 data: AstarteObject,
103 ) -> Result<(), Error>;
104
105 async fn send_individual_with_timestamp(
106 &mut self,
107 interface_name: &str,
108 interface_path: &str,
109 data: AstarteData,
110 timestamp: chrono::DateTime<chrono::Utc>,
111 ) -> Result<(), Error>;
112
113 async fn send_individual(
114 &mut self,
115 interface_name: &str,
116 interface_path: &str,
117 data: AstarteData,
118 ) -> Result<(), Error>;
119
120 async fn set_property(
121 &mut self,
122 interface_name: &str,
123 interface_path: &str,
124 data: AstarteData
125 ) -> Result<(), Error>;
126
127 async fn unset_property(&mut self, interface_name: &str, interface_path: &str) -> Result<(), Error>;
128
129 async fn recv(&self) -> Result<DeviceEvent, RecvError>;
130 }
131
132 impl<C: Connection> DeviceIntrospection for DeviceClient<C> {
133 async fn get_interface<F, O>(&self, interface_name: &str, f: F) -> O
134 where
135 F: FnMut(Option<&Interface>) -> O + Send + 'static,
136 O: 'static;
137 }
138
139 impl<C: Connection> DynamicIntrospection for DeviceClient<C> {
140 async fn add_interface(&mut self, interface: Interface) -> Result<bool, Error>;
141
142 async fn extend_interfaces<I>(&mut self, interfaces: I) -> Result<Vec<String>, Error>
143 where
144 I: IntoIterator<Item = Interface> + Send + 'static;
145
146 async fn add_interface_from_file<P>(&mut self, file_path: P) -> Result<bool, Error>
147 where
148 P: AsRef<Path> + Send + Sync + 'static;
149
150 async fn add_interface_from_str(&mut self, json_str: &str) -> Result<bool, Error>;
151
152 async fn remove_interface(&mut self, interface_name: &str) -> Result<bool, Error>;
153
154 async fn remove_interfaces<I>(&mut self, interfaces_name: I) -> Result<Vec<String>, Error>
155 where
156 I: IntoIterator<Item = String> + Send + 'static,
157 I::IntoIter: Send;
158 }
159
160 impl<C: Connection> PropAccess for DeviceClient<C> {
161 async fn property(&self, interface: &str, path: &str) -> Result<Option<AstarteData>, Error>;
162 async fn interface_props(&self, interface: &str) -> Result<Vec<StoredProp>, Error>;
163 async fn all_props(&self) -> Result<Vec<StoredProp>, Error>;
164 async fn device_props(&self) -> Result<Vec<StoredProp>, Error>;
165 async fn server_props(&self) -> Result<Vec<StoredProp>, Error>;
166 }
167
168 impl<C: Connection> ClientDisconnect for DeviceClient<C> {
169 async fn disconnect(&mut self) -> Result<(), Error>;
170 }
171}
172
173mock! {
174 pub DeviceConnection<C: Connection + 'static> {}
175
176 impl<C: Connection> astarte_device_sdk::EventLoop for DeviceConnection<C> {
177 async fn handle_events(self) -> Result<(), crate::Error>;
178 }
179}
180
181#[cfg(test)]
182mod tests {
183 use super::*;
184
185 #[derive(Debug, Clone)]
187 struct CheckMocks {}
188
189 impl astarte_device_sdk::Client for CheckMocks {
190 async fn send_object_with_timestamp(
191 &mut self,
192 _interface_name: &str,
193 _interface_path: &str,
194 _data: AstarteObject,
195 _timestamp: chrono::DateTime<chrono::Utc>,
196 ) -> Result<(), Error> {
197 Ok(())
198 }
199
200 async fn send_object(
201 &mut self,
202 _interface_name: &str,
203 _interface_path: &str,
204 _data: AstarteObject,
205 ) -> Result<(), Error> {
206 Ok(())
207 }
208
209 async fn send_individual_with_timestamp(
210 &mut self,
211 _interface_name: &str,
212 _interface_path: &str,
213 _data: AstarteData,
214 _timestamp: chrono::DateTime<chrono::Utc>,
215 ) -> Result<(), Error> {
216 Ok(())
217 }
218
219 async fn send_individual(
220 &mut self,
221 _interface_name: &str,
222 _interface_path: &str,
223 _data: AstarteData,
224 ) -> Result<(), Error> {
225 Ok(())
226 }
227
228 async fn set_property(
229 &mut self,
230 _interface_name: &str,
231 _mapping_path: &str,
232 _data: AstarteData,
233 ) -> Result<(), Error> {
234 Ok(())
235 }
236
237 async fn unset_property(
238 &mut self,
239 _interface_name: &str,
240 _mapping_path: &str,
241 ) -> Result<(), Error> {
242 Ok(())
243 }
244
245 async fn recv(&self) -> Result<DeviceEvent, RecvError> {
246 Ok(DeviceEvent {
247 interface: Default::default(),
248 path: Default::default(),
249 data: astarte_device_sdk::Value::Property(None),
250 })
251 }
252 }
253
254 impl DeviceIntrospection for CheckMocks {
255 async fn get_interface<F, O>(&self, interface_name: &str, f: F) -> O
256 where
257 F: FnMut(Option<&Interface>) -> O + Send + 'static,
258 {
259 astarte_device_sdk::introspection::DeviceIntrospection::get_interface(
260 self,
261 interface_name,
262 f,
263 )
264 .await
265 }
266 }
267
268 impl astarte_device_sdk::introspection::DeviceIntrospection for CheckMocks {
269 async fn get_interface<F, O>(&self, _interface_name: &str, mut f: F) -> O
270 where
271 F: FnMut(Option<&Interface>) -> O + Send,
272 {
273 f(None)
274 }
275 }
276
277 impl DynamicIntrospection for CheckMocks {
278 async fn add_interface(&mut self, interface: Interface) -> Result<bool, Error> {
279 astarte_device_sdk::introspection::DynamicIntrospection::add_interface(self, interface)
280 .await
281 }
282
283 async fn extend_interfaces<I>(&mut self, interfaces: I) -> Result<Vec<String>, Error>
284 where
285 I: IntoIterator<Item = Interface> + Send + 'static,
286 {
287 astarte_device_sdk::introspection::DynamicIntrospection::extend_interfaces(
288 self, interfaces,
289 )
290 .await
291 }
292
293 async fn add_interface_from_file<P>(&mut self, file_path: P) -> Result<bool, Error>
294 where
295 P: AsRef<Path> + Send + Sync + 'static,
296 {
297 astarte_device_sdk::introspection::DynamicIntrospection::add_interface_from_file(
298 self, file_path,
299 )
300 .await
301 }
302
303 async fn add_interface_from_str(&mut self, json_str: &str) -> Result<bool, Error> {
304 astarte_device_sdk::introspection::DynamicIntrospection::add_interface_from_str(
305 self, json_str,
306 )
307 .await
308 }
309
310 async fn remove_interface(&mut self, interface_name: &str) -> Result<bool, Error> {
311 astarte_device_sdk::introspection::DynamicIntrospection::remove_interface(
312 self,
313 interface_name,
314 )
315 .await
316 }
317
318 async fn remove_interfaces<I>(&mut self, interfaces_name: I) -> Result<Vec<String>, Error>
319 where
320 I: IntoIterator<Item = String> + Send + 'static,
321 I::IntoIter: Send,
322 {
323 astarte_device_sdk::introspection::DynamicIntrospection::remove_interfaces(
324 self,
325 interfaces_name,
326 )
327 .await
328 }
329 }
330
331 impl astarte_device_sdk::introspection::DynamicIntrospection for CheckMocks {
332 async fn add_interface(&mut self, _interface: Interface) -> Result<bool, Error> {
333 Ok(Default::default())
334 }
335
336 async fn extend_interfaces<I>(&mut self, _interfaces: I) -> Result<Vec<String>, Error>
337 where
338 I: IntoIterator<Item = Interface> + Send,
339 {
340 Ok(Default::default())
341 }
342
343 async fn add_interface_from_file<P>(&mut self, _file_path: P) -> Result<bool, Error>
344 where
345 P: AsRef<Path> + Send + Sync,
346 {
347 Ok(Default::default())
348 }
349
350 async fn add_interface_from_str(&mut self, _json_str: &str) -> Result<bool, Error> {
351 Ok(Default::default())
352 }
353
354 async fn remove_interface(&mut self, _interface_name: &str) -> Result<bool, Error> {
355 Ok(Default::default())
356 }
357
358 async fn remove_interfaces<I>(&mut self, _interfaces_name: I) -> Result<Vec<String>, Error>
359 where
360 I: IntoIterator<Item = String> + Send,
361 I::IntoIter: Send,
362 {
363 Ok(Default::default())
364 }
365 }
366
367 #[test]
368 fn should_construct() {
369 let _c = CheckMocks {};
370 }
371}