1use crate::plugin::SwitchKey;
11use nagisa_types::id::Peer;
12use serde::{Deserialize, Serialize};
13use std::collections::HashMap;
14use std::sync::{Mutex, RwLock};
15
16#[derive(Default)]
17struct Inner {
18 global: HashMap<String, bool>,
20 per_peer: HashMap<(String, Peer), bool>,
22}
23
24#[derive(Clone, Default, Serialize, Deserialize)]
26pub struct EnabledOverrides {
27 pub global: HashMap<String, bool>,
28 pub per_peer: Vec<(String, Peer, bool)>,
29}
30
31type ChangeFn = dyn Fn(&str, Option<Peer>, bool) + Send + Sync;
32
33pub struct EnabledSet {
35 inner: RwLock<Inner>,
36 on_change: Mutex<Option<Box<ChangeFn>>>,
37}
38
39impl EnabledSet {
40 pub fn new() -> Self {
41 Self { inner: RwLock::new(Inner::default()), on_change: Mutex::new(None) }
42 }
43
44 pub fn set(&self, key: impl Into<SwitchKey>, peer: Option<Peer>, enabled: bool) {
50 let name = key.into().resolve();
51 {
52 let mut g = self.inner.write().unwrap_or_else(|e| e.into_inner());
53 match peer {
54 None => {
55 g.global.insert(name.to_string(), enabled);
56 }
57 Some(p) => {
58 g.per_peer.insert((name.to_string(), p), enabled);
59 }
60 }
61 }
62 self.fire(name, peer, enabled);
63 }
64
65 pub fn reset(&self, key: impl Into<SwitchKey>, peer: Option<Peer>) {
67 let name = key.into().resolve();
68 let mut g = self.inner.write().unwrap_or_else(|e| e.into_inner());
69 match peer {
70 None => {
71 g.global.remove(name);
72 }
73 Some(p) => {
74 g.per_peer.remove(&(name.to_string(), p));
75 }
76 }
77 }
78
79 fn key_on(&self, key: &str, default: bool, peer: Option<Peer>) -> bool {
81 let g = self.inner.read().unwrap_or_else(|e| e.into_inner());
82 if let Some(p) = peer {
83 if let Some(v) = g.per_peer.get(&(key.to_string(), p)) {
84 return *v;
85 }
86 }
87 if let Some(v) = g.global.get(key) {
88 return *v;
89 }
90 default
91 }
92
93 #[allow(clippy::too_many_arguments)]
98 pub fn is_enabled_keyed(
99 &self,
100 plugin_key: &str,
101 trigger_key: &str,
102 plugin_default: bool,
103 plugin_can_disable: bool,
104 trigger_default: bool,
105 trigger_can_disable: bool,
106 peer: Option<Peer>,
107 ) -> bool {
108 if !trigger_can_disable {
109 return true;
110 }
111 if plugin_can_disable && !self.key_on(plugin_key, plugin_default, peer) {
112 return false;
113 }
114 self.key_on(trigger_key, trigger_default, peer)
115 }
116
117 pub fn is_enabled(&self, name: &str, default_enable: bool, peer: Option<Peer>) -> bool {
119 self.key_on(name, default_enable, peer)
120 }
121
122 pub fn on_change<F>(&self, f: F)
125 where
126 F: Fn(&str, Option<Peer>, bool) + Send + Sync + 'static,
127 {
128 *self.on_change.lock().unwrap_or_else(|e| e.into_inner()) = Some(Box::new(f));
129 }
130
131 fn fire(&self, key: &str, peer: Option<Peer>, enabled: bool) {
132 if let Some(cb) = self.on_change.lock().unwrap_or_else(|e| e.into_inner()).as_ref() {
133 cb(key, peer, enabled);
134 }
135 }
136
137 pub fn snapshot(&self) -> EnabledOverrides {
139 let g = self.inner.read().unwrap_or_else(|e| e.into_inner());
140 EnabledOverrides {
141 global: g.global.clone(),
142 per_peer: g.per_peer.iter().map(|((k, p), v)| (k.clone(), *p, *v)).collect(),
143 }
144 }
145
146 pub fn restore(&self, ov: EnabledOverrides) {
148 let mut g = self.inner.write().unwrap_or_else(|e| e.into_inner());
149 g.global = ov.global;
150 g.per_peer = ov.per_peer.into_iter().map(|(k, p, v)| ((k, p), v)).collect();
151 }
152}
153
154impl Default for EnabledSet {
155 fn default() -> Self {
156 Self::new()
157 }
158}