lightspeed_astro/
lib.rs

1pub mod props {
2    include!("lightspeed.props.rs");
3}
4
5pub mod devices {
6    include!("lightspeed.devices.rs");
7    pub mod actions {
8        include!("lightspeed.devices.actions.rs");
9    }
10}
11
12pub mod request {
13    include!("lightspeed.request.rs");
14}
15
16pub mod response {
17    include!("lightspeed.response.rs");
18}
19
20pub mod service {
21    include!("lightspeed.service.rs");
22}
23
24pub mod proto {
25    pub const FD_DESCRIPTOR_SET: &[u8] = include_bytes!("lightspeed.bin");
26}
27
28pub mod properties {
29    use serde::{Deserialize, Serialize};
30
31    #[derive(Debug)]
32    pub enum PermissionError {
33        CannotUpdateReadOnlyProp,
34    }
35
36    #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
37    pub enum Permission {
38        ReadOnly = 0,
39        ReadWrite,
40    }
41
42    #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
43    struct Range<T> {
44        min: T,
45        max: T,
46    }
47
48    impl<T> Range<T> {
49        fn new(min: T, max: T) -> Self {
50            Self { min, max }
51        }
52
53        fn max(&self) -> &T {
54            &self.max
55        }
56
57        fn min(&self) -> &T {
58            &self.min
59        }
60    }
61
62    #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
63    #[serde(deny_unknown_fields)]
64    pub struct Property<T> {
65        #[serde(rename = "value")]
66        val: T,
67        permission: Permission,
68        range: Option<Range<T>>,
69    }
70
71    impl<T> Property<T> {
72        pub fn new(value: T, permission: Permission, range: Option<(T, T)>) -> Self {
73            if let Some(r) = range {
74                return Self {
75                    val: value,
76                    permission,
77                    range: Some(Range::new(r.0, r.1)),
78                };
79            } else {
80                return Self {
81                    val: value,
82                    permission,
83                    range: None,
84                };
85            }
86        }
87
88        pub fn value(&self) -> &T {
89            &self.val
90        }
91
92        fn update_allowed(&self) -> Result<(), PermissionError> {
93            if self.permission == Permission::ReadOnly {
94                Err(PermissionError::CannotUpdateReadOnlyProp)
95            } else {
96                Ok(())
97            }
98        }
99
100        /// This method should be called when the client requests to update the value
101        /// so it must go through permissions checks.
102        pub fn update(&mut self, value: T) -> Result<(), PermissionError> {
103            self.update_allowed()?;
104            self.val = value;
105            Ok(())
106        }
107
108        /// This method should be called when the driver wants to update its internal state periodically.
109        /// This way no matter if a prop is read only or not, the driver can proced.
110        pub fn set_val(&mut self, value: T) {
111            self.val = value;
112        }
113    }
114
115    #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
116    pub struct BoolProperty {
117        value: bool,
118        permission: Permission,
119    }
120
121    impl BoolProperty {
122        pub fn new(value: bool, permission: Permission) -> Self {
123            Self { value, permission }
124        }
125
126        fn update_allowed(&self) -> Result<(), PermissionError> {
127            if self.permission == Permission::ReadOnly {
128                Err(PermissionError::CannotUpdateReadOnlyProp)
129            } else {
130                Ok(())
131            }
132        }
133
134        pub fn update(&mut self) -> Result<(), PermissionError> {
135            self.update_allowed()?;
136            self.value = !self.value;
137            Ok(())
138        }
139    }
140
141    #[cfg(test)]
142    mod unit_tests {
143        use super::{BoolProperty, Permission, Property};
144        use std::borrow::Cow;
145
146        #[test]
147        fn test_bool_prop_initialization() {
148            let p = BoolProperty::new(false, Permission::ReadOnly);
149            assert_eq!(p.value, false);
150        }
151
152        #[test]
153        fn test_bool_prop_readonly_cannot_be_updated() -> Result<(), String> {
154            let mut p = BoolProperty::new(false, Permission::ReadOnly);
155            match p.update() {
156                Ok(()) => Err(String::from(
157                    "It should not be possible to update a ReadOnly property",
158                )),
159                Err(_) => {
160                    assert_eq!(p.value, false);
161                    Ok(())
162                }
163            }
164        }
165
166        #[test]
167        fn test_bool_prop_readwrite_can_be_written() -> Result<(), String> {
168            let mut p = BoolProperty::new(false, Permission::ReadWrite);
169            match p.update() {
170                Ok(()) => {
171                    assert_eq!(p.value, true);
172                    Ok(())
173                }
174                Err(_) => Err(String::from(
175                    "It MUST be possible to update a WriteOnly property",
176                )),
177            }
178        }
179
180        #[test]
181        fn test_numeric_prop_readonly_cannot_be_updated() -> Result<(), String> {
182            let mut p: Property<u64> = Property::new(78, Permission::ReadOnly, None);
183            match p.update(55) {
184                Ok(()) => Err(String::from(
185                    "It should not be possible to update a ReadOnly property",
186                )),
187                Err(_) => {
188                    assert_eq!(p.value(), &78_u64);
189                    Ok(())
190                }
191            }
192        }
193
194        #[test]
195        fn test_numeric_prop_readwrite_can_be_written() -> Result<(), String> {
196            let mut p = Property::<u64>::new(78, Permission::ReadWrite, None);
197            match p.update(55) {
198                Ok(()) => {
199                    assert_eq!(p.value(), &55_u64);
200                    Ok(())
201                }
202                Err(_) => Err(String::from(
203                    "It MUST be possible to update a WriteOnly property",
204                )),
205            }
206        }
207
208        #[test]
209        fn test_cow_borrow_prop_readwrite_can_be_written() -> Result<(), String> {
210            let mut p = Property::<Cow<'static, str>>::new(
211                Cow::Borrowed("test"),
212                Permission::ReadWrite,
213                None,
214            );
215            match p.update(Cow::Borrowed("done")) {
216                Ok(()) => {
217                    assert_eq!(p.value(), "done");
218                    Ok(())
219                }
220                Err(_) => Err(String::from(
221                    "It MUST be possible to update a WriteOnly property",
222                )),
223            }
224        }
225
226        #[test]
227        fn test_cow_owned_prop_readwrite_can_be_written() -> Result<(), String> {
228            let mut p = Property::<Cow<'static, str>>::new(
229                Cow::Owned(String::from("test")),
230                Permission::ReadWrite,
231                None,
232            );
233            match p.update(Cow::Owned(String::from("done"))) {
234                Ok(()) => {
235                    assert_eq!(p.value(), "done");
236                    Ok(())
237                }
238                Err(_) => Err(String::from(
239                    "It MUST be possible to update a WriteOnly property",
240                )),
241            }
242        }
243
244        #[test]
245        fn test_str_prop_initialization_no_range() {
246            let test_str = String::from("test");
247            let p = Property::new(test_str.clone(), Permission::ReadOnly, None);
248            assert_eq!(p.value(), &test_str);
249        }
250
251        #[test]
252        fn test_num_prop_initialization_no_range() {
253            let test_val = 5;
254            let p = Property::new(test_val.clone(), Permission::ReadOnly, None);
255            assert_eq!(p.value(), &test_val);
256        }
257
258        #[test]
259        fn test_float_prop_initialization_no_range() {
260            let test_val = 5.32_f64;
261            let p = Property::new(test_val.clone(), Permission::ReadOnly, None);
262            assert_eq!(p.value(), &test_val);
263        }
264
265        #[test]
266        fn test_float_prop_initialization_range() {
267            let test_val = 5.32_f64;
268            let min_range = 10.0_f64;
269            let max_range = 100.0_f64;
270            let p = Property::new(
271                test_val.clone(),
272                Permission::ReadOnly,
273                Some((min_range.clone(), max_range.clone())),
274            );
275            assert_eq!(p.range.unwrap().min(), &min_range);
276            assert_eq!(p.range.unwrap().max(), &max_range);
277        }
278    }
279
280    #[cfg(test)]
281    mod serialization_tests {
282        use super::{BoolProperty, Permission, Property};
283
284        #[test]
285        fn test_serialize_num_prop_no_range() {
286            let p = Property::new(5, Permission::ReadOnly, None);
287            let serialized = serde_json::to_string(&p).unwrap();
288            assert_eq!(
289                serialized,
290                r#"{"value":5,"permission":"ReadOnly","range":null}"#
291            );
292        }
293
294        #[test]
295        fn test_serialize_num_prop_with_range() {
296            let p = Property::new(5, Permission::ReadOnly, Some((1, 100)));
297            let serialized = serde_json::to_string(&p).unwrap();
298            assert_eq!(
299                serialized,
300                r#"{"value":5,"permission":"ReadOnly","range":{"min":1,"max":100}}"#
301            );
302        }
303
304        #[test]
305        fn test_serialize_bool_prop() {
306            let p = BoolProperty::new(true, Permission::ReadOnly);
307            let serialized = serde_json::to_string(&p).unwrap();
308            assert_eq!(serialized, r#"{"value":true,"permission":"ReadOnly"}"#);
309        }
310    }
311}