1use 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#[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#[derive(Debug)]
69pub struct DMXAddress {
70 pub universe: u16,
72 pub address: u16,
74 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 let value: Vec<&str> = value.split(".").collect();
90 if value.len() != 2 { return Err(DMXParseError {}); }
92 universe = u32::from_str(value[0]).or_else(|_| Err(DMXParseError {}))?;
94 if universe == 0 { return Err(DMXParseError {}); }
96 address = u32::from_str(value[1]).or_else(|_| Err(DMXParseError {}))?;
98 absolute = address + ((universe - 1) * 512);
100 } else {
101 absolute = u32::from_str(value).or_else(|_| { Err(DMXParseError {}) })?;
103 let x = absolute % 512;
105 address = if x > 0 { x } else { 512 };
107 if x > 0 {
108 universe = (absolute / 512) + 1;
110 } else {
111 universe = absolute / 512;
113 }
114 }
115 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
130impl 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
137impl 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#[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}