1use thiserror::Error;
2
3#[cfg(feature = "serde")]
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Error)]
8pub enum Error {
9 #[error("invalid domes format")]
10 InvalidFormat,
11 #[error("invalid domes length")]
12 InvalidLength,
13}
14
15#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
17#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
18pub enum TrackingPoint {
19 Monument,
21 Instrument,
26}
27
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
31#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
32pub struct DOMES {
33 pub area: u16,
35 pub site: u8,
37 pub point: TrackingPoint,
39 pub sequential: u16,
41}
42
43impl std::str::FromStr for DOMES {
44 type Err = Error;
45 fn from_str(s: &str) -> Result<Self, Self::Err> {
46 if s.len() == 9 {
47 let point = if s[5..6].eq("M") {
48 TrackingPoint::Monument
49 } else if s[5..6].eq("S") {
50 TrackingPoint::Instrument
51 } else {
52 return Err(Error::InvalidFormat);
53 };
54 let area = s[..3].parse::<u16>().map_err(|_| Error::InvalidFormat)?;
55 let site = s[3..5].parse::<u8>().map_err(|_| Error::InvalidFormat)?;
56 let sequential = s[6..].parse::<u16>().map_err(|_| Error::InvalidFormat)?;
57 Ok(Self {
58 point,
59 area,
60 site,
61 sequential,
62 })
63 } else {
64 Err(Error::InvalidLength)
65 }
66 }
67}
68
69impl std::fmt::Display for DOMES {
70 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
71 let point = match self.point {
72 TrackingPoint::Monument => 'M',
73 TrackingPoint::Instrument => 'S',
74 };
75 write!(
76 f,
77 "{:03}{:02}{}{:03}",
78 self.area, self.site, point, self.sequential
79 )
80 }
81}
82
83#[cfg(test)]
84mod test {
85 use super::{TrackingPoint, DOMES};
86 use std::str::FromStr;
87 #[test]
88 fn parser() {
89 for (descriptor, expected) in [
90 (
91 "10002M006",
92 DOMES {
93 area: 100,
94 site: 2,
95 sequential: 6,
96 point: TrackingPoint::Monument,
97 },
98 ),
99 (
100 "40405S031",
101 DOMES {
102 area: 404,
103 site: 5,
104 sequential: 31,
105 point: TrackingPoint::Instrument,
106 },
107 ),
108 ] {
109 let domes = DOMES::from_str(descriptor).unwrap();
110 assert_eq!(domes, expected, "failed to parse DOMES");
111 assert_eq!(domes.to_string(), descriptor, "DOMES reciprocal failed");
113 }
114 }
115}