#![crate_name = "datetime_macros"]
#![crate_type = "dylib"]
#![feature(core, plugin_registrar, quote, rustc_private)]
extern crate syntax;
extern crate rustc;
extern crate datetime;
extern crate pad;
use syntax::ptr::P;
use syntax::ast;
use syntax::codemap;
use syntax::ext::build::AstBuilder;
use syntax::ext::base::{ExtCtxt, MacResult, MacExpr, DummyResult};
use syntax::parse::token;
use syntax::print::pprust;
use syntax::fold::Folder;
use syntax::ext::quote::rt::ToTokens;
use rustc::plugin::Registry;
use datetime::local::{LocalDate, DatePiece, Month, Weekday};
use datetime::format::{DateFormat, Field, NumArguments, TextArguments};
use pad::Alignment;
#[plugin_registrar]
#[doc(hidden)]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("date", date);
reg.register_macro("date_format", date_format);
}
fn date(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
let format_string = match get_string_argument(cx, tts) {
Some(f) => f,
None => return DummyResult::any(sp),
};
match LocalDate::parse(format_string.as_slice()) {
Some(date) => date_to_code(cx, date),
None => {
cx.span_err(sp, "Invalid date string");
DummyResult::any(sp)
},
}
}
fn date_format(cx: &mut ExtCtxt, sp: codemap::Span, tts: &[ast::TokenTree]) -> Box<MacResult+'static> {
let format_string = match get_string_argument(cx, tts) {
Some(f) => f,
None => return DummyResult::any(sp),
};
let formatter = match DateFormat::parse(format_string.as_slice()) {
Ok(f) => f,
Err(e) => {
cx.span_err(sp, format!("Couldn't parse date string `{:?}`", e).as_slice());
return DummyResult::any(sp);
}
};
let fields = formatter.fields.iter()
.map(|&x| field_to_code(cx, x))
.collect();
let things = cx.expr_vec(sp, fields);
MacExpr::new(quote_expr!(cx, datetime::format::DateFormat { fields: vec! $things }))
}
fn get_string_argument(cx: &mut ExtCtxt, tts: &[ast::TokenTree]) -> Option<String> {
let mut parser = cx.new_parser_from_tts(tts);
let entry = cx.expander().fold_expr(parser.parse_expr());
let formatter = match entry.node {
ast::ExprLit(ref lit) => {
match lit.node {
ast::LitStr(ref s, _) => s.to_string(),
_ => return fail(cx, entry.span, pprust::lit_to_string(&**lit)),
}
}
_ => return fail(cx, entry.span, pprust::expr_to_string(&*entry)),
};
if !parser.eat(&token::Eof) {
cx.span_err(parser.span, "only one argument allowed");
return None;
}
Some(formatter)
}
fn fail(cx: &mut ExtCtxt, span: codemap::Span, message: String) -> Option<String> {
cx.span_err(span, format!("expected string literal but got `{}`", message).as_slice());
return None;
}
fn month_to_code(cx: &mut ExtCtxt, month: Month) -> P<ast::Expr> {
match month {
Month::January => quote_expr!(cx, datetime::local::Month::January),
Month::February => quote_expr!(cx, datetime::local::Month::February),
Month::March => quote_expr!(cx, datetime::local::Month::March),
Month::April => quote_expr!(cx, datetime::local::Month::April),
Month::May => quote_expr!(cx, datetime::local::Month::May),
Month::June => quote_expr!(cx, datetime::local::Month::June),
Month::July => quote_expr!(cx, datetime::local::Month::July),
Month::August => quote_expr!(cx, datetime::local::Month::August),
Month::September => quote_expr!(cx, datetime::local::Month::September),
Month::October => quote_expr!(cx, datetime::local::Month::October),
Month::November => quote_expr!(cx, datetime::local::Month::November),
Month::December => quote_expr!(cx, datetime::local::Month::December),
}
}
fn weekday_to_code(cx: &mut ExtCtxt, weekday: Weekday) -> P<ast::Expr> {
match weekday {
Weekday::Monday => quote_expr!(cx, datetime::local::Weekday::Monday),
Weekday::Tuesday => quote_expr!(cx, datetime::local::Weekday::Tuesday),
Weekday::Wednesday => quote_expr!(cx, datetime::local::Weekday::Wednesday),
Weekday::Thursday => quote_expr!(cx, datetime::local::Weekday::Thursday),
Weekday::Friday => quote_expr!(cx, datetime::local::Weekday::Friday),
Weekday::Saturday => quote_expr!(cx, datetime::local::Weekday::Saturday),
Weekday::Sunday => quote_expr!(cx, datetime::local::Weekday::Sunday),
}
}
fn field_to_code(cx: &mut ExtCtxt, field: Field) -> P<ast::Expr> {
match field {
Field::Literal(s) => quote_expr!(cx, datetime::format::Field::Literal($s)),
Field::Year(a) => { let a = numargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::Year($a)) },
Field::MonthName(s, a) => { let a = textargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::MonthName($s, $a)) },
Field::Day(a) => { let a = numargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::Day($a)) },
Field::YearOfCentury(a) => { let a = numargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::YearOfCentury($a)) },
Field::WeekdayName(s, a) => { let a = textargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::WeekdayName($s, $a)) },
Field::Hour(a) => { let a = numargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::Hour($a)) },
Field::Minute(a) => { let a = numargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::Minute($a)) },
Field::Second(a) => { let a = numargs_to_code(cx, a); quote_expr!(cx, datetime::format::Field::Second($a)) },
}
}
fn date_to_code(cx: &mut ExtCtxt, date: LocalDate) -> Box<MacResult + 'static> {
let year = date.year();
let month = month_to_code(cx, date.month());
let day = date.day();
let weekday = weekday_to_code(cx, date.weekday());
let yearday = date.yearday();
println!("{:?}", date);
MacExpr::new(quote_expr!(cx, unsafe { datetime::local::LocalDate::_new_with_prefilled_values($year, $month, $day, $weekday, $yearday) }))
}
fn numargs_to_code(cx: &mut ExtCtxt, args: NumArguments) -> P<ast::Expr> {
let w = option_to_code(cx, args.args.width);
let a = alignment_to_code(cx, args.args.alignment);
let p = option_to_code(cx, args.args.pad_char);
quote_expr!(cx, datetime::format::NumArguments { args: datetime::format::Arguments { width: $w, alignment: $a, pad_char: $p, } })
}
fn textargs_to_code(cx: &mut ExtCtxt, args: TextArguments) -> P<ast::Expr> {
let w = option_to_code(cx, args.args.width);
let a = alignment_to_code(cx, args.args.alignment);
let p = option_to_code(cx, args.args.pad_char);
quote_expr!(cx, datetime::format::TextArguments { args: datetime::format::Arguments { width: $w, alignment: $a, pad_char: $p, } })
}
fn option_to_code<T: ToTokens>(cx: &mut ExtCtxt, option: Option<T>) -> P<ast::Expr> {
match option {
Some(value) => quote_expr!(cx, Some($value)),
None => quote_expr!(cx, None),
}
}
fn alignment_to_code(cx: &mut ExtCtxt, alignment: Option<Alignment>) -> P<ast::Expr> {
match alignment {
Some(Alignment::Left) => quote_expr!(cx, Some(Alignment::Left)),
Some(Alignment::Right) => quote_expr!(cx, Some(Alignment::Right)),
Some(Alignment::Middle) => quote_expr!(cx, Some(Alignment::Middle)),
Some(Alignment::MiddleRight) => quote_expr!(cx, Some(Alignment::MiddleRight)),
None => quote_expr!(cx, None),
}
}