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: &Ident)) if i.to_string() == "stable" => parse_stable(iter),
55 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "beta" => Ok(Expr::Beta),
56 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "nightly" => parse_nightly(iter),
57 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "since" => parse_since(introducer:i, iter),
58 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "before" => parse_before(introducer:i, iter),
59 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "not" => parse_not(introducer:i, iter),
60 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "any" => parse_any(introducer:i, iter),
61 Some(TokenTree::Ident(i: &Ident)) if i.to_string() == "all" => parse_all(introducer:i, iter),
62 unexpected: &Option => {
63 let span: Span = unexpected
64 .as_ref()
65 .map_or_else(default:Span::call_site, f:TokenTree::span);
66 Err(Error::new(span, msg:"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: Group = match token::parse_optional_paren(iter) {
73 Some(group: Group) => group,
74 None => return Ok(Expr::Nightly),
75 };
76
77 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
78 let date: Date = date::parse(paren, iter:inner)?;
79 token::parse_optional_punct(iter:inner, ch:',');
80 token::parse_end(iter:inner)?;
81
82 Ok(Expr::Date(date))
83}
84
85fn parse_stable(iter: Iter) -> Result<Expr> {
86 let paren: Group = match token::parse_optional_paren(iter) {
87 Some(group: Group) => group,
88 None => return Ok(Expr::Stable),
89 };
90
91 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
92 let release: Release = release::parse(paren, iter:inner)?;
93 token::parse_optional_punct(iter:inner, ch:',');
94 token::parse_end(iter:inner)?;
95
96 Ok(Expr::Release(release))
97}
98
99fn parse_since(introducer: &Ident, iter: Iter) -> Result<Expr> {
100 let paren: Group = token::parse_paren(introducer, iter)?;
101
102 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
103 let bound: Bound = bound::parse(paren, iter:inner)?;
104 token::parse_optional_punct(iter:inner, ch:',');
105 token::parse_end(iter:inner)?;
106
107 Ok(Expr::Since(bound))
108}
109
110fn parse_before(introducer: &Ident, iter: Iter) -> Result<Expr> {
111 let paren: Group = token::parse_paren(introducer, iter)?;
112
113 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
114 let bound: Bound = bound::parse(paren, iter:inner)?;
115 token::parse_optional_punct(iter:inner, ch:',');
116 token::parse_end(iter:inner)?;
117
118 Ok(Expr::Before(bound))
119}
120
121fn parse_not(introducer: &Ident, iter: Iter) -> Result<Expr> {
122 let paren: Group = token::parse_paren(introducer, iter)?;
123
124 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
125 let expr: Expr = self::parse(iter:inner)?;
126 token::parse_optional_punct(iter:inner, ch:',');
127 token::parse_end(iter:inner)?;
128
129 Ok(Expr::Not(Box::new(expr)))
130}
131
132fn parse_any(introducer: &Ident, iter: Iter) -> Result<Expr> {
133 let paren: Group = token::parse_paren(introducer, iter)?;
134
135 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
136 let exprs: Vec = parse_comma_separated(iter:inner)?;
137
138 Ok(Expr::Any(exprs.into_iter().collect()))
139}
140
141fn parse_all(introducer: &Ident, iter: Iter) -> Result<Expr> {
142 let paren: Group = token::parse_paren(introducer, iter)?;
143
144 let ref mut inner: &mut IterImpl = iter::new(tokens:paren.stream());
145 let exprs: Vec = parse_comma_separated(iter: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 = Vec::new();
152
153 while iter.peek().is_some() {
154 let expr: Expr = self::parse(iter)?;
155 exprs.push(expr);
156 if iter.peek().is_none() {
157 break;
158 }
159 token::parse_punct(iter, ch:',')?;
160 }
161
162 Ok(exprs)
163}
164