summaryrefslogtreecommitdiffstats
path: root/src/test/ui/impl-trait/example-calendar.rs
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /src/test/ui/impl-trait/example-calendar.rs
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/test/ui/impl-trait/example-calendar.rs')
-rw-r--r--src/test/ui/impl-trait/example-calendar.rs840
1 files changed, 840 insertions, 0 deletions
diff --git a/src/test/ui/impl-trait/example-calendar.rs b/src/test/ui/impl-trait/example-calendar.rs
new file mode 100644
index 000000000..da45f0d13
--- /dev/null
+++ b/src/test/ui/impl-trait/example-calendar.rs
@@ -0,0 +1,840 @@
+// run-pass
+
+#![feature(fn_traits,
+ step_trait,
+ unboxed_closures,
+)]
+
+//! Derived from: <https://raw.githubusercontent.com/quickfur/dcal/master/dcal.d>.
+//!
+//! Originally converted to Rust by [Daniel Keep](https://github.com/DanielKeep).
+
+use std::fmt::Write;
+
+/// Date representation.
+#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+struct NaiveDate(i32, u32, u32);
+
+impl NaiveDate {
+ pub fn from_ymd(y: i32, m: u32, d: u32) -> NaiveDate {
+ assert!(1 <= m && m <= 12, "m = {:?}", m);
+ assert!(1 <= d && d <= NaiveDate(y, m, 1).days_in_month(), "d = {:?}", d);
+ NaiveDate(y, m, d)
+ }
+
+ pub fn year(&self) -> i32 {
+ self.0
+ }
+
+ pub fn month(&self) -> u32 {
+ self.1
+ }
+
+ pub fn day(&self) -> u32 {
+ self.2
+ }
+
+ pub fn succ(&self) -> NaiveDate {
+ let (mut y, mut m, mut d, n) = (
+ self.year(), self.month(), self.day()+1, self.days_in_month());
+ if d > n {
+ d = 1;
+ m += 1;
+ }
+ if m > 12 {
+ m = 1;
+ y += 1;
+ }
+ NaiveDate::from_ymd(y, m, d)
+ }
+
+ pub fn weekday(&self) -> Weekday {
+ use Weekday::*;
+
+ // 0 = Sunday
+ let year = self.year();
+ let dow_jan_1 = (year*365 + ((year-1) / 4) - ((year-1) / 100) + ((year-1) / 400)) % 7;
+ let dow = (dow_jan_1 + (self.day_of_year() as i32 - 1)) % 7;
+ [Sun, Mon, Tue, Wed, Thu, Fri, Sat][dow as usize]
+ }
+
+ pub fn isoweekdate(&self) -> (i32, u32, Weekday) {
+ let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday();
+
+ // Work out this date's DOtY and week number, not including year adjustment.
+ let doy_0 = self.day_of_year() - 1;
+ let mut week_mon_0: i32 = ((first_dow_mon_0 + doy_0) / 7) as i32;
+
+ if self.first_week_in_prev_year() {
+ week_mon_0 -= 1;
+ }
+
+ let weeks_in_year = self.last_week_number();
+
+ // Work out the final result.
+ // If the week is `-1` or `>= weeks_in_year`, we will need to adjust the year.
+ let year = self.year();
+ let wd = self.weekday();
+
+ if week_mon_0 < 0 {
+ (year - 1, NaiveDate::from_ymd(year - 1, 1, 1).last_week_number(), wd)
+ } else if week_mon_0 >= weeks_in_year as i32 {
+ (year + 1, (week_mon_0 + 1 - weeks_in_year as i32) as u32, wd)
+ } else {
+ (year, (week_mon_0 + 1) as u32, wd)
+ }
+ }
+
+ fn first_week_in_prev_year(&self) -> bool {
+ let first_dow_mon_0 = self.year_first_day_of_week().num_days_from_monday();
+
+ // Any day in the year *before* the first Monday of that year
+ // is considered to be in the last week of the previous year,
+ // assuming the first week has *less* than four days in it.
+ // Adjust the week appropriately.
+ ((7 - first_dow_mon_0) % 7) < 4
+ }
+
+ fn year_first_day_of_week(&self) -> Weekday {
+ NaiveDate::from_ymd(self.year(), 1, 1).weekday()
+ }
+
+ fn weeks_in_year(&self) -> u32 {
+ let days_in_last_week = self.year_first_day_of_week().num_days_from_monday() + 1;
+ if days_in_last_week >= 4 { 53 } else { 52 }
+ }
+
+ fn last_week_number(&self) -> u32 {
+ let wiy = self.weeks_in_year();
+ if self.first_week_in_prev_year() { wiy - 1 } else { wiy }
+ }
+
+ fn day_of_year(&self) -> u32 {
+ (1..self.1).map(|m| NaiveDate::from_ymd(self.year(), m, 1).days_in_month())
+ .fold(0, |a,b| a+b) + self.day()
+ }
+
+ fn is_leap_year(&self) -> bool {
+ let year = self.year();
+ if year % 4 != 0 {
+ return false
+ } else if year % 100 != 0 {
+ return true
+ } else if year % 400 != 0 {
+ return false
+ } else {
+ return true
+ }
+ }
+
+ fn days_in_month(&self) -> u32 {
+ match self.month() {
+ /* Jan */ 1 => 31,
+ /* Feb */ 2 => if self.is_leap_year() { 29 } else { 28 },
+ /* Mar */ 3 => 31,
+ /* Apr */ 4 => 30,
+ /* May */ 5 => 31,
+ /* Jun */ 6 => 30,
+ /* Jul */ 7 => 31,
+ /* Aug */ 8 => 31,
+ /* Sep */ 9 => 30,
+ /* Oct */ 10 => 31,
+ /* Nov */ 11 => 30,
+ /* Dec */ 12 => 31,
+ _ => unreachable!()
+ }
+ }
+}
+
+impl<'a, 'b> std::ops::Add<&'b NaiveDate> for &'a NaiveDate {
+ type Output = NaiveDate;
+
+ fn add(self, other: &'b NaiveDate) -> NaiveDate {
+ assert_eq!(*other, NaiveDate(0, 0, 1));
+ self.succ()
+ }
+}
+
+impl std::iter::Step for NaiveDate {
+ fn steps_between(_: &Self, _: &Self) -> Option<usize> {
+ unimplemented!()
+ }
+
+ fn forward_checked(start: Self, n: usize) -> Option<Self> {
+ Some((0..n).fold(start, |x, _| x.succ()))
+ }
+
+ fn backward_checked(_: Self, _: usize) -> Option<Self> {
+ unimplemented!()
+ }
+}
+
+#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
+pub enum Weekday {
+ Mon,
+ Tue,
+ Wed,
+ Thu,
+ Fri,
+ Sat,
+ Sun,
+}
+
+impl Weekday {
+ pub fn num_days_from_monday(&self) -> u32 {
+ use Weekday::*;
+ match *self {
+ Mon => 0,
+ Tue => 1,
+ Wed => 2,
+ Thu => 3,
+ Fri => 4,
+ Sat => 5,
+ Sun => 6,
+ }
+ }
+
+ pub fn num_days_from_sunday(&self) -> u32 {
+ use Weekday::*;
+ match *self {
+ Sun => 0,
+ Mon => 1,
+ Tue => 2,
+ Wed => 3,
+ Thu => 4,
+ Fri => 5,
+ Sat => 6,
+ }
+ }
+}
+
+/// `GroupBy` implementation.
+struct GroupBy<It: Iterator, F> {
+ it: std::iter::Peekable<It>,
+ f: F,
+}
+
+impl<It, F> Clone for GroupBy<It, F>
+where
+ It: Iterator + Clone,
+ It::Item: Clone,
+ F: Clone,
+{
+ fn clone(&self) -> Self {
+ GroupBy {
+ it: self.it.clone(),
+ f: self.f.clone(),
+ }
+ }
+}
+
+impl<'a, G, It: 'a, F: 'a> Iterator for GroupBy<It, F>
+where It: Iterator + Clone,
+ It::Item: Clone,
+ F: Clone + FnMut(&It::Item) -> G,
+ G: Eq + Clone
+{
+ type Item = (G, InGroup<std::iter::Peekable<It>, F, G>);
+
+ fn next(&mut self) -> Option<Self::Item> {
+ self.it.peek().map(&mut self.f).map(|key| {
+ let start = self.it.clone();
+ while let Some(k) = self.it.peek().map(&mut self.f) {
+ if key != k {
+ break;
+ }
+ self.it.next();
+ }
+
+ (key.clone(), InGroup {
+ it: start,
+ f: self.f.clone(),
+ g: key
+ })
+ })
+ }
+}
+
+#[derive(Copy, Clone)]
+struct InGroup<It, F, G> {
+ it: It,
+ f: F,
+ g: G
+}
+
+impl<It: Iterator, F: FnMut(&It::Item) -> G, G: Eq> Iterator for InGroup<It, F, G> {
+ type Item = It::Item;
+
+ fn next(&mut self) -> Option<It::Item> {
+ self.it.next().and_then(|x| {
+ if (self.f)(&x) == self.g { Some(x) } else { None }
+ })
+ }
+}
+
+trait IteratorExt: Iterator + Sized {
+ fn group_by<G, F>(self, f: F) -> GroupBy<Self, F>
+ where F: Clone + FnMut(&Self::Item) -> G,
+ G: Eq
+ {
+ GroupBy { it: self.peekable(), f }
+ }
+
+ fn join(mut self, sep: &str) -> String
+ where Self::Item: std::fmt::Display {
+ let mut s = String::new();
+ if let Some(e) = self.next() {
+ write!(s, "{}", e).unwrap();
+ for e in self {
+ s.push_str(sep);
+ write!(s, "{}", e).unwrap();
+ }
+ }
+ s
+ }
+
+ // HACK(eddyb): only needed because `impl Trait` can't be
+ // used with trait methods: `.foo()` becomes `.__(foo)`.
+ fn __<F, R>(self, f: F) -> R
+ where F: FnOnce(Self) -> R {
+ f(self)
+ }
+}
+
+impl<It> IteratorExt for It where It: Iterator {}
+
+/// Generates an iterator that yields exactly `n` spaces.
+fn spaces(n: usize) -> std::iter::Take<std::iter::Repeat<char>> {
+ std::iter::repeat(' ').take(n)
+}
+
+fn test_spaces() {
+ assert_eq!(spaces(0).collect::<String>(), "");
+ assert_eq!(spaces(10).collect::<String>(), " ")
+}
+
+/// Returns an iterator of dates in a given year.
+fn dates_in_year(year: i32) -> impl Iterator<Item=NaiveDate>+Clone {
+ InGroup {
+ it: NaiveDate::from_ymd(year, 1, 1)..,
+ f: |d: &NaiveDate| d.year(),
+ g: year
+ }
+}
+
+fn test_dates_in_year() {
+ {
+ let mut dates = dates_in_year(2013);
+ assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 1)));
+
+ // Check increment.
+ assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 2)));
+
+ // Check monthly roll-over.
+ for _ in 3..31 {
+ assert!(dates.next() != None);
+ }
+
+ assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 1, 31)));
+ assert_eq!(dates.next(), Some(NaiveDate::from_ymd(2013, 2, 1)));
+ }
+
+ {
+ // Check length of year.
+ let mut dates = dates_in_year(2013);
+ for _ in 0..365 {
+ assert!(dates.next() != None);
+ }
+ assert_eq!(dates.next(), None);
+ }
+
+ {
+ // Check length of leap year.
+ let mut dates = dates_in_year(1984);
+ for _ in 0..366 {
+ assert!(dates.next() != None);
+ }
+ assert_eq!(dates.next(), None);
+ }
+}
+
+/// Convenience trait for verifying that a given type iterates over
+/// `NaiveDate`s.
+trait DateIterator: Iterator<Item=NaiveDate> + Clone {}
+impl<It> DateIterator for It where It: Iterator<Item=NaiveDate> + Clone {}
+
+fn test_group_by() {
+ let input = [
+ [1, 1],
+ [1, 1],
+ [1, 2],
+ [2, 2],
+ [2, 3],
+ [2, 3],
+ [3, 3]
+ ];
+
+ let by_x = input.iter().cloned().group_by(|a| a[0]);
+ let expected_1: &[&[[i32; 2]]] = &[
+ &[[1, 1], [1, 1], [1, 2]],
+ &[[2, 2], [2, 3], [2, 3]],
+ &[[3, 3]]
+ ];
+ for ((_, a), b) in by_x.zip(expected_1.iter().cloned()) {
+ assert_eq!(&a.collect::<Vec<_>>()[..], b);
+ }
+
+ let by_y = input.iter().cloned().group_by(|a| a[1]);
+ let expected_2: &[&[[i32; 2]]] = &[
+ &[[1, 1], [1, 1]],
+ &[[1, 2], [2, 2]],
+ &[[2, 3], [2, 3], [3, 3]]
+ ];
+ for ((_, a), b) in by_y.zip(expected_2.iter().cloned()) {
+ assert_eq!(&a.collect::<Vec<_>>()[..], b);
+ }
+}
+
+/// Groups an iterator of dates by month.
+fn by_month(it: impl Iterator<Item=NaiveDate> + Clone)
+ -> impl Iterator<Item=(u32, impl Iterator<Item=NaiveDate> + Clone)> + Clone
+{
+ it.group_by(|d| d.month())
+}
+
+fn test_by_month() {
+ let mut months = dates_in_year(2013).__(by_month);
+ for (month, (_, mut date)) in (1..13).zip(&mut months) {
+ assert_eq!(date.nth(0).unwrap(), NaiveDate::from_ymd(2013, month, 1));
+ }
+ assert!(months.next().is_none());
+}
+
+/// Groups an iterator of dates by week.
+fn by_week(it: impl DateIterator)
+ -> impl Iterator<Item=(u32, impl DateIterator)> + Clone
+{
+ // We go forward one day because `isoweekdate` considers the week to start on a Monday.
+ it.group_by(|d| d.succ().isoweekdate().1)
+}
+
+fn test_isoweekdate() {
+ fn weeks_uniq(year: i32) -> Vec<((i32, u32), u32)> {
+ let mut weeks = dates_in_year(year).map(|d| d.isoweekdate())
+ .map(|(y,w,_)| (y,w));
+ let mut result = vec![];
+ let mut accum = (weeks.next().unwrap(), 1);
+ for yw in weeks {
+ if accum.0 == yw {
+ accum.1 += 1;
+ } else {
+ result.push(accum);
+ accum = (yw, 1);
+ }
+ }
+ result.push(accum);
+ result
+ }
+
+ let wu_1984 = weeks_uniq(1984);
+ assert_eq!(&wu_1984[..2], &[((1983, 52), 1), ((1984, 1), 7)]);
+ assert_eq!(&wu_1984[wu_1984.len()-2..], &[((1984, 52), 7), ((1985, 1), 1)]);
+
+ let wu_2013 = weeks_uniq(2013);
+ assert_eq!(&wu_2013[..2], &[((2013, 1), 6), ((2013, 2), 7)]);
+ assert_eq!(&wu_2013[wu_2013.len()-2..], &[((2013, 52), 7), ((2014, 1), 2)]);
+
+ let wu_2015 = weeks_uniq(2015);
+ assert_eq!(&wu_2015[..2], &[((2015, 1), 4), ((2015, 2), 7)]);
+ assert_eq!(&wu_2015[wu_2015.len()-2..], &[((2015, 52), 7), ((2015, 53), 4)]);
+}
+
+fn test_by_week() {
+ let mut weeks = dates_in_year(2013).__(by_week);
+ assert_eq!(
+ &*weeks.next().unwrap().1.collect::<Vec<_>>(),
+ &[
+ NaiveDate::from_ymd(2013, 1, 1),
+ NaiveDate::from_ymd(2013, 1, 2),
+ NaiveDate::from_ymd(2013, 1, 3),
+ NaiveDate::from_ymd(2013, 1, 4),
+ NaiveDate::from_ymd(2013, 1, 5),
+ ]
+ );
+ assert_eq!(
+ &*weeks.next().unwrap().1.collect::<Vec<_>>(),
+ &[
+ NaiveDate::from_ymd(2013, 1, 6),
+ NaiveDate::from_ymd(2013, 1, 7),
+ NaiveDate::from_ymd(2013, 1, 8),
+ NaiveDate::from_ymd(2013, 1, 9),
+ NaiveDate::from_ymd(2013, 1, 10),
+ NaiveDate::from_ymd(2013, 1, 11),
+ NaiveDate::from_ymd(2013, 1, 12),
+ ]
+ );
+ assert_eq!(weeks.next().unwrap().1.nth(0).unwrap(), NaiveDate::from_ymd(2013, 1, 13));
+}
+
+/// The number of columns per day in the formatted output.
+const COLS_PER_DAY: u32 = 3;
+
+/// The number of columns per week in the formatted output.
+const COLS_PER_WEEK: u32 = 7 * COLS_PER_DAY;
+
+/// Formats an iterator of weeks into an iterator of strings.
+fn format_weeks(it: impl Iterator<Item = impl DateIterator>) -> impl Iterator<Item=String> {
+ it.map(|week| {
+ let mut buf = String::with_capacity((COLS_PER_DAY * COLS_PER_WEEK + 2) as usize);
+
+ // Format each day into its own cell and append to target string.
+ let mut last_day = 0;
+ let mut first = true;
+ for d in week {
+ last_day = d.weekday().num_days_from_sunday();
+
+ // Insert enough filler to align the first day with its respective day-of-week.
+ if first {
+ buf.extend(spaces((COLS_PER_DAY * last_day) as usize));
+ first = false;
+ }
+
+ write!(buf, " {:>2}", d.day()).unwrap();
+ }
+
+ // Insert more filler at the end to fill up the remainder of the week,
+ // if its a short week (e.g., at the end of the month).
+ buf.extend(spaces((COLS_PER_DAY * (6 - last_day)) as usize));
+ buf
+ })
+}
+
+fn test_format_weeks() {
+ let jan_2013 = dates_in_year(2013)
+ .__(by_month).next() // pick January 2013 for testing purposes
+ // NOTE: This `map` is because `next` returns an `Option<_>`.
+ .map(|(_, month)|
+ month.__(by_week)
+ .map(|(_, weeks)| weeks)
+ .__(format_weeks)
+ .join("\n"));
+
+ assert_eq!(
+ jan_2013.as_ref().map(|s| &**s),
+ Some(" 1 2 3 4 5\n\
+ \x20 6 7 8 9 10 11 12\n\
+ \x2013 14 15 16 17 18 19\n\
+ \x2020 21 22 23 24 25 26\n\
+ \x2027 28 29 30 31 ")
+ );
+}
+
+/// Formats the name of a month, centered on `COLS_PER_WEEK`.
+fn month_title(month: u32) -> String {
+ const MONTH_NAMES: &'static [&'static str] = &[
+ "January", "February", "March", "April", "May", "June",
+ "July", "August", "September", "October", "November", "December"
+ ];
+ assert_eq!(MONTH_NAMES.len(), 12);
+
+ // Determine how many spaces before and after the month name
+ // we need to center it over the formatted weeks in the month.
+ let name = MONTH_NAMES[(month - 1) as usize];
+ assert!(name.len() < COLS_PER_WEEK as usize);
+ let before = (COLS_PER_WEEK as usize - name.len()) / 2;
+ let after = COLS_PER_WEEK as usize - name.len() - before;
+
+ // Note: being slightly more verbose to avoid extra allocations.
+ let mut result = String::with_capacity(COLS_PER_WEEK as usize);
+ result.extend(spaces(before));
+ result.push_str(name);
+ result.extend(spaces(after));
+ result
+}
+
+fn test_month_title() {
+ assert_eq!(month_title(1).len(), COLS_PER_WEEK as usize);
+}
+
+/// Formats a month.
+fn format_month(it: impl DateIterator) -> impl Iterator<Item=String> {
+ let mut month_days = it.peekable();
+ let title = month_title(month_days.peek().unwrap().month());
+
+ Some(title).into_iter()
+ .chain(month_days.__(by_week)
+ .map(|(_, week)| week)
+ .__(format_weeks))
+}
+
+fn test_format_month() {
+ let month_fmt = dates_in_year(2013)
+ .__(by_month).next() // Pick January as a test case
+ .map(|(_, days)| days.into_iter()
+ .__(format_month)
+ .join("\n"));
+
+ assert_eq!(
+ month_fmt.as_ref().map(|s| &**s),
+ Some(" January \n\
+ \x20 1 2 3 4 5\n\
+ \x20 6 7 8 9 10 11 12\n\
+ \x2013 14 15 16 17 18 19\n\
+ \x2020 21 22 23 24 25 26\n\
+ \x2027 28 29 30 31 ")
+ );
+}
+
+/// Formats an iterator of months.
+fn format_months(it: impl Iterator<Item = impl DateIterator>)
+ -> impl Iterator<Item=impl Iterator<Item=String>>
+{
+ it.map(format_month)
+}
+
+/// Takes an iterator of iterators of strings; the sub-iterators are consumed
+/// in lock-step, with their elements joined together.
+trait PasteBlocks: Iterator + Sized
+where Self::Item: Iterator<Item = String> {
+ fn paste_blocks(self, sep_width: usize) -> PasteBlocksIter<Self::Item> {
+ PasteBlocksIter {
+ iters: self.collect(),
+ cache: vec![],
+ col_widths: None,
+ sep_width: sep_width,
+ }
+ }
+}
+
+impl<It> PasteBlocks for It where It: Iterator, It::Item: Iterator<Item=String> {}
+
+struct PasteBlocksIter<StrIt>
+where StrIt: Iterator<Item=String> {
+ iters: Vec<StrIt>,
+ cache: Vec<Option<String>>,
+ col_widths: Option<Vec<usize>>,
+ sep_width: usize,
+}
+
+impl<StrIt> Iterator for PasteBlocksIter<StrIt>
+where StrIt: Iterator<Item=String> {
+ type Item = String;
+
+ fn next(&mut self) -> Option<String> {
+ self.cache.clear();
+
+ // `cache` is now the next line from each iterator.
+ self.cache.extend(self.iters.iter_mut().map(|it| it.next()));
+
+ // If every line in `cache` is `None`, we have nothing further to do.
+ if self.cache.iter().all(|e| e.is_none()) { return None }
+
+ // Get the column widths if we haven't already.
+ let col_widths = match self.col_widths {
+ Some(ref v) => &**v,
+ None => {
+ self.col_widths = Some(self.cache.iter()
+ .map(|ms| ms.as_ref().map(|s| s.len()).unwrap_or(0))
+ .collect());
+ &**self.col_widths.as_ref().unwrap()
+ }
+ };
+
+ // Fill in any `None`s with spaces.
+ let mut parts = col_widths.iter().cloned().zip(self.cache.iter_mut())
+ .map(|(w,ms)| ms.take().unwrap_or_else(|| spaces(w).collect()));
+
+ // Join them all together.
+ let first = parts.next().unwrap_or(String::new());
+ let sep_width = self.sep_width;
+ Some(parts.fold(first, |mut accum, next| {
+ accum.extend(spaces(sep_width));
+ accum.push_str(&next);
+ accum
+ }))
+ }
+}
+
+fn test_paste_blocks() {
+ let row = dates_in_year(2013)
+ .__(by_month).map(|(_, days)| days)
+ .take(3)
+ .__(format_months)
+ .paste_blocks(1)
+ .join("\n");
+ assert_eq!(
+ &*row,
+ " January February March \n\
+ \x20 1 2 3 4 5 1 2 1 2\n\
+ \x20 6 7 8 9 10 11 12 3 4 5 6 7 8 9 3 4 5 6 7 8 9\n\
+ \x2013 14 15 16 17 18 19 10 11 12 13 14 15 16 10 11 12 13 14 15 16\n\
+ \x2020 21 22 23 24 25 26 17 18 19 20 21 22 23 17 18 19 20 21 22 23\n\
+ \x2027 28 29 30 31 24 25 26 27 28 24 25 26 27 28 29 30\n\
+ \x20 31 "
+ );
+}
+
+/// Produces an iterator that yields `n` elements at a time.
+trait Chunks: Iterator + Sized {
+ fn chunks(self, n: usize) -> ChunksIter<Self> {
+ assert!(n > 0);
+ ChunksIter {
+ it: self,
+ n: n,
+ }
+ }
+}
+
+impl<It> Chunks for It where It: Iterator {}
+
+struct ChunksIter<It>
+where It: Iterator {
+ it: It,
+ n: usize,
+}
+
+// Note: `chunks` in Rust is more-or-less impossible without overhead of some kind.
+// Aliasing rules mean you need to add dynamic borrow checking, and the design of
+// `Iterator` means that you need to have the iterator's state kept in an allocation
+// that is jointly owned by the iterator itself and the sub-iterator.
+// As such, I've chosen to cop-out and just heap-allocate each chunk.
+
+impl<It> Iterator for ChunksIter<It>
+where It: Iterator {
+ type Item = Vec<It::Item>;
+
+ fn next(&mut self) -> Option<Vec<It::Item>> {
+ let first = self.it.next()?;
+
+ let mut result = Vec::with_capacity(self.n);
+ result.push(first);
+
+ Some((&mut self.it).take(self.n-1)
+ .fold(result, |mut acc, next| { acc.push(next); acc }))
+ }
+}
+
+fn test_chunks() {
+ let r = &[1, 2, 3, 4, 5, 6, 7];
+ let c = r.iter().cloned().chunks(3).collect::<Vec<_>>();
+ assert_eq!(&*c, &[vec![1, 2, 3], vec![4, 5, 6], vec![7]]);
+}
+
+/// Formats a year.
+fn format_year(year: i32, months_per_row: usize) -> String {
+ const COL_SPACING: usize = 1;
+
+ // Start by generating all dates for the given year.
+ dates_in_year(year)
+
+ // Group them by month and throw away month number.
+ .__(by_month).map(|(_, days)| days)
+
+ // Group the months into horizontal rows.
+ .chunks(months_per_row)
+
+ // Format each row...
+ .map(|r| r.into_iter()
+ // ... by formatting each month ...
+ .__(format_months)
+
+ // ... and horizontally pasting each respective month's lines together.
+ .paste_blocks(COL_SPACING)
+ .join("\n")
+ )
+
+ // Insert a blank line between each row.
+ .join("\n\n")
+}
+
+fn test_format_year() {
+ const MONTHS_PER_ROW: usize = 3;
+
+ macro_rules! assert_eq_cal {
+ ($lhs:expr, $rhs:expr) => {
+ if $lhs != $rhs {
+ println!("got:\n```\n{}\n```\n", $lhs.replace(" ", "."));
+ println!("expected:\n```\n{}\n```", $rhs.replace(" ", "."));
+ panic!("calendars didn't match!");
+ }
+ }
+ }
+
+ assert_eq_cal!(&format_year(1984, MONTHS_PER_ROW), "\
+\x20 January February March \n\
+\x20 1 2 3 4 5 6 7 1 2 3 4 1 2 3\n\
+\x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 4 5 6 7 8 9 10\n\
+\x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 11 12 13 14 15 16 17\n\
+\x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 18 19 20 21 22 23 24\n\
+\x2029 30 31 26 27 28 29 25 26 27 28 29 30 31\n\
+\n\
+\x20 April May June \n\
+\x20 1 2 3 4 5 6 7 1 2 3 4 5 1 2\n\
+\x20 8 9 10 11 12 13 14 6 7 8 9 10 11 12 3 4 5 6 7 8 9\n\
+\x2015 16 17 18 19 20 21 13 14 15 16 17 18 19 10 11 12 13 14 15 16\n\
+\x2022 23 24 25 26 27 28 20 21 22 23 24 25 26 17 18 19 20 21 22 23\n\
+\x2029 30 27 28 29 30 31 24 25 26 27 28 29 30\n\
+\n\
+\x20 July August September \n\
+\x20 1 2 3 4 5 6 7 1 2 3 4 1\n\
+\x20 8 9 10 11 12 13 14 5 6 7 8 9 10 11 2 3 4 5 6 7 8\n\
+\x2015 16 17 18 19 20 21 12 13 14 15 16 17 18 9 10 11 12 13 14 15\n\
+\x2022 23 24 25 26 27 28 19 20 21 22 23 24 25 16 17 18 19 20 21 22\n\
+\x2029 30 31 26 27 28 29 30 31 23 24 25 26 27 28 29\n\
+\x20 30 \n\
+\n\
+\x20 October November December \n\
+\x20 1 2 3 4 5 6 1 2 3 1\n\
+\x20 7 8 9 10 11 12 13 4 5 6 7 8 9 10 2 3 4 5 6 7 8\n\
+\x2014 15 16 17 18 19 20 11 12 13 14 15 16 17 9 10 11 12 13 14 15\n\
+\x2021 22 23 24 25 26 27 18 19 20 21 22 23 24 16 17 18 19 20 21 22\n\
+\x2028 29 30 31 25 26 27 28 29 30 23 24 25 26 27 28 29\n\
+\x20 30 31 ");
+
+ assert_eq_cal!(&format_year(2015, MONTHS_PER_ROW), "\
+\x20 January February March \n\
+\x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5 6 7\n\
+\x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 8 9 10 11 12 13 14\n\
+\x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 15 16 17 18 19 20 21\n\
+\x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 22 23 24 25 26 27 28\n\
+\x2025 26 27 28 29 30 31 29 30 31 \n\
+\n\
+\x20 April May June \n\
+\x20 1 2 3 4 1 2 1 2 3 4 5 6\n\
+\x20 5 6 7 8 9 10 11 3 4 5 6 7 8 9 7 8 9 10 11 12 13\n\
+\x2012 13 14 15 16 17 18 10 11 12 13 14 15 16 14 15 16 17 18 19 20\n\
+\x2019 20 21 22 23 24 25 17 18 19 20 21 22 23 21 22 23 24 25 26 27\n\
+\x2026 27 28 29 30 24 25 26 27 28 29 30 28 29 30 \n\
+\x20 31 \n\
+\n\
+\x20 July August September \n\
+\x20 1 2 3 4 1 1 2 3 4 5\n\
+\x20 5 6 7 8 9 10 11 2 3 4 5 6 7 8 6 7 8 9 10 11 12\n\
+\x2012 13 14 15 16 17 18 9 10 11 12 13 14 15 13 14 15 16 17 18 19\n\
+\x2019 20 21 22 23 24 25 16 17 18 19 20 21 22 20 21 22 23 24 25 26\n\
+\x2026 27 28 29 30 31 23 24 25 26 27 28 29 27 28 29 30 \n\
+\x20 30 31 \n\
+\n\
+\x20 October November December \n\
+\x20 1 2 3 1 2 3 4 5 6 7 1 2 3 4 5\n\
+\x20 4 5 6 7 8 9 10 8 9 10 11 12 13 14 6 7 8 9 10 11 12\n\
+\x2011 12 13 14 15 16 17 15 16 17 18 19 20 21 13 14 15 16 17 18 19\n\
+\x2018 19 20 21 22 23 24 22 23 24 25 26 27 28 20 21 22 23 24 25 26\n\
+\x2025 26 27 28 29 30 31 29 30 27 28 29 30 31 ");
+}
+
+fn main() {
+ // Run tests.
+ test_spaces();
+ test_dates_in_year();
+ test_group_by();
+ test_by_month();
+ test_isoweekdate();
+ test_by_week();
+ test_format_weeks();
+ test_month_title();
+ test_format_month();
+ test_paste_blocks();
+ test_chunks();
+ test_format_year();
+}