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#[cfg(feature = "serde")]
79impl serde::Serialize for VersionConstraint {
80 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
81 serializer.serialize_str(&self.to_string())
82 }
83}
84
85#[cfg(feature = "serde")]
86impl<'de> serde::Deserialize<'de> for VersionConstraint {
87 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
88 let s = String::deserialize(deserializer)?;
89 s.parse().map_err(serde::de::Error::custom)
90 }
91}
92
93#[cfg(all(test, feature = "serde"))]
94mod version_constraint_serde_tests {
95 use super::VersionConstraint;
96
97 #[test]
98 fn test_serialize() {
99 assert_eq!(
100 serde_json::to_string(&VersionConstraint::GreaterThanEqual).unwrap(),
101 "\">=\"",
102 );
103 assert_eq!(
104 serde_json::to_string(&VersionConstraint::LessThanEqual).unwrap(),
105 "\"<=\"",
106 );
107 assert_eq!(
108 serde_json::to_string(&VersionConstraint::Equal).unwrap(),
109 "\"=\"",
110 );
111 assert_eq!(
112 serde_json::to_string(&VersionConstraint::GreaterThan).unwrap(),
113 "\">>\"",
114 );
115 assert_eq!(
116 serde_json::to_string(&VersionConstraint::LessThan).unwrap(),
117 "\"<<\"",
118 );
119 }
120
121 #[test]
122 fn test_deserialize() {
123 assert_eq!(
124 serde_json::from_str::<VersionConstraint>("\">=\"").unwrap(),
125 VersionConstraint::GreaterThanEqual,
126 );
127 assert_eq!(
128 serde_json::from_str::<VersionConstraint>("\"<=\"").unwrap(),
129 VersionConstraint::LessThanEqual,
130 );
131 assert_eq!(
132 serde_json::from_str::<VersionConstraint>("\"=\"").unwrap(),
133 VersionConstraint::Equal,
134 );
135 assert_eq!(
136 serde_json::from_str::<VersionConstraint>("\">>\"").unwrap(),
137 VersionConstraint::GreaterThan,
138 );
139 assert_eq!(
140 serde_json::from_str::<VersionConstraint>("\"<<\"").unwrap(),
141 VersionConstraint::LessThan,
142 );
143 }
144
145 #[test]
146 fn test_deserialize_invalid() {
147 assert!(serde_json::from_str::<VersionConstraint>("\"!!\"").is_err());
148 }
149}
150
151#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
154#[allow(non_camel_case_types)]
155#[repr(u16)]
156#[allow(missing_docs)]
157pub enum SyntaxKind {
158 IDENT = 0, COLON, PIPE,
161 COMMA, L_PARENS, R_PARENS, L_BRACKET, R_BRACKET, NOT, L_ANGLE, R_ANGLE, EQUAL, WHITESPACE, NEWLINE, DOLLAR, L_CURLY,
174 R_CURLY,
175 COMMENT, ERROR, ROOT, ENTRY, RELATION, ARCHQUAL, VERSION, CONSTRAINT, ARCHITECTURES,
186 PROFILES,
187 SUBSTVAR,
188}
189
190#[cfg(feature = "lossless")]
192impl From<SyntaxKind> for rowan::SyntaxKind {
193 fn from(kind: SyntaxKind) -> Self {
194 Self(kind as u16)
195 }
196}
197
198pub struct Lexer<'a> {
200 input: Peekable<Chars<'a>>,
201}
202
203impl<'a> Lexer<'a> {
204 pub fn new(input: &'a str) -> Self {
206 Lexer {
207 input: input.chars().peekable(),
208 }
209 }
210
211 fn is_whitespace(c: char) -> bool {
212 c == ' ' || c == '\t' || c == '\r'
213 }
214
215 fn is_valid_ident_char(c: char) -> bool {
216 c.is_ascii_alphanumeric() || c == '-' || c == '.' || c == '+' || c == '~'
217 }
218
219 fn read_while<F>(&mut self, predicate: F) -> String
220 where
221 F: Fn(char) -> bool,
222 {
223 let mut result = String::new();
224 while let Some(&c) = self.input.peek() {
225 if predicate(c) {
226 result.push(c);
227 self.input.next();
228 } else {
229 break;
230 }
231 }
232 result
233 }
234
235 fn next_token(&mut self) -> Option<(SyntaxKind, String)> {
236 if let Some(&c) = self.input.peek() {
237 match c {
238 ':' => {
239 self.input.next();
240 Some((SyntaxKind::COLON, c.to_string()))
241 }
242 '|' => {
243 self.input.next();
244 Some((SyntaxKind::PIPE, c.to_string()))
245 }
246 ',' => {
247 self.input.next();
248 Some((SyntaxKind::COMMA, c.to_string()))
249 }
250 '(' => {
251 self.input.next();
252 Some((SyntaxKind::L_PARENS, c.to_string()))
253 }
254 ')' => {
255 self.input.next();
256 Some((SyntaxKind::R_PARENS, c.to_string()))
257 }
258 '[' => {
259 self.input.next();
260 Some((SyntaxKind::L_BRACKET, c.to_string()))
261 }
262 ']' => {
263 self.input.next();
264 Some((SyntaxKind::R_BRACKET, c.to_string()))
265 }
266 '!' => {
267 self.input.next();
268 Some((SyntaxKind::NOT, c.to_string()))
269 }
270 '$' => {
271 self.input.next();
272 Some((SyntaxKind::DOLLAR, c.to_string()))
273 }
274 '{' => {
275 self.input.next();
276 Some((SyntaxKind::L_CURLY, c.to_string()))
277 }
278 '}' => {
279 self.input.next();
280 Some((SyntaxKind::R_CURLY, c.to_string()))
281 }
282 '<' => {
283 self.input.next();
284 Some((SyntaxKind::L_ANGLE, c.to_string()))
285 }
286 '>' => {
287 self.input.next();
288 Some((SyntaxKind::R_ANGLE, c.to_string()))
289 }
290 '=' => {
291 self.input.next();
292 Some((SyntaxKind::EQUAL, c.to_string()))
293 }
294 '\n' => {
295 self.input.next();
296 Some((SyntaxKind::NEWLINE, c.to_string()))
297 }
298 '#' => {
299 let comment = self.read_while(|c| c != '\n');
300 Some((SyntaxKind::COMMENT, comment))
301 }
302 _ if Self::is_whitespace(c) => {
303 let whitespace = self.read_while(Self::is_whitespace);
304 Some((SyntaxKind::WHITESPACE, whitespace))
305 }
306 _ if Self::is_valid_ident_char(c) => {
308 let key = self.read_while(Self::is_valid_ident_char);
309 Some((SyntaxKind::IDENT, key))
310 }
311 _ => {
312 self.input.next();
313 Some((SyntaxKind::ERROR, c.to_string()))
314 }
315 }
316 } else {
317 None
318 }
319 }
320}
321
322impl Iterator for Lexer<'_> {
323 type Item = (SyntaxKind, String);
324
325 fn next(&mut self) -> Option<Self::Item> {
326 self.next_token()
327 }
328}
329
330pub(crate) fn lex(input: &str) -> Vec<(SyntaxKind, String)> {
331 let mut lexer = Lexer::new(input);
332 lexer.by_ref().collect::<Vec<_>>()
333}