Skip to main content

aspect_core/pointcut/
pattern.rs

1//! Pattern types for matching functions.
2
3/// Function visibility pattern.
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum Visibility {
6    /// Public: `pub`
7    Public,
8    /// Crate-public: `pub(crate)`
9    Crate,
10    /// Super-public: `pub(super)`
11    Super,
12    /// Private (no visibility modifier)
13    Private,
14}
15
16impl Visibility {
17    /// Check if a visibility string matches this pattern.
18    pub fn matches(&self, vis: &str) -> bool {
19        match (self, vis) {
20            (Visibility::Public, "pub") => true,
21            (Visibility::Crate, "pub(crate)") => true,
22            (Visibility::Super, "pub(super)") => true,
23            (Visibility::Private, "") => true,
24            _ => false,
25        }
26    }
27}
28
29/// Function name pattern.
30#[derive(Debug, Clone, PartialEq, Eq)]
31pub enum NamePattern {
32    /// Match any name: `*`
33    Wildcard,
34    /// Exact name match: `save_user`
35    Exact(String),
36    /// Prefix match: `save*`
37    Prefix(String),
38    /// Suffix match: `*_user`
39    Suffix(String),
40    /// Contains match: `*save*`
41    Contains(String),
42}
43
44impl NamePattern {
45    /// Check if a function name matches this pattern.
46    pub fn matches(&self, name: &str) -> bool {
47        match self {
48            NamePattern::Wildcard => true,
49            NamePattern::Exact(expected) => name == expected,
50            NamePattern::Prefix(prefix) => name.starts_with(prefix),
51            NamePattern::Suffix(suffix) => name.ends_with(suffix),
52            NamePattern::Contains(substring) => name.contains(substring),
53        }
54    }
55}
56
57/// Execution pattern: matches function signatures.
58///
59/// Examples:
60/// - `execution(pub fn *(..))` - all public functions
61/// - `execution(fn save(..))` - function named "save"
62/// - `execution(pub fn save*(..) -> Result<*, *>)` - public functions starting with "save" returning Result
63#[derive(Debug, Clone, PartialEq)]
64pub struct ExecutionPattern {
65    /// Visibility pattern (pub, pub(crate), etc.)
66    pub visibility: Option<Visibility>,
67
68    /// Function name pattern
69    pub name: NamePattern,
70
71    /// Return type pattern (simplified for now)
72    pub return_type: Option<String>,
73}
74
75impl ExecutionPattern {
76    /// Create a pattern that matches all functions.
77    pub fn any() -> Self {
78        Self {
79            visibility: None,
80            name: NamePattern::Wildcard,
81            return_type: None,
82        }
83    }
84
85    /// Create a pattern that matches public functions.
86    pub fn public() -> Self {
87        Self {
88            visibility: Some(Visibility::Public),
89            name: NamePattern::Wildcard,
90            return_type: None,
91        }
92    }
93
94    /// Create a pattern that matches a specific function name.
95    pub fn named(name: impl Into<String>) -> Self {
96        Self {
97            visibility: None,
98            name: NamePattern::Exact(name.into()),
99            return_type: None,
100        }
101    }
102}
103
104/// Module pattern: matches functions by module path.
105///
106/// Examples:
107/// - `within(crate::api)` - functions in the api module
108/// - `within(crate::api::users)` - functions in the users submodule
109#[derive(Debug, Clone, PartialEq, Eq)]
110pub struct ModulePattern {
111    /// Module path to match (e.g., "crate::api")
112    pub path: String,
113}
114
115impl ModulePattern {
116    /// Create a new module pattern.
117    pub fn new(path: impl Into<String>) -> Self {
118        Self {
119            path: path.into(),
120        }
121    }
122
123    /// Check if a module path matches this pattern.
124    ///
125    /// Supports exact match and prefix match (for submodules).
126    pub fn matches_path(&self, module_path: &str) -> bool {
127        module_path == self.path || module_path.starts_with(&format!("{}::", self.path))
128    }
129}
130
131#[cfg(test)]
132mod tests {
133    use super::*;
134
135    #[test]
136    fn test_visibility_matches() {
137        assert!(Visibility::Public.matches("pub"));
138        assert!(Visibility::Crate.matches("pub(crate)"));
139        assert!(Visibility::Private.matches(""));
140        assert!(!Visibility::Public.matches("pub(crate)"));
141    }
142
143    #[test]
144    fn test_name_pattern_matches() {
145        let wildcard = NamePattern::Wildcard;
146        assert!(wildcard.matches("anything"));
147        assert!(wildcard.matches("save_user"));
148
149        let exact = NamePattern::Exact("save".to_string());
150        assert!(exact.matches("save"));
151        assert!(!exact.matches("save_user"));
152
153        let prefix = NamePattern::Prefix("save".to_string());
154        assert!(prefix.matches("save"));
155        assert!(prefix.matches("save_user"));
156        assert!(!prefix.matches("update_user"));
157
158        let suffix = NamePattern::Suffix("_user".to_string());
159        assert!(suffix.matches("save_user"));
160        assert!(suffix.matches("update_user"));
161        assert!(!suffix.matches("save"));
162    }
163
164    #[test]
165    fn test_module_pattern_matches() {
166        let pattern = ModulePattern::new("crate::api");
167
168        assert!(pattern.matches_path("crate::api"));
169        assert!(pattern.matches_path("crate::api::users"));
170        assert!(pattern.matches_path("crate::api::users::models"));
171        assert!(!pattern.matches_path("crate::internal"));
172        assert!(!pattern.matches_path("crate"));
173    }
174
175    #[test]
176    fn test_execution_pattern_builders() {
177        let any = ExecutionPattern::any();
178        assert_eq!(any.name, NamePattern::Wildcard);
179        assert!(any.visibility.is_none());
180
181        let public = ExecutionPattern::public();
182        assert_eq!(public.visibility, Some(Visibility::Public));
183
184        let named = ExecutionPattern::named("save_user");
185        assert_eq!(named.name, NamePattern::Exact("save_user".to_string()));
186    }
187}