aspect_core/pointcut/
matcher.rs1use super::ast::Pointcut;
4use super::pattern::{ExecutionPattern, ModulePattern};
5
6#[derive(Debug, Clone)]
8pub struct FunctionInfo {
9 pub name: String,
11
12 pub module_path: String,
14
15 pub visibility: String,
17
18 pub return_type: Option<String>,
20}
21
22impl FunctionInfo {
23 pub fn new(
25 name: impl Into<String>,
26 module_path: impl Into<String>,
27 visibility: impl Into<String>,
28 ) -> Self {
29 Self {
30 name: name.into(),
31 module_path: module_path.into(),
32 visibility: visibility.into(),
33 return_type: None,
34 }
35 }
36
37 pub fn with_return_type(mut self, return_type: impl Into<String>) -> Self {
39 self.return_type = Some(return_type.into());
40 self
41 }
42}
43
44pub trait Matcher {
46 fn matches(&self, function: &FunctionInfo) -> bool;
48}
49
50impl Matcher for Pointcut {
51 fn matches(&self, function: &FunctionInfo) -> bool {
52 match self {
53 Pointcut::Execution(pattern) => pattern.matches(function),
54 Pointcut::Within(pattern) => pattern.matches(function),
55 Pointcut::And(left, right) => left.matches(function) && right.matches(function),
56 Pointcut::Or(left, right) => left.matches(function) || right.matches(function),
57 Pointcut::Not(inner) => !inner.matches(function),
58 }
59 }
60}
61
62impl Matcher for ExecutionPattern {
63 fn matches(&self, function: &FunctionInfo) -> bool {
64 if let Some(ref vis) = self.visibility {
66 if !vis.matches(&function.visibility) {
67 return false;
68 }
69 }
70
71 if !self.name.matches(&function.name) {
73 return false;
74 }
75
76 if let Some(ref expected_return) = self.return_type {
78 match &function.return_type {
79 Some(actual_return) if actual_return.contains(expected_return) => {}
80 _ => return false,
81 }
82 }
83
84 true
85 }
86}
87
88impl Matcher for ModulePattern {
89 fn matches(&self, function: &FunctionInfo) -> bool {
90 self.matches_path(&function.module_path)
91 }
92}
93
94#[cfg(test)]
95mod tests {
96 use super::*;
97 use crate::pointcut::pattern::{NamePattern, Visibility};
98
99 #[test]
100 fn test_execution_pattern_matching() {
101 let pattern = ExecutionPattern {
102 visibility: Some(Visibility::Public),
103 name: NamePattern::Exact("save_user".to_string()),
104 return_type: None,
105 };
106
107 let func1 = FunctionInfo::new("save_user", "crate::api", "pub");
108 assert!(pattern.matches(&func1));
109
110 let func2 = FunctionInfo::new("update_user", "crate::api", "pub");
111 assert!(!pattern.matches(&func2));
112
113 let func3 = FunctionInfo::new("save_user", "crate::api", "");
114 assert!(!pattern.matches(&func3)); }
116
117 #[test]
118 fn test_module_pattern_matching() {
119 let pattern = ModulePattern::new("crate::api");
120
121 let func1 = FunctionInfo::new("save", "crate::api", "pub");
122 assert!(pattern.matches(&func1));
123
124 let func2 = FunctionInfo::new("save", "crate::api::users", "pub");
125 assert!(pattern.matches(&func2));
126
127 let func3 = FunctionInfo::new("save", "crate::internal", "pub");
128 assert!(!pattern.matches(&func3));
129 }
130
131 #[test]
132 fn test_pointcut_and() {
133 let exec = ExecutionPattern {
134 visibility: Some(Visibility::Public),
135 name: NamePattern::Wildcard,
136 return_type: None,
137 };
138
139 let within = ModulePattern::new("crate::api");
140
141 let pointcut = Pointcut::Execution(exec).and(Pointcut::Within(within));
142
143 let func1 = FunctionInfo::new("save", "crate::api", "pub");
144 assert!(pointcut.matches(&func1));
145
146 let func2 = FunctionInfo::new("save", "crate::api", "");
147 assert!(!pointcut.matches(&func2)); let func3 = FunctionInfo::new("save", "crate::internal", "pub");
150 assert!(!pointcut.matches(&func3)); }
152
153 #[test]
154 fn test_pointcut_or() {
155 let pattern1 = ExecutionPattern::named("save");
156 let pattern2 = ExecutionPattern::named("update");
157
158 let pointcut = Pointcut::Execution(pattern1).or(Pointcut::Execution(pattern2));
159
160 let func1 = FunctionInfo::new("save", "crate::api", "pub");
161 assert!(pointcut.matches(&func1));
162
163 let func2 = FunctionInfo::new("update", "crate::api", "pub");
164 assert!(pointcut.matches(&func2));
165
166 let func3 = FunctionInfo::new("delete", "crate::api", "pub");
167 assert!(!pointcut.matches(&func3));
168 }
169
170 #[test]
171 fn test_pointcut_not() {
172 let pattern = ExecutionPattern {
173 visibility: Some(Visibility::Public),
174 name: NamePattern::Wildcard,
175 return_type: None,
176 };
177
178 let pointcut = Pointcut::Execution(pattern).not();
179
180 let func1 = FunctionInfo::new("save", "crate::api", "pub");
181 assert!(!pointcut.matches(&func1)); let func2 = FunctionInfo::new("save", "crate::internal", "");
184 assert!(pointcut.matches(&func2)); }
186}