cmake_parser/doc/
cmake_positional.rs

1use crate::{CMakeParse, CommandParseError, Token};
2
3pub trait CMakePositional<'t>: 't + Sized {
4    fn positional<'tv>(
5        default_name: &'static [u8],
6        tokens: &'tv [Token<'t>],
7        has_keyword: bool,
8    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError>;
9
10    fn positional_complete<'tv>(
11        default_name: &'static [u8],
12        tokens: &'tv [Token<'t>],
13        has_keyword: bool,
14    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
15        Self::positional(default_name, tokens, has_keyword).and_then(|(result, tokens)| {
16            if tokens.is_empty() {
17                Ok((result, tokens))
18            } else {
19                Err(CommandParseError::NotEmpty)
20            }
21        })
22    }
23
24    fn in_range<'tv>(
25        default_name: &'static [u8],
26        to: &'static [u8],
27        allow_empty: bool,
28        tokens: &'tv [Token<'t>],
29        has_keyword: bool,
30    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
31        if let Some((range_to, range_from)) = tokens
32            .iter()
33            .position(|token| token.as_ref() == to)
34            .map(|mid| tokens.split_at(mid))
35        {
36            Self::positional(default_name, range_to, has_keyword).map(|(res, _)| (res, range_from))
37        } else if allow_empty {
38            Self::positional(default_name, tokens, has_keyword)
39        } else {
40            Err(CommandParseError::TokenRequired)
41        }
42    }
43}
44
45impl<'t> CMakePositional<'t> for Token<'t> {
46    fn positional<'tv>(
47        default_name: &'static [u8],
48        mut tokens: &'tv [Token<'t>],
49        has_keyword: bool,
50    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
51        if has_keyword {
52            let (_, rest) = Keyword::positional(default_name, tokens, has_keyword)?;
53            tokens = rest;
54        }
55        CMakeParse::parse(tokens)
56    }
57}
58
59impl<'t, T> CMakePositional<'t> for Vec<T>
60where
61    T: CMakeParse<'t>,
62{
63    fn positional<'tv>(
64        default_name: &'static [u8],
65        mut tokens: &'tv [Token<'t>],
66        has_keyword: bool,
67    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
68        if has_keyword {
69            let (_, rest) = Keyword::positional(default_name, tokens, has_keyword)?;
70            tokens = rest;
71        }
72        CMakeParse::parse(tokens)
73    }
74}
75
76impl<'t, T> CMakePositional<'t> for Option<T>
77where
78    T: CMakePositional<'t>,
79{
80    fn positional<'tv>(
81        keyword: &'static [u8],
82        tokens: &'tv [Token<'t>],
83        has_keyword: bool,
84    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
85        match T::positional(keyword, tokens, has_keyword).map(|(res, tokens)| (Some(res), tokens)) {
86            Ok(result) => Ok(result),
87            Err(_) => Ok((None, tokens)),
88        }
89    }
90}
91
92impl<'t, T> CMakePositional<'t> for Box<T>
93where
94    T: CMakePositional<'t>,
95{
96    fn positional<'tv>(
97        keyword: &'static [u8],
98        tokens: &'tv [Token<'t>],
99        has_keyword: bool,
100    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
101        T::positional(keyword, tokens, has_keyword).map(|(res, tokens)| (Box::new(res), tokens))
102    }
103}
104
105impl<'t> CMakePositional<'t> for bool {
106    fn positional<'tv>(
107        default_name: &'static [u8],
108        tokens: &'tv [Token<'t>],
109        _: bool,
110    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
111        tokens
112            .split_first()
113            .filter(|(first, _)| first.as_bytes() == default_name)
114            .map(|(_, rest)| (true, rest))
115            .or(Some((false, tokens)))
116            .ok_or(CommandParseError::TokenRequired)
117    }
118}
119
120impl<'t> CMakePositional<'t> for () {
121    fn positional<'tv>(
122        default_name: &'static [u8],
123        tokens: &'tv [Token<'t>],
124        has_keyword: bool,
125    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
126        <bool as CMakePositional>::positional(default_name, tokens, has_keyword)
127            .map(|(_, tokens)| ((), tokens))
128    }
129}
130
131#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
132pub struct Keyword;
133
134impl<'t> CMakePositional<'t> for Keyword {
135    fn positional<'tv>(
136        default_name: &'static [u8],
137        tokens: &'tv [Token<'t>],
138        _: bool,
139    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
140        tokens
141            .split_first()
142            .filter(|(first, _)| first.as_bytes() == default_name)
143            .map(|(_, rest)| (Keyword, rest))
144            .ok_or(CommandParseError::TokenRequired)
145    }
146}