1use std::iter::Peekable;
3use std::str::Chars;
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash)]
7pub enum BuildProfile {
8 Enabled(String),
10
11 Disabled(String),
13}
14
15impl std::fmt::Display for BuildProfile {
16 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
17 match self {
18 BuildProfile::Enabled(s) => f.write_str(s),
19 BuildProfile::Disabled(s) => write!(f, "!{}", s),
20 }
21 }
22}
23
24impl std::str::FromStr for BuildProfile {
25 type Err = String;
26
27 fn from_str(s: &str) -> Result<Self, Self::Err> {
28 if let Some(s) = s.strip_prefix('!') {
29 Ok(BuildProfile::Disabled(s.to_string()))
30 } else {
31 Ok(BuildProfile::Enabled(s.to_string()))
32 }
33 }
34}
35
36#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
38pub enum VersionConstraint {
39 LessThan, LessThanEqual, Equal, GreaterThan, GreaterThanEqual, }
50
51impl std::str::FromStr for VersionConstraint {
52 type Err = String;
53
54 fn from_str(s: &str) -> Result<Self, Self::Err> {
55 match s {
56 ">=" => Ok(VersionConstraint::GreaterThanEqual),
57 "<=" => Ok(VersionConstraint::LessThanEqual),
58 "=" => Ok(VersionConstraint::Equal),
59 ">>" => Ok(VersionConstraint::GreaterThan),
60 "<<" => Ok(VersionConstraint::LessThan),
61 _ => Err(format!("Invalid version constraint: {}", s)),
62 }
63 }
64}
65
66impl std::fmt::Display for VersionConstraint {
67 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
68 match self {
69 VersionConstraint::GreaterThanEqual => f.write_str(">="),
70 VersionConstraint::LessThanEqual => f.write_str("<="),
71 VersionConstraint::Equal => f.write_str("="),
72 VersionConstraint::GreaterThan => f.write_str(">>"),
73 VersionConstraint::LessThan => f.write_str("<<"),
74 }
75 }
76}
77
78#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
81#[allow(non_camel_case_types)]
82#[repr(u16)]
83#[allow(missing_docs)]
84pub enum SyntaxKind {
85 IDENT = 0, COLON, PIPE,
88 COMMA, L_PARENS, R_PARENS, L_BRACKET, R_BRACKET, NOT, L_ANGLE, R_ANGLE, EQUAL, WHITESPACE, NEWLINE, DOLLAR, L_CURLY,
101 R_CURLY,
102 ERROR, ROOT, ENTRY, RELATION, ARCHQUAL, VERSION, CONSTRAINT, ARCHITECTURES,
112 PROFILES,
113 SUBSTVAR,
114}
115
116#[cfg(feature = "lossless")]
118impl From<SyntaxKind> for rowan::SyntaxKind {
119 fn from(kind: SyntaxKind) -> Self {
120 Self(kind as u16)
121 }
122}
123
124pub struct Lexer<'a> {
126 input: Peekable<Chars<'a>>,
127}
128
129impl<'a> Lexer<'a> {
130 pub fn new(input: &'a str) -> Self {
132 Lexer {
133 input: input.chars().peekable(),
134 }
135 }
136
137 fn is_whitespace(c: char) -> bool {
138 c == ' ' || c == '\t' || c == '\r'
139 }
140
141 fn is_valid_ident_char(c: char) -> bool {
142 c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '+' || c == '~'
143 }
144
145 fn read_while<F>(&mut self, predicate: F) -> String
146 where
147 F: Fn(char) -> bool,
148 {
149 let mut result = String::new();
150 while let Some(&c) = self.input.peek() {
151 if predicate(c) {
152 result.push(c);
153 self.input.next();
154 } else {
155 break;
156 }
157 }
158 result
159 }
160
161 fn next_token(&mut self) -> Option<(SyntaxKind, String)> {
162 if let Some(&c) = self.input.peek() {
163 match c {
164 ':' => {
165 self.input.next();
166 Some((SyntaxKind::COLON, c.to_string()))
167 }
168 '|' => {
169 self.input.next();
170 Some((SyntaxKind::PIPE, c.to_string()))
171 }
172 ',' => {
173 self.input.next();
174 Some((SyntaxKind::COMMA, c.to_string()))
175 }
176 '(' => {
177 self.input.next();
178 Some((SyntaxKind::L_PARENS, c.to_string()))
179 }
180 ')' => {
181 self.input.next();
182 Some((SyntaxKind::R_PARENS, c.to_string()))
183 }
184 '[' => {
185 self.input.next();
186 Some((SyntaxKind::L_BRACKET, c.to_string()))
187 }
188 ']' => {
189 self.input.next();
190 Some((SyntaxKind::R_BRACKET, c.to_string()))
191 }
192 '!' => {
193 self.input.next();
194 Some((SyntaxKind::NOT, c.to_string()))
195 }
196 '$' => {
197 self.input.next();
198 Some((SyntaxKind::DOLLAR, c.to_string()))
199 }
200 '{' => {
201 self.input.next();
202 Some((SyntaxKind::L_CURLY, c.to_string()))
203 }
204 '}' => {
205 self.input.next();
206 Some((SyntaxKind::R_CURLY, c.to_string()))
207 }
208 '<' => {
209 self.input.next();
210 Some((SyntaxKind::L_ANGLE, c.to_string()))
211 }
212 '>' => {
213 self.input.next();
214 Some((SyntaxKind::R_ANGLE, c.to_string()))
215 }
216 '=' => {
217 self.input.next();
218 Some((SyntaxKind::EQUAL, c.to_string()))
219 }
220 '\n' => {
221 self.input.next();
222 Some((SyntaxKind::NEWLINE, c.to_string()))
223 }
224 _ if Self::is_whitespace(c) => {
225 let whitespace = self.read_while(Self::is_whitespace);
226 Some((SyntaxKind::WHITESPACE, whitespace))
227 }
228 _ if Self::is_valid_ident_char(c) => {
230 let key = self.read_while(Self::is_valid_ident_char);
231 Some((SyntaxKind::IDENT, key))
232 }
233 _ => {
234 self.input.next();
235 Some((SyntaxKind::ERROR, c.to_string()))
236 }
237 }
238 } else {
239 None
240 }
241 }
242}
243
244impl Iterator for Lexer<'_> {
245 type Item = (SyntaxKind, String);
246
247 fn next(&mut self) -> Option<Self::Item> {
248 self.next_token()
249 }
250}
251
252pub(crate) fn lex(input: &str) -> Vec<(SyntaxKind, String)> {
253 let mut lexer = Lexer::new(input);
254 lexer.by_ref().collect::<Vec<_>>()
255}