vane_core/compile/
merge.rs1use std::path::PathBuf;
2
3use crate::error::Error;
4use crate::rule::RawRule;
5
6#[derive(Debug, Clone, serde::Deserialize, serde::Serialize)]
7pub struct RawRuleFile {
8 pub path: PathBuf,
9 #[serde(default)]
10 pub order: i32,
11 #[serde(default)]
12 pub rules: Vec<RawRule>,
13}
14
15#[derive(Debug, Clone)]
16pub struct MergedConfig {
17 pub rules: Vec<RawRule>,
18 pub source_files: Vec<PathBuf>,
19}
20
21pub fn merge(mut files: Vec<RawRuleFile>) -> Result<MergedConfig, Error> {
27 files.sort_by(|a, b| a.order.cmp(&b.order).then_with(|| a.path.cmp(&b.path)));
28
29 let mut rules: Vec<RawRule> = Vec::new();
30 let mut source_files: Vec<PathBuf> = Vec::with_capacity(files.len());
31 for file in files {
32 source_files.push(file.path);
33 for rule in file.rules {
34 if rules.iter().any(|existing| existing.name == rule.name) {
35 return Err(Error::compile(format!("duplicate rule name: {:?}", rule.name)));
36 }
37 rules.push(rule);
38 }
39 }
40 Ok(MergedConfig { rules, source_files })
41}
42
43#[cfg(test)]
44mod tests {
45 use super::*;
46 use crate::error::ErrorKind;
47
48 fn rule(name: &str) -> RawRule {
49 let raw = serde_json::json!({
50 "name": name,
51 "listen": [":443"],
52 "terminate": { "type": "http_proxy" },
53 });
54 serde_json::from_value(raw).expect("parse rule")
55 }
56
57 fn file(path: &str, order: i32, rules: Vec<RawRule>) -> RawRuleFile {
58 RawRuleFile { path: PathBuf::from(path), order, rules }
59 }
60
61 #[test]
62 fn sorts_by_order_then_path_stable() {
63 let files = vec![
65 file("b.json", 10, vec![rule("b")]),
66 file("a.json", 10, vec![rule("a")]),
67 file("0.json", 0, vec![rule("zero")]),
68 ];
69 let merged = merge(files).expect("merge ok");
70 let names: Vec<_> = merged.rules.iter().map(|r| r.name.as_str()).collect();
71 assert_eq!(names, vec!["zero", "a", "b"]);
72 }
73
74 #[test]
75 fn rejects_duplicate_rule_names_with_compile_error() {
76 let files = vec![file("a.json", 0, vec![rule("same")]), file("b.json", 1, vec![rule("same")])];
77 let err = merge(files).expect_err("duplicate must error");
78 assert!(matches!(err.kind(), ErrorKind::Compile));
79 }
80
81 #[test]
82 fn preserves_every_source_file_path() {
83 let files = vec![file("x.json", 0, vec![]), file("y.json", 0, vec![])];
84 let merged = merge(files).expect("merge ok");
85 assert_eq!(merged.source_files, vec![PathBuf::from("x.json"), PathBuf::from("y.json")],);
86 }
87}