1 use std::error;
2 use std::fmt;
3 use std::str::{self, FromStr};
4 
5 use serde::{de, ser};
6 
7 /// A parsed TOML datetime value
8 ///
9 /// This structure is intended to represent the datetime primitive type that can
10 /// be encoded into TOML documents. This type is a parsed version that contains
11 /// all metadata internally.
12 ///
13 /// Currently this type is intentionally conservative and only supports
14 /// `to_string` as an accessor. Over time though it's intended that it'll grow
15 /// more support!
16 ///
17 /// Note that if you're using `Deserialize` to deserialize a TOML document, you
18 /// can use this as a placeholder for where you're expecting a datetime to be
19 /// specified.
20 ///
21 /// Also note though that while this type implements `Serialize` and
22 /// `Deserialize` it's only recommended to use this type with the TOML format,
23 /// otherwise encoded in other formats it may look a little odd.
24 #[derive(PartialEq, Clone)]
25 pub struct Datetime {
26     date: Option<Date>,
27     time: Option<Time>,
28     offset: Option<Offset>,
29 }
30 
31 /// Error returned from parsing a `Datetime` in the `FromStr` implementation.
32 #[derive(Debug, Clone)]
33 pub struct DatetimeParseError {
34     _private: (),
35 }
36 
37 // Currently serde itself doesn't have a datetime type, so we map our `Datetime`
38 // to a special valid in the serde data model. Namely one with thiese special
39 // fields/struct names.
40 //
41 // In general the TOML encoder/decoder will catch this and not literally emit
42 // these strings but rather emit datetimes as they're intended.
43 pub const FIELD: &str = "$__toml_private_datetime";
44 pub const NAME: &str = "$__toml_private_Datetime";
45 
46 #[derive(PartialEq, Clone)]
47 struct Date {
48     year: u16,
49     month: u8,
50     day: u8,
51 }
52 
53 #[derive(PartialEq, Clone)]
54 struct Time {
55     hour: u8,
56     minute: u8,
57     second: u8,
58     nanosecond: u32,
59 }
60 
61 #[derive(PartialEq, Clone)]
62 enum Offset {
63     Z,
64     Custom { hours: i8, minutes: u8 },
65 }
66 
67 impl fmt::Debug for Datetime {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result68     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69         fmt::Display::fmt(self, f)
70     }
71 }
72 
73 impl fmt::Display for Datetime {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result74     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
75         if let Some(ref date) = self.date {
76             write!(f, "{}", date)?;
77         }
78         if let Some(ref time) = self.time {
79             if self.date.is_some() {
80                 write!(f, "T")?;
81             }
82             write!(f, "{}", time)?;
83         }
84         if let Some(ref offset) = self.offset {
85             write!(f, "{}", offset)?;
86         }
87         Ok(())
88     }
89 }
90 
91 impl fmt::Display for Date {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result92     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93         write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
94     }
95 }
96 
97 impl fmt::Display for Time {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result98     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
99         write!(f, "{:02}:{:02}:{:02}", self.hour, self.minute, self.second)?;
100         if self.nanosecond != 0 {
101             let s = format!("{:09}", self.nanosecond);
102             write!(f, ".{}", s.trim_end_matches('0'))?;
103         }
104         Ok(())
105     }
106 }
107 
108 impl fmt::Display for Offset {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result109     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110         match *self {
111             Offset::Z => write!(f, "Z"),
112             Offset::Custom { hours, minutes } => write!(f, "{:+03}:{:02}", hours, minutes),
113         }
114     }
115 }
116 
117 impl FromStr for Datetime {
118     type Err = DatetimeParseError;
119 
from_str(date: &str) -> Result<Datetime, DatetimeParseError>120     fn from_str(date: &str) -> Result<Datetime, DatetimeParseError> {
121         // Accepted formats:
122         //
123         // 0000-00-00T00:00:00.00Z
124         // 0000-00-00T00:00:00.00
125         // 0000-00-00
126         // 00:00:00.00
127         if date.len() < 3 {
128             return Err(DatetimeParseError { _private: () });
129         }
130         let mut offset_allowed = true;
131         let mut chars = date.chars();
132 
133         // First up, parse the full date if we can
134         let full_date = if chars.clone().nth(2) == Some(':') {
135             offset_allowed = false;
136             None
137         } else {
138             let y1 = u16::from(digit(&mut chars)?);
139             let y2 = u16::from(digit(&mut chars)?);
140             let y3 = u16::from(digit(&mut chars)?);
141             let y4 = u16::from(digit(&mut chars)?);
142 
143             match chars.next() {
144                 Some('-') => {}
145                 _ => return Err(DatetimeParseError { _private: () }),
146             }
147 
148             let m1 = digit(&mut chars)?;
149             let m2 = digit(&mut chars)?;
150 
151             match chars.next() {
152                 Some('-') => {}
153                 _ => return Err(DatetimeParseError { _private: () }),
154             }
155 
156             let d1 = digit(&mut chars)?;
157             let d2 = digit(&mut chars)?;
158 
159             let date = Date {
160                 year: y1 * 1000 + y2 * 100 + y3 * 10 + y4,
161                 month: m1 * 10 + m2,
162                 day: d1 * 10 + d2,
163             };
164 
165             if date.month < 1 || date.month > 12 {
166                 return Err(DatetimeParseError { _private: () });
167             }
168             if date.day < 1 || date.day > 31 {
169                 return Err(DatetimeParseError { _private: () });
170             }
171 
172             Some(date)
173         };
174 
175         // Next parse the "partial-time" if available
176         let next = chars.clone().next();
177         let partial_time = if full_date.is_some()
178             && (next == Some('T') || next == Some('t') || next == Some(' '))
179         {
180             chars.next();
181             true
182         } else {
183             full_date.is_none()
184         };
185 
186         let time = if partial_time {
187             let h1 = digit(&mut chars)?;
188             let h2 = digit(&mut chars)?;
189             match chars.next() {
190                 Some(':') => {}
191                 _ => return Err(DatetimeParseError { _private: () }),
192             }
193             let m1 = digit(&mut chars)?;
194             let m2 = digit(&mut chars)?;
195             match chars.next() {
196                 Some(':') => {}
197                 _ => return Err(DatetimeParseError { _private: () }),
198             }
199             let s1 = digit(&mut chars)?;
200             let s2 = digit(&mut chars)?;
201 
202             let mut nanosecond = 0;
203             if chars.clone().next() == Some('.') {
204                 chars.next();
205                 let whole = chars.as_str();
206 
207                 let mut end = whole.len();
208                 for (i, byte) in whole.bytes().enumerate() {
209                     match byte {
210                         b'0'..=b'9' => {
211                             if i < 9 {
212                                 let p = 10_u32.pow(8 - i as u32);
213                                 nanosecond += p * u32::from(byte - b'0');
214                             }
215                         }
216                         _ => {
217                             end = i;
218                             break;
219                         }
220                     }
221                 }
222                 if end == 0 {
223                     return Err(DatetimeParseError { _private: () });
224                 }
225                 chars = whole[end..].chars();
226             }
227 
228             let time = Time {
229                 hour: h1 * 10 + h2,
230                 minute: m1 * 10 + m2,
231                 second: s1 * 10 + s2,
232                 nanosecond,
233             };
234 
235             if time.hour > 24 {
236                 return Err(DatetimeParseError { _private: () });
237             }
238             if time.minute > 59 {
239                 return Err(DatetimeParseError { _private: () });
240             }
241             if time.second > 59 {
242                 return Err(DatetimeParseError { _private: () });
243             }
244             if time.nanosecond > 999_999_999 {
245                 return Err(DatetimeParseError { _private: () });
246             }
247 
248             Some(time)
249         } else {
250             offset_allowed = false;
251             None
252         };
253 
254         // And finally, parse the offset
255         let offset = if offset_allowed {
256             let next = chars.clone().next();
257             if next == Some('Z') || next == Some('z') {
258                 chars.next();
259                 Some(Offset::Z)
260             } else if next.is_none() {
261                 None
262             } else {
263                 let sign = match next {
264                     Some('+') => 1,
265                     Some('-') => -1,
266                     _ => return Err(DatetimeParseError { _private: () }),
267                 };
268                 chars.next();
269                 let h1 = digit(&mut chars)? as i8;
270                 let h2 = digit(&mut chars)? as i8;
271                 match chars.next() {
272                     Some(':') => {}
273                     _ => return Err(DatetimeParseError { _private: () }),
274                 }
275                 let m1 = digit(&mut chars)?;
276                 let m2 = digit(&mut chars)?;
277 
278                 Some(Offset::Custom {
279                     hours: sign * (h1 * 10 + h2),
280                     minutes: m1 * 10 + m2,
281                 })
282             }
283         } else {
284             None
285         };
286 
287         // Return an error if we didn't hit eof, otherwise return our parsed
288         // date
289         if chars.next().is_some() {
290             return Err(DatetimeParseError { _private: () });
291         }
292 
293         Ok(Datetime {
294             date: full_date,
295             time,
296             offset,
297         })
298     }
299 }
300 
digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError>301 fn digit(chars: &mut str::Chars<'_>) -> Result<u8, DatetimeParseError> {
302     match chars.next() {
303         Some(c) if '0' <= c && c <= '9' => Ok(c as u8 - b'0'),
304         _ => Err(DatetimeParseError { _private: () }),
305     }
306 }
307 
308 impl ser::Serialize for Datetime {
serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error> where S: ser::Serializer,309     fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
310     where
311         S: ser::Serializer,
312     {
313         use serde::ser::SerializeStruct;
314 
315         let mut s = serializer.serialize_struct(NAME, 1)?;
316         s.serialize_field(FIELD, &self.to_string())?;
317         s.end()
318     }
319 }
320 
321 impl<'de> de::Deserialize<'de> for Datetime {
deserialize<D>(deserializer: D) -> Result<Datetime, D::Error> where D: de::Deserializer<'de>,322     fn deserialize<D>(deserializer: D) -> Result<Datetime, D::Error>
323     where
324         D: de::Deserializer<'de>,
325     {
326         struct DatetimeVisitor;
327 
328         impl<'de> de::Visitor<'de> for DatetimeVisitor {
329             type Value = Datetime;
330 
331             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
332                 formatter.write_str("a TOML datetime")
333             }
334 
335             fn visit_map<V>(self, mut visitor: V) -> Result<Datetime, V::Error>
336             where
337                 V: de::MapAccess<'de>,
338             {
339                 let value = visitor.next_key::<DatetimeKey>()?;
340                 if value.is_none() {
341                     return Err(de::Error::custom("datetime key not found"));
342                 }
343                 let v: DatetimeFromString = visitor.next_value()?;
344                 Ok(v.value)
345             }
346         }
347 
348         static FIELDS: [&str; 1] = [FIELD];
349         deserializer.deserialize_struct(NAME, &FIELDS, DatetimeVisitor)
350     }
351 }
352 
353 struct DatetimeKey;
354 
355 impl<'de> de::Deserialize<'de> for DatetimeKey {
deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error> where D: de::Deserializer<'de>,356     fn deserialize<D>(deserializer: D) -> Result<DatetimeKey, D::Error>
357     where
358         D: de::Deserializer<'de>,
359     {
360         struct FieldVisitor;
361 
362         impl<'de> de::Visitor<'de> for FieldVisitor {
363             type Value = ();
364 
365             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
366                 formatter.write_str("a valid datetime field")
367             }
368 
369             fn visit_str<E>(self, s: &str) -> Result<(), E>
370             where
371                 E: de::Error,
372             {
373                 if s == FIELD {
374                     Ok(())
375                 } else {
376                     Err(de::Error::custom("expected field with custom name"))
377                 }
378             }
379         }
380 
381         deserializer.deserialize_identifier(FieldVisitor)?;
382         Ok(DatetimeKey)
383     }
384 }
385 
386 pub struct DatetimeFromString {
387     pub value: Datetime,
388 }
389 
390 impl<'de> de::Deserialize<'de> for DatetimeFromString {
deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error> where D: de::Deserializer<'de>,391     fn deserialize<D>(deserializer: D) -> Result<DatetimeFromString, D::Error>
392     where
393         D: de::Deserializer<'de>,
394     {
395         struct Visitor;
396 
397         impl<'de> de::Visitor<'de> for Visitor {
398             type Value = DatetimeFromString;
399 
400             fn expecting(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
401                 formatter.write_str("string containing a datetime")
402             }
403 
404             fn visit_str<E>(self, s: &str) -> Result<DatetimeFromString, E>
405             where
406                 E: de::Error,
407             {
408                 match s.parse() {
409                     Ok(date) => Ok(DatetimeFromString { value: date }),
410                     Err(e) => Err(de::Error::custom(e)),
411                 }
412             }
413         }
414 
415         deserializer.deserialize_str(Visitor)
416     }
417 }
418 
419 impl fmt::Display for DatetimeParseError {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result420     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421         "failed to parse datetime".fmt(f)
422     }
423 }
424 
425 impl error::Error for DatetimeParseError {}
426