Skip to main content

aspect_core/pointcut/
ast.rs

1//! Abstract Syntax Tree for pointcut expressions.
2
3use super::pattern::{ExecutionPattern, ModulePattern};
4use super::parser::parse_pointcut;
5
6/// A pointcut expression that matches joinpoints (functions).
7#[derive(Debug, Clone, PartialEq)]
8pub enum Pointcut {
9    /// Match function execution: `execution(pub fn save(..))`
10    Execution(ExecutionPattern),
11
12    /// Match functions within a module: `within(crate::api)`
13    Within(ModulePattern),
14
15    /// Logical AND: both pointcuts must match
16    And(Box<Pointcut>, Box<Pointcut>),
17
18    /// Logical OR: either pointcut must match
19    Or(Box<Pointcut>, Box<Pointcut>),
20
21    /// Logical NOT: pointcut must not match
22    Not(Box<Pointcut>),
23}
24
25impl Pointcut {
26    /// Parse a pointcut expression from a string.
27    ///
28    /// # Examples
29    ///
30    /// ```rust
31    /// use aspect_core::pointcut::Pointcut;
32    ///
33    /// let pc = Pointcut::parse("execution(pub fn *(..))").unwrap();
34    /// let pc = Pointcut::parse("within(crate::api)").unwrap();
35    /// let pc = Pointcut::parse("execution(pub fn *(..)) && within(crate::api)").unwrap();
36    /// ```
37    pub fn parse(input: &str) -> Result<Self, String> {
38        parse_pointcut(input)
39    }
40
41    /// Create an AND pointcut.
42    pub fn and(self, other: Pointcut) -> Pointcut {
43        Pointcut::And(Box::new(self), Box::new(other))
44    }
45
46    /// Create an OR pointcut.
47    pub fn or(self, other: Pointcut) -> Pointcut {
48        Pointcut::Or(Box::new(self), Box::new(other))
49    }
50
51    /// Create a NOT pointcut.
52    pub fn not(self) -> Pointcut {
53        Pointcut::Not(Box::new(self))
54    }
55
56    /// Convenience method to create an execution pointcut for all public functions.
57    ///
58    /// Equivalent to `Pointcut::parse("execution(pub fn *(..))")`.
59    ///
60    /// # Example
61    ///
62    /// ```rust
63    /// use aspect_core::pointcut::Pointcut;
64    ///
65    /// let pc = Pointcut::public_functions();
66    /// ```
67    pub fn public_functions() -> Self {
68        use super::pattern::{ExecutionPattern, NamePattern, Visibility};
69        Pointcut::Execution(ExecutionPattern {
70            visibility: Some(Visibility::Public),
71            name: NamePattern::Wildcard,
72            return_type: None,
73        })
74    }
75
76    /// Convenience method to create an execution pointcut for all functions.
77    ///
78    /// Equivalent to `Pointcut::parse("execution(fn *(..))")`.
79    ///
80    /// # Example
81    ///
82    /// ```rust
83    /// use aspect_core::pointcut::Pointcut;
84    ///
85    /// let pc = Pointcut::all_functions();
86    /// ```
87    pub fn all_functions() -> Self {
88        use super::pattern::{ExecutionPattern, NamePattern};
89        Pointcut::Execution(ExecutionPattern {
90            visibility: None,
91            name: NamePattern::Wildcard,
92            return_type: None,
93        })
94    }
95
96    /// Convenience method to create a within pointcut for a module.
97    ///
98    /// # Example
99    ///
100    /// ```rust
101    /// use aspect_core::pointcut::Pointcut;
102    ///
103    /// let pc = Pointcut::within_module("crate::api");
104    /// ```
105    pub fn within_module(module_path: impl Into<String>) -> Self {
106        use super::pattern::ModulePattern;
107        Pointcut::Within(ModulePattern {
108            path: module_path.into(),
109        })
110    }
111}
112
113#[cfg(test)]
114mod tests {
115    use super::*;
116
117    #[test]
118    fn test_pointcut_combinators() {
119        use crate::pointcut::pattern::*;
120
121        let pc1 = Pointcut::Execution(ExecutionPattern {
122            visibility: Some(Visibility::Public),
123            name: NamePattern::Wildcard,
124            return_type: None,
125        });
126
127        let pc2 = Pointcut::Within(ModulePattern {
128            path: "crate::api".to_string(),
129        });
130
131        let and_pc = pc1.clone().and(pc2.clone());
132        assert!(matches!(and_pc, Pointcut::And(_, _)));
133
134        let or_pc = pc1.clone().or(pc2.clone());
135        assert!(matches!(or_pc, Pointcut::Or(_, _)));
136
137        let not_pc = pc1.not();
138        assert!(matches!(not_pc, Pointcut::Not(_)));
139    }
140}