1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
//! Describes `leap` second information, contained in `header`
use hifitime::TimeScale;
use thiserror::Error;

/// `Leap` to describe leap seconds.
/// GLO = UTC = GPS - ΔtLS   
/// GPS = UTC + ΔtLS   
#[derive(Default, Copy, Clone, PartialEq, Eq, Debug)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct Leap {
    /// current number
    pub leap: u32,
    /// ΔtLS : "future or past leap second(s)",
    /// actual number of leap seconds between GPS/GAL and GLO,
    /// or BDS and UTC.
    pub delta_tls: Option<u32>,
    /// weeks counter
    pub week: Option<u32>,
    /// days counter
    pub day: Option<u32>,
    pub timescale: Option<TimeScale>,
}

/// `Leap` parsing related errors
#[derive(Error, Debug)]
pub enum Error {
    #[error("failed to parse leap integer number")]
    ParseIntError(#[from] std::num::ParseIntError),
    #[error("failed to parse leap timescale")]
    TimeScaleError(#[from] hifitime::Errors),
}

impl Leap {
    /// Builds a new `Leap` object to describe leap seconds
    pub fn new(
        leap: u32,
        delta_tls: Option<u32>,
        week: Option<u32>,
        day: Option<u32>,
        timescale: Option<TimeScale>,
    ) -> Self {
        Self {
            leap,
            delta_tls,
            week,
            day,
            timescale,
        }
    }
}

impl std::str::FromStr for Leap {
    type Err = Error;
    /// Builds `Leap` from standard RINEX descriptor
    fn from_str(s: &str) -> Result<Self, Self::Err> {
        let mut ls = Leap::default();
        // leap second has two format
        let items: Vec<&str> = s.split_ascii_whitespace().collect();
        match items.len() > 2 {
            false => {
                // [1] simple format: basic
                ls.leap = items[0].trim().parse::<u32>()?;
            },
            true => {
                // [2] complex format: advanced infos
                let (leap, rem) = s.split_at(5);
                let (tls, rem) = rem.split_at(5);
                let (week, rem) = rem.split_at(5);
                let (day, rem) = rem.split_at(5);
                let system = rem.trim();
                ls.leap = leap.trim().parse::<u32>()?;
                ls.delta_tls = Some(tls.trim().parse::<u32>()?);
                ls.week = Some(week.trim().parse::<u32>()?);
                ls.day = Some(day.trim().parse::<u32>()?);
                if system.eq("") {
                    ls.timescale = None
                } else {
                    ls.timescale = Some(TimeScale::from_str(system)?)
                }
            },
        }
        Ok(ls)
    }
}

#[cfg(test)]
mod test {
    use super::*;
    use std::str::FromStr;
    #[test]
    fn basic_format() {
        let content = "18";
        let leap = Leap::from_str(content);
        assert!(leap.is_ok());
        let leap = leap.unwrap();
        assert_eq!(leap.leap, 18);
    }
    #[test]
    fn standard_format() {
        let content = "18    18  2185     7";
        let leap = Leap::from_str(content);
        assert!(leap.is_ok());
        let leap = leap.unwrap();
        assert_eq!(leap.leap, 18);
        assert_eq!(leap.week, Some(2185));
        assert_eq!(leap.day, Some(7));
    }
    #[test]
    fn parse_with_timescale() {
        let content = "18    18  2185     7GPS";
        let leap = Leap::from_str(content);
        assert!(leap.is_ok());
        let leap = leap.unwrap();
        assert_eq!(leap.leap, 18);
        assert_eq!(leap.week, Some(2185));
        assert_eq!(leap.day, Some(7));
    }
}