1use std::sync::Arc;
42use std::sync::atomic::{AtomicU64, Ordering};
43
44use crate::component::Component;
45use crate::RIE;
46
47pub struct CallContext {
51 pub method_name: &'static str,
53 pub args: Vec<ArgValue>,
55}
56
57impl CallContext {
58 pub fn new(method_name: &'static str) -> Self {
60 CallContext {
61 method_name,
62 args: Vec::new(),
63 }
64 }
65 pub fn with_arg(mut self, arg: ArgValue) -> Self {
67 self.args.push(arg);
68 self
69 }
70}
71
72#[derive(Debug, Clone)]
76pub enum ArgValue {
77 I64(i64),
78 Str(String),
79 Bool(bool),
80 Other(String),
81}
82
83impl From<i64> for ArgValue {
84 fn from(v: i64) -> Self { ArgValue::I64(v) }
85}
86impl From<&str> for ArgValue {
87 fn from(v: &str) -> Self { ArgValue::Str(v.to_string()) }
88}
89impl From<String> for ArgValue {
90 fn from(v: String) -> Self { ArgValue::Str(v) }
91}
92impl From<bool> for ArgValue {
93 fn from(v: bool) -> Self { ArgValue::Bool(v) }
94}
95
96#[derive(Debug)]
100pub enum CallResult {
101 Ok,
102 Err(String),
103}
104
105pub trait Interceptor: Send + Sync + 'static {
112 #[allow(unused_variables)]
113 fn before(&self, ctx: &CallContext) -> RIE<()> { Ok(()) }
114 #[allow(unused_variables)]
115 fn after(&self, ctx: &CallContext, result: &mut CallResult) {}
116}
117
118pub struct InterceptorChain {
122 interceptors: Vec<Arc<dyn Interceptor>>,
123}
124
125impl InterceptorChain {
126 pub fn new() -> Self {
127 InterceptorChain { interceptors: Vec::new() }
128 }
129 pub fn push<I: Interceptor>(&mut self, interceptor: I) {
131 self.interceptors.push(Arc::new(interceptor));
132 }
133 pub fn push_arc(&mut self, interceptor: Arc<dyn Interceptor>) {
135 self.interceptors.push(interceptor);
136 }
137 pub fn before_all(&self, ctx: &CallContext) -> RIE<()> {
139 for interceptor in &self.interceptors {
140 interceptor.before(ctx)?;
141 }
142 Ok(())
143 }
144 pub fn after_all(&self, ctx: &CallContext, result: &mut CallResult) {
146 for interceptor in self.interceptors.iter().rev() {
147 interceptor.after(ctx, result);
148 }
149 }
150}
151impl Default for InterceptorChain { fn default() -> Self { Self::new() } }
152
153pub struct LoggingInterceptor;
157
158impl Component for LoggingInterceptor {
159 type Deps = ();
160 fn build(_: Self::Deps) -> Self { LoggingInterceptor }
161 const SCOPE: crate::Scope = crate::Scope::Singleton;
162}
163impl Default for LoggingInterceptor { fn default() -> Self { LoggingInterceptor } }
164impl Interceptor for LoggingInterceptor {
165 fn before(&self, ctx: &CallContext) -> RIE<()> {
166 tracing::info!("→ {} {:?}", ctx.method_name, ctx.args);
167 Ok(())
168 }
169 fn after(&self, ctx: &CallContext, result: &mut CallResult) {
170 match result {
171 CallResult::Ok => tracing::info!("← {} OK", ctx.method_name),
172 CallResult::Err(e) => tracing::warn!("← {} ERR: {}", ctx.method_name, e),
173 }
174 }
175}
176
177pub struct MetricsInterceptor { pub counter: AtomicU64 }
179
180impl Component for MetricsInterceptor {
181 type Deps = ();
182 fn build(_: Self::Deps) -> Self { MetricsInterceptor { counter: AtomicU64::new(0) } }
183 const SCOPE: crate::Scope = crate::Scope::Singleton;
184}
185impl MetricsInterceptor {
186 pub fn new() -> Self { MetricsInterceptor { counter: AtomicU64::new(0) } }
187 pub fn count(&self) -> u64 { self.counter.load(Ordering::Relaxed) }
188}
189impl Default for MetricsInterceptor { fn default() -> Self { Self::new() } }
190impl Interceptor for MetricsInterceptor {
191 fn before(&self, _ctx: &CallContext) -> RIE<()> {
192 self.counter.fetch_add(1, Ordering::Relaxed);
193 Ok(())
194 }
195}
196
197use std::collections::HashMap;
206use std::sync::Mutex;
207use std::sync::OnceLock;
208
209static INTERCEPTOR_CHAINS: OnceLock<Mutex<HashMap<usize, Arc<InterceptorChain>>>> =
210 OnceLock::new();
211
212fn chains_map() -> &'static Mutex<HashMap<usize, Arc<InterceptorChain>>> {
213 INTERCEPTOR_CHAINS.get_or_init(|| Mutex::new(HashMap::new()))
214}
215
216pub fn set_interceptor_chain(key: usize, chain: Arc<InterceptorChain>) {
218 chains_map().lock().unwrap().insert(key, chain);
219}
220
221pub fn get_interceptor_chain(key: usize) -> Option<Arc<InterceptorChain>> {
223 chains_map().lock().unwrap().get(&key).cloned()
224}