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}