1use std::error;
2use std::fmt;
3use std::str::{self, FromStr};
4
5use serde::{de, ser};
6
7#[derive(PartialEq, Clone)]
25pub struct Datetime {
26 date: Option<Date>,
27 time: Option<Time>,
28 offset: Option<Offset>,
29}
30
31#[derive(Debug, Clone)]
33pub struct DatetimeParseError {
34 _private: (),
35}
36
37pub const FIELD: &str = "$__toml_private_datetime";
44pub const NAME: &str = "$__toml_private_Datetime";
45
46#[derive(PartialEq, Clone)]
47struct Date {
48 year: u16,
49 month: u8,
50 day: u8,
51}
52
53#[derive(PartialEq, Clone)]
54struct Time {
55 hour: u8,
56 minute: u8,
57 second: u8,
58 nanosecond: u32,
59}
60
61#[derive(PartialEq, Clone)]
62enum Offset {
63 Z,
64 Custom { hours: i8, minutes: u8 },
65}
66
67impl fmt::Debug for Datetime {
68 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
69 fmt::Display::fmt(self, f)
70 }
71}
72
73impl fmt::Display for Datetime {
74 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
91impl fmt::Display for Date {
92 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
93 write!(f, "{:04}-{:02}-{:02}", self.year, self.month, self.day)
94 }
95}
96
97impl fmt::Display for Time {
98 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
108impl fmt::Display for Offset {
109 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
117impl FromStr for Datetime {
118 type Err = DatetimeParseError;
119
120 fn from_str(date: &str) -> Result<Datetime, DatetimeParseError> {
121 if date.len() < 3 {
128 return Err(DatetimeParseError { _private: () });
129 }
130 let mut offset_allowed = true;
131 let mut chars = date.chars();
132
133 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 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 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 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
301fn 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
308impl ser::Serialize for Datetime {
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
321impl<'de> de::Deserialize<'de> for Datetime {
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
353struct DatetimeKey;
354
355impl<'de> de::Deserialize<'de> for DatetimeKey {
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
386pub struct DatetimeFromString {
387 pub value: Datetime,
388}
389
390impl<'de> de::Deserialize<'de> for DatetimeFromString {
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
419impl fmt::Display for DatetimeParseError {
420 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
421 "failed to parse datetime".fmt(f)
422 }
423}
424
425impl error::Error for DatetimeParseError {}