]> git.proxmox.com Git - rustc.git/blame - src/test/ui/impl-trait/example-calendar.rs
New upstream version 1.49.0~beta.4+dfsg1
[rustc.git] / src / test / ui / impl-trait / example-calendar.rs
CommitLineData
b7449926 1// run-pass
29967ef6 2// ignore-compare-mode-chalk
b7449926 3
0531ce1d 4#![feature(fn_traits,
abe05a73 5 step_trait,
f9f354fc 6 step_trait_ext,
2c00a5a8 7 unboxed_closures,
abe05a73 8)]
5bcae85e
SL
9
10//! Derived from: <https://raw.githubusercontent.com/quickfur/dcal/master/dcal.d>.
11//!
12//! Originally converted to Rust by [Daniel Keep](https://github.com/DanielKeep).
13
14use std::fmt::Write;
5bcae85e
SL
15
16/// Date representation.
17#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
18struct NaiveDate(i32, u32, u32);
19
20impl NaiveDate {
21 pub fn from_ymd(y: i32, m: u32, d: u32) -> NaiveDate {
22 assert!(1 <= m && m <= 12, "m = {:?}", m);
23 assert!(1 <= d && d <= NaiveDate(y, m, 1).days_in_month(), "d = {:?}", d);
24 NaiveDate(y, m, d)
25 }
26
27 pub fn year(&self) -> i32 {
28 self.0
29 }
30
31 pub fn month(&self) -> u32 {
32 self.1
33 }
34
35 pub fn day(&self) -> u32 {
36 self.2
37 }
38
39 pub fn succ(&self) -> NaiveDate {
40 let (mut y, mut m, mut d, n) = (
41 self.year(), self.month(), self.day()+1, self.days_in_month());
42 if d > n {
43 d = 1;
44 m += 1;
45 }
46 if m > 12 {
47 m = 1;
48 y += 1;
49 }
50 NaiveDate::from_ymd(y, m, d)
51 }
52
53 pub fn weekday(&self) -> Weekday {
54 use Weekday::*;
55
56 // 0 = Sunday
57 let year = self.year();
58 let dow_jan_1 = (year*365 + ((year-1) / 4) - ((year-1) / 100) + ((year-1) / 400)) % 7;
59 let dow = (dow_jan_1 + (self.day_of_year() as i32 - 1)) % 7;
60 [Sun, Mon, Tue, Wed, Thu, Fri, Sat][dow as usize]
61 }
62
63 pub fn isoweekdate(&self) -> (i32, u32, Weekday) {
64 let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday();
65
66 // Work out this date's DOtY and week number, not including year adjustment.
67 let doy_0 = self.day_of_year() - 1;
68 let mut week_mon_0: i32 = ((first_dow_mon_0 + doy_0) / 7) as i32;
69
70 if self.first_week_in_prev_year() {
71 week_mon_0 -= 1;
72 }
73
74 let weeks_in_year = self.last_week_number();
75
76 // Work out the final result.
0731742a 77 // If the week is `-1` or `>= weeks_in_year`, we will need to adjust the year.
5bcae85e
SL
78 let year = self.year();
79 let wd = self.weekday();
80
81 if week_mon_0 < 0 {
82 (year - 1, NaiveDate::from_ymd(year - 1, 1, 1).last_week_number(), wd)
83 } else if week_mon_0 >= weeks_in_year as i32 {
84 (year + 1, (week_mon_0 + 1 - weeks_in_year as i32) as u32, wd)
85 } else {
86 (year, (week_mon_0 + 1) as u32, wd)
87 }
88 }
89
90 fn first_week_in_prev_year(&self) -> bool {
91 let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday();
92
93 // Any day in the year *before* the first Monday of that year
94 // is considered to be in the last week of the previous year,
95 // assuming the first week has *less* than four days in it.
96 // Adjust the week appropriately.
97 ((7 - first_dow_mon_0) % 7) < 4
98 }
99
100 fn year_first_day_of_week(&self) -> Weekday {
101 NaiveDate::from_ymd(self.year(), 1, 1).weekday()
102 }
103
104 fn weeks_in_year(&self) -> u32 {
105 let days_in_last_week = self.year_first_day_of_week().num_days_from_monday() + 1;
106 if days_in_last_week >= 4 { 53 } else { 52 }
107 }
108
109 fn last_week_number(&self) -> u32 {
110 let wiy = self.weeks_in_year();
111 if self.first_week_in_prev_year() { wiy - 1 } else { wiy }
112 }
113
114 fn day_of_year(&self) -> u32 {
115 (1..self.1).map(|m| NaiveDate::from_ymd(self.year(), m, 1).days_in_month())
116 .fold(0, |a,b| a+b) + self.day()
117 }
118
119 fn is_leap_year(&self) -> bool {
120 let year = self.year();
121 if year % 4 != 0 {
122 return false
123 } else if year % 100 != 0 {
124 return true
125 } else if year % 400 != 0 {
126 return false
127 } else {
128 return true
129 }
130 }
131
132 fn days_in_month(&self) -> u32 {
133 match self.month() {
134 /* Jan */ 1 => 31,
135 /* Feb */ 2 => if self.is_leap_year() { 29 } else { 28 },
136 /* Mar */ 3 => 31,
137 /* Apr */ 4 => 30,
138 /* May */ 5 => 31,
139 /* Jun */ 6 => 30,
140 /* Jul */ 7 => 31,
141 /* Aug */ 8 => 31,
142 /* Sep */ 9 => 30,
143 /* Oct */ 10 => 31,
144 /* Nov */ 11 => 30,
145 /* Dec */ 12 => 31,
146 _ => unreachable!()
147 }
148 }
149}
150
151impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate {
152 type Output = NaiveDate;
153
154 fn add(self, other: &'b NaiveDate) -> NaiveDate {
155 assert_eq!(*other, NaiveDate(0, 0, 1));
156 self.succ()
157 }
158}
159
f9f354fc 160unsafe impl std::iter::Step for NaiveDate {
041b39d2 161 fn steps_between(_: &Self, _: &Self) -> Option<usize> {
5bcae85e
SL
162 unimplemented!()
163 }
164
f9f354fc
XL
165 fn forward_checked(start: Self, n: usize) -> Option<Self> {
166 Some((0..n).fold(start, |x, _| x.succ()))
5bcae85e
SL
167 }
168
f9f354fc 169 fn backward_checked(_: Self, _: usize) -> Option<Self> {
dc9dc135
XL
170 unimplemented!()
171 }
5bcae85e
SL
172}
173
174#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
175pub enum Weekday {
176 Mon,
177 Tue,
178 Wed,
179 Thu,
180 Fri,
181 Sat,
182 Sun,
183}
184
185impl Weekday {
186 pub fn num_days_from_monday(&self) -> u32 {
187 use Weekday::*;
188 match *self {
189 Mon => 0,
190 Tue => 1,
191 Wed => 2,
192 Thu => 3,
193 Fri => 4,
194 Sat => 5,
195 Sun => 6,
196 }
197 }
198
199 pub fn num_days_from_sunday(&self) -> u32 {
200 use Weekday::*;
201 match *self {
202 Sun => 0,
203 Mon => 1,
204 Tue => 2,
205 Wed => 3,
206 Thu => 4,
207 Fri => 5,
208 Sat => 6,
209 }
210 }
211}
212
0731742a 213/// `GroupBy` implementation.
5bcae85e
SL
214struct GroupBy<It: Iterator, F> {
215 it: std::iter::Peekable<It>,
216 f: F,
217}
218
219impl<It, F> Clone for GroupBy<It, F>
2c00a5a8
XL
220where
221 It: Iterator + Clone,
222 It::Item: Clone,
223 F: Clone,
224{
225 fn clone(&self) -> Self {
5bcae85e
SL
226 GroupBy {
227 it: self.it.clone(),
2c00a5a8 228 f: self.f.clone(),
5bcae85e
SL
229 }
230 }
231}
232
233impl<'a, G, It: 'a, F: 'a> Iterator for GroupBy<It, F>
234where It: Iterator + Clone,
235 It::Item: Clone,
236 F: Clone + FnMut(&It::Item) -> G,
237 G: Eq + Clone
238{
239 type Item = (G, InGroup<std::iter::Peekable<It>, F, G>);
240
241 fn next(&mut self) -> Option<Self::Item> {
242 self.it.peek().map(&mut self.f).map(|key| {
243 let start = self.it.clone();
244 while let Some(k) = self.it.peek().map(&mut self.f) {
245 if key != k {
246 break;
247 }
248 self.it.next();
249 }
250
251 (key.clone(), InGroup {
252 it: start,
253 f: self.f.clone(),
254 g: key
255 })
256 })
257 }
258}
259
260#[derive(Copy, Clone)]
261struct InGroup<It, F, G> {
262 it: It,
263 f: F,
264 g: G
265}
266
267impl<It: Iterator, F: FnMut(&It::Item) -> G, G: Eq> Iterator for InGroup<It, F, G> {
268 type Item = It::Item;
269
270 fn next(&mut self) -> Option<It::Item> {
271 self.it.next().and_then(|x| {
272 if (self.f)(&x) == self.g { Some(x) } else { None }
273 })
274 }
275}
276
277trait IteratorExt: Iterator + Sized {
2c00a5a8
XL
278 fn group_by<G, F>(self, f: F) -> GroupBy<Self, F>
279 where F: Clone + FnMut(&Self::Item) -> G,
5bcae85e
SL
280 G: Eq
281 {
2c00a5a8 282 GroupBy { it: self.peekable(), f }
5bcae85e
SL
283 }
284
285 fn join(mut self, sep: &str) -> String
286 where Self::Item: std::fmt::Display {
287 let mut s = String::new();
288 if let Some(e) = self.next() {
13cf67c4 289 write!(s, "{}", e).unwrap();
5bcae85e
SL
290 for e in self {
291 s.push_str(sep);
13cf67c4 292 write!(s, "{}", e).unwrap();
5bcae85e
SL
293 }
294 }
295 s
296 }
297
0731742a 298 // HACK(eddyb): only needed because `impl Trait` can't be
5bcae85e
SL
299 // used with trait methods: `.foo()` becomes `.__(foo)`.
300 fn __<F, R>(self, f: F) -> R
301 where F: FnOnce(Self) -> R {
302 f(self)
303 }
304}
305
306impl<It> IteratorExt for It where It: Iterator {}
307
0731742a 308/// Generates an iterator that yields exactly `n` spaces.
5bcae85e
SL
309fn spaces(n: usize) -> std::iter::Take<std::iter::Repeat<char>> {
310 std::iter::repeat(' ').take(n)
311}
312
313fn test_spaces() {
314 assert_eq!(spaces(0).collect::<String>(), "");
315 assert_eq!(spaces(10).collect::<String>(), " ")
316}
317
5bcae85e 318/// Returns an iterator of dates in a given year.
5bcae85e
SL
319fn dates_in_year(year: i32) -> impl Iterator<Item=NaiveDate>+Clone {
320 InGroup {
321 it: NaiveDate::from_ymd(year, 1, 1)..,
2c00a5a8 322 f: |d: &NaiveDate| d.year(),
5bcae85e
SL
323 g: year
324 }
325}
326
327fn test_dates_in_year() {
328 {
329 let mut dates = dates_in_year(2013);
330 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 1)));
331
0731742a 332 // Check increment.
5bcae85e
SL
333 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 2)));
334
0731742a 335 // Check monthly roll-over.
5bcae85e
SL
336 for _ in 3..31 {
337 assert!(dates.next() != None);
338 }
339
340 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 31)));
341 assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 2, 1)));
342 }
343
344 {
0731742a 345 // Check length of year.
5bcae85e
SL
346 let mut dates = dates_in_year(2013);
347 for _ in 0..365 {
348 assert!(dates.next() != None);
349 }
350 assert_eq!(dates.next(), None);
351 }
352
353 {
0731742a 354 // Check length of leap year.
5bcae85e
SL
355 let mut dates = dates_in_year(1984);
356 for _ in 0..366 {
357 assert!(dates.next() != None);
358 }
359 assert_eq!(dates.next(), None);
360 }
361}
362
5bcae85e
SL
363/// Convenience trait for verifying that a given type iterates over
364/// `NaiveDate`s.
5bcae85e
SL
365trait DateIterator: Iterator<Item=NaiveDate> + Clone {}
366impl<It> DateIterator for It where It: Iterator<Item=NaiveDate> + Clone {}
367
368fn test_group_by() {
369 let input = [
370 [1, 1],
371 [1, 1],
372 [1, 2],
373 [2, 2],
374 [2, 3],
375 [2, 3],
376 [3, 3]
377 ];
378
379 let by_x = input.iter().cloned().group_by(|a| a[0]);
380 let expected_1: &[&[[i32; 2]]] = &[
381 &[[1, 1], [1, 1], [1, 2]],
382 &[[2, 2], [2, 3], [2, 3]],
383 &[[3, 3]]
384 ];
385 for ((_, a), b) in by_x.zip(expected_1.iter().cloned()) {
386 assert_eq!(&a.collect::<Vec<_>>()[..], b);
387 }
388
389 let by_y = input.iter().cloned().group_by(|a| a[1]);
390 let expected_2: &[&[[i32; 2]]] = &[
391 &[[1, 1], [1, 1]],
392 &[[1, 2], [2, 2]],
393 &[[2, 3], [2, 3], [3, 3]]
394 ];
395 for ((_, a), b) in by_y.zip(expected_2.iter().cloned()) {
396 assert_eq!(&a.collect::<Vec<_>>()[..], b);
397 }
398}
399
5bcae85e 400/// Groups an iterator of dates by month.
abe05a73 401fn by_month(it: impl Iterator<Item=NaiveDate> + Clone)
0731742a 402 -> impl Iterator<Item=(u32, impl Iterator<Item=NaiveDate> + Clone)> + Clone
abe05a73 403{
5bcae85e
SL
404 it.group_by(|d| d.month())
405}
406
407fn test_by_month() {
408 let mut months = dates_in_year(2013).__(by_month);
409 for (month, (_, mut date)) in (1..13).zip(&mut months) {
410 assert_eq!(date.nth(0).unwrap(), NaiveDate::from_ymd(2013, month, 1));
411 }
412 assert!(months.next().is_none());
413}
414
5bcae85e 415/// Groups an iterator of dates by week.
abe05a73
XL
416fn by_week(it: impl DateIterator)
417 -> impl Iterator<Item=(u32, impl DateIterator)> + Clone
418{
5bcae85e
SL
419 // We go forward one day because `isoweekdate` considers the week to start on a Monday.
420 it.group_by(|d| d.succ().isoweekdate().1)
421}
422
423fn test_isoweekdate() {
424 fn weeks_uniq(year: i32) -> Vec<((i32, u32), u32)> {
425 let mut weeks = dates_in_year(year).map(|d| d.isoweekdate())
426 .map(|(y,w,_)| (y,w));
427 let mut result = vec![];
428 let mut accum = (weeks.next().unwrap(), 1);
429 for yw in weeks {
430 if accum.0 == yw {
431 accum.1 += 1;
432 } else {
433 result.push(accum);
434 accum = (yw, 1);
435 }
436 }
437 result.push(accum);
438 result
439 }
440
441 let wu_1984 = weeks_uniq(1984);
442 assert_eq!(&wu_1984[..2], &[((1983, 52), 1), ((1984, 1), 7)]);
443 assert_eq!(&wu_1984[wu_1984.len()-2..], &[((1984, 52), 7), ((1985, 1), 1)]);
444
445 let wu_2013 = weeks_uniq(2013);
446 assert_eq!(&wu_2013[..2], &[((2013, 1), 6), ((2013, 2), 7)]);
447 assert_eq!(&wu_2013[wu_2013.len()-2..], &[((2013, 52), 7), ((2014, 1), 2)]);
448
449 let wu_2015 = weeks_uniq(2015);
450 assert_eq!(&wu_2015[..2], &[((2015, 1), 4), ((2015, 2), 7)]);
451 assert_eq!(&wu_2015[wu_2015.len()-2..], &[((2015, 52), 7), ((2015, 53), 4)]);
452}
453
454fn test_by_week() {
455 let mut weeks = dates_in_year(2013).__(by_week);
456 assert_eq!(
457 &*weeks.next().unwrap().1.collect::<Vec<_>>(),
458 &[
459 NaiveDate::from_ymd(2013, 1, 1),
460 NaiveDate::from_ymd(2013, 1, 2),
461 NaiveDate::from_ymd(2013, 1, 3),
462 NaiveDate::from_ymd(2013, 1, 4),
463 NaiveDate::from_ymd(2013, 1, 5),
464 ]
465 );
466 assert_eq!(
467 &*weeks.next().unwrap().1.collect::<Vec<_>>(),
468 &[
469 NaiveDate::from_ymd(2013, 1, 6),
470 NaiveDate::from_ymd(2013, 1, 7),
471 NaiveDate::from_ymd(2013, 1, 8),
472 NaiveDate::from_ymd(2013, 1, 9),
473 NaiveDate::from_ymd(2013, 1, 10),
474 NaiveDate::from_ymd(2013, 1, 11),
475 NaiveDate::from_ymd(2013, 1, 12),
476 ]
477 );
478 assert_eq!(weeks.next().unwrap().1.nth(0).unwrap(), NaiveDate::from_ymd(2013, 1, 13));
479}
480
481/// The number of columns per day in the formatted output.
482const COLS_PER_DAY: u32 = 3;
483
484/// The number of columns per week in the formatted output.
485const COLS_PER_WEEK: u32 = 7 * COLS_PER_DAY;
486
5bcae85e 487/// Formats an iterator of weeks into an iterator of strings.
abe05a73 488fn format_weeks(it: impl Iterator<Item = impl DateIterator>) -> impl Iterator<Item=String> {
5bcae85e
SL
489 it.map(|week| {
490 let mut buf = String::with_capacity((COLS_PER_DAY * COLS_PER_WEEK + 2) as usize);
491
492 // Format each day into its own cell and append to target string.
493 let mut last_day = 0;
494 let mut first = true;
495 for d in week {
496 last_day = d.weekday().num_days_from_sunday();
497
498 // Insert enough filler to align the first day with its respective day-of-week.
499 if first {
500 buf.extend(spaces((COLS_PER_DAY * last_day) as usize));
501 first = false;
502 }
503
13cf67c4 504 write!(buf, " {:>2}", d.day()).unwrap();
5bcae85e
SL
505 }
506
507 // Insert more filler at the end to fill up the remainder of the week,
0731742a 508 // if its a short week (e.g., at the end of the month).
5bcae85e
SL
509 buf.extend(spaces((COLS_PER_DAY * (6 - last_day)) as usize));
510 buf
511 })
512}
513
514fn test_format_weeks() {
515 let jan_2013 = dates_in_year(2013)
516 .__(by_month).next() // pick January 2013 for testing purposes
517 // NOTE: This `map` is because `next` returns an `Option<_>`.
518 .map(|(_, month)|
519 month.__(by_week)
520 .map(|(_, weeks)| weeks)
521 .__(format_weeks)
522 .join("\n"));
523
524 assert_eq!(
525 jan_2013.as_ref().map(|s| &**s),
526 Some(" 1 2 3 4 5\n\
527 \x20 6 7 8 9 10 11 12\n\
528 \x2013 14 15 16 17 18 19\n\
529 \x2020 21 22 23 24 25 26\n\
530 \x2027 28 29 30 31 ")
531 );
532}
533
0731742a 534/// Formats the name of a month, centered on `COLS_PER_WEEK`.
5bcae85e
SL
535fn month_title(month: u32) -> String {
536 const MONTH_NAMES: &'static [&'static str] = &[
537 "January", "February", "March", "April", "May", "June",
538 "July", "August", "September", "October", "November", "December"
539 ];
540 assert_eq!(MONTH_NAMES.len(), 12);
541
542 // Determine how many spaces before and after the month name
543 // we need to center it over the formatted weeks in the month.
544 let name = MONTH_NAMES[(month - 1) as usize];
545 assert!(name.len() < COLS_PER_WEEK as usize);
546 let before = (COLS_PER_WEEK as usize - name.len()) / 2;
547 let after = COLS_PER_WEEK as usize - name.len() - before;
548
0731742a 549 // Note: being slightly more verbose to avoid extra allocations.
5bcae85e
SL
550 let mut result = String::with_capacity(COLS_PER_WEEK as usize);
551 result.extend(spaces(before));
552 result.push_str(name);
553 result.extend(spaces(after));
554 result
555}
556
557fn test_month_title() {
558 assert_eq!(month_title(1).len(), COLS_PER_WEEK as usize);
559}
560
5bcae85e 561/// Formats a month.
abe05a73 562fn format_month(it: impl DateIterator) -> impl Iterator<Item=String> {
5bcae85e
SL
563 let mut month_days = it.peekable();
564 let title = month_title(month_days.peek().unwrap().month());
565
566 Some(title).into_iter()
567 .chain(month_days.__(by_week)
568 .map(|(_, week)| week)
569 .__(format_weeks))
570}
571
572fn test_format_month() {
573 let month_fmt = dates_in_year(2013)
574 .__(by_month).next() // Pick January as a test case
575 .map(|(_, days)| days.into_iter()
576 .__(format_month)
577 .join("\n"));
578
579 assert_eq!(
580 month_fmt.as_ref().map(|s| &**s),
581 Some(" January \n\
582 \x20 1 2 3 4 5\n\
583 \x20 6 7 8 9 10 11 12\n\
584 \x2013 14 15 16 17 18 19\n\
585 \x2020 21 22 23 24 25 26\n\
586 \x2027 28 29 30 31 ")
587 );
588}
589
5bcae85e 590/// Formats an iterator of months.
abe05a73
XL
591fn format_months(it: impl Iterator<Item = impl DateIterator>)
592 -> impl Iterator<Item=impl Iterator<Item=String>>
593{
5bcae85e
SL
594 it.map(format_month)
595}
596
5bcae85e
SL
597/// Takes an iterator of iterators of strings; the sub-iterators are consumed
598/// in lock-step, with their elements joined together.
5bcae85e 599trait PasteBlocks: Iterator + Sized
0731742a 600where Self::Item: Iterator<Item = String> {
5bcae85e
SL
601 fn paste_blocks(self, sep_width: usize) -> PasteBlocksIter<Self::Item> {
602 PasteBlocksIter {
603 iters: self.collect(),
604 cache: vec![],
605 col_widths: None,
606 sep_width: sep_width,
607 }
608 }
609}
610
611impl<It> PasteBlocks for It where It: Iterator, It::Item: Iterator<Item=String> {}
612
613struct PasteBlocksIter<StrIt>
614where StrIt: Iterator<Item=String> {
615 iters: Vec<StrIt>,
616 cache: Vec<Option<String>>,
617 col_widths: Option<Vec<usize>>,
618 sep_width: usize,
619}
620
621impl<StrIt> Iterator for PasteBlocksIter<StrIt>
622where StrIt: Iterator<Item=String> {
623 type Item = String;
624
625 fn next(&mut self) -> Option<String> {
626 self.cache.clear();
627
628 // `cache` is now the next line from each iterator.
629 self.cache.extend(self.iters.iter_mut().map(|it| it.next()));
630
631 // If every line in `cache` is `None`, we have nothing further to do.
632 if self.cache.iter().all(|e| e.is_none()) { return None }
633
634 // Get the column widths if we haven't already.
635 let col_widths = match self.col_widths {
636 Some(ref v) => &**v,
637 None => {
638 self.col_widths = Some(self.cache.iter()
639 .map(|ms| ms.as_ref().map(|s| s.len()).unwrap_or(0))
640 .collect());
641 &**self.col_widths.as_ref().unwrap()
642 }
643 };
644
645 // Fill in any `None`s with spaces.
646 let mut parts = col_widths.iter().cloned().zip(self.cache.iter_mut())
647 .map(|(w,ms)| ms.take().unwrap_or_else(|| spaces(w).collect()));
648
649 // Join them all together.
650 let first = parts.next().unwrap_or(String::new());
651 let sep_width = self.sep_width;
652 Some(parts.fold(first, |mut accum, next| {
653 accum.extend(spaces(sep_width));
654 accum.push_str(&next);
655 accum
656 }))
657 }
658}
659
660fn test_paste_blocks() {
661 let row = dates_in_year(2013)
662 .__(by_month).map(|(_, days)| days)
663 .take(3)
664 .__(format_months)
665 .paste_blocks(1)
666 .join("\n");
667 assert_eq!(
668 &*row,
669 " January February March \n\
670 \x20 1 2 3 4 5 1 2 1 2\n\
671 \x20 6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9\n\
672 \x2013 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16\n\
673 \x2020 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23\n\
674 \x2027 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30\n\
675 \x20 31 "
676 );
677}
678
5bcae85e 679/// Produces an iterator that yields `n` elements at a time.
5bcae85e
SL
680trait Chunks: Iterator + Sized {
681 fn chunks(self, n: usize) -> ChunksIter<Self> {
682 assert!(n > 0);
683 ChunksIter {
684 it: self,
685 n: n,
686 }
687 }
688}
689
690impl<It> Chunks for It where It: Iterator {}
691
692struct ChunksIter<It>
693where It: Iterator {
694 it: It,
695 n: usize,
696}
697
0731742a 698// Note: `chunks` in Rust is more-or-less impossible without overhead of some kind.
5bcae85e
SL
699// Aliasing rules mean you need to add dynamic borrow checking, and the design of
700// `Iterator` means that you need to have the iterator's state kept in an allocation
701// that is jointly owned by the iterator itself and the sub-iterator.
702// As such, I've chosen to cop-out and just heap-allocate each chunk.
703
704impl<It> Iterator for ChunksIter<It>
705where It: Iterator {
706 type Item = Vec<It::Item>;
707
708 fn next(&mut self) -> Option<Vec<It::Item>> {
0731742a 709 let first = self.it.next()?;
5bcae85e
SL
710
711 let mut result = Vec::with_capacity(self.n);
712 result.push(first);
713
714 Some((&mut self.it).take(self.n-1)
715 .fold(result, |mut acc, next| { acc.push(next); acc }))
716 }
717}
718
719fn test_chunks() {
720 let r = &[1, 2, 3, 4, 5, 6, 7];
721 let c = r.iter().cloned().chunks(3).collect::<Vec<_>>();
722 assert_eq!(&*c, &[vec![1, 2, 3], vec![4, 5, 6], vec![7]]);
723}
724
5bcae85e 725/// Formats a year.
5bcae85e
SL
726fn format_year(year: i32, months_per_row: usize) -> String {
727 const COL_SPACING: usize = 1;
728
729 // Start by generating all dates for the given year.
730 dates_in_year(year)
731
732 // Group them by month and throw away month number.
733 .__(by_month).map(|(_, days)| days)
734
735 // Group the months into horizontal rows.
736 .chunks(months_per_row)
737
0731742a 738 // Format each row...
5bcae85e 739 .map(|r| r.into_iter()
0731742a 740 // ... by formatting each month ...
5bcae85e
SL
741 .__(format_months)
742
0731742a 743 // ... and horizontally pasting each respective month's lines together.
5bcae85e
SL
744 .paste_blocks(COL_SPACING)
745 .join("\n")
746 )
747
0731742a 748 // Insert a blank line between each row.
5bcae85e
SL
749 .join("\n\n")
750}
751
752fn test_format_year() {
753 const MONTHS_PER_ROW: usize = 3;
754
755 macro_rules! assert_eq_cal {
756 ($lhs:expr, $rhs:expr) => {
757 if $lhs != $rhs {
758 println!("got:\n```\n{}\n```\n", $lhs.replace(" ", "."));
759 println!("expected:\n```\n{}\n```", $rhs.replace(" ", "."));
760 panic!("calendars didn't match!");
761 }
762 }
763 }
764
765 assert_eq_cal!(&format_year(1984, MONTHS_PER_ROW), "\
766\x20 January February March \n\
767\x20 1 2 3 4 5 6 7 1 2 3 4 1 2 3\n\
768\x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 4 5 6 7 8 9 10\n\
769\x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 11 12 13 14 15 16 17\n\
770\x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 18 19 20 21 22 23 24\n\
771\x2029 30 31 26 27 28 29 25 26 27 28 29 30 31\n\
772\n\
773\x20 April May June \n\
774\x20 1 2 3 4 5 6 7 1 2 3 4 5 1 2\n\
775\x20 8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9\n\
776\x2015 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16\n\
777\x2022 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23\n\
778\x2029 30 27 28 29 30 31 24 25 26 27 28 29 30\n\
779\n\
780\x20 July August September \n\
781\x20 1 2 3 4 5 6 7 1 2 3 4 1\n\
782\x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8\n\
783\x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15\n\
784\x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22\n\
785\x2029 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29\n\
786\x20 30 \n\
787\n\
788\x20 October November December \n\
789\x20 1 2 3 4 5 6 1 2 3 1\n\
790\x20 7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8\n\
791\x2014 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15\n\
792\x2021 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22\n\
793\x2028 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29\n\
794\x20 30 31 ");
795
796 assert_eq_cal!(&format_year(2015, MONTHS_PER_ROW), "\
797\x20 January February March \n\
798\x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7\n\
799\x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14\n\
800\x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21\n\
801\x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28\n\
802\x2025 26 27 28 29 30 31 29 30 31 \n\
803\n\
804\x20 April May June \n\
805\x20 1 2 3 4 1 2 1 2 3 4 5 6\n\
806\x20 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13\n\
807\x2012 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20\n\
808\x2019 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27\n\
809\x2026 27 28 29 30 24 25 26 27 28 29 30 28 29 30 \n\
810\x20 31 \n\
811\n\
812\x20 July August September \n\
813\x20 1 2 3 4 1 1 2 3 4 5\n\
814\x20 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12\n\
815\x2012 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19\n\
816\x2019 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26\n\
817\x2026 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 \n\
818\x20 30 31 \n\
819\n\
820\x20 October November December \n\
821\x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5\n\
822\x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12\n\
823\x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19\n\
824\x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26\n\
825\x2025 26 27 28 29 30 31 29 30 27 28 29 30 31 ");
826}
827
828fn main() {
829 // Run tests.
830 test_spaces();
831 test_dates_in_year();
832 test_group_by();
833 test_by_month();
834 test_isoweekdate();
835 test_by_week();
836 test_format_weeks();
837 test_month_title();
838 test_format_month();
839 test_paste_blocks();
840 test_chunks();
841 test_format_year();
842}