use thiserror::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[derive(Debug, Error)]
pub enum Error {
#[error("invalid domes format")]
InvalidFormat,
#[error("invalid domes length")]
InvalidLength,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TrackingPoint {
Monument,
Instrument,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DOMES {
pub area: u16,
pub site: u8,
pub point: TrackingPoint,
pub sequential: u16,
}
impl std::str::FromStr for DOMES {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() == 9 {
let point = if s[5..6].eq("M") {
TrackingPoint::Monument
} else if s[5..6].eq("S") {
TrackingPoint::Instrument
} else {
return Err(Error::InvalidFormat);
};
let area = s[..3].parse::<u16>().map_err(|_| Error::InvalidFormat)?;
let site = s[3..5].parse::<u8>().map_err(|_| Error::InvalidFormat)?;
let sequential = s[6..].parse::<u16>().map_err(|_| Error::InvalidFormat)?;
Ok(Self {
point,
area,
site,
sequential,
})
} else {
Err(Error::InvalidLength)
}
}
}
impl std::fmt::Display for DOMES {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
let point = match self.point {
TrackingPoint::Monument => 'M',
TrackingPoint::Instrument => 'S',
};
write!(
f,
"{:03}{:02}{}{:03}",
self.area, self.site, point, self.sequential
)
}
}
#[cfg(test)]
mod test {
use super::{TrackingPoint, DOMES};
use std::str::FromStr;
#[test]
fn parser() {
for (descriptor, expected) in [
(
"10002M006",
DOMES {
area: 100,
site: 2,
sequential: 6,
point: TrackingPoint::Monument,
},
),
(
"40405S031",
DOMES {
area: 404,
site: 5,
sequential: 31,
point: TrackingPoint::Instrument,
},
),
] {
let domes = DOMES::from_str(descriptor).unwrap();
assert_eq!(domes, expected, "failed to parse DOMES");
assert_eq!(domes.to_string(), descriptor, "DOMES reciprocal failed");
}
}
}