vane_core/compile/
merge.rs1use std::path::PathBuf;
2
3use crate::error::Error;
4use crate::preset::RuleEntry;
5
6#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
7pub struct RawRuleFile {
8 #[serde(default)]
12 pub path: PathBuf,
13 #[serde(default)]
14 pub order: i32,
15 #[serde(default)]
16 pub rules: Vec<RuleEntry>,
17}
18
19#[derive(Debug, Clone)]
20pub struct MergedConfig {
21 pub rules: Vec<RuleEntry>,
25 pub source_files: Vec<PathBuf>,
26}
27
28pub fn merge(mut files: Vec<RawRuleFile>) -> Result<MergedConfig, Error> {
42 files.sort_by(|a, b| a.order.cmp(&b.order).then_with(|| a.path.cmp(&b.path)));
43
44 let mut rules: Vec<RuleEntry> = Vec::new();
45 let mut source_files: Vec<PathBuf> = Vec::with_capacity(files.len());
46 for file in files {
47 source_files.push(file.path);
48 rules.extend(file.rules);
49 }
50 Ok(MergedConfig { rules, source_files })
51}
52
53#[cfg(test)]
54mod tests {
55 use super::*;
56 use crate::rule::RawRule;
57
58 fn raw_rule(name: &str) -> RawRule {
59 let raw = serde_json::json!({
60 "name": name,
61 "listen": [":443"],
62 "terminate": { "type": "http_proxy" },
63 });
64 serde_json::from_value(raw).expect("parse rule")
65 }
66
67 fn entry(name: &str) -> RuleEntry {
68 RuleEntry::Raw(raw_rule(name))
69 }
70
71 fn file(path: &str, order: i32, rules: Vec<RuleEntry>) -> RawRuleFile {
72 RawRuleFile { path: PathBuf::from(path), order, rules }
73 }
74
75 fn entry_name(e: &RuleEntry) -> &str {
76 match e {
77 RuleEntry::Raw(r) => r.name.as_str(),
78 RuleEntry::Preset(inv) => inv.name.as_str(),
79 }
80 }
81
82 #[test]
83 fn sorts_by_order_then_path_stable() {
84 let files = vec![
86 file("b.json", 10, vec![entry("b")]),
87 file("a.json", 10, vec![entry("a")]),
88 file("0.json", 0, vec![entry("zero")]),
89 ];
90 let merged = merge(files).expect("merge ok");
91 let names: Vec<_> = merged.rules.iter().map(entry_name).collect();
92 assert_eq!(names, vec!["zero", "a", "b"]);
93 }
94
95 #[test]
96 fn duplicate_names_pass_through_merge_without_check() {
97 let files =
101 vec![file("a.json", 0, vec![entry("same")]), file("b.json", 1, vec![entry("same")])];
102 let merged = merge(files).expect("merge ok — no dup check here");
103 assert_eq!(merged.rules.len(), 2);
104 }
105
106 #[test]
107 fn preserves_every_source_file_path() {
108 let files = vec![file("x.json", 0, vec![]), file("y.json", 0, vec![])];
109 let merged = merge(files).expect("merge ok");
110 assert_eq!(merged.source_files, vec![PathBuf::from("x.json"), PathBuf::from("y.json")]);
111 }
112}