| use crate::bound::{self, Bound}; |
| use crate::date::{self, Date}; |
| use crate::error::{Error, Result}; |
| use crate::iter::{self, Iter}; |
| use crate::release::{self, Release}; |
| use crate::token; |
| use crate::version::{Channel, Version}; |
| use proc_macro::{Ident, Span, TokenTree}; |
| |
| pub enum Expr { |
| Stable, |
| Beta, |
| Nightly, |
| Date(Date), |
| Since(Bound), |
| Before(Bound), |
| Release(Release), |
| Not(Box<Expr>), |
| Any(Vec<Expr>), |
| All(Vec<Expr>), |
| } |
| |
| impl Expr { |
| pub fn eval(&self, rustc: Version) -> bool { |
| use self::Expr::*; |
| |
| match self { |
| Stable => rustc.channel == Channel::Stable, |
| Beta => rustc.channel == Channel::Beta, |
| Nightly => match rustc.channel { |
| Channel::Nightly(_) | Channel::Dev => true, |
| Channel::Stable | Channel::Beta => false, |
| }, |
| Date(date) => match rustc.channel { |
| Channel::Nightly(rustc) => rustc == *date, |
| Channel::Stable | Channel::Beta | Channel::Dev => false, |
| }, |
| Since(bound) => rustc >= *bound, |
| Before(bound) => rustc < *bound, |
| Release(release) => { |
| rustc.channel == Channel::Stable |
| && rustc.minor == release.minor |
| && release.patch.map_or(true, |patch| rustc.patch == patch) |
| } |
| Not(expr) => !expr.eval(rustc), |
| Any(exprs) => exprs.iter().any(|e| e.eval(rustc)), |
| All(exprs) => exprs.iter().all(|e| e.eval(rustc)), |
| } |
| } |
| } |
| |
| pub fn parse(iter: Iter) -> Result<Expr> { |
| match &iter.next() { |
| Some(TokenTree::Ident(i)) if i.to_string() == "stable" => parse_stable(iter), |
| Some(TokenTree::Ident(i)) if i.to_string() == "beta" => Ok(Expr::Beta), |
| Some(TokenTree::Ident(i)) if i.to_string() == "nightly" => parse_nightly(iter), |
| Some(TokenTree::Ident(i)) if i.to_string() == "since" => parse_since(i, iter), |
| Some(TokenTree::Ident(i)) if i.to_string() == "before" => parse_before(i, iter), |
| Some(TokenTree::Ident(i)) if i.to_string() == "not" => parse_not(i, iter), |
| Some(TokenTree::Ident(i)) if i.to_string() == "any" => parse_any(i, iter), |
| Some(TokenTree::Ident(i)) if i.to_string() == "all" => parse_all(i, iter), |
| unexpected => { |
| let span = unexpected |
| .as_ref() |
| .map_or_else(Span::call_site, TokenTree::span); |
| Err(Error::new(span, "expected one of `stable`, `beta`, `nightly`, `since`, `before`, `not`, `any`, `all`")) |
| } |
| } |
| } |
| |
| fn parse_nightly(iter: Iter) -> Result<Expr> { |
| let paren = match token::parse_optional_paren(iter) { |
| Some(group) => group, |
| None => return Ok(Expr::Nightly), |
| }; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let date = date::parse(paren, inner)?; |
| token::parse_optional_punct(inner, ','); |
| token::parse_end(inner)?; |
| |
| Ok(Expr::Date(date)) |
| } |
| |
| fn parse_stable(iter: Iter) -> Result<Expr> { |
| let paren = match token::parse_optional_paren(iter) { |
| Some(group) => group, |
| None => return Ok(Expr::Stable), |
| }; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let release = release::parse(paren, inner)?; |
| token::parse_optional_punct(inner, ','); |
| token::parse_end(inner)?; |
| |
| Ok(Expr::Release(release)) |
| } |
| |
| fn parse_since(introducer: &Ident, iter: Iter) -> Result<Expr> { |
| let paren = token::parse_paren(introducer, iter)?; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let bound = bound::parse(paren, inner)?; |
| token::parse_optional_punct(inner, ','); |
| token::parse_end(inner)?; |
| |
| Ok(Expr::Since(bound)) |
| } |
| |
| fn parse_before(introducer: &Ident, iter: Iter) -> Result<Expr> { |
| let paren = token::parse_paren(introducer, iter)?; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let bound = bound::parse(paren, inner)?; |
| token::parse_optional_punct(inner, ','); |
| token::parse_end(inner)?; |
| |
| Ok(Expr::Before(bound)) |
| } |
| |
| fn parse_not(introducer: &Ident, iter: Iter) -> Result<Expr> { |
| let paren = token::parse_paren(introducer, iter)?; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let expr = self::parse(inner)?; |
| token::parse_optional_punct(inner, ','); |
| token::parse_end(inner)?; |
| |
| Ok(Expr::Not(Box::new(expr))) |
| } |
| |
| fn parse_any(introducer: &Ident, iter: Iter) -> Result<Expr> { |
| let paren = token::parse_paren(introducer, iter)?; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let exprs = parse_comma_separated(inner)?; |
| |
| Ok(Expr::Any(exprs.into_iter().collect())) |
| } |
| |
| fn parse_all(introducer: &Ident, iter: Iter) -> Result<Expr> { |
| let paren = token::parse_paren(introducer, iter)?; |
| |
| let ref mut inner = iter::new(paren.stream()); |
| let exprs = parse_comma_separated(inner)?; |
| |
| Ok(Expr::All(exprs.into_iter().collect())) |
| } |
| |
| fn parse_comma_separated(iter: Iter) -> Result<Vec<Expr>> { |
| let mut exprs = Vec::new(); |
| |
| while iter.peek().is_some() { |
| let expr = self::parse(iter)?; |
| exprs.push(expr); |
| if iter.peek().is_none() { |
| break; |
| } |
| token::parse_punct(iter, ',')?; |
| } |
| |
| Ok(exprs) |
| } |