1use std::cell::RefCell;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
4pub enum CheckpointMode {
5 Off,
6 Conservative,
7 Aggressive,
8}
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub struct AdExecutionPolicy {
12 pub checkpoint_mode: CheckpointMode,
13}
14
15impl Default for AdExecutionPolicy {
16 fn default() -> Self {
17 Self {
18 checkpoint_mode: CheckpointMode::Off,
19 }
20 }
21}
22
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub(crate) enum StorageDecision {
25 Retain,
26 Replay,
27}
28
29#[derive(Debug, Clone, Copy, PartialEq, Eq)]
32pub enum CheckpointHint {
33 CheapReplay,
34 ExpensiveReplay,
35 MustRetain,
36}
37
38thread_local! {
39 static POLICY_STACK: RefCell<Vec<AdExecutionPolicy>> =
40 RefCell::new(vec![AdExecutionPolicy::default()]);
41}
42
43struct PolicyScopeGuard;
44
45impl PolicyScopeGuard {
46 fn push(policy: AdExecutionPolicy) -> Self {
47 POLICY_STACK.with(|stack| stack.borrow_mut().push(policy));
48 Self
49 }
50}
51
52impl Drop for PolicyScopeGuard {
53 fn drop(&mut self) {
54 POLICY_STACK.with(|stack| {
55 let popped = stack.borrow_mut().pop();
56 debug_assert!(popped.is_some());
57 });
58 }
59}
60
61pub fn with_ad_policy<R>(policy: AdExecutionPolicy, f: impl FnOnce() -> R) -> R {
62 let _guard = PolicyScopeGuard::push(policy);
63 f()
64}
65
66pub(crate) fn current_ad_policy() -> AdExecutionPolicy {
67 POLICY_STACK.with(|stack| stack.borrow().last().copied().unwrap_or_default())
68}
69
70pub(crate) fn storage_decision(
71 policy: AdExecutionPolicy,
72 checkpoint_hint: CheckpointHint,
73) -> StorageDecision {
74 match (policy.checkpoint_mode, checkpoint_hint) {
75 (_, CheckpointHint::MustRetain) => StorageDecision::Retain,
76 (CheckpointMode::Off, _) => StorageDecision::Retain,
77 (CheckpointMode::Conservative, CheckpointHint::CheapReplay) => StorageDecision::Replay,
78 (CheckpointMode::Conservative, CheckpointHint::ExpensiveReplay) => StorageDecision::Retain,
79 (CheckpointMode::Aggressive, _) => StorageDecision::Replay,
80 }
81}