rustversion/
expr.rs

1use crate::bound::{self, Bound};
2use crate::date::{self, Date};
3use crate::error::{Error, Result};
4use crate::iter::{self, Iter};
5use crate::release::{self, Release};
6use crate::token;
7use crate::version::{Channel, Version};
8use proc_macro::{Ident, Span, TokenTree};
9
10pub enum Expr {
11    Stable,
12    Beta,
13    Nightly,
14    Date(Date),
15    Since(Bound),
16    Before(Bound),
17    Release(Release),
18    Not(Box<Expr>),
19    Any(Vec<Expr>),
20    All(Vec<Expr>),
21}
22
23impl Expr {
24    pub fn eval(&self, rustc: Version) -> bool {
25        use self::Expr::*;
26
27        match self {
28            Stable => rustc.channel == Channel::Stable,
29            Beta => rustc.channel == Channel::Beta,
30            Nightly => match rustc.channel {
31                Channel::Nightly(_) | Channel::Dev => true,
32                Channel::Stable | Channel::Beta => false,
33            },
34            Date(date) => match rustc.channel {
35                Channel::Nightly(rustc) => rustc == *date,
36                Channel::Stable | Channel::Beta | Channel::Dev => false,
37            },
38            Since(bound) => rustc >= *bound,
39            Before(bound) => rustc < *bound,
40            Release(release) => {
41                rustc.channel == Channel::Stable
42                    && rustc.minor == release.minor
43                    && release.patch.map_or(true, |patch| rustc.patch == patch)
44            }
45            Not(expr) => !expr.eval(rustc),
46            Any(exprs) => exprs.iter().any(|e| e.eval(rustc)),
47            All(exprs) => exprs.iter().all(|e| e.eval(rustc)),
48        }
49    }
50}
51
52pub fn parse(iter: Iter) -> Result<Expr> {
53    match &iter.next() {
54        Some(TokenTree::Ident(i)) if i.to_string() == "stable" => parse_stable(iter),
55        Some(TokenTree::Ident(i)) if i.to_string() == "beta" => Ok(Expr::Beta),
56        Some(TokenTree::Ident(i)) if i.to_string() == "nightly" => parse_nightly(iter),
57        Some(TokenTree::Ident(i)) if i.to_string() == "since" => parse_since(i, iter),
58        Some(TokenTree::Ident(i)) if i.to_string() == "before" => parse_before(i, iter),
59        Some(TokenTree::Ident(i)) if i.to_string() == "not" => parse_not(i, iter),
60        Some(TokenTree::Ident(i)) if i.to_string() == "any" => parse_any(i, iter),
61        Some(TokenTree::Ident(i)) if i.to_string() == "all" => parse_all(i, iter),
62        unexpected => {
63            let span = unexpected
64                .as_ref()
65                .map_or_else(Span::call_site, TokenTree::span);
66            Err(Error::new(span, "expected one of `stable`, `beta`, `nightly`, `since`, `before`, `not`, `any`, `all`"))
67        }
68    }
69}
70
71fn parse_nightly(iter: Iter) -> Result<Expr> {
72    let paren = match token::parse_optional_paren(iter) {
73        Some(group) => group,
74        None => return Ok(Expr::Nightly),
75    };
76
77    let ref mut inner = iter::new(paren.stream());
78    let date = date::parse(paren, inner)?;
79    token::parse_optional_punct(inner, ',');
80    token::parse_end(inner)?;
81
82    Ok(Expr::Date(date))
83}
84
85fn parse_stable(iter: Iter) -> Result<Expr> {
86    let paren = match token::parse_optional_paren(iter) {
87        Some(group) => group,
88        None => return Ok(Expr::Stable),
89    };
90
91    let ref mut inner = iter::new(paren.stream());
92    let release = release::parse(paren, inner)?;
93    token::parse_optional_punct(inner, ',');
94    token::parse_end(inner)?;
95
96    Ok(Expr::Release(release))
97}
98
99fn parse_since(introducer: &Ident, iter: Iter) -> Result<Expr> {
100    let paren = token::parse_paren(introducer, iter)?;
101
102    let ref mut inner = iter::new(paren.stream());
103    let bound = bound::parse(paren, inner)?;
104    token::parse_optional_punct(inner, ',');
105    token::parse_end(inner)?;
106
107    Ok(Expr::Since(bound))
108}
109
110fn parse_before(introducer: &Ident, iter: Iter) -> Result<Expr> {
111    let paren = token::parse_paren(introducer, iter)?;
112
113    let ref mut inner = iter::new(paren.stream());
114    let bound = bound::parse(paren, inner)?;
115    token::parse_optional_punct(inner, ',');
116    token::parse_end(inner)?;
117
118    Ok(Expr::Before(bound))
119}
120
121fn parse_not(introducer: &Ident, iter: Iter) -> Result<Expr> {
122    let paren = token::parse_paren(introducer, iter)?;
123
124    let ref mut inner = iter::new(paren.stream());
125    let expr = self::parse(inner)?;
126    token::parse_optional_punct(inner, ',');
127    token::parse_end(inner)?;
128
129    Ok(Expr::Not(Box::new(expr)))
130}
131
132fn parse_any(introducer: &Ident, iter: Iter) -> Result<Expr> {
133    let paren = token::parse_paren(introducer, iter)?;
134
135    let ref mut inner = iter::new(paren.stream());
136    let exprs = parse_comma_separated(inner)?;
137
138    Ok(Expr::Any(exprs.into_iter().collect()))
139}
140
141fn parse_all(introducer: &Ident, iter: Iter) -> Result<Expr> {
142    let paren = token::parse_paren(introducer, iter)?;
143
144    let ref mut inner = iter::new(paren.stream());
145    let exprs = parse_comma_separated(inner)?;
146
147    Ok(Expr::All(exprs.into_iter().collect()))
148}
149
150fn parse_comma_separated(iter: Iter) -> Result<Vec<Expr>> {
151    let mut exprs = Vec::new();
152
153    while iter.peek().is_some() {
154        let expr = self::parse(iter)?;
155        exprs.push(expr);
156        if iter.peek().is_none() {
157            break;
158        }
159        token::parse_punct(iter, ',')?;
160    }
161
162    Ok(exprs)
163}