1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use crate::{CMakeParse, CommandParseError, Token};

pub trait CMakePositional<'t>: 't + Sized {
    fn positional<'tv>(
        default_name: &'static [u8],
        tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError>;

    fn positional_complete<'tv>(
        default_name: &'static [u8],
        tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        Self::positional(default_name, tokens, has_keyword).and_then(|(result, tokens)| {
            if tokens.is_empty() {
                Ok((result, tokens))
            } else {
                Err(CommandParseError::NotEmpty)
            }
        })
    }
}

impl<'t> CMakePositional<'t> for Token<'t> {
    fn positional<'tv>(
        default_name: &'static [u8],
        mut tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        if has_keyword {
            let (_, rest) = Keyword::positional(default_name, tokens, has_keyword)?;
            tokens = rest;
        }
        CMakeParse::parse(tokens)
    }
}

impl<'t, T> CMakePositional<'t> for Vec<T>
where
    T: CMakeParse<'t>,
{
    fn positional<'tv>(
        default_name: &'static [u8],
        mut tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        if has_keyword {
            let (_, rest) = Keyword::positional(default_name, tokens, has_keyword)?;
            tokens = rest;
        }
        CMakeParse::parse(tokens)
    }
}

impl<'t, T> CMakePositional<'t> for Option<T>
where
    T: CMakePositional<'t>,
{
    fn positional<'tv>(
        keyword: &'static [u8],
        tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        match T::positional(keyword, tokens, has_keyword).map(|(res, tokens)| (Some(res), tokens)) {
            Ok(result) => Ok(result),
            Err(_) => Ok((None, tokens)),
        }
    }
}

impl<'t, T> CMakePositional<'t> for Box<T>
where
    T: CMakePositional<'t>,
{
    fn positional<'tv>(
        keyword: &'static [u8],
        tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        T::positional(keyword, tokens, has_keyword).map(|(res, tokens)| (Box::new(res), tokens))
    }
}

impl<'t> CMakePositional<'t> for bool {
    fn positional<'tv>(
        default_name: &'static [u8],
        tokens: &'tv [Token<'t>],
        _: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        tokens
            .split_first()
            .filter(|(first, _)| first.as_bytes() == default_name)
            .map(|(_, rest)| (true, rest))
            .or(Some((false, tokens)))
            .ok_or(CommandParseError::TokenRequired)
    }
}

impl<'t> CMakePositional<'t> for () {
    fn positional<'tv>(
        default_name: &'static [u8],
        tokens: &'tv [Token<'t>],
        has_keyword: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        <bool as CMakePositional>::positional(default_name, tokens, has_keyword)
            .map(|(_, tokens)| ((), tokens))
    }
}

#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Keyword;

impl<'t> CMakePositional<'t> for Keyword {
    fn positional<'tv>(
        default_name: &'static [u8],
        tokens: &'tv [Token<'t>],
        _: bool,
    ) -> Result<(Self, &'tv [Token<'t>]), CommandParseError> {
        tokens
            .split_first()
            .filter(|(first, _)| first.as_bytes() == default_name)
            .map(|(_, rest)| (Keyword, rest))
            .ok_or(CommandParseError::TokenRequired)
    }
}