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}