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 COMMENT, ERROR, ROOT, ENTRY, RELATION, ARCHQUAL, VERSION, CONSTRAINT, ARCHITECTURES,
113 PROFILES,
114 SUBSTVAR,
115}
116
117#[cfg(feature = "lossless")]
119impl From<SyntaxKind> for rowan::SyntaxKind {
120 fn from(kind: SyntaxKind) -> Self {
121 Self(kind as u16)
122 }
123}
124
125pub struct Lexer<'a> {
127 input: Peekable<Chars<'a>>,
128}
129
130impl<'a> Lexer<'a> {
131 pub fn new(input: &'a str) -> Self {
133 Lexer {
134 input: input.chars().peekable(),
135 }
136 }
137
138 fn is_whitespace(c: char) -> bool {
139 c == ' ' || c == '\t' || c == '\r'
140 }
141
142 fn is_valid_ident_char(c: char) -> bool {
143 c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '+' || c == '~'
144 }
145
146 fn read_while<F>(&mut self, predicate: F) -> String
147 where
148 F: Fn(char) -> bool,
149 {
150 let mut result = String::new();
151 while let Some(&c) = self.input.peek() {
152 if predicate(c) {
153 result.push(c);
154 self.input.next();
155 } else {
156 break;
157 }
158 }
159 result
160 }
161
162 fn next_token(&mut self) -> Option<(SyntaxKind, String)> {
163 if let Some(&c) = self.input.peek() {
164 match c {
165 ':' => {
166 self.input.next();
167 Some((SyntaxKind::COLON, c.to_string()))
168 }
169 '|' => {
170 self.input.next();
171 Some((SyntaxKind::PIPE, c.to_string()))
172 }
173 ',' => {
174 self.input.next();
175 Some((SyntaxKind::COMMA, c.to_string()))
176 }
177 '(' => {
178 self.input.next();
179 Some((SyntaxKind::L_PARENS, c.to_string()))
180 }
181 ')' => {
182 self.input.next();
183 Some((SyntaxKind::R_PARENS, c.to_string()))
184 }
185 '[' => {
186 self.input.next();
187 Some((SyntaxKind::L_BRACKET, c.to_string()))
188 }
189 ']' => {
190 self.input.next();
191 Some((SyntaxKind::R_BRACKET, c.to_string()))
192 }
193 '!' => {
194 self.input.next();
195 Some((SyntaxKind::NOT, c.to_string()))
196 }
197 '$' => {
198 self.input.next();
199 Some((SyntaxKind::DOLLAR, c.to_string()))
200 }
201 '{' => {
202 self.input.next();
203 Some((SyntaxKind::L_CURLY, c.to_string()))
204 }
205 '}' => {
206 self.input.next();
207 Some((SyntaxKind::R_CURLY, c.to_string()))
208 }
209 '<' => {
210 self.input.next();
211 Some((SyntaxKind::L_ANGLE, c.to_string()))
212 }
213 '>' => {
214 self.input.next();
215 Some((SyntaxKind::R_ANGLE, c.to_string()))
216 }
217 '=' => {
218 self.input.next();
219 Some((SyntaxKind::EQUAL, c.to_string()))
220 }
221 '\n' => {
222 self.input.next();
223 Some((SyntaxKind::NEWLINE, c.to_string()))
224 }
225 '#' => {
226 let comment = self.read_while(|c| c != '\n');
227 Some((SyntaxKind::COMMENT, comment))
228 }
229 _ if Self::is_whitespace(c) => {
230 let whitespace = self.read_while(Self::is_whitespace);
231 Some((SyntaxKind::WHITESPACE, whitespace))
232 }
233 _ if Self::is_valid_ident_char(c) => {
235 let key = self.read_while(Self::is_valid_ident_char);
236 Some((SyntaxKind::IDENT, key))
237 }
238 _ => {
239 self.input.next();
240 Some((SyntaxKind::ERROR, c.to_string()))
241 }
242 }
243 } else {
244 None
245 }
246 }
247}
248
249impl Iterator for Lexer<'_> {
250 type Item = (SyntaxKind, String);
251
252 fn next(&mut self) -> Option<Self::Item> {
253 self.next_token()
254 }
255}
256
257pub(crate) fn lex(input: &str) -> Vec<(SyntaxKind, String)> {
258 let mut lexer = Lexer::new(input);
259 lexer.by_ref().collect::<Vec<_>>()
260}