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