gnss_rs/
cospar.rs

1//! COSPAR (Launch) ID number
2use thiserror::Error;
3
4#[cfg(feature = "serde")]
5use serde::{Deserialize, Serialize};
6
7#[derive(Debug, Error)]
8pub enum Error {
9    #[error("Invalid COSPAR number")]
10    InvalidFormat,
11}
12
13/// COSPAR ID number
14#[derive(Debug, Clone, PartialEq, PartialOrd)]
15#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
16pub struct COSPAR {
17    /// Launch year
18    year: u16,
19    /// Launch number for that year, in chronological order.
20    launch: u16,
21    /// Up to three letter code representing the sequential
22    /// identifier of a piece in a Launch.
23    code: String,
24}
25
26impl std::fmt::Display for COSPAR {
27    fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
28        write!(f, "{:04}-{:03}{}", self.year, self.launch, self.code)
29    }
30}
31
32impl std::str::FromStr for COSPAR {
33    type Err = Error;
34    fn from_str(s: &str) -> Result<Self, Self::Err> {
35        if s.len() < 9 {
36            return Err(Error::InvalidFormat);
37        }
38        let offset = s.find('-').ok_or(Error::InvalidFormat)?;
39        let (year, rem) = s.split_at(offset);
40        let year = year.parse::<u16>().map_err(|_| Error::InvalidFormat)?;
41        let launch = rem[1..4]
42            .trim()
43            .parse::<u16>()
44            .map_err(|_| Error::InvalidFormat)?;
45        Ok(Self {
46            year,
47            launch,
48            code: rem[4..].trim().to_string(),
49        })
50    }
51}
52
53#[cfg(test)]
54mod test {
55    use crate::cospar::COSPAR;
56    use std::str::FromStr;
57    #[test]
58    fn cospar() {
59        for (desc, expected) in [
60            (
61                "2018-080A",
62                COSPAR {
63                    year: 2018,
64                    launch: 80,
65                    code: "A".to_string(),
66                },
67            ),
68            (
69                "1996-068A",
70                COSPAR {
71                    year: 1996,
72                    launch: 68,
73                    code: "A".to_string(),
74                },
75            ),
76        ] {
77            let cospar = COSPAR::from_str(desc).unwrap();
78            assert_eq!(cospar, expected);
79            let recip = cospar.to_string();
80            assert_eq!(recip, desc, "cospar reciprocal");
81        }
82    }
83}