cmake_parser/doc/command/scripting/
foreach.rs

1use cmake_parser_derive::CMake;
2
3use crate::{
4    doc::command_scope::{CommandScope, ToCommandScope},
5    Token,
6};
7
8/// Evaluate a group of commands for each value in a list.
9///
10/// Reference: <https://cmake.org/cmake/help/v3.26/command/foreach.html>
11#[derive(CMake, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
12#[cmake(pkg = "crate", untagged)]
13pub enum ForEach<'t> {
14    RangeStop(RangeStop<'t>),
15    RangeStartStop(RangeStartStop<'t>),
16    InZipLists(InZipLists<'t>),
17    In(In<'t>),
18}
19
20impl<'t> ToCommandScope for ForEach<'t> {
21    fn to_command_scope(&self) -> CommandScope {
22        CommandScope::Scripting
23    }
24}
25
26#[derive(CMake, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
27#[cmake(pkg = "crate", positional, complete)]
28pub struct RangeStop<'t> {
29    #[cmake(keyword_after = "RANGE")]
30    pub loop_var: Token<'t>,
31    pub stop: Token<'t>,
32}
33
34#[derive(CMake, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
35#[cmake(pkg = "crate", positional, complete)]
36pub struct RangeStartStop<'t> {
37    #[cmake(keyword_after = "RANGE")]
38    pub loop_var: Token<'t>,
39    pub start: Token<'t>,
40    pub stop: Token<'t>,
41    pub step: Option<Token<'t>>,
42}
43
44#[derive(CMake, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
45#[cmake(pkg = "crate")]
46pub struct In<'t> {
47    #[cmake(positional, keyword_after = "IN")]
48    pub loop_var: Token<'t>,
49    pub lists: Option<Vec<Token<'t>>>,
50    pub items: Option<Vec<Token<'t>>>,
51}
52
53#[derive(CMake, Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
54#[cmake(pkg = "crate", default = "loop_var")]
55pub struct InZipLists<'t> {
56    #[cmake(rename = b"")]
57    pub loop_var: Vec<Token<'t>>,
58    pub zip_lists: ZipLists<'t>,
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
62pub struct ZipLists<'t> {
63    pub lists: Vec<Token<'t>>,
64}
65
66impl<'t> crate::CMakeParse<'t> for ZipLists<'t> {
67    fn matches_type(_: &[u8], keyword: &[u8], tokens: &[Token<'t>]) -> bool {
68        keyword == b"IN" && tokens.first().map(|x| x.as_bytes()) == Some(b"ZIP_LISTS")
69    }
70
71    fn need_push_keyword(_: &Token<'t>) -> bool {
72        false
73    }
74
75    fn rest<'tv>(tokens: &'tv [Token<'t>]) -> &'tv [Token<'t>] {
76        &tokens[1..]
77    }
78
79    fn parse<'tv>(
80        tokens: &'tv [Token<'t>],
81    ) -> Result<(Self, &'tv [Token<'t>]), crate::CommandParseError> {
82        crate::CMakeParse::parse(tokens).map(|(lists, tokens)| (Self { lists }, tokens))
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use super::*;
89    use crate::doc::cmake_parse::tests::{token, tokens_vec};
90    use crate::*;
91    use pretty_assertions::assert_eq;
92
93    #[test]
94    fn foreach() {
95        let src = include_bytes!("../../../../../fixture/commands/scripting/foreach");
96        let cmakelists = parse_cmakelists(src).unwrap();
97        let doc = Doc::from(cmakelists);
98        assert_eq!(
99            doc.to_commands_iter().collect::<Vec<_>>(),
100            vec![
101                Ok(Command::ForEach(Box::new(ForEach::RangeStop(RangeStop {
102                    loop_var: token(b"variable1"),
103                    stop: token(b"10"),
104                })))),
105                Ok(Command::ForEach(Box::new(ForEach::RangeStartStop(
106                    RangeStartStop {
107                        loop_var: token(b"variable1"),
108                        start: token(b"1"),
109                        stop: token(b"10"),
110                        step: None,
111                    }
112                )))),
113                Ok(Command::ForEach(Box::new(ForEach::RangeStartStop(
114                    RangeStartStop {
115                        loop_var: token(b"variable1"),
116                        start: token(b"1"),
117                        stop: token(b"10"),
118                        step: Some(token(b"3")),
119                    }
120                )))),
121                Ok(Command::ForEach(Box::new(ForEach::In(In {
122                    loop_var: token(b"variable1"),
123                    lists: Some(tokens_vec([b"A", b"B", b"C", b"D", b"E", b"F"])),
124                    items: None,
125                })))),
126                Ok(Command::ForEach(Box::new(ForEach::In(In {
127                    loop_var: token(b"variable1"),
128                    lists: None,
129                    items: Some(tokens_vec([b"${A}", b"${B}"])),
130                })))),
131                Ok(Command::ForEach(Box::new(ForEach::In(In {
132                    loop_var: token(b"variable1"),
133                    lists: Some(tokens_vec([b"A", b"B", b"C", b"D", b"E", b"F"])),
134                    items: Some(tokens_vec([b"${A}", b"${B}"])),
135                })))),
136                Ok(Command::ForEach(Box::new(ForEach::InZipLists(
137                    InZipLists {
138                        loop_var: tokens_vec([b"variable1"]),
139                        zip_lists: ZipLists {
140                            lists: tokens_vec([b"English", b"Bahasa"]),
141                        }
142                    }
143                )))),
144                Ok(Command::ForEach(Box::new(ForEach::InZipLists(
145                    InZipLists {
146                        loop_var: tokens_vec([b"en", b"ba"]),
147                        zip_lists: ZipLists {
148                            lists: tokens_vec([b"English", b"Bahasa"]),
149                        }
150                    }
151                )))),
152            ]
153        )
154    }
155}