use thiserror::Error;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "python")]
mod python;
#[cfg(feature = "python")]
use pyo3::prelude::pyclass;
#[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 = "python", pyclass)]
#[cfg_attr(feature = "python", pyo3(module = "gnss"))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum TrackingPoint {
Monument,
Instrument,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "python", pyclass)]
#[cfg_attr(feature = "python", pyo3(module = "gnss"))]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct DOMES {
pub area: u16,
pub site: u8,
pub point: TrackingPoint,
pub sequential: u16,
}
impl core::fmt::LowerHex for TrackingPoint {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
TrackingPoint::Monument => write!(f, "M"),
TrackingPoint::Instrument => write!(f, "S"),
}
}
}
impl core::fmt::Display for TrackingPoint {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
match self {
TrackingPoint::Monument => write!(f, "Monument"),
TrackingPoint::Instrument => write!(f, "Instrument"),
}
}
}
impl core::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 core::fmt::Display for DOMES {
fn fmt(&self, f: &mut core::fmt::Formatter) -> core::fmt::Result {
write!(
f,
"{:03}{:02}{:x}{:03}",
self.area, self.site, self.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");
}
}
}