1use super::*;
2use chrono::NaiveTime;
3use core::cmp::Ordering;
4
5impl Edtf {
17 pub fn sort_order(&self) -> SortOrder {
34 SortOrder(*self)
35 }
36
37 pub fn sort_order_start(&self) -> SortOrderStart {
53 SortOrderStart(edtf_start_date(self))
54 }
55
56 pub fn sort_order_end(&self) -> SortOrderEnd {
72 SortOrderEnd(edtf_end_date(self))
73 }
74}
75
76#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
78#[cfg(feature = "chrono")]
79#[derive(PartialEq, Eq, PartialOrd, Ord)]
80pub struct SortOrderStart(Infinite);
81
82#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
84#[cfg(feature = "chrono")]
85#[derive(PartialEq, Eq, PartialOrd, Ord)]
86pub struct SortOrderEnd(Infinite);
87
88#[cfg_attr(docsrs, doc(cfg(feature = "chrono")))]
90#[cfg(feature = "chrono")]
91pub struct SortOrder(Edtf);
92
93impl Ord for SortOrder {
94 fn cmp(&self, other: &Self) -> Ordering {
95 sort_order(&self.0, &other.0)
96 }
97}
98
99impl PartialOrd for SortOrder {
100 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
101 Some(self.cmp(other))
102 }
103}
104
105impl PartialEq for SortOrder {
106 fn eq(&self, other: &Self) -> bool {
107 self.cmp(other).is_eq()
108 }
109}
110
111impl Eq for SortOrder {}
112
113fn sort_order(a: &Edtf, b: &Edtf) -> Ordering {
114 edtf_start_date(a)
115 .cmp(&edtf_start_date(b))
116 .then_with(|| edtf_end_date(a).cmp(&edtf_end_date(b)))
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
120enum Infinite {
121 NegativeInfinity,
122 YYearNegative(i64),
123 Value(Date, NaiveTime),
124 YYearPositive(i64),
125 Infinity,
126}
127
128impl From<Date> for Infinite {
129 fn from(d: Date) -> Self {
130 Self::Value(d, NaiveTime::from_num_seconds_from_midnight(0, 0))
131 }
132}
133
134fn edtf_start_date(edtf: &Edtf) -> Infinite {
135 use self::Infinite::*;
136 match *edtf {
137 Edtf::Date(d) => d.into(),
138 Edtf::Interval(d, _) => d.into(),
139 Edtf::IntervalFrom(d, _) => d.into(),
140 Edtf::IntervalTo(_, _) => NegativeInfinity,
142 Edtf::DateTime(d) => {
143 let tz = chrono::Utc;
144 let dt = d.to_chrono(&tz);
145 let date = dt.date().naive_utc();
146 let time = dt.time();
147 Value(date.into(), time)
148 }
149 Edtf::YYear(y) => {
150 let val = y.value();
151 if val < i32::MIN as i64 {
152 YYearNegative(val)
153 } else if val > i32::MAX as i64 {
154 YYearPositive(val)
155 } else {
156 let v32 = val as i32;
157 if !Date::year_in_range(v32) {
158 if v32 < 0 {
159 YYearNegative(val)
160 } else {
161 YYearPositive(val)
162 }
163 } else {
164 Date::from_ymd(v32, 0, 0).into()
165 }
166 }
167 }
168 }
169}
170
171fn edtf_end_date(edtf: &Edtf) -> Infinite {
172 use self::Infinite::*;
173 match *edtf {
174 Edtf::Date(_) | Edtf::DateTime(_) | Edtf::YYear(_) => edtf_start_date(edtf),
175 Edtf::Interval(_, d) => d.into(),
176 Edtf::IntervalFrom(_, _) => Infinity,
177 Edtf::IntervalTo(_, d) => d.into(),
178 }
179}
180
181#[cfg(test)]
182fn cmp(a: &str, b: &str) -> Ordering {
183 sort_order(&Edtf::parse(a).unwrap(), &Edtf::parse(b).unwrap())
184}
185
186#[test]
187fn test_cmp_single() {
188 assert_eq!(cmp("2009", "2010"), Ordering::Less);
189 assert_eq!(cmp("2011", "2010"), Ordering::Greater);
190 assert_eq!(cmp("2010", "2010"), Ordering::Equal);
191 assert_eq!(cmp("2010-08", "2010"), Ordering::Greater);
192 assert_eq!(cmp("2010-08", "2010-09"), Ordering::Less);
193 assert_eq!(cmp("2010-08", "2010-08"), Ordering::Equal);
194}
195
196#[test]
197fn test_cmp_single_interval() {
198 assert_eq!(cmp("2009", "2010/2011"), Ordering::Less);
199 assert_eq!(cmp("2011", "2009/2011"), Ordering::Greater);
200 assert_eq!(cmp("2010", "2010/2010"), Ordering::Equal);
201 assert_eq!(cmp("2010", "2010/2011"), Ordering::Less);
202 assert_eq!(cmp("2010-08", "2010/2011"), Ordering::Greater);
203}
204
205#[test]
206fn test_cmp_double_interval() {
207 assert_eq!(cmp("2009/2011", "2010/2011"), Ordering::Less);
212 assert_eq!(cmp("2009/2011", "2011/2012",), Ordering::Less);
215 assert_eq!(cmp("2009/2011", "2010/2012"), Ordering::Less);
218 assert_eq!(cmp("2009/2011", "2012/2013"), Ordering::Less);
221 assert_eq!(cmp("2009/2011", "2009-03/2010-07"), Ordering::Less);
224}
225
226#[test]
227fn test_cmp_double_interval_open() {
228 assert_eq!(cmp("../2011", "2010/2011"), Ordering::Less);
233 assert_eq!(cmp("../2011", "../2011"), Ordering::Equal);
236 assert_eq!(cmp("../2011", "2010/2011"), Ordering::Less);
239 assert_eq!(cmp("2010/..", "2010/2011",), Ordering::Greater);
244}
245
246#[test]
247fn test_cmp_yyear() {
248 assert_eq!(cmp("Y-10000", "2010"), Ordering::Less);
249 assert_eq!(cmp("Y10000", "2010"), Ordering::Greater);
250 assert_eq!(cmp("Y10000", "2010/.."), Ordering::Greater);
251 assert_eq!(cmp("Y-10000", "2010/.."), Ordering::Less);
252 assert_eq!(cmp("Y-10000", "../2010"), Ordering::Greater);
253}
254
255#[test]
256fn test_cmp_datetime() {
257 assert_eq!(cmp("2010-08-12T00:00:00Z", "2010-08-12"), Ordering::Equal);
258 assert_eq!(
259 cmp("2010-08-12T00:00:00Z", "2010-08-12T00:00:05Z"),
260 Ordering::Less
261 );
262 assert_eq!(
263 cmp("2010-08-12T00:00:00Z", "2010-08-12T00:00:00-01:00"),
264 Ordering::Less
265 );
266 assert_eq!(
269 cmp("2010-08-12T23:50:00-01:00", "2010-08-13"),
270 Ordering::Greater
271 );
272}