Skip to main content

amethystate_core/primitives/
intercept.rs

1use crate::path::StorePath;
2use std::sync::Arc;
3use std::sync::atomic::{AtomicUsize, Ordering};
4
5pub(crate) const MAX_INTERCEPT_DEPTH: usize = 10;
6
7/// Why a change did not get past the interceptors.
8#[derive(Debug, Clone, PartialEq, Eq)]
9pub enum Refusal {
10    /// One of them turned it down, in these words.
11    Said(String),
12
13    /// They wrote back into what they guard deeper than a write may nest.
14    Recursed,
15}
16
17pub struct InterceptGuard {
18    depth: Arc<AtomicUsize>,
19}
20
21impl InterceptGuard {
22    pub(crate) fn enter(depth: &Arc<AtomicUsize>, path: StorePath) -> Option<Self> {
23        let prev = depth.fetch_add(1, Ordering::Acquire);
24        if prev >= MAX_INTERCEPT_DEPTH {
25            depth.fetch_sub(1, Ordering::Release);
26            tracing::warn!(
27                target: "amethystate::intercept",
28                path = %path,
29                depth = prev + 1,
30                "maximum intercept depth reached, skipping execution"
31            );
32            None
33        } else {
34            Some(Self {
35                depth: depth.clone(),
36            })
37        }
38    }
39}
40
41impl Drop for InterceptGuard {
42    fn drop(&mut self) {
43        self.depth.fetch_sub(1, Ordering::Release);
44    }
45}
46
47pub struct InterceptDisposer {
48    pub id: u64,
49    pub path: StorePath,
50    pub(crate) cleanup: Arc<dyn Fn(u64) + Send + Sync + 'static>,
51}
52
53impl InterceptDisposer {
54    pub fn remove(self) {
55        (self.cleanup)(self.id);
56    }
57}
58
59impl std::fmt::Debug for InterceptDisposer {
60    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
61        f.debug_struct("InterceptDisposer")
62            .field("id", &self.id)
63            .field("path", &self.path)
64            .finish()
65    }
66}