dmx_struct/
lib.rs

1//! A module for a struct that holds dmx-address information.
2//!
3//! **dmx-struct is in pre-release state. Any breaking changes may be implemented without further notice!**
4//!
5//! ## Description
6//!
7//! This is a simple crate that contains a struct `DMXAddress`. The struct implements the trait `TryFrom` that understands notation with dot (eg. 1.234, 5.231) and absolute dmx addresses to parse.
8//!
9//! The module is designed to never panic but return `DMXParseError` instead
10//!
11//! The module holds both, the absolute address and the address separated by universe and address so no calculation needed for further oparations
12//!
13//! ## Usage
14//! The main struct `DMXAddress` implements the trait `TryFrom<&str>` so usage is straight forward:
15//!
16//! ```crate
17//! [dependencies]
18//! dmx-struct = "0.1.0"
19//! ```
20//!
21//! ### Example try from
22//!
23//!```rust
24//! use std::convert::TryFrom;
25//!
26//! use dmx_struct::{DMXAddress, DMXParseError};
27//!
28//! fn test() {
29//!     let dmx_address: Result<DMXAddress, DMXParseError> = DMXAddress::try_from("1.511");
30//!     let dmx_address: Result<DMXAddress, DMXParseError> = DMXAddress::try_from("1024");
31//! }
32//! ```
33//!
34//! ### Example try into
35//!
36//! ```rust
37//! use std::convert::TryInto;
38//!
39//! use dmx_struct::{DMXAddress, DMXParseError};
40//!
41//! fn test() {
42//!     let dmx_address: Result<DMXAddress, DMXParseError> = "1.511".try_into();
43//!     let dmx_address: Result<DMXAddress, DMXParseError> = "1024".try_into();
44//! }
45//! ```
46
47use std::convert::{TryFrom, TryInto};
48use std::error::Error;
49use std::fmt::{Display, Formatter};
50use std::str::FromStr;
51
52#[cfg(test)]
53mod doc_test;
54
55///This Error is return if an invalid &str is tried to be deparsed as dmx-address instead of panicing
56#[derive(Debug)]
57pub struct DMXParseError;
58
59impl std::fmt::Display for DMXParseError {
60    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
61        write!(f, "something went terribly wrong")
62    }
63}
64
65impl Error for DMXParseError {}
66
67///DMXAddress color representation used in GDTF
68#[derive(Debug)]
69pub struct DMXAddress {
70    ///Universe of the DMXAddress starting from 1
71    pub universe: u16,
72    ///The address in the dmx universe (1-512)
73    pub address: u16,
74    ///The absolute dmx address including the universe (1-32767488)
75    pub absolute: u32,
76}
77
78impl TryFrom<&str> for DMXAddress {
79    type Error = DMXParseError;
80
81    fn try_from(value: &str) -> Result<Self, Self::Error> {
82        let universe;
83        let address;
84        let absolute;
85
86        if value.contains(".") {
87            //The input is of format 1.234
88            //Splitting the input by .
89            let value: Vec<&str> = value.split(".").collect();
90            //Only one . allowed in this format
91            if value.len() != 2 { return Err(DMXParseError {}); }
92            //Value before . is universe
93            universe = u32::from_str(value[0]).or_else(|_| Err(DMXParseError {}))?;
94            //If the universe is 0, the input was not valid
95            if universe == 0 { return Err(DMXParseError {}); }
96            //Value after . is address
97            address = u32::from_str(value[1]).or_else(|_| Err(DMXParseError {}))?;
98            //calculating the absolute address from universe and address
99            absolute = address + ((universe - 1) * 512);
100        } else {
101            //The input holds the absolute address
102            absolute = u32::from_str(value).or_else(|_| { Err(DMXParseError {}) })?;
103            //Calculating the address from the absolute address
104            let x = absolute % 512;
105            //Special case if the address is 512 the % operator will return 0 but should return 512 because dmx starts counting at 1
106            address = if x > 0 { x } else { 512 };
107            if x > 0 {
108                //If address was not 512 adding one to the universe because dmx starts counting at 1
109                universe = (absolute / 512) + 1;
110            } else {
111                //If address was 512 not adding one to the universe because dmx starts counting at 1
112                universe = absolute / 512;
113            }
114        }
115        //Some dmx validity checks.
116        //63'999 is max number of universes supported by sACN
117        //dmx address is max 512 by definition
118        //address 0 and universe 0 are not valid. Start count at 1
119        if universe > 63_999 || address > 512 || address == 0 || universe == 0 {
120            return Err(DMXParseError {});
121        }
122        Ok(DMXAddress {
123            universe: universe.try_into().unwrap(),
124            address: address.try_into().unwrap(),
125            absolute: absolute,
126        })
127    }
128}
129
130///Dmx addresses can be compared with ==
131impl PartialEq for DMXAddress {
132    fn eq(&self, other: &Self) -> bool {
133        self.universe == other.universe && self.address == other.address && self.absolute == other.absolute
134    }
135}
136
137///Dmx addresses can be used in format with {}. It will return the format 'universe.address'
138impl Display for DMXAddress {
139    fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
140        write!(f, "{}.{:03}", self.universe, self.address)
141    }
142}
143
144///Some tests
145#[cfg(test)]
146mod tests {
147    use std::convert::TryFrom;
148
149    use crate::DMXAddress;
150
151    #[test]
152    fn test_valid_separated() {
153        assert_eq!(
154            DMXAddress { universe: 4, address: 465, absolute: 2001 },
155            DMXAddress::try_from("4.465").unwrap()
156        );
157    }
158
159    #[test]
160    fn test_valid_separated_2() {
161        assert_eq!(
162            DMXAddress { universe: 5, address: 1, absolute: 2049 },
163            DMXAddress::try_from("5.1").unwrap()
164        );
165    }
166
167    #[test]
168    fn test_valid_separated_3() {
169        assert_eq!(
170            DMXAddress { universe: 4, address: 512, absolute: 2048 },
171            DMXAddress::try_from("4.512").unwrap()
172        );
173    }
174
175    #[test]
176    fn test_valid_separated_4() {
177        assert_eq!(
178            DMXAddress { universe: 2, address: 4, absolute: 516 },
179            DMXAddress::try_from("2.4").unwrap()
180        );
181    }
182
183    #[test]
184    fn test_valid_separated_5() {
185        assert_eq!(
186            DMXAddress { universe: 1, address: 1, absolute: 1 },
187            DMXAddress::try_from("1.1").unwrap()
188        );
189    }
190
191    #[test]
192    fn test_valid_separated_6() {
193        assert_eq!(
194            DMXAddress { universe: 2, address: 1, absolute: 513 },
195            DMXAddress::try_from("2.1").unwrap()
196        );
197    }
198
199    #[test]
200    fn test_valid_separated_7() {
201        assert_eq!(
202            DMXAddress { universe: 1, address: 512, absolute: 512 },
203            DMXAddress::try_from("1.512").unwrap()
204        );
205    }
206
207    #[test]
208    fn test_valid_single() {
209        assert_eq!(
210            DMXAddress { universe: 1, address: 224, absolute: 224 },
211            DMXAddress::try_from("224").unwrap()
212        );
213    }
214
215    #[test]
216    fn test_valid_single_2() {
217        assert_eq!(
218            DMXAddress { universe: 3, address: 210, absolute: 1234 },
219            DMXAddress::try_from("1234").unwrap()
220        );
221    }
222
223    #[test]
224    fn test_valid_single_3() {
225        assert_eq!(
226            DMXAddress { universe: 4, address: 1, absolute: 1537 },
227            DMXAddress::try_from("1537").unwrap()
228        );
229    }
230
231    #[test]
232    fn test_valid_single_4() {
233        assert_eq!(
234            DMXAddress { universe: 3, address: 512, absolute: 1536 },
235            DMXAddress::try_from("1536").unwrap()
236        );
237    }
238
239
240    #[test]
241    fn test_valid_single_5() {
242        assert_eq!(
243            DMXAddress { universe: 2, address: 1, absolute: 513 },
244            DMXAddress::try_from("513").unwrap()
245        );
246    }
247
248    #[test]
249    fn test_valid_single_6() {
250        assert_eq!(
251            DMXAddress { universe: 1, address: 512, absolute: 512 },
252            DMXAddress::try_from("512").unwrap()
253        );
254    }
255
256    #[test]
257    fn test_valid_single_7() {
258        assert_eq!(
259            DMXAddress { universe: 256, address: 512, absolute: 131072 },
260            DMXAddress::try_from("131072").unwrap()
261        );
262    }
263
264    #[test]
265    fn test_invalid_1() {
266        match DMXAddress::try_from("something invalid") {
267            Ok(_) => { panic!("test_invalid should return an error"); }
268            Err(_) => {}
269        }
270    }
271
272    #[test]
273    fn test_invalid_2() {
274        match DMXAddress::try_from("2.") {
275            Ok(_) => { panic!("test_invalid should return an error"); }
276            Err(_) => {}
277        }
278    }
279
280    #[test]
281    fn test_invalid_3() {
282        match DMXAddress::try_from(".2") {
283            Ok(_) => { panic!("test_invalid should return an error"); }
284            Err(_) => {}
285        }
286    }
287
288    #[test]
289    fn test_invalid_4() {
290        match DMXAddress::try_from(".") {
291            Ok(_) => { panic!("test_invalid should return an error"); }
292            Err(_) => {}
293        }
294    }
295
296    #[test]
297    fn test_invalid_5() {
298        match DMXAddress::try_from("0.1") {
299            Ok(_) => { panic!("test_invalid should return an error"); }
300            Err(_) => {}
301        }
302    }
303
304    #[test]
305    fn test_invalid_6() {
306        match DMXAddress::try_from("2.0") {
307            Ok(_) => { panic!("test_invalid should return an error"); }
308            Err(_) => {}
309        }
310    }
311
312    #[test]
313    fn test_invalid_7() {
314        match DMXAddress::try_from("0.0") {
315            Ok(_) => { panic!("test_invalid should return an error"); }
316            Err(_) => {}
317        }
318    }
319
320    #[test]
321    fn test_invalid_8() {
322        match DMXAddress::try_from("2.513") {
323            Ok(_) => { panic!("test_invalid should return an error"); }
324            Err(_) => {}
325        }
326    }
327
328    #[test]
329    fn test_invalid_9() {
330        match DMXAddress::try_from("63999.513") {
331            Ok(_) => { panic!("test_invalid should return an error"); }
332            Err(_) => {}
333        }
334    }
335
336    #[test]
337    fn test_invalid_10() {
338        match DMXAddress::try_from("0") {
339            Ok(_) => { panic!("test_invalid should return an error"); }
340            Err(_) => {}
341        }
342    }
343
344    #[test]
345    fn test_invalid_11() {
346        match DMXAddress::try_from("98981265123519681981681514984984984464984984") {
347            Ok(_) => { panic!("test_invalid should return an error"); }
348            Err(_) => {}
349        }
350    }
351
352    #[test]
353    fn test_invalid_12() {
354        match DMXAddress::try_from("-3") {
355            Ok(_) => { panic!("test_invalid should return an error"); }
356            Err(_) => {}
357        }
358    }
359
360    #[test]
361    fn test_invalid_13() {
362        match DMXAddress::try_from("-1.3") {
363            Ok(_) => { panic!("test_invalid should return an error"); }
364            Err(_) => {}
365        }
366    }
367
368    #[test]
369    fn test_invalid_14() {
370        match DMXAddress::try_from("1.-3") {
371            Ok(_) => { panic!("test_invalid should return an error"); }
372            Err(_) => {}
373        }
374    }
375
376    #[test]
377    fn test_invalid_15() {
378        match DMXAddress::try_from("-1.-4") {
379            Ok(_) => { panic!("test_invalid should return an error"); }
380            Err(_) => {}
381        }
382    }
383
384    #[test]
385    fn test_display() {
386        assert_eq!(format!("{}", DMXAddress { universe: 1, address: 342, absolute: 342 }), "1.342");
387    }
388
389    #[test]
390    fn test_display_2() {
391        assert_eq!(format!("{}", DMXAddress { universe: 1, address: 12, absolute: 12 }), "1.012");
392    }
393
394    #[test]
395    fn test_display_3() {
396        assert_eq!(format!("{}", DMXAddress { universe: 1, address: 9, absolute: 9 }), "1.009");
397    }
398}