1use crate::{CommandParseError, Token};
2
3pub trait CMakeParse<'t>: 't + Sized {
4 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError>;
5
6 fn complete(tokens: &[Token<'t>]) -> Result<Self, CommandParseError> {
7 let (result, tokens) = Self::parse(tokens)?;
8 if !tokens.is_empty() {
9 return Err(CommandParseError::Incomplete);
10 }
11
12 Ok(result)
13 }
14
15 fn default_value() -> Option<Self> {
16 None
17 }
18
19 fn matches(&self, field_keyword: &[u8], keyword: &[u8], tokens: &[Token<'t>]) -> bool {
20 Self::matches_type(field_keyword, keyword, tokens)
21 }
22
23 fn matches_type(
24 field_keyword: &[u8],
25 keyword: &[u8],
26 #[allow(unused_variables)] tokens: &[Token<'t>],
27 ) -> bool {
28 field_keyword == keyword
29 }
30
31 fn need_update(
32 #[allow(unused_variables)] field_keyword: &[u8],
33 #[allow(unused_variables)] keyword: &Token<'t>,
34 buffer: &[Token<'t>],
35 ) -> bool {
36 !buffer.is_empty()
37 }
38
39 fn start<'tv>(
40 &mut self,
41 field_keyword: &[u8],
42 keyword: &Token<'t>,
43 tokens: &'tv [Token<'t>],
44 buffer: &mut Vec<Token<'t>>,
45 ) -> Result<(bool, &'tv [Token<'t>]), CommandParseError> {
46 if Self::need_update(field_keyword, keyword, buffer) {
47 self.update(buffer)?;
48 buffer.clear();
49 }
50
51 if Self::need_push_keyword(keyword) {
52 buffer.push(keyword.clone());
53 }
54
55 Ok((Self::update_mode(keyword), Self::rest(tokens)))
56 }
57
58 fn rest<'tv>(tokens: &'tv [Token<'t>]) -> &'tv [Token<'t>] {
59 tokens
60 }
61
62 fn need_push_keyword(keyword: &Token<'t>) -> bool {
63 !Self::update_mode(keyword)
64 }
65
66 fn update_mode(#[allow(unused_variables)] keyword: &Token<'t>) -> bool {
67 true
68 }
69
70 fn update<'tv>(&mut self, tokens: &'tv [Token<'t>]) -> Result<(), CommandParseError> {
71 Self::complete(tokens).map(|res| *self = res)
72 }
73
74 fn end<'tv>(mut self, tokens: &'tv [Token<'t>]) -> Result<Self, CommandParseError> {
75 self.update(tokens)?;
76
77 Ok(self)
78 }
79
80 fn push_keyword(&mut self, buffer: &mut Vec<Token<'t>>, keyword: &Token<'t>) -> bool {
81 buffer.push(keyword.clone());
82 Self::reset_mode()
83 }
84
85 fn reset_mode() -> bool {
86 false
87 }
88}
89
90impl<'t> CMakeParse<'t> for Token<'t> {
91 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
92 tokens
93 .split_first()
94 .map(|(first, rest)| (first.clone(), rest))
95 .ok_or(CommandParseError::TokenRequired)
96 }
97
98 fn reset_mode() -> bool {
99 true
100 }
101}
102
103impl<'t, T> CMakeParse<'t> for Option<T>
104where
105 T: CMakeParse<'t>,
106{
107 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
108 T::parse(tokens).map(|(result, rest)| (Some(result), rest))
109 }
110
111 fn complete(tokens: &[Token<'t>]) -> Result<Self, CommandParseError> {
112 match Self::parse(tokens) {
113 Ok((result, _)) => Ok(result),
114 Err(CommandParseError::TokenRequired) => Ok(None),
115 Err(err) => Err(err),
116 }
117 }
118
119 fn default_value() -> Option<Self> {
120 Some(T::default_value())
121 }
122
123 fn matches_type(field_keyword: &[u8], keyword: &[u8], tokens: &[Token<'t>]) -> bool {
124 T::matches_type(field_keyword, keyword, tokens)
125 }
126
127 fn update<'tv>(&mut self, tokens: &'tv [Token<'t>]) -> Result<(), CommandParseError> {
128 if let Some(t) = self {
129 t.update(tokens)
130 } else {
131 Self::complete(tokens).map(|res| *self = res)
132 }
133 }
134
135 fn need_update(field_keyword: &[u8], keyword: &Token<'t>, buffer: &[Token<'t>]) -> bool {
136 T::need_update(field_keyword, keyword, buffer)
137 }
138
139 fn need_push_keyword(keyword: &Token<'t>) -> bool {
140 T::need_push_keyword(keyword)
141 }
142
143 fn rest<'tv>(tokens: &'tv [Token<'t>]) -> &'tv [Token<'t>] {
144 T::rest(tokens)
145 }
146
147 fn update_mode(keyword: &Token<'t>) -> bool {
148 T::update_mode(keyword)
149 }
150
151 fn reset_mode() -> bool {
152 T::reset_mode()
153 }
154}
155
156impl<'t> CMakeParse<'t> for bool {
157 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
158 Ok(tokens
159 .split_first()
160 .map(|(_, rest)| (true, rest))
161 .unwrap_or_else(|| (false, tokens)))
162 }
163
164 fn update_mode(#[allow(unused_variables)] keyword: &Token<'t>) -> bool {
165 false
166 }
167}
168
169impl<'t> CMakeParse<'t> for () {
170 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
171 Ok(tokens
172 .split_first()
173 .map(|(_, rest)| ((), rest))
174 .unwrap_or_else(|| ((), &[])))
175 }
176
177 fn update_mode(#[allow(unused_variables)] keyword: &Token<'t>) -> bool {
178 false
179 }
180}
181
182impl<'t, T> CMakeParse<'t> for Vec<T>
183where
184 T: CMakeParse<'t>,
185{
186 fn parse<'tv>(
187 mut tokens: &'tv [Token<'t>],
188 ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
189 let mut result = vec![];
190 loop {
191 let (val, new_tokens) = T::parse(tokens)?;
192 result.push(val);
193 if new_tokens.len() == tokens.len() {
194 break;
195 }
196 tokens = new_tokens;
197 if tokens.is_empty() {
198 break;
199 }
200 }
201 Ok((result, tokens))
202 }
203
204 fn need_update(field_keyword: &[u8], keyword: &Token<'t>, buffer: &[Token<'t>]) -> bool {
205 T::need_update(field_keyword, keyword, buffer)
206 }
207
208 fn need_push_keyword(keyword: &Token<'t>) -> bool {
209 T::need_push_keyword(keyword)
210 }
211
212 fn matches_type(field_keyword: &[u8], keyword: &[u8], tokens: &[Token<'t>]) -> bool {
213 T::matches_type(field_keyword, keyword, tokens)
214 }
215
216 fn update<'tv>(&mut self, tokens: &'tv [Token<'t>]) -> Result<(), CommandParseError> {
217 Self::complete(tokens).map(|res| self.extend(res))
218 }
219
220 fn rest<'tv>(tokens: &'tv [Token<'t>]) -> &'tv [Token<'t>] {
221 T::rest(tokens)
222 }
223}
224
225impl<'t, T> CMakeParse<'t> for Box<T>
226where
227 T: CMakeParse<'t>,
228{
229 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
230 T::parse(tokens).map(|(result, rest)| (Box::new(result), rest))
231 }
232
233 fn matches_type(field_keyword: &[u8], keyword: &[u8], tokens: &[Token<'t>]) -> bool {
234 T::matches_type(field_keyword, keyword, tokens)
235 }
236
237 fn need_update(field_keyword: &[u8], keyword: &Token<'t>, buffer: &[Token<'t>]) -> bool {
238 T::need_update(field_keyword, keyword, buffer)
239 }
240
241 fn need_push_keyword(keyword: &Token<'t>) -> bool {
242 T::need_push_keyword(keyword)
243 }
244
245 fn update_mode(keyword: &Token<'t>) -> bool {
246 T::update_mode(keyword)
247 }
248
249 fn rest<'tv>(tokens: &'tv [Token<'t>]) -> &'tv [Token<'t>] {
250 T::rest(tokens)
251 }
252
253 fn reset_mode() -> bool {
254 T::reset_mode()
255 }
256}
257
258impl<'t, T1, T2> CMakeParse<'t> for (T1, T2)
259where
260 T1: CMakeParse<'t>,
261 T2: CMakeParse<'t>,
262{
263 fn parse<'tv>(tokens: &'tv [Token<'t>]) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
264 T1::parse(tokens)
265 .and_then(|(t1, tokens)| T2::parse(tokens).map(|(t2, tokens)| ((t1, t2), tokens)))
266 }
267}
268
269#[cfg(test)]
270pub(crate) mod tests {
271 use super::*;
272 use crate::*;
273
274 #[test]
275 fn cmake_parse_token() {
276 let token: Token<'_> = assert_parse([b"QQQ", b"aa", b"END"], b"QQQ");
277 assert_eq!(token.as_bytes(), b"aa");
278
279 let option_token: Option<Token<'_>> = assert_parse([b"QQQ", b"aa", b"END"], b"QQQ");
280 assert_eq!(option_token.unwrap().as_bytes(), b"aa");
281 }
282
283 #[test]
284 fn cmake_parse_bool() {
285 let token_bool_true: bool = assert_parse([b"QQQ", b"END"], b"QQQ");
286 assert!(token_bool_true);
287
288 let token_bool_false: bool = assert_parse([b"END"], b"QQQ");
289 assert!(!token_bool_false);
290
291 let token_option_bool_true: Option<bool> = assert_parse([b"QQQ", b"END"], b"QQQ");
292 assert_eq!(token_option_bool_true, Some(true));
293
294 let token_option_bool_false: Option<bool> = assert_parse([b"END"], b"QQQ");
295 assert_eq!(token_option_bool_false, Some(false));
296 }
297
298 #[test]
299 fn cmake_parse_vec_none() {
300 let option_vec_token: Option<Vec<Token<'_>>> = assert_parse([b"END"], b"QQQ");
301 assert_eq!(option_vec_token, None);
302 }
303
304 #[test]
305 fn cmake_parse_vec() {
306 let vec_token: Vec<Token<'_>> = assert_parse([b"QQQ", b"aa", b"bb", b"END"], b"QQQ");
307 assert_eq!(vec_token, tokens([b"aa", b"bb"]).to_vec());
308
309 let vec_vec_token: Vec<Vec<Token<'_>>> = assert_parse(
310 [
311 b"QQQ", b"aa", b"bb", b"QQQ", b"cc", b"dd", b"QQQ", b"ee", b"ff", b"END",
312 ],
313 b"QQQ",
314 );
315 assert_eq!(
316 vec_vec_token,
317 vec![
318 tokens([b"aa", b"bb"]).to_vec(),
319 tokens([b"cc", b"dd"]).to_vec(),
320 tokens([b"ee", b"ff"]).to_vec(),
321 ]
322 );
323
324 let def_vec_token: Vec<Vec<Token<'_>>> = assert_parse(
325 [
326 b"aa", b"bb", b"CMD", b"cc", b"dd", b"CMD", b"ee", b"ff", b"END",
327 ],
328 b"CMD",
329 );
330 assert_eq!(
331 def_vec_token,
332 vec![
333 tokens([b"aa", b"bb"]).to_vec(),
334 tokens([b"cc", b"dd"]).to_vec(),
335 tokens([b"ee", b"ff"]).to_vec(),
336 ]
337 );
338
339 let option_vec_token: Option<Vec<Token<'_>>> =
340 assert_parse([b"QQQ", b"aa", b"bb", b"END"], b"QQQ");
341 assert_eq!(option_vec_token, Some(tokens([b"aa", b"bb"]).to_vec()));
342
343 let option_vec_vec_token: Option<Vec<Vec<Token<'_>>>> = assert_parse(
344 [
345 b"QQQ", b"aa", b"bb", b"QQQ", b"cc", b"dd", b"QQQ", b"ee", b"ff", b"END",
346 ],
347 b"QQQ",
348 );
349 assert_eq!(
350 option_vec_vec_token,
351 Some(vec![
352 tokens([b"aa", b"bb"]).to_vec(),
353 tokens([b"cc", b"dd"]).to_vec(),
354 tokens([b"ee", b"ff"]).to_vec(),
355 ])
356 );
357 }
358
359 #[test]
360 fn cmake_parse_enum() {
361 #[derive(CMake, Debug, PartialEq)]
362 #[cmake(pkg = "crate")]
363 enum Test {
364 PostBuild,
365 Compile,
366 }
367
368 let enm: Test = assert_parse([b"COMPILE", b"END"], b"WHEN");
369 assert_eq!(enm, Test::Compile);
370
371 let enm: Option<Test> = assert_parse([b"COMPILE", b"END"], b"WHEN");
372 assert_eq!(enm, Some(Test::Compile));
373
374 let enm: Option<Test> = assert_parse([b"END"], b"WHEN");
375 assert_eq!(enm, None);
376 }
377
378 pub fn token(buf: &[u8]) -> Token<'_> {
379 Token::text_node(buf, false)
380 }
381
382 pub fn tokens<const T: usize>(buf: [&[u8]; T]) -> [Token<'_>; T] {
383 buf.map(token)
384 }
385
386 pub fn tokens_vec<const T: usize>(buf: [&[u8]; T]) -> Vec<Token<'_>> {
387 tokens(buf).to_vec()
388 }
389
390 pub fn quoted_token(buf: &[u8]) -> Token<'_> {
391 Token::text_node(buf, true)
392 }
393
394 pub fn quoted_tokens<const T: usize>(buf: [&[u8]; T]) -> [Token<'_>; T] {
395 buf.map(quoted_token)
396 }
397
398 pub fn quoted_tokens_vec<const T: usize>(buf: [&[u8]; T]) -> Vec<Token<'_>> {
399 quoted_tokens(buf).to_vec()
400 }
401
402 pub fn parse<'t, 'tv, T, E>(
403 mut tokens: &'tv [Token<'t>],
404 field_keyword: &[u8],
405 def_mode: bool,
406 ) -> Result<(T, &'tv [Token<'t>]), CommandParseError>
407 where
408 T: CMakeParse<'t> + std::fmt::Debug,
409 E: CMakeParse<'t>,
410 {
411 #[derive(Default)]
412 struct Buffers<'b> {
413 field: Vec<Token<'b>>,
414 another: Vec<Token<'b>>,
415 }
416 let mut buffers = Buffers::default();
417
418 let mut field = CMakeParse::default_value();
419 let mut another: Option<E> = CMakeParse::default_value();
420
421 #[derive(Debug, Copy, Clone)]
422 enum CMakeParserMode {
423 Field,
424 Another,
425 }
426
427 let default_mode = if def_mode {
428 Some(CMakeParserMode::Field)
429 } else {
430 None
431 };
432
433 let mut current_mode = default_mode;
434
435 loop {
436 let Some((first, rest)) = tokens.split_first() else {
437 break;
438 };
439 tokens = rest;
440 let keyword = first.as_bytes();
441 if field.matches(field_keyword, keyword, tokens) {
442 let (update_mode, rest) =
443 field.start(field_keyword, first, tokens, &mut buffers.field)?;
444 tokens = rest;
445 if update_mode {
446 current_mode = Some(CMakeParserMode::Field)
447 } else {
448 current_mode = default_mode;
449 }
450 } else if another.matches(b"END", keyword, tokens) {
451 let (update_mode, rest) =
452 another.start(b"END", first, tokens, &mut buffers.another)?;
453 tokens = rest;
454 if update_mode {
455 current_mode = Some(CMakeParserMode::Another);
456 } else {
457 current_mode = default_mode;
458 }
459 } else {
460 match ¤t_mode {
461 Some(mode) => {
462 if match mode {
463 CMakeParserMode::Field => field.push_keyword(&mut buffers.field, first),
464 CMakeParserMode::Another => {
465 another.push_keyword(&mut buffers.another, first)
466 }
467 } {
468 current_mode = default_mode;
469 }
470 }
471 None => {
472 return Err(crate::CommandParseError::UnknownOption(
473 std::string::String::from_utf8_lossy(keyword).to_string(),
474 ))
475 }
476 }
477 }
478 }
479 Ok((
480 field
481 .end(&buffers.field)?
482 .ok_or_else(|| crate::CommandParseError::MissingToken("field".to_string()))?,
483 tokens,
484 ))
485 }
486
487 pub fn assert_parse<'t, 'tv, T, const C: usize>(ts: [&'t [u8]; C], keyword: &[u8]) -> T
488 where
489 T: CMakeParse<'t> + std::fmt::Debug,
490 {
491 let tokens = tokens(ts);
492 let (res, tokens) =
493 parse::<_, bool>(&tokens[..], keyword, keyword == b"CMD").expect("parse result");
494 assert!(tokens.is_empty());
495 res
496 }
497}