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