elastic_types/date/
format.rs

1use chrono::{self, NaiveDate, NaiveDateTime, NaiveTime, Utc};
2use chrono::format::{DelayedFormat, Item};
3use std::ops::Deref;
4use std::marker::PhantomData;
5use std::borrow::Borrow;
6use std::error::Error;
7use std::fmt::{Display, Formatter, Result as FmtResult};
8use std::vec::IntoIter;
9use super::ChronoDateTime;
10
11/** 
12A date value produced and consumed by date formats.
13
14`DateValue` is a very thin wrapper over `DateTime<Utc>` that doesn't carry any formatting semantics.
15Like `FormattableDateValue`, this type is used for binding generics in methods that accept date values but it ignores any format on the input type.
16You probably won't need to use it directly except to clobber the format on a `Date<M>` or `DateTime<Utc>` value.
17*/
18#[derive(Debug, Clone, PartialEq)]
19pub struct DateValue(ChronoDateTime);
20
21impl DateValue {
22    /** Equivalent to `DateTime<Utc>::now()` */
23    pub fn now() -> Self {
24        DateValue(Utc::now())
25    }
26
27    /** Construct a `DateValue` from individual parts. */
28    pub fn build(year: i32, month: u32, day: u32, hour: u32, minute: u32, second: u32, milli: u32) -> Self {
29        let ndate = NaiveDate::from_ymd(year, month, day);
30        let ntime = NaiveTime::from_hms_milli(hour, minute, second, milli);
31
32        let date = ChronoDateTime::from_utc(NaiveDateTime::new(ndate, ntime), Utc);
33
34        DateValue(date)
35    }
36}
37
38impl<TFormat> From<FormattableDateValue<TFormat>> for DateValue {
39    fn from(date: FormattableDateValue<TFormat>) -> Self {
40        date.0
41    }
42}
43
44impl From<ChronoDateTime> for DateValue {
45    fn from(date: ChronoDateTime) -> Self {
46        DateValue(date)
47    }
48}
49
50impl PartialEq<ChronoDateTime> for DateValue {
51    fn eq(&self, other: &ChronoDateTime) -> bool {
52        PartialEq::eq(&self.0, other)
53    }
54
55    fn ne(&self, other: &ChronoDateTime) -> bool {
56        PartialEq::ne(&self.0, other)
57    }
58}
59
60impl PartialEq<DateValue> for ChronoDateTime {
61    fn eq(&self, other: &DateValue) -> bool {
62        PartialEq::eq(self, &other.0)
63    }
64
65    fn ne(&self, other: &DateValue) -> bool {
66        PartialEq::ne(self, &other.0)
67    }
68}
69
70impl Borrow<ChronoDateTime> for DateValue {
71    fn borrow(&self) -> &ChronoDateTime {
72        &self.0
73    }
74}
75
76impl Deref for DateValue {
77    type Target = ChronoDateTime;
78
79    fn deref(&self) -> &Self::Target {
80        &self.0
81    }
82}
83
84/** 
85A date value paired with a format.
86
87`FormattableDateValue<F>` bundles a `DateValue` with a specific format and is used to ensure the formats of mappable date types aren't accidentally changed.
88Like `DateValue`, this type is used for binding generics in methods that accept date values but it requires the input type uses a specific format.
89You probably don't need to use it directly except to ensure date formats aren't silently changed.
90*/
91#[derive(Debug, Clone, PartialEq)]
92pub struct FormattableDateValue<TFormat>(DateValue, PhantomData<TFormat>);
93
94impl<TFormat> FormattableDateValue<TFormat>
95where
96    TFormat: DateFormat,
97{
98    /** Format the wrapped date value using the generic format. */
99    pub fn format<'a>(&'a self) -> FormattedDate<'a> {
100        TFormat::format(&self.0)
101    }
102
103    /** Parse a date value using the generic format. */
104    pub fn parse(date: &str) -> Result<Self, ParseError> {
105        let date = TFormat::parse(date)?;
106
107        Ok(FormattableDateValue::from(date))
108    }
109}
110
111impl<TFormat> From<DateValue> for FormattableDateValue<TFormat> {
112    fn from(date: DateValue) -> Self {
113        FormattableDateValue(date.into(), PhantomData)
114    }
115}
116
117impl<TFormat> Borrow<ChronoDateTime> for FormattableDateValue<TFormat> {
118    fn borrow(&self) -> &ChronoDateTime {
119        self.0.borrow()
120    }
121}
122
123impl<TFormat> PartialEq<ChronoDateTime> for FormattableDateValue<TFormat> {
124    fn eq(&self, other: &ChronoDateTime) -> bool {
125        PartialEq::eq(&self.0, other)
126    }
127
128    fn ne(&self, other: &ChronoDateTime) -> bool {
129        PartialEq::ne(&self.0, other)
130    }
131}
132
133impl<TFormat> PartialEq<FormattableDateValue<TFormat>> for ChronoDateTime {
134    fn eq(&self, other: &FormattableDateValue<TFormat>) -> bool {
135        PartialEq::eq(self, &other.0)
136    }
137
138    fn ne(&self, other: &FormattableDateValue<TFormat>) -> bool {
139        PartialEq::ne(self, &other.0)
140    }
141}
142
143/**
144A format used for parsing and formatting dates.
145
146The format is specified as two functions: `parse` and `format`.
147A general `DateValue` is used as an intermediate value passed as input and produced as output for formatting.
148
149# Examples
150
151The easiest way to implement `DateFormat` is to derive `ElasticDateFormat`
152on a unit struct:
153
154```
155# #[macro_use]
156# extern crate elastic_types;
157# #[macro_use]
158# extern crate elastic_types_derive;
159# extern crate chrono;
160# use elastic_types::prelude::*;
161# fn main() {
162#[derive(Default, ElasticDateFormat)]
163#[elastic(date_format="yyyy-MM-dd'T'HH:mm:ss")]
164struct MyFormat;
165# }
166```
167
168The `#[elastic(date_format)]` attribute is required,
169and must contain a valid [format string](http://www.joda.org/joda-time/apidocs/org/joda/time/format/DateTimeFormat.html).
170
171> NOTE: Only a small subset of the Joda time format is supported.
172
173You can customise the indexed format name by adding an `#[elastic(date_format_name)]` attribute:
174
175```
176# #[macro_use]
177# extern crate elastic_types;
178# #[macro_use]
179# extern crate elastic_types_derive;
180# extern crate chrono;
181# use elastic_types::prelude::*;
182# fn main() {
183#[derive(Default, ElasticDateFormat)]
184#[elastic(date_format="yyyyMMdd'T'HHmmssZ", date_format_name="basic_date_time_no_millis")]
185struct MyFormat;
186# }
187```
188*/
189pub trait DateFormat
190where
191    Self: Default,
192{
193    /** Parses a date string to a `chrono::DateTime<Utc>` result. */
194    fn parse(date: &str) -> Result<DateValue, ParseError>;
195
196    /** Formats a given `chrono::DateTime<Utc>` as a string. */
197    fn format<'a>(date: &'a DateValue) -> FormattedDate<'a>;
198
199    /**
200    The name of the format.
201    
202    This is the string used when defining the format in the field mapping.
203    */
204    fn name() -> &'static str;
205}
206
207/**
208A formatted date.
209
210This type can avoid allocating strings for date formats.
211*/
212pub struct FormattedDate<'a> {
213    inner: FormattedDateInner<'a>,
214}
215
216enum FormattedDateInner<'a> {
217    Delayed(DelayedFormat<IntoIter<Item<'a>>>),
218    Buffered(String),
219    Number(i64),
220}
221
222impl<'a> Display for FormattedDateInner<'a> {
223    fn fmt(&self, f: &mut Formatter) -> FmtResult {
224        fn fmt_inner<T>(inner: &T, f: &mut Formatter) -> FmtResult
225        where
226            T: Display,
227        {
228            inner.fmt(f)
229        }
230
231        match *self {
232            FormattedDateInner::Delayed(ref inner) => fmt_inner(inner, f),
233            FormattedDateInner::Buffered(ref inner) => fmt_inner(inner, f),
234            FormattedDateInner::Number(ref inner) => fmt_inner(inner, f),
235        }
236    }
237}
238
239impl<'a> Display for FormattedDate<'a> {
240    fn fmt(&self, f: &mut Formatter) -> FmtResult {
241        self.inner.fmt(f)
242    }
243}
244
245impl<'a> From<DelayedFormat<IntoIter<Item<'a>>>> for FormattedDate<'a> {
246    fn from(formatted: DelayedFormat<IntoIter<Item<'a>>>) -> Self {
247        FormattedDate {
248            inner: FormattedDateInner::Delayed(formatted),
249        }
250    }
251}
252
253impl<'a> From<String> for FormattedDate<'a> {
254    fn from(formatted: String) -> Self {
255        FormattedDate {
256            inner: FormattedDateInner::Buffered(formatted),
257        }
258    }
259}
260
261impl<'a> From<i64> for FormattedDate<'a> {
262    fn from(formatted: i64) -> Self {
263        FormattedDate {
264            inner: FormattedDateInner::Number(formatted),
265        }
266    }
267}
268
269/** Represents an error encountered during parsing. */
270#[derive(Debug)]
271pub struct ParseError {
272    kind: ParseErrorKind,
273}
274
275#[derive(Debug)]
276enum ParseErrorKind {
277    Chrono(chrono::ParseError),
278    Other(String),
279}
280
281impl Display for ParseError {
282    fn fmt(&self, f: &mut Formatter) -> FmtResult {
283        match self.kind {
284            ParseErrorKind::Chrono(ref err) => write!(f, "Chrono error: {}", err),
285            ParseErrorKind::Other(ref err) => write!(f, "Error: {}", err),
286        }
287    }
288}
289
290impl Error for ParseError {
291    fn description(&self) -> &str {
292        match self.kind {
293            ParseErrorKind::Chrono(ref err) => err.description(),
294            ParseErrorKind::Other(ref err) => &err[..],
295        }
296    }
297
298    fn cause(&self) -> Option<&Error> {
299        match self.kind {
300            ParseErrorKind::Chrono(ref err) => Some(err),
301            ParseErrorKind::Other(_) => None,
302        }
303    }
304}
305
306impl From<chrono::ParseError> for ParseError {
307    fn from(err: chrono::ParseError) -> ParseError {
308        ParseError {
309            kind: ParseErrorKind::Chrono(err),
310        }
311    }
312}
313
314impl From<String> for ParseError {
315    fn from(err: String) -> ParseError {
316        ParseError {
317            kind: ParseErrorKind::Other(err),
318        }
319    }
320}