Skip to main content

aspect_core/
lib.rs

1//! # aspect-core
2//!
3//! Core abstractions for aspect-oriented programming in Rust.
4//!
5//! This crate provides the fundamental traits and types for building and using
6//! aspects in Rust. Aspects help modularize cross-cutting concerns like logging,
7//! performance monitoring, caching, security, and more.
8//!
9//! ## Core Concepts
10//!
11//! - **Aspect**: A module that encapsulates a cross-cutting concern
12//! - **JoinPoint**: A point in program execution where an aspect can be applied
13//! - **Advice**: The action taken by an aspect at a joinpoint
14//!
15//! ## Example
16//!
17//! ```rust
18//! use aspect_core::prelude::*;
19//! use std::any::Any;
20//!
21//! // Define an aspect
22//! #[derive(Default)]
23//! struct Logger;
24//!
25//! impl Aspect for Logger {
26//!     fn before(&self, ctx: &JoinPoint) {
27//!         println!("[ENTRY] {}", ctx.function_name);
28//!     }
29//!
30//!     fn after(&self, ctx: &JoinPoint, _result: &dyn Any) {
31//!         println!("[EXIT] {}", ctx.function_name);
32//!     }
33//! }
34//! ```
35//!
36//! ## Advice Types
37//!
38//! - **before**: Runs before the target function
39//! - **after**: Runs after successful execution
40//! - **after_error**: Runs when an error occurs
41//! - **around**: Wraps the entire function execution
42//!
43//! ## Thread Safety
44//!
45//! All aspects must implement `Send + Sync` to be used across thread boundaries.
46
47#![deny(missing_docs)]
48
49pub mod aspect;
50pub mod error;
51pub mod joinpoint;
52pub mod pointcut;
53
54// Re-export core types
55pub use aspect::Aspect;
56pub use error::AspectError;
57pub use joinpoint::{JoinPoint, Location, ProceedingJoinPoint};
58
59/// Prelude module for convenient imports
60pub mod prelude {
61    pub use crate::aspect::Aspect;
62    pub use crate::joinpoint::{JoinPoint, Location, ProceedingJoinPoint};
63    pub use crate::error::AspectError;
64}
65
66#[cfg(test)]
67mod tests {
68    use super::*;
69    use std::any::Any;
70    use std::sync::{Arc, Mutex};
71
72    #[derive(Clone)]
73    struct TestAspect {
74        called: Arc<Mutex<Vec<String>>>,
75    }
76
77    impl Default for TestAspect {
78        fn default() -> Self {
79            Self {
80                called: Arc::new(Mutex::new(Vec::new())),
81            }
82        }
83    }
84
85    impl Aspect for TestAspect {
86        fn before(&self, ctx: &JoinPoint) {
87            self.called
88                .lock()
89                .unwrap()
90                .push(format!("before:{}", ctx.function_name));
91        }
92
93        fn after(&self, ctx: &JoinPoint, _result: &dyn Any) {
94            self.called
95                .lock()
96                .unwrap()
97                .push(format!("after:{}", ctx.function_name));
98        }
99    }
100
101    #[test]
102    fn test_aspect_trait() {
103        let aspect = TestAspect::default();
104        let ctx = JoinPoint {
105            function_name: "test_function",
106            module_path: "test::module",
107            location: Location {
108                file: "test.rs",
109                line: 42,
110            },
111        };
112
113        aspect.before(&ctx);
114        aspect.after(&ctx, &42);
115
116        let calls = aspect.called.lock().unwrap();
117        assert_eq!(calls.len(), 2);
118        assert_eq!(calls[0], "before:test_function");
119        assert_eq!(calls[1], "after:test_function");
120    }
121
122    #[test]
123    fn test_joinpoint_creation() {
124        let jp = JoinPoint {
125            function_name: "my_function",
126            module_path: "my::module",
127            location: Location {
128                file: "src/lib.rs",
129                line: 100,
130            },
131        };
132
133        assert_eq!(jp.function_name, "my_function");
134        assert_eq!(jp.module_path, "my::module");
135        assert_eq!(jp.location.file, "src/lib.rs");
136        assert_eq!(jp.location.line, 100);
137    }
138}