Skip to main content

cup_cli/config/
utils.rs

1use std::collections::{HashMap, HashSet};
2
3use petgraph::{Graph, algo, visit::Dfs};
4
5/// 展开指定特性列表对应的特性集
6pub fn expand_features<'a>(
7    features: &Vec<String>,
8    table: &'a HashMap<String, Vec<String>>,
9) -> anyhow::Result<HashSet<&'a String>> {
10    let mut graph = Graph::new();
11    let mut indeies = HashMap::new();
12
13    // 构造特性关系图
14    for (feature, deps) in table {
15        let index = if indeies.contains_key(feature) {
16            indeies[feature]
17        } else {
18            let i = graph.add_node(feature);
19            indeies.insert(feature.to_string(), i);
20            i
21        };
22        for dep in deps {
23            let dep_index = if indeies.contains_key(dep) {
24                indeies[dep]
25            } else {
26                let i = graph.add_node(dep);
27                indeies.insert(dep.to_string(), i);
28                i
29            };
30            graph.add_edge(index, dep_index, ());
31        }
32    }
33
34    // 检查是否存在循环依赖
35    if algo::is_cyclic_directed(&graph) {
36        return Err(anyhow::anyhow!(
37            "There is a cyclic dependency between features."
38        ));
39    }
40
41    // 生成展开的特性集
42    let mut result = HashSet::new();
43    for feature in features {
44        if !indeies.contains_key(feature) {
45            return Err(anyhow::anyhow!("Feature {} is not defined.", feature));
46        }
47        let index = indeies[feature];
48        result.insert(graph[index]);
49        let mut visited = Dfs::new(&graph, index);
50        while let Some(node) = visited.next(&graph) {
51            result.insert(graph[node]);
52        }
53    }
54
55    Ok(result)
56}