Skip to content

Commit

Permalink
Simplify code generation by using both macro_rules! and a build script.
Browse files Browse the repository at this point in the history
  • Loading branch information
SimonSapin committed Jul 17, 2016
1 parent a9b9776 commit 4c32cfb
Show file tree
Hide file tree
Showing 2 changed files with 97 additions and 64 deletions.
89 changes: 27 additions & 62 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,20 @@ use std::{env, fs, path};
use std::io::Write;

fn main() {
let months = [
// Length (number of days) in months in common (non-leap) years, then leap years.
let path = path::Path::new(&env::var("OUT_DIR").unwrap()).join("generated_data.rs");
let mut f = fs::File::create(&path).unwrap();
macro_rules! w {
($($tt: tt)*) => {{ writeln!(f, $($tt)*).unwrap(); }}
}

let mut number = 1;
let mut running_sum_common = 0;
let mut running_sum_leap = 0;

w!("macro_rules! with_month_data {{");
w!(" ($macro_name: ident) => {{");
w!(" $macro_name! {{");
for &(name, length_common, length_leap) in &[
("January", 31, 31),
("February", 28, 29),
("March", 31, 31),
Expand All @@ -16,67 +28,20 @@ fn main() {
("October", 31, 31),
("November", 30, 30),
("December", 31, 31),
].iter().scan((1, 0, 0), |&mut (ref mut number, ref mut running_sum_common, ref mut running_sum_leap),
&(name, length_common, length_leap)| {
let month = (
name,
*number,
// First and last day of the month, where 0 is January 1st and 355 or 366 is December 31st.
(*running_sum_common, *running_sum_common + length_common - 1),
(*running_sum_leap, *running_sum_leap + length_leap - 1)
);
*number += 1;
*running_sum_common += length_common;
*running_sum_leap += length_leap;
Some(month)
}).collect::<Vec<_>>();

let path = path::Path::new(&env::var("OUT_DIR").unwrap()).join("month_generated.rs");
let mut f = fs::File::create(&path).unwrap();
macro_rules! w {
($($tt: tt)*) => {{ writeln!(f, $($tt)*).unwrap(); }}
}

w!("#[derive(Debug, Eq, PartialEq, Copy, Clone)]");
w!("pub enum Month {{");
for &(name, number, _, _) in &months {
w!(" {} = {},", name, number);
}
w!("}}");
w!("");
w!("impl Month {{");
w!(" /// Days between Jan 1st and the first day of this month.");
w!(" fn days_since_january_1st(self, year_kind: YearKind) -> i32 {{");
w!(" match year_kind {{");
w!(" YearKind::Common => match self {{");
for &(name, _, (first, _), _) in &months {
w!(" Month::{} => {},", name, first);
}
w!(" }},");
w!(" YearKind::Leap => match self {{");
for &(name, _, _, (first, _)) in &months {
w!(" Month::{} => {},", name, first);
}
w!(" }},");
w!(" }}");
w!(" }}");
w!("");
w!(" /// In: 0 for Jan 1st, 365 or 366 for Dec 31.");
w!(" /// Out: Month and day of the month (1 for the first day).");
w!(" fn from_day_of_the_year(day: i32, year_kind: YearKind) -> (Month, u8) {{");
w!(" match year_kind {{");
w!(" YearKind::Common => match day {{");
for &(name, _, (first, last), _) in &months {
w!(" {}...{} => (Month::{}, (day - {} + 1) as u8),", first, last, name, first);
}
w!(" _ => panic!(\"Day #{{}} of the year is out of range\", day)");
w!(" }},");
w!(" YearKind::Leap => match day {{");
for &(name, _, _, (first, last)) in &months {
w!(" {}...{} => (Month::{}, (day - {} + 1) as u8),", first, last, name, first);
] {
w!("{} {{", name);
w!(" number = {},", number);
w!(" common_years = {{ first_day = {}, last_day = {}, }},",
running_sum_common,
running_sum_common + length_common - 1);
w!(" leap_years = {{ first_day = {}, last_day = {}, }},",
running_sum_leap,
running_sum_leap + length_leap - 1);
w!("}},");
number += 1;
running_sum_common += length_common;
running_sum_leap += length_leap;
}
w!(" _ => panic!(\"Day #{{}} of the year is out of range\", day)");
w!(" }},");
w!(" }}");
w!(" }}");
w!("}}");
Expand Down
72 changes: 70 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,6 @@ mod time_zones;
pub use time_zones::{TimeZone, Utc, FixedOffsetFromUtc};
use core::fmt;

include!(concat!(env!("OUT_DIR"), "/month_generated.rs"));

/// In seconds since 1970-01-01 00:00:00 UTC.
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub struct UnixTimestamp(pub i64);
Expand Down Expand Up @@ -127,3 +125,73 @@ impl From<i32> for YearKind {
}
}
}

macro_rules! declare_month {
(
$(
$name: ident {
number = $number: expr,
common_years = {
first_day = $first_day_in_common_years: expr,
last_day = $last_day_in_common_years: expr,
},
leap_years = {
first_day = $first_day_in_leap_years: expr,
last_day = $last_day_in_leap_years: expr,
},
},
)+
) => {
#[derive(Debug, Eq, PartialEq, Copy, Clone)]
pub enum Month {
$(
$name = $number,
)+
}

impl Month {
/// Days between Jan 1st and the first day of this month.
fn days_since_january_1st(self, year_kind: YearKind) -> i32 {
match year_kind {
YearKind::Common => match self {
$(
Month::$name => $first_day_in_common_years,
)+
},
YearKind::Leap => match self {
$(
Month::$name => $first_day_in_leap_years,
)+
},
}
}

/// In: 0 for Jan 1st, 365 or 366 for Dec 31.
/// Out: Month and day of the month (1 for the first day).
fn from_day_of_the_year(day: i32, year_kind: YearKind) -> (Month, u8) {
match year_kind {
YearKind::Common => match day {
$(
$first_day_in_common_years ... $last_day_in_leap_years => {
(Month::$name, (day - $first_day_in_common_years + 1) as u8)
}
)+
_ => panic!("Day #{} of the year is out of range", day)
},
YearKind::Leap => match day {
$(
$first_day_in_leap_years ... $last_day_in_leap_years => {
(Month::$name, (day - $first_day_in_leap_years + 1) as u8)
}
)+
_ => panic!("Day #{} of the year is out of range", day)
},
}
}
}
}
}

include!(concat!(env!("OUT_DIR"), "/generated_data.rs"));

with_month_data!(declare_month);

0 comments on commit 4c32cfb

Please sign in to comment.