1use bytes::parse_bytes;
2use std::convert::TryFrom;
3use std::io::Write;
4
5use DecodeError;
6use EncodeError;
7
8#[derive(Eq, PartialEq, Debug, Copy, Clone)]
9pub struct DhmTimestamp(u8, u8, u8);
10
11impl DhmTimestamp {
13 pub fn new(d: u8, h: u8, m: u8) -> Option<Self> {
14 if d <= 99 && h <= 99 && m <= 99 {
18 Some(Self(d, h, m))
19 } else {
20 None
21 }
22 }
23}
24
25impl TryFrom<Timestamp> for DhmTimestamp {
26 type Error = ();
27
28 fn try_from(t: Timestamp) -> Result<Self, ()> {
29 if let Timestamp::DDHHMM(d, h, m) = t {
30 Ok(Self(d, h, m))
31 } else {
32 Err(())
33 }
34 }
35}
36
37#[derive(Eq, PartialEq, Debug, Clone)]
38pub enum Timestamp {
39 DDHHMM(u8, u8, u8),
41 HHMMSS(u8, u8, u8),
43 Unsupported(Vec<u8>),
45}
46
47impl Timestamp {
48 pub fn new_dhm(d: u8, h: u8, m: u8) -> Option<Self> {
50 if d <= 99 && h <= 99 && m <= 99 {
51 Some(Self::DDHHMM(d, h, m))
52 } else {
53 None
54 }
55 }
56
57 pub fn new_hms(h: u8, m: u8, s: u8) -> Option<Self> {
59 if h <= 99 && m <= 99 && s <= 99 {
60 Some(Self::HHMMSS(h, m, s))
61 } else {
62 None
63 }
64 }
65
66 pub fn encode<W: Write>(&self, buf: &mut W) -> Result<(), EncodeError> {
67 match self {
68 Self::DDHHMM(d, h, m) => write!(buf, "{:02}{:02}{:02}z", d, h, m)?,
69 Self::HHMMSS(h, m, s) => write!(buf, "{:02}{:02}{:02}h", h, m, s)?,
70 Self::Unsupported(s) => buf.write_all(s)?,
71 };
72
73 Ok(())
74 }
75}
76
77impl TryFrom<&[u8]> for Timestamp {
78 type Error = DecodeError;
79
80 fn try_from(b: &[u8]) -> Result<Self, Self::Error> {
81 if b.len() != 7 {
82 return Err(DecodeError::InvalidTimestamp(b.to_owned()));
83 }
84
85 if b[6] == b'/' {
86 return Ok(Timestamp::Unsupported(b.to_owned()));
87 }
88
89 let one =
90 parse_bytes(&b[0..2]).ok_or_else(|| DecodeError::InvalidTimestamp(b.to_owned()))?;
91 let two =
92 parse_bytes(&b[2..4]).ok_or_else(|| DecodeError::InvalidTimestamp(b.to_owned()))?;
93 let three =
94 parse_bytes(&b[4..6]).ok_or_else(|| DecodeError::InvalidTimestamp(b.to_owned()))?;
95
96 Ok(match b[6] {
97 b'z' | b'Z' => Timestamp::DDHHMM(one, two, three),
98 b'h' | b'H' => Timestamp::HHMMSS(one, two, three),
99 _ => return Err(DecodeError::InvalidTimestamp(b.to_owned())),
100 })
101 }
102}
103
104impl From<DhmTimestamp> for Timestamp {
105 fn from(t: DhmTimestamp) -> Self {
106 Self::DDHHMM(t.0, t.1, t.2)
107 }
108}
109
110#[cfg(test)]
111mod tests {
112 use super::*;
113
114 #[test]
115 fn parse_ddhhmm() {
116 assert_eq!(
117 Timestamp::try_from(&b"123456z"[..]),
118 Ok(Timestamp::DDHHMM(12, 34, 56))
119 );
120
121 assert_eq!(
122 Timestamp::try_from(&b"123456Z"[..]),
123 Ok(Timestamp::DDHHMM(12, 34, 56))
124 );
125 }
126
127 #[test]
128 fn parse_hhmmss() {
129 assert_eq!(
130 Timestamp::try_from(&b"123456h"[..]),
131 Ok(Timestamp::HHMMSS(12, 34, 56))
132 );
133
134 assert_eq!(
135 Timestamp::try_from(&b"123456H"[..]),
136 Ok(Timestamp::HHMMSS(12, 34, 56))
137 );
138 }
139
140 #[test]
141 fn parse_local_time() {
142 assert_eq!(
143 Timestamp::try_from(&b"123456/"[..]),
144 Ok(Timestamp::Unsupported(b"123456/".to_vec()))
145 );
146 }
147
148 #[test]
149 fn invalid_timestamp() {
150 assert_eq!(
151 Timestamp::try_from(&b"1234567"[..]),
152 Err(DecodeError::InvalidTimestamp(b"1234567".to_vec()))
153 );
154 }
155
156 #[test]
157 fn invalid_timestamp2() {
158 assert_eq!(
159 Timestamp::try_from(&b"123a56z"[..]),
160 Err(DecodeError::InvalidTimestamp(b"123a56z".to_vec()))
161 );
162 }
163
164 #[test]
165 fn encode_ddhhmm() {
166 let mut buf = vec![];
167 Timestamp::DDHHMM(65, 43, 21).encode(&mut buf).unwrap();
168 assert_eq!(b"654321z"[..], buf);
169 }
170
171 #[test]
172 fn encode_hhmmss() {
173 let mut buf = vec![];
174 Timestamp::HHMMSS(65, 43, 21).encode(&mut buf).unwrap();
175 assert_eq!(b"654321h"[..], buf);
176 }
177
178 #[test]
179 fn encode_local_time() {
180 let mut buf = vec![];
181 Timestamp::Unsupported(b"135a67z".to_vec())
182 .encode(&mut buf)
183 .unwrap();
184 assert_eq!(b"135a67z"[..], buf);
185 }
186
187 #[test]
188 fn convert_dhm_timestamp_to_normal_timestamp() {
189 let timestamp: Timestamp = DhmTimestamp::new(12, 34, 56).unwrap().into();
190 assert_eq!(Timestamp::new_dhm(12, 34, 56).unwrap(), timestamp);
191 }
192
193 #[test]
194 fn convert_timestamp_to_dhm_timestamp_success() {
195 use std::convert::TryInto;
196
197 let timestamp = Timestamp::new_dhm(65, 43, 21).unwrap();
198 assert_eq!(
199 DhmTimestamp::new(65, 43, 21).unwrap(),
200 timestamp.try_into().unwrap()
201 );
202 }
203
204 #[test]
205 fn convert_timestamp_to_dhm_timestamp_failure() {
206 use std::convert::TryInto;
207
208 let timestamp = Timestamp::new_hms(65, 43, 21).unwrap();
209 let dhm: Result<DhmTimestamp, ()> = timestamp.try_into();
210 assert_eq!(Err(()), dhm);
211 }
212}