cmake_parser/doc/
cmake_parse.rs

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 &current_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}