1use camel_api::CamelError;
4
5#[derive(Debug, Clone, PartialEq, Eq)]
7pub struct InterceptRule {
8 pub uri: String,
10 pub action: InterceptAction,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub enum InterceptAction {
17 SkipTo { uri: String },
19 DivertCopyTo { uri: String },
21}
22
23#[derive(Debug, Clone, Default)]
25pub struct InterceptRules {
26 rules: Vec<InterceptRule>,
27}
28
29impl InterceptRules {
30 pub fn new(rules: Vec<InterceptRule>) -> Result<Self, CamelError> {
36 for (idx, rule) in rules.iter().enumerate() {
37 let target = match &rule.action {
38 InterceptAction::SkipTo { uri } => uri,
39 InterceptAction::DivertCopyTo { uri } => uri,
40 };
41 if !target.starts_with("mock:") {
42 return Err(CamelError::Config(format!(
43 "rule {idx}: intercept target '{target}' must start with 'mock:'"
44 )));
45 }
46 }
47 Ok(Self { rules })
48 }
49
50 pub fn lookup(&self, send_uri: &str) -> Option<&InterceptAction> {
54 for rule in &self.rules {
55 if rule.uri == send_uri {
56 return Some(&rule.action);
57 }
58 }
59 None
60 }
61
62 pub fn is_empty(&self) -> bool {
64 self.rules.is_empty()
65 }
66}
67
68#[cfg(test)]
69mod tests {
70 use super::*;
71
72 #[test]
73 fn non_mock_action_targets_are_rejected_at_rule_construction() {
74 let skip_bad = InterceptRule {
75 uri: "kafka:x".into(),
76 action: InterceptAction::SkipTo {
77 uri: "direct:y".into(),
78 },
79 };
80 let divert_bad = InterceptRule {
81 uri: "kafka:z".into(),
82 action: InterceptAction::DivertCopyTo {
83 uri: "seda:w".into(),
84 },
85 };
86
87 let err = InterceptRules::new(vec![skip_bad.clone()]).unwrap_err(); match err {
89 CamelError::Config(msg) => {
90 assert!(msg.contains("0"));
91 assert!(msg.contains("direct:y"));
92 }
93 other => panic!("expected Config, got {other:?}"),
94 }
95
96 let err = InterceptRules::new(vec![divert_bad.clone()]).unwrap_err(); match err {
98 CamelError::Config(msg) => {
99 assert!(msg.contains("0"));
100 assert!(msg.contains("seda:w"));
101 }
102 other => panic!("expected Config, got {other:?}"),
103 }
104
105 let err = InterceptRules::new(vec![skip_bad.clone(), divert_bad.clone()]).unwrap_err(); match err {
107 CamelError::Config(msg) => {
108 assert!(msg.contains("0"));
109 assert!(msg.contains("direct:y"));
110 }
111 other => panic!("expected Config, got {other:?}"),
112 }
113
114 let valid = InterceptRule {
116 uri: "kafka:ok".into(),
117 action: InterceptAction::SkipTo {
118 uri: "mock:ok".into(),
119 },
120 };
121 let err = InterceptRules::new(vec![valid, divert_bad]).unwrap_err(); match err {
123 CamelError::Config(msg) => {
124 assert!(msg.contains("rule 1:"));
125 assert!(msg.contains("seda:w"));
126 }
127 other => panic!("expected Config, got {other:?}"),
128 }
129 }
130
131 #[test]
132 fn duplicate_uris_preserve_declaration_order() {
133 assert!(InterceptRules::default().is_empty());
134 assert!(InterceptRules::default().lookup("x").is_none());
135
136 let r1 = InterceptRule {
137 uri: "seda:out".into(),
138 action: InterceptAction::SkipTo {
139 uri: "mock:a".into(),
140 },
141 };
142 let r2 = InterceptRule {
143 uri: "seda:out".into(),
144 action: InterceptAction::SkipTo {
145 uri: "mock:b".into(),
146 },
147 };
148 let rules = InterceptRules::new(vec![r1, r2]).expect("valid mock targets"); assert_eq!(
150 rules.lookup("seda:out"),
151 Some(&InterceptAction::SkipTo {
152 uri: "mock:a".into()
153 })
154 );
155 assert_eq!(rules.lookup("seda:out2"), None);
156 }
157
158 #[test]
159 fn mock_targets_accepted() {
160 let rules = InterceptRules::new(vec![
161 InterceptRule {
162 uri: "kafka:x".into(),
163 action: InterceptAction::SkipTo {
164 uri: "mock:y".into(),
165 },
166 },
167 InterceptRule {
168 uri: "kafka:z".into(),
169 action: InterceptAction::DivertCopyTo {
170 uri: "mock:w".into(),
171 },
172 },
173 ])
174 .expect("valid mock targets"); assert_eq!(
176 rules.lookup("kafka:x"),
177 Some(&InterceptAction::SkipTo {
178 uri: "mock:y".into()
179 })
180 );
181 assert_eq!(
182 rules.lookup("kafka:z"),
183 Some(&InterceptAction::DivertCopyTo {
184 uri: "mock:w".into()
185 })
186 );
187 }
188}