synthax 0.4.1

Synthesize syntax with quasiquoting plugins.
// Copyright 2016 Kyle Mayes
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use syntax::ast::*;
use syntax::codemap::{Span};
use syntax::ext::base::{ExtCtxt};
use syntax::parse::{PResult};
use syntax::parse::parser::{Parser};
use syntax::ptr::{P};
use syntax::tokenstream::{TokenTree};

//================================================
// Functions
//================================================

#[doc(hidden)]
pub fn parse_tts<'a, T, F: Fn(&mut Parser<'a>) -> PResult<'a, T>>(
    context: &ExtCtxt<'a>, span: Span, tts: &[TokenTree], f: F
) -> Option<T> {
    let mut parser = context.new_parser_from_tts(tts);
    match f(&mut parser) {
        Ok(ok) => Some(ok),
        Err(mut err) => {
            if err.span.primary_span().map_or(true, |s| s == ::syntax::codemap::DUMMY_SP) {
                err.set_span(span);
            }
            err.emit();
            None
        },
    }
}

#[doc(hidden)]
pub fn parse_bare_fn_ty(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> P<BareFnTy> {
    match parse_tts(context, span, tts, |p| p.parse_ty()).unwrap().node {
        TyKind::BareFn(ref ty) => ty.clone(),
        _ => {
            context.span_err(span, "expected bare fn ty");
            panic!();
        },
    }
}

#[doc(hidden)]
pub fn parse_expr(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> Option<P<Expr>> {
    parse_tts(context, span, tts, |p| p.parse_expr())
}

#[doc(hidden)]
pub fn parse_field_pat(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> FieldPat {
    match parse_tts(context, span, tts, |p| p.parse_pat()).unwrap().node {
        PatKind::Struct(_, ref pats, _) => if !pats.is_empty() {
            pats[0].node.clone()
        } else {
            context.span_err(span, "expected field pat");
            panic!();
        },
        _ => unreachable!(),
    }
}

#[doc(hidden)]
pub fn parse_foreign_item(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> ForeignItem {
    match parse_tts(context, span, tts, |p| p.parse_item()).unwrap().unwrap().node {
        ItemKind::ForeignMod(ref foreign) => if !foreign.items.is_empty() {
            foreign.items[0].clone()
        } else {
            context.span_err(span, "expected foreign item");
            panic!();
        },
        _ => unreachable!(),
    }
}

#[doc(hidden)]
pub fn parse_foreign_mod(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> ForeignMod {
    match parse_tts(context, span, tts, |p| p.parse_item()).unwrap().map(|i| i.node.clone()) {
        Some(ItemKind::ForeignMod(foreign)) => foreign,
        _ => {
            context.span_err(span, "expected foreign mod");
            panic!();
        },
    }
}

#[doc(hidden)]
pub fn parse_lifetime(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> Lifetime {
    let mut parser = context.new_parser_from_tts(tts);
    if let Some(lifetime) = parser.eat_lifetime() {
        lifetime
    } else {
        context.span_err(span, "expected lifetime");
        panic!();
    }
}

#[doc(hidden)]
pub fn parse_local(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> P<Local> {
    match parse_tts(context, span, tts, |p| p.parse_stmt()).unwrap().map(|s| s.node.clone()) {
        Some(StmtKind::Local(local)) => local,
        _ => {
            context.span_err(span, "expected local");
            panic!();
        },
    }
}

#[doc(hidden)]
pub fn parse_struct_field(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> StructField {
    match parse_tts(context, span, tts, |p| p.parse_item()).unwrap().map(|i| i.node.clone()) {
        Some(ItemKind::Struct(VariantData::Struct(ref fields, _), _)) => if !fields.is_empty() {
            fields[0].clone()
        } else {
            context.span_err(span, "expected struct field");
            panic!();
        },
        _ => unreachable!(),
    }
}

#[doc(hidden)]
pub fn parse_variant(context: &ExtCtxt, span: Span, tts: &[TokenTree]) -> Variant {
    match parse_tts(context, span, tts, |p| p.parse_item()).unwrap().map(|i| i.node.clone()) {
        Some(ItemKind::Enum(ref def, _)) => if !def.variants.is_empty() {
            def.variants[0].clone()
        } else {
            context.span_err(span, "expected variant");
            panic!();
        },
        _ => unreachable!(),
    }
}