1 use crate::bound::{self, Bound};
2 use crate::date::{self, Date};
3 use crate::error::{Error, Result};
4 use crate::iter::{self, Iter};
5 use crate::release::{self, Release};
6 use crate::token;
7 use crate::version::{Channel, Version};
8 use proc_macro::{Ident, Span, TokenTree};
9
10 pub 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
23 impl Expr {
eval(&self, rustc: Version) -> bool24 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
parse(iter: Iter) -> Result<Expr>52 pub 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
parse_nightly(iter: Iter) -> Result<Expr>71 fn 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
parse_stable(iter: Iter) -> Result<Expr>85 fn 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
parse_since(introducer: &Ident, iter: Iter) -> Result<Expr>99 fn 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
parse_before(introducer: &Ident, iter: Iter) -> Result<Expr>110 fn 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
parse_not(introducer: &Ident, iter: Iter) -> Result<Expr>121 fn 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
parse_any(introducer: &Ident, iter: Iter) -> Result<Expr>132 fn 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
parse_all(introducer: &Ident, iter: Iter) -> Result<Expr>141 fn 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
parse_comma_separated(iter: Iter) -> Result<Vec<Expr>>150 fn 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 }
164