mockforge_core/overrides/
loader.rs1use globwalk::GlobWalkerBuilder;
7use std::collections::HashMap;
8
9use super::models::{OverrideRule, Overrides, PatchOp};
10use crate::templating::expand_tokens as core_expand_tokens;
11
12impl Overrides {
13 pub async fn load_from_globs(patterns: &[&str]) -> anyhow::Result<Self> {
15 let patterns: Vec<String> =
17 if let Ok(env_patterns) = std::env::var("MOCKFORGE_HTTP_OVERRIDES_GLOB") {
18 env_patterns.split(',').map(|s| s.trim().to_string()).collect()
19 } else {
20 patterns.iter().map(|s| s.to_string()).collect()
21 };
22
23 let mut rules = Vec::new();
24 let mut regex_cache = HashMap::new();
25
26 for pat in patterns {
27 if std::path::Path::new(&pat).is_absolute() && std::path::Path::new(&pat).is_file() {
29 let path = std::path::Path::new(&pat).to_path_buf();
31 if path.extension().map(|e| e == "yaml" || e == "yml").unwrap_or(false) {
32 let text = tokio::fs::read_to_string(&path).await?;
33 let mut file_rules: Vec<OverrideRule> = serde_yaml::from_str(&text)?;
34
35 for r in file_rules.iter_mut() {
36 for op in r.patch.iter_mut() {
38 match op {
39 PatchOp::Add { value, .. } | PatchOp::Replace { value, .. } => {
40 *value = core_expand_tokens(value);
41 }
42 _ => {}
43 }
44 }
45
46 for target in &r.targets {
48 if target.starts_with("regex:") || target.starts_with("path:") {
49 let pattern = target
50 .strip_prefix("regex:")
51 .or_else(|| target.strip_prefix("path:"))
52 .unwrap();
53 if !regex_cache.contains_key(pattern) {
54 let regex = regex::Regex::new(pattern)?;
55 regex_cache.insert(pattern.to_string(), regex);
56 }
57 }
58 }
59 }
60
61 rules.extend(file_rules);
62 }
63 } else {
64 for entry in GlobWalkerBuilder::from_patterns(".", &[pat]).build()? {
66 let entry = entry?;
67 let path = entry.path().to_path_buf();
68 if path.extension().map(|e| e == "yaml" || e == "yml").unwrap_or(false) {
69 let text = tokio::fs::read_to_string(&path).await?;
70 let mut file_rules: Vec<OverrideRule> = serde_yaml::from_str(&text)?;
71
72 for r in file_rules.iter_mut() {
73 for op in r.patch.iter_mut() {
75 match op {
76 PatchOp::Add { value, .. } | PatchOp::Replace { value, .. } => {
77 *value = core_expand_tokens(value);
78 }
79 _ => {}
80 }
81 }
82
83 for target in &r.targets {
85 if target.starts_with("regex:") || target.starts_with("path:") {
86 let pattern = target
87 .strip_prefix("regex:")
88 .or_else(|| target.strip_prefix("path:"))
89 .unwrap();
90 if !regex_cache.contains_key(pattern) {
91 let regex = regex::Regex::new(pattern)?;
92 regex_cache.insert(pattern.to_string(), regex);
93 }
94 }
95 }
96 }
97
98 rules.extend(file_rules);
99 }
100 }
101 }
102 }
103
104 Ok(Self { rules, regex_cache })
105 }
106}