risedev/config/
id_expander.rs

1// Copyright 2025 RisingWave Labs
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use anyhow::{Result, anyhow};
16use regex::Regex;
17use yaml_rust::Yaml;
18
19/// Expands `x-*` to `x-2333` by finding id in map.
20pub struct IdExpander {
21    ids: Vec<String>,
22}
23
24impl IdExpander {
25    pub fn new(yaml: &Yaml) -> Result<Self> {
26        let yaml = yaml
27            .as_vec()
28            .ok_or_else(|| anyhow!("Not an array: {:?}", yaml))?;
29        let mut ids = vec![];
30        for item in yaml {
31            if let Some(item) = item.as_hash() {
32                let id = item
33                    .get(&Yaml::String("id".to_owned()))
34                    .ok_or_else(|| anyhow!("Missing id field: {:?}", item))?
35                    .as_str()
36                    .ok_or_else(|| anyhow!("Id isn't a string: {:?}", item))?;
37                ids.push(id.to_owned());
38            } else {
39                return Err(anyhow!("Not a hashmap: {:?}", item));
40            }
41        }
42        Ok(Self { ids })
43    }
44
45    pub fn visit_vec(&mut self, mut yv: Vec<Yaml>) -> Result<Vec<Yaml>> {
46        for y in &mut yv {
47            *y = self.visit(y.clone())?;
48        }
49        Ok(yv)
50    }
51
52    pub fn visit(&mut self, y: Yaml) -> Result<Yaml> {
53        match y {
54            Yaml::Hash(y) => {
55                let mut ny = y;
56                for (_, v) in &mut ny {
57                    let result = if let Some(v) = v.as_str() {
58                        if let Some((before, after)) = v.split_once('*') {
59                            let regex = Regex::new(&format!("^{}(.*){}$", before, after))?;
60                            let mut matched_ids = vec![];
61                            for id in &self.ids {
62                                if regex.is_match(id) {
63                                    matched_ids.push(Yaml::String(id.clone()));
64                                }
65                            }
66                            Yaml::Array(matched_ids)
67                        } else {
68                            Yaml::String(v.to_owned())
69                        }
70                    } else {
71                        self.visit(v.clone())?
72                    };
73                    *v = result;
74                }
75                Ok(Yaml::Hash(ny))
76            }
77            Yaml::Array(y) => Ok(Yaml::Array(self.visit_vec(y)?)),
78            other => Ok(other),
79        }
80    }
81}
82
83#[cfg(test)]
84mod tests {
85    use yaml_rust::YamlLoader;
86
87    use super::*;
88
89    #[test]
90    fn test_expand_id() {
91        let yaml = YamlLoader::load_from_str(
92            "
93- id: b-233
94  provide: \"a-*\"
95- id: a-233
96    ",
97        )
98        .unwrap();
99        let yaml_result = YamlLoader::load_from_str(
100            "
101- id: b-233
102  provide: [\"a-233\"]
103- id: a-233
104      ",
105        )
106        .unwrap();
107        let mut visitor = IdExpander::new(&yaml[0]).unwrap();
108
109        assert_eq!(visitor.visit_vec(yaml).unwrap(), yaml_result);
110    }
111
112    #[test]
113    fn test_expand_id_array() {
114        let yaml = YamlLoader::load_from_str(
115            "
116- id: b-233
117  provide: \"a-*\"
118- id: a-233
119- id: a-234
120- id: aa-233
121    ",
122        )
123        .unwrap()
124        .remove(0);
125        let yaml_result = YamlLoader::load_from_str(
126            "
127- id: b-233
128  provide: [\"a-233\", \"a-234\"]
129- id: a-233
130- id: a-234
131- id: aa-233
132      ",
133        )
134        .unwrap()
135        .remove(0);
136        let mut visitor = IdExpander::new(&yaml).unwrap();
137
138        assert_eq!(visitor.visit(yaml).unwrap(), yaml_result);
139    }
140}